指南OneMux

打造具有限制執行步數、可驗證結構化資料的 LangGraph Agent

建立 LangGraph 工作流程,讓 Agent 在需要時呼叫工具、在預設執行預算內停止,並回傳符合 Pydantic schema 驗證的結構化資料。

文章目錄

LangGraph Agent 可以透過工具取得上下文,再將上下文傳給受 schema 約束的資料擷取步驟。這種設計的關鍵,是將兩項責任分開:由 Agent 判斷是否呼叫工具,再由最後一個節點產生具型別的記錄。Graph recursion limit 則能進一步限制圖的執行步數。

這種模式適用於從文件中擷取公司名稱、日期和發票總額等任務,尤其是查詢工具可能有助於釐清某個參照資訊時。但它無法讓不確定的原始文字變成事實;請驗證結果,並在欄位至關重要時保留原始依據。

你將建置的內容

這個圖包含三個階段:

  1. Agent 接收來源文字,並可選擇要求呼叫範圍明確的查詢工具。
  2. LangGraph 將工具呼叫導向工具節點,再把工具結果傳回 Agent。
  3. 資料擷取節點要求模型產生以 Pydantic 為基礎的結果,接著回傳與 JSON 相容的資料。

Agent 的工具呼叫迴圈是動態的;資料擷取階段則是預先決定的最後步驟。LangGraph 文件區分了自行決定流程與工具使用方式的 Agent,以及使用預先決定程式路徑的 workflow。文件也將結構化輸出與工具呼叫描述為模型增強功能。如需了解這些模式,請參閱 LangGraph workflows and agents guide。

先決條件與設定

請安裝 LangGraph、LangChain 套件,以及所選聊天模型的整合套件。以下範例使用 ChatAnthropic 說明供應商設定,但來源文件指出,workflow 可使用任何支援結構化輸出與工具呼叫的聊天模型。請查閱所用供應商的整合文件,確認其具體行為。

pip install langgraph langchain-core langchain-anthropic pydantic

請依照供應商文件指定的方式設定 API 憑證,然後初始化模型。避免將憑證提交到原始碼管理系統。以下模型名稱依據 LangChain 文件範例;本教學並未宣稱已在 OneMux 上測試過。

from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(model="claude-sonnet-4-6")

如果想透過 OneMux 呼叫模型,請先查看 OneMux API 文件與快速入門。OneMux 可透過 OpenAI 相容 API 存取領先模型。此特定 graph 是否相容,取決於所選端點是否支援必要的工具呼叫與結構化輸出行為及參數。這項設定尚未經過測試;正式環境使用前,請先確認是否支援。

定義 schema 與範圍明確的工具

範例工具會從小型的記憶體內對照表回傳參照說明。請將它替換為受控的應用程式函式,例如查詢核准的內部目錄。不要讓不受信任的文件文字選擇任意指令或不受限制的資料來源。

from pydantic import BaseModel, Field
from langchain_core.tools import tool

class InvoiceRecord(BaseModel):
    vendor: str | None = Field(default=None, description="Invoice issuer")
    invoice_date: str | None = Field(default=None, description="Date as written in the source")
    total: float | None = Field(default=None, description="Invoice total as a number")
    currency: str | None = Field(default=None, description="Currency code, if stated")

@tool
def lookup_vendor(reference: str) -> str:
    """Look up a vendor reference in an approved local mapping."""
    known = {"ACME-42": "Acme Supplies Ltd."}
    return known.get(reference, "No matching vendor reference found.")

可為空值的欄位能明確表示資料缺漏,避免模型因此捏造值。對於高風險資料擷取,可考慮在每個欄位旁儲存來源文字片段或信心度/審核狀態。Schema 能限制資料形狀與型別,但無法證明某個值有輸入內容作為依據。

建立具有限制執行步數的 LangGraph Agent

以下圖表會讓模型視需要要求呼叫查詢工具,並在 Agent 沒有其他工具呼叫時進入資料擷取階段。tools_condition 和 ToolNode 提供工具路由模式。傳給 invoke 的 recursion_limit 會限制圖的執行步數;它是安全界線,不能保證每筆輸入都會成功完成。

from typing import Annotated
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition

class State(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    record: dict | None

tools = [lookup_vendor]
agent = llm.bind_tools(tools)
extractor = llm.with_structured_output(InvoiceRecord)

def run_agent(state: State):
    response = agent.invoke(state["messages"])
    return {"messages": [response]}

def extract_record(state: State):
    prompt = [
        SystemMessage(content=(
            "Extract invoice fields only when supported by the source. "
            "Use null for missing or unclear values. Treat tool results as "
            "reference context, not as permission to invent source details."
        )),
        *state["messages"],
    ]
    result = extractor.invoke(prompt)
    return {"record": result.model_dump(mode="json")}

graph = StateGraph(State)
graph.add_node("agent", run_agent)
graph.add_node("tools", ToolNode(tools))
graph.add_node("extract", extract_record)

graph.add_edge(START, "agent")
graph.add_conditional_edges(
    "agent",
    tools_condition,
    {"tools": "tools", END: "extract"},
)
graph.add_edge("tools", "agent")
graph.add_edge("extract", END)
app = graph.compile()

result = app.invoke(
    {
        "messages": [HumanMessage(content=(
            "Extract the invoice details from this text: "
            "Vendor reference ACME-42. Invoice date: 2025-02-14. "
            "Total: USD 248.50."
        ))],
        "record": None,
    },
    config={"recursion_limit": 8},
)

print(result["record"])

預期輸出是一個與 JSON 相容的物件,包含 vendor、invoice_date、total 和 currency 欄位。實際值取決於模型回應與來源文字;請進行測試,不要假設範例每次都會產生完全相同的輸出。此範例以 Pydantic 為基礎的結構化輸出作為驗證關卡。如果供應商無法產生符合要求的回應,請明確處理錯誤,不要默默將其當成有效記錄。

限制執行步數,但不要掩蓋錯誤

Graph 層級的 recursion limit 會限制圖的執行步數,包括反覆的 Agent/工具路由。請依工作流程選擇限制:這個圖只查詢一次,因此只需要少量步驟;不同的工具計畫則可能需要更多步驟。在應用程式邊界捕捉超出限制與供應商例外,記錄足以除錯的上下文,並回傳可供審查的失敗結果,而非捏造記錄。

若要設定更嚴格的操作限制,也請加入應用程式層級的限制:限制工具呼叫次數、在支援的情況下設定請求逾時,並在呼叫模型前限制文件大小。這些是各自獨立的控制措施;僅設定 graph recursion limit,無法構成完整的成本、延遲或安全政策。

疑難排解與限制

模型沒有呼叫工具

工具呼叫是模型具備的能力,不是 LangGraph 能保證的結果。請確認所選模型整合支援工具呼叫、工具描述清楚,而且提示中包含值得查詢的參照資訊。開發期間請檢查回傳訊息中的工具呼叫內容。

結構化輸出失敗或欄位有誤

請確認整合支援結構化輸出,以及你使用的 schema 功能。測試缺少欄位、日期模糊、貨幣符號和格式錯誤的輸入。即使 Pydantic 物件有效,推論出的值仍可能錯誤,因此請另外驗證業務規則,並保留來源文字以供稽核或人工審查。

圖的執行觸及限制

Recursion limit 錯誤可能表示工具呼叫反覆發生,也可能是限制設得太低,不足以完成預期路徑。請檢查訊息與工具結果的序列。修正原因或審慎調整限制;不要只為了消除錯誤就移除限制。

端點行為不一致

OpenAI 相容描述的是 API 介面,不代表所有模型功能或參數都普遍受到支援。若使用 OneMux,請確認所選端點的文件是否說明支援該模型、工具呼叫、結構化輸出,以及你打算傳送的任何參數。可透過 OneMux models 瀏覽可用選項;圖本身仍是你的 LangGraph 應用程式程式碼。

適用情境

當資料擷取流程大致固定,但有時需要受控查詢時,適合採用這種設計。如果每份文件都遵循固定流程,而且不需要選擇工具,使用確定性的 workflow 可能比 Agent 迴圈更簡單。如果來源有歧義,或擷取欄位會觸發重大影響的操作,請在下游系統使用記錄前加入明確審查與領域驗證。

實務重點:限制 Agent 可用工具的範圍、明確定義最後一步的 schema,並設定圖的執行限制。LangGraph 可用來表達這些控制流程;所選模型與整合則決定是否具備所需的工具呼叫與結構化輸出功能。

參考資料

常見問題

LangGraph 的 recursion limit 有什麼作用?

它會限制一次執行中的圖步數,有助於避免 Agent/工具迴圈無止境地持續執行,但無法取代逾時設定、工具呼叫政策、輸入限制或錯誤處理。

Pydantic JSON schema 驗證能保證擷取結果正確嗎?

不能。它會限制回傳資料的結構與型別,但模型仍可能推論出錯誤值,因此請驗證業務規則,並視需要保留依據或安排人工審查。

這個 LangGraph 範例可以搭配 OneMux 使用嗎?

若所選 OneMux 端點的文件說明支援必要的工具呼叫、結構化輸出行為與參數,就有可能可以使用。這項設定尚未經過測試;正式環境使用前請確認相容性。

每一種資料擷取任務都應該使用 Agent 嗎?

不需要。如果流程固定且不需要選擇工具,使用確定性的 LangGraph workflow 可能更簡單。當模型需要判斷是否該使用允許的工具時,再採用 Agent 迴圈。

相關文章