← All posts

從 LangGraph 到 Claude Code:Agent 開發分成兩個世界嗎

🌐 Read in English

2026 年中,agent 開發的選擇比過去多很多,但工具之間的差異也大到讓人困惑。先認識場上的主要框架。

LangChain 與 LangGraph: LangChain 是目前最大的 LLM 應用開發生態,最初以 chain 的概念串接 LLM 與外部工具,提供 prompt template、output parser、retriever、memory 等標準元件,搭配上千個第三方整合,讓開發者可以快速組裝 RAG、chatbot 與簡單的 agent。隨著 agent 需求複雜化(條件分支、人工介入、錯誤重試等場景),團隊推出了 LangGraph,以有向圖(directed graph)重新定義控制流。開發者定義 StateGraph,畫出 node 與 edge,每個 node 是一個 Python 函式,conditional edge 處理路由,state schema 以 TypedDict 管理全域狀態。LangGraph 還內建 checkpointing(可在任意節點暫停與恢復)和 human-in-the-loop 支援,部署方面有 LangGraph Platform,搭配 LangSmith 做 tracing 與 eval。課堂與企業多從這裡入門,原因是它把每一個決策點都攤開在程式碼裡。

Google Agent Development Kit(ADK): ADK 是 Google 在 2025 年推出的 agent 開發框架,定位介於 LangGraph 和 Claude Code 之間。同樣 code-first,但抽象層更高:開發者把帶型別註記與 docstring 的函式交給 Agent,tool schema 自動生成,tool-use 迴圈由 Runner 內建處理。ADK 原生支援 multi-agent 階層(parent-child agent 組合)、內建 eval 工具,以及 Event Compaction,自動將較舊互動摘要為精簡 state 以節省 token。語言支援涵蓋 Python、TypeScript、Go、Java、Kotlin,部署路徑是 Vertex AI Agent Engine,跨組織互通透過現由 Linux Foundation 治理的 A2A 協定。整體體感比 LangGraph 高一層:少了自建 loop 的負擔,但仍保有程式碼層的控制力。

Claude Code: Claude Code 是 Anthropic 推出的 CLI agent,也是 harness 模式的代表產品(關於 harness 的概念,我另外寫了一篇)。開發者不寫流程圖,而是以 markdown 檔宣告行為:CLAUDE.md 定義專案層級的行為準則與防護規則,skills/ 放領域知識與可執行腳本,agents/ 定義可被派發的 subagent。模型在這些邊界內自主決定執行順序。內建 tool 涵蓋檔案讀寫(Read、Write、Edit)、shell(Bash)、搜尋(Glob、Grep、WebSearch)等,外部 API 透過 MCP(Model Context Protocol) 接入。

Claude Code 的一個核心機制是 hooks,也就是開發者自訂的 shell 指令,會在特定生命週期事件自動觸發。目前有六個事件點:PreToolUse(tool 執行前攔截,可阻擋或改寫)、PostToolUse(tool 執行後,可做 lint、auto-format)、UserPromptSubmit(使用者送出 prompt 時)、Stop(agent 結束時)、Notification(等待使用者輸入時通知)、SubagentStop(subagent 結束時)。其中 PreToolUse 的設計特別值得注意:exit code 2 會阻擋 tool 並把錯誤訊息回饋給模型,而且這個阻擋即使在 --dangerously-skip-permissions 模式下也無法繞過,等於是開發者能寫進去的硬性保證。2026 年初 Anthropic 又加入了 HTTP hooks(把事件 POST 到遠端 endpoint)和 async hooks(背景執行不阻塞 agent),進一步擴展了可控範圍。

模型方面,Claude Code 設計上以 Anthropic 的 Claude 系列為主,但透過 LiteLLM 或設定 ANTHROPIC_BASE_URL 環境變數,可以接入 GPT、Gemini、DeepSeek、本地模型(Ollama、LM Studio)等任何支援 Anthropic Messages API 格式的服務。要把這套模式嵌入自有服務,對應路徑是 Claude Agent SDK(前身叫 Claude Code SDK,2026 年 3 月更名),本質是同一套 harness 以 library 形式開放,適合嵌入 CI/CD 或自有應用。

OpenClaw: OpenClaw 是同一模式面向個人助理的開源實例,以 AGENTS.md、SOUL.md、MEMORY.md 構成 markdown 工作區,提供內建 tool、session 管理與訊息閘道。形狀與 Claude Code 高度一致,差別在於場景:Claude Code 面向開發工作流,OpenClaw 面向日常的個人助理任務。

我的背景幾乎以前兩者為主。課堂上用 LangGraph 做 multi-agent 架構,實習專案也建在 LangGraph 上,之後在技術活動接觸了 ADK。但打開發者社群大家拿 Claude Code 的 skills 與 subagents 玩出各種變形,OpenClaw 蔚為風潮,彷彿一個 agent 的全部就是幾個 .md 檔。

那些 markdown 檔究竟是什麼?它們跟課堂上的 graph 是同一種東西嗎? 我拿同一個 travel agent 在兩種模式下各實作了一次,從 node 如何呼叫 LLM、tool 如何被執行、subagent 如何被派發的層次來回答。


1. 差異的核心:誰擁有 agent loop

所有 agent 的執行核心都是同一個循環:Plan → Act → Observe,重複至任務完成。課堂與社群的分歧,不在於這個 loop 本身,而在於它由誰實作、由誰決定下一步:

  • Framework(LangGraph、ADK): loop 由開發者的程式碼定義。開發者畫出 node 與 edge,LLM 只是 graph 中的一個節點,路由、狀態轉移與終止條件皆為顯式程式碼。控制流在開發者手上。
  • Harness(Claude Code、OpenClaw): loop 由模型驅動。開發者不定義流程,而是以 markdown 宣告行為:該做什麼、不該做什麼、品質標準為何。模型在這些邊界內自主決定執行順序,控制流在模型手上。

「Agent harness」在 2026 年上半年成為顯學,Addy Osmani 給出的定義最為簡潔:Agent = Model + Harness。模型之外的一切,包括工具、權限、context 管理、sandbox 與錯誤恢復,都屬於 harness。他進一步主張,普通的模型配上出色的 harness,勝過出色的模型配上糟糕的 harness。文中記錄了一個團隊在完全不更動模型的前提下,僅靠調整 harness 便將自家 coding agent 從 Terminal Bench 的 Top 30 推進至 Top 5。

Claude Code 原始碼的分析則揭示了另一個事實:一個 production agent 的絕大部分程式碼,屬於權限控管、context 管理、tool routing 與恢復機制這類 harness 基礎設施,真正的模型決策邏輯僅占極小比例。這正好解釋了前述的落差。使用 LangGraph 時,這些基礎設施由開發者實作;使用 Claude Code 時,它們由 Anthropic 實作並維護,開發者的工作面縮減至行為宣告層。

以下以機制文件最完整的 Claude Code 作為解剖對象,結論對 OpenClaw 同樣成立。

Framework(LangGraph / ADK) 開發者程式碼擁有 loop Node A(LLM 呼叫) Conditional Edge(顯式路由) Node B(tool 呼叫) State schema 顯式定義,流程可預測 Harness(Claude Code / OpenClaw) 模型驅動 loop,行為以宣告界定 CLAUDE.md / Skills(markdown) Agentic Loop(模型決定下一步) 內建 tools + subagents + hooks Context 管理、權限、恢復由 harness 內建
控制流歸屬對比:framework 的 loop 實作於開發者程式碼;harness 的 loop 由模型驅動,行為以 markdown 宣告。

2. 同一個 Travel Agent,兩種實作

抽象比較的說服力有限,以下直接對照同一個需求在兩種模式下的形狀。需求為多角色旅遊規劃 agent:意圖分析 → 搜尋 → 行程規劃 → 辯論式審查 → 輸出防護。LangGraph 版本取自我的課程專案,是實際運行過的系統。

2.1 LangGraph 版:一切都是顯式的

系統由 9 個 node 組成:input guard、intent profile、search、orchestrator、planner、replanner、debate、explain、output guard,以 orchestrator 為中心做 conditional routing,專案含測試約 23,000 行 Python。graph 定義的核心如下:

def build_travel_graph():
    builder = StateGraph(State)

    builder.add_node("input_guard", input_guard_node)
    builder.add_node("intent_profile", intent_profile_node)
    builder.add_node("search", search_node)
    builder.add_node("orchestrator", orchestrator_node)
    builder.add_node("planner", planner_node)
    builder.add_node("replanner", replanner_node)
    builder.add_node("debate", debate_node)
    builder.add_node("output_guard", output_guard_node)

    builder.add_edge(START, "input_guard")
    builder.add_edge("input_guard", "orchestrator")
    # 每個 specialist 完成後回到 orchestrator

    builder.add_conditional_edges(
        "orchestrator",
        orchestrator_routing,   # 開發者實作的路由函式
        {"planner": "planner", "debate": "debate", "END": END, ...},
    )
    return builder.compile()

這段程式碼編譯出來的 graph 長這樣(類似 LangGraph 自身的視覺化輸出):

START input_guard orchestrator intent_profile search planner debate replanner output_guard END conditional routing
Travel agent 的 LangGraph node 結構:orchestrator 以 conditional edge 將任務分派給各 specialist,每個 specialist 完成後回到 orchestrator。

Node 如何呼叫 LLM

在 LangGraph 中,node 就是一個 Python 函式:接收當前 state,做任意運算(通常包含一次以上的 LLM 呼叫),回傳對 state 的更新。LLM 呼叫是顯式的一行 invoke,以我的 planner 為例,推理步驟直接要求結構化輸出:

def planner_node(state: State) -> dict:
    prompt = build_planner_prompt(state)          # 從 state 組出上下文
    response = llm.invoke(
        [SystemMessage(content=prompt)],
        response_format={"type": "json_object"},  # 強制 JSON,方便寫回 state
    )
    itineraries = parse_itineraries(response)
    return {"itineraries": itineraries}           # 回傳值即 state 更新

注意這裡沒有任何魔法:LLM 收到什麼 context、輸出被塞進 state 的哪個欄位,全部由開發者決定。這是 framework 模式的第一層顯式性。

Tool 如何被呼叫:兩種路徑

第二層顯式性在 tool。LangGraph 的 tool 呼叫有兩種路徑,對應「誰決定呼叫」這個問題的兩種答案。

路徑一:LLM 決定(bind_tools 模式)。 把 tool schema 綁定到 LLM 上,模型輸出 tool_calls,由開發者(或 prebuilt 的 ToolNode)執行後把結果以 ToolMessage 回灌,再次呼叫 LLM,直到模型不再要求 tool:

llm_with_tools = llm.bind_tools([search_flights, search_hotels, search_weather])

def research_node(state: State) -> dict:
    messages = state["messages"]
    while True:
        response = llm_with_tools.invoke(messages)
        messages.append(response)
        if not response.tool_calls:              # 模型不再需要 tool,結束
            break
        for call in response.tool_calls:         # 逐一執行模型要求的 tool
            result = TOOLS[call["name"]].invoke(call["args"])
            messages.append(ToolMessage(content=result, tool_call_id=call["id"]))
    return {"messages": messages, "research": response.content}

值得注意的是:這個 while 迴圈,就是 harness 一詞所指的「agent loop」的最小版本。用 LangGraph 時這是開發者親手寫的(或用 create_react_agent 這類 prebuilt 元件生成),迴圈上限、逾時、錯誤重試也都由開發者決定。

路徑二:程式碼決定。 我的專案大多採用這條路:node 內由 Python 直接呼叫 tool 函式,LLM 只負責前後的推理與整理。tool 權限用一個顯式的白名單 dict 管理:

# agent_tools.py:顯式的 per-agent tool 權限
TOOLS_BY_AGENT: Dict[str, Dict[str, Callable]] = {
    "planner_agent": {
        "search_weather":  search_weather,
        "search_flights":  search_flights,
        "search_hotels":   search_hotels,
        "web_search":      web_search,
    },
    "research_agent": { ... },
}

def get_tools_for_agent(agent_name: str) -> Dict[str, Callable]:
    # 未註冊的 agent 預設沒有任何 tool
    return TOOLS_BY_AGENT.get(agent_name, {})

路徑二犧牲了彈性(tool 的使用時機寫死在程式裡),換來完全的可預測性:哪個 agent 能碰哪個 tool、何時呼叫、參數怎麼組,全部可以單元測試。對 guard 類節點與金流類操作,這種確定性是必要的。

顯式性的完整清單

graph 之外,系統還維護一個 30 餘欄位的 state schema,包含 budgetdebate_countreplan_attemptsthreat_blocked 等欄位,每個 node 對 state 的讀寫構成顯式契約。整理起來,LangGraph 模式下開發者親手持有四樣東西:

  • Loop: 自己寫(或用 prebuilt 生成),上限與終止條件是程式碼
  • Tool: 顯式註冊與綁定,權限是白名單 dict
  • Context: 進 LLM 的每一段 prompt 由開發者組裝,state 是唯一的真相來源
  • 路由: orchestrator 的每一條轉移是 conditional edge

優勢是每一項都可測試、可審計:迴圈上限(max_replan_attempts: 2)寫死在 state 中,是不依賴模型配合的硬性保證;CI/CD、red team 測試、OWASP 檢查全部掛載於明確的節點邊界。成本則是每新增一項能力,state schema、node、edge、routing 四處都得動,而且系統寫到後期會浮現一種微妙的感受:orchestrator 的路由邏輯,本質上是在以程式碼重新實作模型原本就能自行完成的決策。

2.2 Claude Code 版:行為宣告,流程交給模型

同一需求在 harness 模式下不存在 graph,實作產物是一組行為宣告:

.claude/
├── CLAUDE.md                    # 專案層級行為準則(≈ system prompt + 防護規則)
├── skills/
│   └── travel-planning/
│       └── SKILL.md             # 規劃流程知識:步驟、輸出格式、品質標準
└── agents/
    ├── researcher.md            # subagent:搜尋航班/住宿,回傳結構化結果
    ├── planner.md               # subagent:產出行程草案
    └── critic.md                # subagent:辯論式審查,評分與修改建議

Loop 在哪裡:harness 內建的 tool-use 循環

Claude Code 的核心機制,是把 2.1 路徑一那個 while 迴圈完全內建。每一輪的流程固定為:

  1. 模型輸出回應,其中可能包含一個或多個 tool_use 區塊(例如「呼叫 WebSearch,參數是 …」)
  2. Harness 攔截該區塊,先過權限層:settings 中的 allow/deny 規則、目前的 permission mode、以及 PreToolUse hook(可用任意程式碼審核甚至改寫這次呼叫)
  3. 通過後執行 tool,將 tool_result 寫回 context
  4. 模型讀到結果,決定下一步:繼續呼叫 tool、修正做法,或結束任務

這個循環由 harness 持有,開發者看不到也不需要寫。與 LangGraph 的對應關係精確而完整:bind_tools 對應內建 tool 清單、開發者手寫的 while 對應 harness loop、TOOLS_BY_AGENT 白名單對應權限層。差別在於表達媒介,一邊是 Python,一邊是設定與 markdown。

Tool 從哪裡來:三層來源

  • 內建 tool: Read、Write、Edit、Bash、Glob、Grep、WebSearch 等,涵蓋檔案系統、shell 與網路,開箱即用
  • MCP servers: 以 Model Context Protocol 掛載外部服務,航班、訂房這類第三方 API 在這一層接入;對模型而言它們與內建 tool 無異
  • Skills 附帶的 scripts: skill 除了知識文件,也可以夾帶可執行腳本,由模型在需要時透過 Bash 呼叫

travel agent 需要的 search_flightssearch_hotels 在 LangGraph 版是開發者寫的 Python 函式加白名單註冊;在這裡則是一個 MCP server 的兩個 endpoint,模型自己決定何時呼叫、如何組參數。

Subagent 如何被派發

Subagent 是理解 harness 模式的關鍵:對模型而言,派發 subagent 本身也是一次 tool call(Claude Code 中的 Task tool)。主 agent 判斷「這件事該交給 critic」,於是發出一個帶著任務描述的 tool call;harness 收到後,以 critic.md 的內容作為 system prompt,啟動一個擁有獨立 context window 的子執行環境,結束後把結果濃縮成單一訊息回傳主 agent。

---
name: critic
description: 審查行程草案,從預算、可行性、偏好符合度評分
tools: Read, WebSearch          # tool 白名單:此 subagent 只能用這兩個
---
你是行程審查員。收到行程草案後:
1. 檢查預算是否超支、交通銜接是否可行
2. 給出 0-100 分,低於 75 分必須附上具體修改指示
3. 最多審查兩輪,第二輪後無論分數皆放行並註明風險

frontmatter 的 tools: 欄位就是這個 subagent 的 tool 白名單,功能與 TOOLS_BY_AGENT dict 完全等價。而 context 隔離取代了 state schema 的角色:LangGraph 用 30 個欄位讓 9 個 node 共享狀態,Claude Code 讓每個 subagent 各自帶開一個乾淨的 context,只把結論帶回來。前者精確但要維護 schema,後者省事但資訊傳遞的顆粒度由模型的摘要品質決定。

保證強度:硬保證與軟約定

關鍵在 critic.md 的最後一行。LangGraph 版以 max_replan_attempts: 2 這個 state 欄位結構性保證的迴圈上限,在這裡是一句自然語言指示。模型多數情況下會遵守,但這份保證是機率性的,而非結構性的。這是兩種模式最根本的差異:framework 寫在程式碼裡的限制是硬保證,harness 寫在 markdown 裡的指示是軟約定。 hooks 機制可以把部分軟約定升級回硬保證(如前述,PreToolUse hook 的 exit code 2 連 skip-permissions 都擋不掉),但在預設狀態下,行為邊界的強度取決於模型的指令遵循能力。

LangGraph:9 nodes + state schema orchestrator input_guard search planner debate replanner output_guard State(30+ 欄位的 TypedDict) ≈23,000 行 Python(含測試)· 流程 = 程式碼 Claude Code:markdown 宣告 CLAUDE.md(行為準則+防護) skills/travel-planning/SKILL.md researcher planner critic Agentic loop + context(harness 內建) 4-5 個 markdown 檔 · 流程 = 模型決策
同一需求的兩種實作形狀:左側流程固定於 graph,右側流程由模型在 harness 邊界內即時決定。

實作量的落差需要正確解讀:markdown 模式並未消除基礎設施,而是將 loop、tool routing、權限與 context 管理轉移給 harness 供應商實作與維護。開發者交出的是控制權,換回的是迭代速度。若要將這套模式嵌入自有的服務,對應路徑是 Claude Agent SDK,Anthropic 官方即將其定位為通用 agent harness。

3. Google ADK:兩者之間的中間路線

ADK 的定位介於 framework 與 harness 之間:code-first 如前者,內建基礎設施的完整度則趨近後者。實際使用後的體感是它比 LangGraph 高一層。tool 的接入方式最能說明這一點,把帶型別註記與 docstring 的 Python 函式直接交給 Agent,tool schema 的生成、tool-use 迴圈的執行、結果的回灌,全部由 Runner 處理:

def search_flights(origin: str, destination: str, date: str) -> dict:
    """Search available flights between two cities on a given date."""
    ...

agent = Agent(
    model="gemini-2.5-pro",
    instruction="你是旅遊規劃助理,先查航班與天氣再產出行程。",
    tools=[search_flights, search_hotels, search_weather],
)

對照前兩節:LangGraph 中這個 loop 是開發者手寫的 while,Claude Code 中它藏在 harness 裡,ADK 中它由 Runner 提供,但開發者仍然在程式碼層,可以攔截事件、掛 callback、組合 multi-agent 階層。定義的是 agent 的組成,而非流程圖;graph 式 routing 為選配能力,而非必經之路。官方 quickstart 支援 Python、TypeScript、Go、Java、Kotlin 五種語言,入門門檻確實比 LangGraph 低不少。

ADK 近期的重點投資也全數落在基礎設施層。Event Compaction 自動將較舊互動摘要為精簡 state,以降低長對話的 token 成本,這正是 harness 模式引以為傲的「context 管理內建」。現由 Linux Foundation 治理的 A2A 協定,搭配 Vertex AI Agent Engine 的託管部署,則瞄準跨組織 multi-agent 互通,也就是課堂上 A2A travel 範例背後真正想解的問題。

三者的定位可以濃縮為一句:LangGraph 提供最大控制,ADK 提供控制加基礎設施,Claude Code 提供基礎設施加速度。

4. 選型準則

選型取決於三個問題:流程是否需要 deterministic、失敗的代價多高、團隊的技術生態偏好。

維度LangGraphGoogle ADKClaude CodeAgent SDKOpenClaw
本質圖形化 agent frameworkCode-first agent framework互動式 CLI agentClaude Code 的 library 版本個人助理 harness
控制流歸屬開發者程式碼開發者程式碼(loop 內建)模型模型(由應用程式驅動)模型
Tool 呼叫bind_tools 或程式碼直呼,迴圈自建函式交給 Agent,Runner 執行迴圈模型發起,harness 攔截與執行同 Claude Code模型發起,harness 執行
Tool 權限白名單 dict(自行實作)程式碼層控制settings + frontmatter + hooks程式碼層 hooks + settingsmarkdown 工作區宣告
迴圈保證結構性(state + edge)結構性軟約定(指示 + hooks 可升級為硬保證)同 Claude Code軟約定
Context 管理自行組裝 prompt 與 stateEvent Compaction 內建Harness 內建,subagent 隔離同 Claude CodeHarness 內建
可測試性逐 node 單元測試內建 eval 工具以端到端為主可嵌入 CI/CD以端到端為主
模型支援任意模型Gemini、Claude(via Vertex)、Ollama、LiteLLM 等Claude 為主,可透過 LiteLLM 接其他模型同 Claude Code多模型
部署自架 / LangGraph PlatformVertex AI Agent Engine本機自架,嵌入自有服務本機 / 自架
上手至第一個 agent小時分鐘小時(需寫程式)分鐘
適用場景合規、金融、高風險流程GCP 生態的 production 服務探索、內部工具、開發自動化嵌入應用或跑在 CI個人助理、日常任務自動化

具體判斷:

  • 選 framework(LangGraph): 流程錯誤會造成真實損失(金流、合規)、需要逐步審計、需要向非 AI 工程師解釋系統行為。企業與課堂從這裡教起,原因正是這類場景對保證強度的要求。
  • 選 ADK: 位於 GCP 生態、需要託管部署但不願自行實作 context 管理與 eval 基礎設施、或需要 A2A 跨組織互通。
  • 選 Claude Code: 任務本質開放(研究、程式開發、資料探索)、流程本來就該由模型判斷、或想在最短時間內驗證一個 agent 構想。需要嵌入自有服務時,改用 Agent SDK。
  • 選 OpenClaw: 需要的是個人助理而非開發工具,日常任務自動化、資訊整理、跨平台訊息管理等場景。
  • 混用為常態: 先以 Claude Code 或 OpenClaw 快速驗證行為,確認後將需要硬保證的部分固化為 LangGraph/ADK 的服務。

結論

Markdown 之外,是一整套始終存在、只是不再由開發者實作的 harness 基礎設施。在 LangGraph 裡親手寫的 tool-use 迴圈、那份 TOOLS_BY_AGENT 白名單、那些組裝 prompt 的程式碼,在 Claude Code 裡以 harness loop、frontmatter 權限與 context 管理的形式繼續運作,只是實作者從開發者變成了 Anthropic。LangGraph 與 ADK 也並非落伍,而是把控制權留在開發者手上,並以程式碼量作為代價。

課堂教的 graph 與社群玩的 markdown,其實是同一個 agent loop 的兩種所有權模型。搞清楚這一點,選型就從陣營之爭還原為工程權衡。拿一個熟悉的題目在兩種模式下各做一次,差異自然浮現。


延伸閱讀

#ai#agents