iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
AI Engineering

AI Agent 系統開發 30 天系列 第 8

讓 LangGraph Agent 使用工具查詢外部資料

  • 分享至 

  • xImage
  •  

在上一篇中,我們實作了由程式決定順序的固定流程:抽取工單後,流程透過固定 Edge 一定會走到建單節點。這種設計非常適合步驟明確、每一步都必須依序執行的任務。

而在另一種常見的場景中,系統需要根據使用者的輸入動態決定下一步。假設我們正在開發一個電商客服助理

  • 當顧客詢問「請問純棉挺版長袖上衣的材質是什麼?」時,模型需要查詢商城的內部商品資料庫(呼叫 get_product_spec 工具)。
  • 當顧客只是說「你好」或「謝謝」時,模型應該直接禮貌回覆,不需要查任何資料。

在這類場景下,我們無法在寫程式時預先寫死哪時候該查資料,必須把「要不要查資料」的決定權交給模型。本篇我們將介紹如何定義 Tool,並透過 LangGraph 的條件邊(Conditional Edge)組裝出具備動態決策能力的 Agent 迴圈(Agent Loop)。

加入 Tool 後的決策流程(Agent Loop)

在固定流程中,步驟是一直線走到底(節點 A 執行完一定接節點 B)。但在加入 Tool 之後,流程需要根據模型的判斷來動態切換:

https://ithelp.ithome.com.tw/upload/images/20260919/201118960730jSkpIu.png

這個流程的核心在於一個簡單的判斷與迴圈:

  1. 模型思考(agent 節點):模型閱讀使用者的問題,判斷自己是能「直接回覆」,還是「需要呼叫工具」來查詢外部資訊。
  2. 條件分流(條件邊):程式檢查模型的判斷——若模型說要查資料,將流程引導至工具節點;若模型已經給出最終回答,流程就此結束(END)。
  3. 執行工具(tools 節點):執行真正的 Python 函式(例如查詢商品資料庫),取得資料。
  4. 帶回模型組織回覆(迴圈):將工具查到的資料送回模型節點,由模型根據最新取得的資訊,組織出完整且口語的回覆。

接下來,我們就一步一步透過程式碼實作這套架構。

第一步:使用 @tool 定義 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: 說明轉為參數說明)。模型在判斷「何時該呼叫工具」以及「參數該填什麼」時,正是依賴這份說明。

第二步:將 Tool 綁定到模型(bind_tools)

定義好工具後,我們使用 model.bind_tools() 將工具清單綁定到模型物件上:

model_with_tools = model.bind_tools([get_product_spec])

bind_tools 會將工具的 JSON Schema 附加在發送給模型的請求中。當呼叫 model_with_tools.invoke(messages) 時,模型就能根據 Docstring 的說明,主動選擇是否在回覆中夾帶 tool_calls

第三步:使用 ToolNode 與條件邊組裝 Graph

現在我們把模型與工具組裝成 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 的核心在於三個元件的分工:

  1. ToolNode(工具執行節點)
    註冊可用的 Python 工具清單。當收到模型發出的 tool_calls 時,自動執行對應的函式,並將結果包裝成 ToolMessage 寫入 State。

  2. tools_condition(條件分流路由)
    檢查模型回傳的 AIMessage 是否包含 tool_calls。若有工具呼叫請求則導向 "tools" 節點;若已產生文字回覆則導向 END 結束流程。

  3. tools → agent 連線(形成決策迴圈)
    將工具查詢結果(ToolMessage)送回模型節點,由模型結合上下文組織成完整的自然語言回覆,完成一輪 Agent Loop。

第四步:執行並觀察完整的訊息軌跡(Trace)

範例程式放在 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 三種尺寸,詳細數據以平量公分標示於尺寸表。
- 出貨時間:下單後約三個工作天內寄出。

訊息流的 4 個階段

檢視執行後的 result["messages"],可以看到整趟決策迴圈中訊息的累積過程:

  1. HumanMessage(使用者輸入):傳入顧客的提問「請問純棉挺版長袖上衣的材質如何?」。
  2. AIMessageagent 節點第 1 次推論):模型判斷需要查詢規格,回傳夾帶 tool_calls=[{"name": "get_product_spec", "args": {"product_name": "純棉挺版長袖上衣"}}] 的訊息,由 tools_condition 引導至工具節點。
  3. ToolMessagetools 節點執行)ToolNode 呼叫 get_product_spec,將回傳的規格資料寫入 State,並將結果送回 agent 節點。
  4. AIMessageagent 節點第 2 次推論):模型根據剛剛取得的規格內容,組織出完整的口語回答;回覆中未包含 tool_callstools_condition 判定回答完畢,將流程引導至 END 結束。

如果顧客只是說「你好」,模型在第 1 次推論時就會直接產生問候文字(無 tool_calls),tools_condition 會立即將流程導向 END,完全不會進入 tools 節點。

最後,小結一下。本篇實作了具備動態決策能力的 Agent 迴圈,核心機制建立在三個元件的分工上:

  1. @tool 與 Docstring 宣告工具規格:不需要手寫 JSON Schema,LangGraph 會將函式的型別標註與文件字串自動轉為工具描述,由模型依據說明判斷呼叫時機與抽取參數。
  2. 用條件邊(tools_condition)動態切換執行路徑:取代程式寫死的固定連線,依據模型回傳的 AIMessage 是否包含 tool_calls,自動決定導向工具節點或直接結束流程(END)。
  3. 透過 ToolNode 與連線形成決策迴圈ToolNode 執行真實 Python 函式並將結果封裝為 ToolMessage 寫入 State,再連線回傳給模型節點,由模型根據查得的真實資料組織最終回答,完成 Agent Loop 的閉環。

當使用者的提問包含了不同意圖時,直接讓模型面對所有工具容易誤判。下一篇將使用 Pydantic 驗證使用者意圖,並透過條件邊將對話分流至專屬的處理路徑。


上一篇
將工作流程拆成多個處理節點
系列文
AI Agent 系統開發 30 天8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言