在上一篇中,我們實作了由程式決定順序的固定流程:抽取工單後,流程透過固定 Edge 一定會走到建單節點。這種設計非常適合步驟明確、每一步都必須依序執行的任務。
而在另一種常見的場景中,系統需要根據使用者的輸入動態決定下一步。假設我們正在開發一個電商客服助理:
get_product_spec 工具)。在這類場景下,我們無法在寫程式時預先寫死哪時候該查資料,必須把「要不要查資料」的決定權交給模型。本篇我們將介紹如何定義 Tool,並透過 LangGraph 的條件邊(Conditional Edge)組裝出具備動態決策能力的 Agent 迴圈(Agent Loop)。
在固定流程中,步驟是一直線走到底(節點 A 執行完一定接節點 B)。但在加入 Tool 之後,流程需要根據模型的判斷來動態切換:

這個流程的核心在於一個簡單的判斷與迴圈:
agent 節點):模型閱讀使用者的問題,判斷自己是能「直接回覆」,還是「需要呼叫工具」來查詢外部資訊。END)。tools 節點):執行真正的 Python 函式(例如查詢商品資料庫),取得資料。接下來,我們就一步一步透過程式碼實作這套架構。
在 LangGraph 中,任何可被模型呼叫的 Python 函式都可以使用 @tool 裝飾器定義。我們模擬一個工具回傳商品規格資料,依據傳入的商品名稱查詢資訊:
from langchain_core.tools import tool
@tool
def get_product_spec(product_name: str) -> str:
"""查詢指定商品的材質、版型、尺寸與出貨規格。
顧客說出具體商品名稱,並詢問材質、版型、尺寸或出貨時間時使用;
問題不涉及特定商品,或只需要一般服飾知識時不要呼叫。
Args:
product_name: 顧客說出的完整商品名稱,例如「純棉挺版長袖上衣」。
"""
product_specs = {
"純棉挺版長袖上衣": (
"材質:100% 純棉,觸感挺實不易變形。\n"
"版型:寬鬆剪裁設計。\n"
"尺寸:S / M / L 三種,尺寸表以平量公分標示。\n"
"出貨:下單後三個工作天內寄出。"
),
}
return product_specs.get(product_name.strip(), f"查無商品名稱:{product_name}")
函式內部緊接在 def 下方、用三引號 """ ... """ 包夾的文字是 Python 的 Docstring(文件字串)。與一般用 # 開頭的註解不同,Python 在執行時會保留這段文字。
LangGraph 會讀取這段 Docstring 與型別標註,自動轉換成提供給模型的 JSON Schema 規格說明(將函式描述轉為 Tool description、將 Args: 說明轉為參數說明)。模型在判斷「何時該呼叫工具」以及「參數該填什麼」時,正是依賴這份說明。
定義好工具後,我們使用 model.bind_tools() 將工具清單綁定到模型物件上:
model_with_tools = model.bind_tools([get_product_spec])
bind_tools 會將工具的 JSON Schema 附加在發送給模型的請求中。當呼叫 model_with_tools.invoke(messages) 時,模型就能根據 Docstring 的說明,主動選擇是否在回覆中夾帶 tool_calls。
現在我們把模型與工具組裝成 StateGraph:
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
def build_graph(model):
# 1. 將工具綁定到模型
model_with_tools = model.bind_tools([get_product_spec])
# 2. 定義模型節點函式
def call_model(state: MessagesState):
response = model_with_tools.invoke(state["messages"])
return {"messages": [response]}
builder = StateGraph(MessagesState)
# 3. 註冊節點
builder.add_node("agent", call_model)
builder.add_node("tools", ToolNode([get_product_spec]))
# 4. 連接邊與條件分流
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")
return builder.compile()
組裝這張 Graph 的核心在於三個元件的分工:
ToolNode(工具執行節點)
註冊可用的 Python 工具清單。當收到模型發出的 tool_calls 時,自動執行對應的函式,並將結果包裝成 ToolMessage 寫入 State。
tools_condition(條件分流路由)
檢查模型回傳的 AIMessage 是否包含 tool_calls。若有工具呼叫請求則導向 "tools" 節點;若已產生文字回覆則導向 END 結束流程。
tools → agent 連線(形成決策迴圈)
將工具查詢結果(ToolMessage)送回模型節點,由模型結合上下文組織成完整的自然語言回覆,完成一輪 Agent Loop。
範例程式放在 langgraph/langgraph-first-tool。執行後可以在終端機看見整段 Agent Loop 的訊息推進軌跡:
uv run langgraph-first-tool
結果:
模型要求:get_product_spec({'product_name': '純棉挺版長袖上衣'})
Tool 回傳:
材質:100% 純棉,觸感挺實不易變形。
版型:寬鬆剪裁設計。
尺寸:S / M / L 三種,尺寸表以平量公分標示。
出貨:下單後三個工作天內寄出。
Agent 回覆:
純棉挺版長袖上衣的規格如下:
- 材質:採用 100% 純棉,觸感挺實且不易變形。
- 版型:為寬鬆剪裁設計,穿著舒適。
- 尺寸:提供 S、M、L 三種尺寸,詳細數據以平量公分標示於尺寸表。
- 出貨時間:下單後約三個工作天內寄出。
檢視執行後的 result["messages"],可以看到整趟決策迴圈中訊息的累積過程:
HumanMessage(使用者輸入):傳入顧客的提問「請問純棉挺版長袖上衣的材質如何?」。AIMessage(agent 節點第 1 次推論):模型判斷需要查詢規格,回傳夾帶 tool_calls=[{"name": "get_product_spec", "args": {"product_name": "純棉挺版長袖上衣"}}] 的訊息,由 tools_condition 引導至工具節點。ToolMessage(tools 節點執行):ToolNode 呼叫 get_product_spec,將回傳的規格資料寫入 State,並將結果送回 agent 節點。AIMessage(agent 節點第 2 次推論):模型根據剛剛取得的規格內容,組織出完整的口語回答;回覆中未包含 tool_calls,tools_condition 判定回答完畢,將流程引導至 END 結束。如果顧客只是說「你好」,模型在第 1 次推論時就會直接產生問候文字(無 tool_calls),tools_condition 會立即將流程導向 END,完全不會進入 tools 節點。
最後,小結一下。本篇實作了具備動態決策能力的 Agent 迴圈,核心機制建立在三個元件的分工上:
@tool 與 Docstring 宣告工具規格:不需要手寫 JSON Schema,LangGraph 會將函式的型別標註與文件字串自動轉為工具描述,由模型依據說明判斷呼叫時機與抽取參數。tools_condition)動態切換執行路徑:取代程式寫死的固定連線,依據模型回傳的 AIMessage 是否包含 tool_calls,自動決定導向工具節點或直接結束流程(END)。ToolNode 與連線形成決策迴圈:ToolNode 執行真實 Python 函式並將結果封裝為 ToolMessage 寫入 State,再連線回傳給模型節點,由模型根據查得的真實資料組織最終回答,完成 Agent Loop 的閉環。當使用者的提問包含了不同意圖時,直接讓模型面對所有工具容易誤判。下一篇將使用 Pydantic 驗證使用者意圖,並透過條件邊將對話分流至專屬的處理路徑。