以下觀點是個人在學習中的思考,如果有不對的地方歡迎指正。
我會盡量從「設計框架」的視角來講:每一層解決什麼問題。

從 LangChain 到 LangGraph

0. “智能體”的最終形態

  • 單個智能體的理想形態:像人一樣,能在環境裏自動獲取信息;主動規劃怎麼解決任務;會用工具做事;做完能反思要不要調整;需要調整就記住,方便下次更好地做。
  • 所以一個智能體框架最重要的三層:大模型層工具層記憶層

1. 大模型層:我輸入,它輸出(但要先把“怎麼問”整理好)

1.1 大模型按功能的三種常見形態

  • Text LLM(文本補全):更像“續寫器”,通常輸入是一段純文本。
  • 你:請幫我翻譯 HELLO
  • 模型:你好
  • Chat Model(對話模型):通常用 messages 來組織角色、歷史消息(本質仍是 LLM,只是接口/訓練方式更偏對話)。
  • 你:你好嗎
  • 輸入會被組織成:System / Human / AI(歷史)…
  • 模型:我很好
  • Embedding Model(嵌入模型):把文本變成浮點數向量,用來做檢索/相似度。
  • 你:你好
  • 模型:[0.1121, 0.4552](這裏只是舉例)

簡單理解就是:大模型像一個函數,你給它輸入,它給你輸出;不同類型主要是「輸入輸出的形態」和「適用場景」不一樣。日常做 agent 絕大部分時候用的是對話模型。

1.2 大模型參數:告訴模型“怎麼想”,以及“發給誰”

  • “發給誰”(連接/路由類參數):base_url / api_key / model
  • “怎麼想”(採樣/長度類參數):temperature / max_tokens / top_p(以及一些模型還會有其它控制項)。

如果説我們的提示詞是在説“思考什麼”,那這些參數就在説“如何去思考”。

1.3 框架在大模型層要封裝什麼

至少要做兩件事:輸入處理 + 請求發送(以及對應的輸出接收)。

典型流程:
用户代碼層(設置模型+參數) -> LangChain SDK(存儲參數/組裝請求) -> HTTP 客户端(發請求) -> 模型服務(返回結果)

用户層代碼可能是:

response = llm.invoke("你好")

chain = llm | StrOutputParser()
response = chain.invoke("你好,請介紹一下人工智能")

invoke() 大概做了這些事(偽代碼):

1) 把用户輸入轉換成模型需要的格式(尤其是 messages)
2) 構建 HTTP 請求體(參數 + 輸入)
3) 構建 HTTP 請求頭(認證等)
4) POST 發出去,收結果

1.4 LangChain 在大模型層的設計:Model I/O

LangChain 的 Model I/O 大致就是:輸入怎麼組織、輸出怎麼結構化。

輸入處理

  • 消息(messages):SystemMessage / HumanMessage / AIMessage / ToolMessage…(用來表達角色、歷史、工具回傳等)。
  • 提示詞模板(prompt templates):當你不想每次手寫“系統消息+歷史+用户輸入”時,就需要一個可複用、可注入變量的模板。
  • PromptTemplate:普通文本模板,變量注入。
  • ChatPromptTemplate:生成 messages 列表的模板。
  • XxxMessagePromptTemplate:更細粒度的 message 模板(比如 System/Human 這類)。
  • FewShotPromptTemplate:放少量樣例做示範(few-shot)。

輸出處理

  • 模型很多時候只回字符串,但我們常常要結構化結果,所以需要 OutputParser(以及更嚴格的結構化輸出方式)。
  • 常見做法是兩步:
  1. 在提示詞裏先“約定輸出格式”(比如 JSON 結構)。
  2. 拿到輸出後做解析/校驗,不符合就再修一下(或者讓模型重試)。

2. 記憶層

  • 模型本身不會“記得”任何事情。短期記憶可以每次請求都附帶歷史消息,但歷史越多,token 成本越高,也會拖慢響應。

2.1 LangChain 的一些經典短期記憶形態

  • ChatMessageHistory:直接操作消息列表;重啓就沒了(不持久化)。
  • ConversationBufferMemory:全量對話都餵給模型,簡單但很燒 token。
  • ConversationBufferWindowMemory:只保留最近 k 輪,對早期上下文會丟。
  • ConversationSummaryMemory:把歷史總結成摘要,壓縮 token。
  • ConversationSummaryBufferMemory:混合:最近窗口 + 更早摘要。

(補一句:LangChain 新版本在“記憶”這塊也有新寫法/新推薦用法,但上面這些作為理解“記憶譜系”依然挺直觀。)

2.2 長期記憶:RAG

短期記憶解決“剛剛聊過什麼”,長期記憶更多解決“很久以前的知識/資料怎麼召回”。最常見的技術就是 RAG


3. 工具層:讓模型能“動手”,而不是隻會説

3.1 Tool:工具從定義到執行

工具層是模型和外部世界交互的核心組件。我對 LangChain 的工具體系理解是:

  • 工具定義:用 @tool(或類似機制)定義工具的 name/args/description/return
  • 工具綁定:把可用工具列表交給 agent/模型,讓它知道能用哪些工具。
  • 工具調用:模型決定“要用哪個工具+參數是什麼”(很多時候依賴 function calling/結構化調用)。
  • 工具執行:框架真的去調用工具,把結果回傳給模型/狀態。

3.2 MCP

如果説我們自己寫的 tool 更像“我自己長出來的手腳”,那 MCP(Model Context Protocol,由 Anthropic 提出)更像“外接各種現成工具/資源的統一接口”。重點是:工具不一定是你寫的,而是外部按協議提供的;你按協議去連、去用(常見是對接 MCP server 暴露的 tools/resources 等)。

3.3 RAG 也是一種“工具化能力”(RAG暫時不展開説明)

RAG(檢索增強生成)經常被當作工具層的一部分能力:讓模型在生成前先去“查”。

完整流程是:

  1. 文件處理:解析(各種格式提取文字)+ 切分(chunk)。
  2. 入庫:向量化(embedding)+ 寫入向量庫。
  3. 用户提問:檢索 -> 把相關片段塞進上下文 -> 生成回答。

4. 關於智能體的構建:Chain / LCEL 把步驟串起來

  • Chain(鏈)這套思路很直觀:把一步一步的操作組合起來,每一步都明確輸入輸出是什麼。
  • 這其實就是一種“提示鏈模式”:複雜任務拆解成多個小步,然後串起來跑。

LangChain 裏常見的表達是 LCEL(LangChain Expression Language),類似這樣:

# 1. 導入所需組件
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_openai import ChatOpenAI

# 2. 初始化模型
chat_model = ChatOpenAI(
    model="gpt-4",
    temperature=0.7,
    api_key="your-api-key-here",
)

# 3. 創建 JSON 輸出解析器
json_parser = JsonOutputParser()

# 4. 創建提示詞模板
prompt_template = PromptTemplate(
    template="""請用簡單的一句話描述一下什麼是{name}?

請嚴格按照以下格式輸出:
{format_instructions}

描述:""",
    input_variables=["name"],
    partial_variables={"format_instructions": json_parser.get_format_instructions()},
)

# 5. 使用 LCEL 構建鏈
chain = prompt_template | chat_model | json_parser

# 6. 調用鏈
response = chain.invoke({"name": "反洗錢"})
print("解析後的結構化數據:", response)

5. LangGraph 解決的核心:把“黑盒 while”變成“可視的狀態機”

5.1 先説痛點:用 LangChain 也能寫循環,但很容易變黑盒

(下面是偽代碼,表達的是“痛點”,不是説 LangChain 只能這樣寫)

chat_history = []  # 必須手動維護的消息池
max_retries = 3
current_step = 0

while current_step < max_retries:
    try:
        response = agent_chain.invoke({"input": "查詢天氣", "history": chat_history})

        if response.is_error:
            chat_history.append(ErrorMessage(content=response.error))
            current_step += 1
            continue
        else:
            save_to_db(chat_history)
            break
    except Exception:
        print("系統崩潰,進度全丟")

這裏能總結出幾個問題:

  1. 狀態要你手動維護:每個 chain 都是相對獨立的,你得自己在循環裏喂 history、接結果、再塞回去。
  2. 宕機恢復麻煩:狀態在進程內存裏,一崩就斷點丟;要“可恢復”你得自己做持久化和恢復邏輯。
  3. 可觀測性弱:while 循環是黑盒,什麼時候重試、卡在哪裏、為什麼跳轉,很難一眼看出來。

5.2 LangGraph 的做法:全局狀態 + 節點更新 + 檢查點(快照)

我對 LangGraph 的理解是:

  • 你定義一個全局 State(通常就是字典/TypedDict),節點從 state 取需要的東西,執行完再把“更新”寫回 state。
  • 引擎可以在每個節點之後做 checkpoint(快照),所以崩了也能從最後一次快照恢復。
  • 流程是“圖”而不是“線性鏈+while”,所以路由/重試/並行這些邏輯更顯式、更可視化。

同樣用偽代碼表達一下(示意用法):

class State(TypedDict):
    messages: Annotated[list, add_messages]
    retry_count: int

def call_agent(state: State):
    return {"messages": [llm.invoke(state["messages"])]}

def call_tool(state: State):
    return {
        "messages": [tool.invoke(...)],
        "retry_count": state["retry_count"] + 1,
    }

workflow = StateGraph(State)
workflow.add_node("agent", call_agent)
workflow.add_node("tool", call_tool)

workflow.add_conditional_edges(
    "tool",
    should_retry,
    {"retry": "agent", "end": END},
)

app = workflow.compile(checkpointer=MemorySaver())

6. LangGraph 的基礎構件(詞彙表)

  • State:全局共享的數據結構(也是每一步的“快照內容”);決定了信息怎麼被保存、怎麼合併。
  • Node:節點,一個步驟/一個函數;理想情況下儘量小而清晰(不一定要求絕對無狀態,但“單一職責”會更好維護)。
  • Edge:邊,決定節點之間怎麼連、按什麼順序執行;包括普通邊和條件邊(conditional)。
  • Command / Send:可以理解成“更強的流程控制能力”,比如顯式跳轉、並行派發子任務等(具體能力和寫法要以版本為準)。

6.1 基本搭建流程

  1. 定義 State:決定全局狀態類型,以及狀態更新/合併規則(比如新值覆蓋舊值,還是 append 到列表尾)。
  2. 定義節點:
  • 節點函數:接收 state,返回“要更新的字段”。
  • 條件函數:讀取 state,返回下一步該走哪個節點。
  1. 組裝圖:
  • StateGraph(...)
  • add_node(...)
  • 設入口/出口
  • 連線(普通邊/條件邊)
  1. 編譯與運行:
  • compile(...)
  • 執行工作流

7. 依舊關於 LangGraph 的“優化點”:並行、路由、反思、規劃

Google 的《智能體設計模式》裏提到很多模式(據説有 21 種),這裏先收斂成一條“演化路線”:

  1. 提示鏈模式:把複雜任務拆分。
  2. 路由模式:根據輸入/狀態選擇子模塊。
  3. 並行模式:任務更復雜時,性能優化會變成剛需。
  4. 反思模式:做完能自評、能迭代。
  5. 工具模式:突破訓練數據的侷限,讓模型能動手。
  6. 規劃模式:讓模型主動把任務拆成子任務。

7.1 路由這塊:我對 LangChain vs LangGraph 的對比

先回顧一下 LangChain 路由(偏“固定流程”):

  1. 意圖識別:用 LLM 對輸入做分類(比如“怎麼解方程” -> 數學)。
  2. 動態映射:配置一堆類似 if-else 的映射,把標籤映射到提示詞模板/子鏈。
  3. 執行:按映射結果跑下去。

問題是:邏輯路線本質上是事先固定的;而且很多時候每一步只是“產出數據”,很難做更復雜的流程控制(比如“發現風險就跳到某節點”,或者“拆成 5 個子任務並行”)。

LangGraph 的路由更像“狀態驅動 + 節點自治”:

  • 節點可以在更新 state 的同時,給出“下一步該去哪裏”的指令(比如跳轉到某節點)( Command)。
  • 當 Agent 拆解出多個子任務時,可以把這些任務並行派發出去(Send)。並行分支一般會各自跑在隔離的狀態副本上,跑完之後再按 state 的合併規則把結果彙總回主路徑。

所以結論還是:智能體的理想形態就是“像人一樣”的那套流程(獲取信息 -> 規劃 -> 用工具 -> 反思 -> 記憶)。

LangGraph 主要把這幾個點“工程化”了:路由更顯式並行更自然反思/重試更可控長流程更可恢復(checkpoint)
尤其是:當你不得不循環時,循環不能是黑盒;要可視化、可控、可恢復,最好還能把狀態更新自動化。


8. 多智能體協作:從中心化到去中心化(為什麼)

隨着任務複雜度上漲,單智能體會碰天花板,多智能體的形態常見有:

  • 簡單線性:AgentA -> AgentB -> end
  • 路由分發:用户輸入 -> 路由判斷 -> 特定 Agent
  • 中心化編排:主管 Agent 動態分配任務
  • 去中心化/網狀協作:Agent 通過狀態/指令自主觸發協作

“從中心化到去中心化”的核心原因:

  • 主管要吃掉所有反饋,提示詞變得臃腫,幻覺風險會上升。
  • 通信鏈路變長:A 和 B 通信還得繞主管,延遲也會增加。