手把手教你選型AI Agent框架:從LangGraph到MCP協(xié)議的多Agent協(xié)作落地踩坑指南)
1. 從一次線上事故說(shuō)起AI Agent 框架選型到底在選什么去年底我接手一個(gè)內(nèi)部知識(shí)庫(kù)問(wèn)答項(xiàng)目需求聽起來(lái)很樸素用戶提問(wèn)Agent 自動(dòng)檢索文檔、調(diào)用幾個(gè)內(nèi)部 API、最后生成帶引用的回答。團(tuán)隊(duì)一開始選了某個(gè)主打“多 Agent 協(xié)作”的框架三個(gè) Agent 分別負(fù)責(zé)檢索、推理、總結(jié)Demo 跑得漂漂亮亮。上線第三天出事了一個(gè)用戶問(wèn)了個(gè)跨部門流程問(wèn)題檢索 Agent 返回了 12 條文檔推理 Agent 在上下文里塞了 8000 多 token總結(jié) Agent 又把這 8000 token 全量讀了一遍最后回答里引用了三條根本不存在的制度編號(hào)。排查花了一整天因?yàn)槿齻€(gè) Agent 之間的消息傳遞沒(méi)有統(tǒng)一的狀態(tài)快照日志里只能看到“Agent B 收到了 Agent A 的輸出”具體收到了什么、為什么這么推理全靠猜。這次事故讓我徹底想明白一件事AI Agent 框架選型選的不是“哪個(gè)框架更先進(jìn)”而是“哪個(gè)框架的失敗模式你能接受、能排查、能兜底”。LangGraph、MCP 協(xié)議、多 Agent 協(xié)作這三者經(jīng)常被放在一起比較但它們其實(shí)不在同一個(gè)抽象層級(jí)上——LangGraph 是編排層MCP 是工具接口層多 Agent 是架構(gòu)模式層。把它們混為一談是選型踩坑的根源。這篇文章面向的是已經(jīng)寫過(guò) LLM 調(diào)用、準(zhǔn)備把 Agent 推進(jìn)到真實(shí)項(xiàng)目的工程師。我會(huì)給出一張可復(fù)制的選型對(duì)照表把 MCP 協(xié)議的接入配置片段寫清楚再帶你跑通一個(gè)最小可用的多 Agent 協(xié)作鏈路最后把我在 401、local proxy failed、reading choices 這些報(bào)錯(cuò)上踩過(guò)的坑攤開講。你不需要是框架專家但需要能看懂 Python 和 JSON。先說(shuō)結(jié)論方便你帶著判斷往下讀如果你的任務(wù)步驟可以被提前畫出來(lái)優(yōu)先 LangGraph如果你的工具需要在多個(gè)框架間復(fù)用優(yōu)先 MCP如果你的任務(wù)確實(shí)需要不同專業(yè)角色且上下文隔離收益大于通信成本才考慮多 Agent。三者可以疊加但疊加順序應(yīng)該是“先 MCP 定工具、再 LangGraph 定編排、最后按需拆多 Agent”。2. TaoToken 前置準(zhǔn)備把模型接入這層先做扎實(shí)在聊框架之前得先把模型接入這層做扎實(shí)。很多 Agent 框架的報(bào)錯(cuò)追到根上不是框架的問(wèn)題是 Base URL、Key、Model ID 這三件套沒(méi)對(duì)齊。我現(xiàn)在的習(xí)慣是不管最終用哪個(gè)框架先用一個(gè)統(tǒng)一的接入點(diǎn)把模型調(diào)通再往上搭編排。TaoToken 在這里扮演的角色是統(tǒng)一的模型接入層。它的 API 地址是https://taotoken.net/api兼容 OpenAI 的接口格式所以 LangGraph、AutoGen、CrewAI 這些框架里凡是走 OpenAI 兼容協(xié)議的模型客戶端改一下base_url和api_key就能接上。官網(wǎng)在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注冊(cè)后在控制臺(tái)生成 Key。這里有個(gè)我反復(fù)強(qiáng)調(diào)的工程習(xí)慣把接入配置抽成環(huán)境變量不要硬編碼在代碼里。Agent 項(xiàng)目經(jīng)常要在本地、測(cè)試、生產(chǎn)三套環(huán)境切換硬編碼的 Key 和 URL 是事故高發(fā)區(qū)。我用的.env長(zhǎng)這樣# .env TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的key TAOTOKEN_MODEL_IDclaude-sonnet-4-20250514然后在 Python 里統(tǒng)一讀取import os from dotenv import load_dotenv load_dotenv() BASE_URL os.getenv(TAOTOKEN_BASE_URL) API_KEY os.getenv(TAOTOKEN_API_KEY) MODEL_ID os.getenv(TAOTOKEN_MODEL_ID) assert BASE_URL and API_KEY and MODEL_ID, 接入三件套缺失檢查 .env為什么強(qiáng)調(diào) Model ID 也要抽出來(lái)因?yàn)?Agent 項(xiàng)目里不同節(jié)點(diǎn)可能用不同模型——路由節(jié)點(diǎn)用便宜快的小模型推理節(jié)點(diǎn)用強(qiáng)模型。把 Model ID 做成配置項(xiàng)后面在 LangGraph 的節(jié)點(diǎn)里按需覆蓋就非常自然。如果你用的是 Claude Code 這類工具做輔助開發(fā)它的配置也是同樣的三件套邏輯Base URL 填https://taotoken.net/apiKey 填控制臺(tái)生成的Model ID 按你選的填。配置入口在https://taotoken.net/api-keys文檔在https://taotoken.net/doc。我建議你先把這一步用 curl 驗(yàn)證通過(guò)再進(jìn)框架否則框架報(bào)錯(cuò)時(shí)你分不清是接入問(wèn)題還是編排問(wèn)題。curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: 只回復(fù)兩個(gè)字通了}] }返回里choices[0].message.content是“通了”說(shuō)明接入層沒(méi)問(wèn)題。這一步花五分鐘能省掉后面幾小時(shí)的扯皮。3. 可復(fù)制配置LangGraph 編排 MCP 工具接入片段這一節(jié)是全文最“能直接抄”的部分。我按“先 MCP 定工具、再 LangGraph 定編排”的順序給配置。3.1 MCP 工具服務(wù)配置MCP 的核心價(jià)值是把工具定義標(biāo)準(zhǔn)化。一個(gè) MCP Server 暴露一組工具任何支持 MCP 的客戶端都能調(diào)用。下面是一個(gè)最小 MCP Server 的配置片段用 JSON 描述工具清單這是 MCP 客戶端讀取的配置文件路徑按你的項(xiàng)目放我放在./mcp/config.json{ mcpServers: { internal-kb: { command: python, args: [-m, mcp_server_kb], env: { KB_API_BASE: https://internal.example.com/kb, KB_API_TOKEN: ${KB_API_TOKEN} } }, market-data: { command: python, args: [-m, mcp_server_market], env: { MARKET_API_BASE: https://internal.example.com/market } } } }注意${KB_API_TOKEN}這種寫法是讓 MCP 客戶端從環(huán)境變量注入不要把密鑰寫進(jìn) JSON 提交到倉(cāng)庫(kù)。工具本身的設(shè)計(jì)原則我在后面第五節(jié)會(huì)展開這里先記住每個(gè) MCP Server 只負(fù)責(zé)一類工具接口窄而深。3.2 LangGraph 狀態(tài)與節(jié)點(diǎn)配置LangGraph 的核心是 StateGraph。先定義 State把 Agent 在每一步需要持有的信息都放進(jìn)去from typing import TypedDict, Annotated from langgraph.graph import StateGraph, END from langgraph.checkpoint.memory import MemorySaver import operator class AgentState(TypedDict): messages: Annotated[list, operator.add] retrieved_docs: list tool_calls: list final_answer: str next_step: strAnnotated[list, operator.add]這個(gè)寫法是告訴 LangGraph這個(gè)字段在多節(jié)點(diǎn)寫入時(shí)用“追加”而不是“覆蓋”。消息歷史必須這么處理否則后一個(gè)節(jié)點(diǎn)會(huì)把前一個(gè)節(jié)點(diǎn)的消息沖掉這是新手最常踩的坑之一。然后是節(jié)點(diǎn)和邊的定義def retrieve_node(state: AgentState) - AgentState: query state[messages][-1][content] docs kb_search(query) # 走 MCP 工具 return {retrieved_docs: docs, next_step: reason} def reason_node(state: AgentState) - AgentState: prompt build_prompt(state[messages], state[retrieved_docs]) resp llm_client.chat(prompt, modelMODEL_ID) return {messages: [{role: assistant, content: resp}], next_step: answer} def route(state: AgentState) - str: return state[next_step] graph StateGraph(AgentState) graph.add_node(retrieve, retrieve_node) graph.add_node(reason, reason_node) graph.set_entry_point(retrieve) graph.add_conditional_edges(retrieve, route, {reason: reason, answer: END}) graph.add_edge(reason, END) app graph.compile(checkpointerMemorySaver())checkpointerMemorySaver()是 LangGraph 的殺手锏它給每一步做狀態(tài)快照。生產(chǎn)環(huán)境換成持久化的 checkpointer比如基于 Postgres 的出問(wèn)題時(shí)可以從任意 checkpoint 恢復(fù)而不是從頭重跑燒 token。3.3 三件套在框架里的落點(diǎn)不管用哪個(gè)框架你都要能回答B(yǎng)ase URL 填哪、Key 填哪、Model ID 填哪。在 LangGraph 里這三件套落在你初始化 LLM 客戶端的地方from langchain_openai import ChatOpenAI llm_client ChatOpenAI( base_urlBASE_URL, # https://taotoken.net/api api_keyAPI_KEY, modelMODEL_ID, temperature0 )在 Cline、CC Switch 這類工具里三件套落在設(shè)置面板的對(duì)應(yīng)字段。在 Codex 的auth.json里落在base_url、api_key、model三個(gè)鍵。只要這三件套對(duì)齊90% 的“框架跑不起來(lái)”問(wèn)題會(huì)消失。4. 驗(yàn)證請(qǐng)求跑通最小多 Agent 協(xié)作鏈路配置寫完了得驗(yàn)證。我習(xí)慣分三層驗(yàn)證單工具、單 Agent、多 Agent。逐層往上出問(wèn)題時(shí)能快速定位是哪一層。4.1 單工具驗(yàn)證先確認(rèn) MCP 工具能單獨(dú)調(diào)通。用 MCP 客戶端直接調(diào)internal-kb的檢索工具from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client params StdioServerParameters(commandpython, args[-m, mcp_server_kb]) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools() print([t.name for t in tools]) result await session.call_tool(kb_search, {query: 報(bào)銷流程}) print(result.content[:200])能打印出工具列表和檢索結(jié)果說(shuō)明 MCP 這層通了。4.2 單 Agent 驗(yàn)證再跑 LangGraph 的單 Agent 鏈路config {configurable: {thread_id: test-001}} result app.invoke( {messages: [{role: user, content: 差旅報(bào)銷需要哪些材料}]}, configconfig ) print(result[final_answer]) print(checkpoint:, app.get_state(config).values.keys())預(yù)期結(jié)果是拿到一段帶引用的回答并且get_state能讀出完整狀態(tài)。如果這里報(bào)reading choices之類的錯(cuò)八成是模型返回格式?jīng)]對(duì)上去第五節(jié)看排查。4.3 多 Agent 協(xié)作驗(yàn)證最后驗(yàn)證多 Agent。我用一個(gè) Hub-and-Spoke 結(jié)構(gòu)中心路由 Agent 分發(fā)任務(wù)兩個(gè)專業(yè) Agent 分別處理檢索和推理。關(guān)鍵是把每個(gè) Agent 的上下文隔離只通過(guò)結(jié)構(gòu)化消息傳遞def router_agent(state): intent classify(state[messages][-1][content]) return {next_step: intent} def retrieval_agent(state): docs kb_search(state[messages][-1][content]) # 只回傳摘要不回傳全文控制通信稅 summary summarize(docs, max_tokens500) return {retrieved_docs: [summary]} def analysis_agent(state): answer llm_client.chat(build_prompt(state[retrieved_docs])) return {final_answer: answer}驗(yàn)證時(shí)重點(diǎn)看兩個(gè)指標(biāo)端到端耗時(shí)和總 token 消耗。我實(shí)測(cè)下來(lái)同一個(gè)任務(wù)單 Agent 加 LangGraph 編排耗時(shí)約 630 秒、消耗約 15000 token三個(gè) Agent 協(xié)作耗時(shí)約 890 秒、消耗約 28000 token輸出質(zhì)量幾乎沒(méi)差異。這個(gè)數(shù)據(jù)不是讓你別用多 Agent而是提醒你多 Agent 的通信稅是真實(shí)存在的用之前先算賬。5. 本篇常見(jiàn)錯(cuò)排查401、local proxy failed、reading choices、OAuth這一節(jié)按真實(shí)報(bào)錯(cuò)來(lái)。我把最近三個(gè)月在 Agent 項(xiàng)目里遇到的報(bào)錯(cuò)整理成對(duì)照表每條都給排查路徑。報(bào)錯(cuò)關(guān)鍵詞常見(jiàn)根因排查動(dòng)作401 UnauthorizedKey 沒(méi)注入 / 環(huán)境變量名寫錯(cuò) / Key 過(guò)期打印os.getenv確認(rèn)非空curl 直連驗(yàn)證local proxy failed本地代理配置殘留 / 環(huán)境變量HTTP_PROXY干擾檢查 shell 里的代理變量清掉后重試reading choices返回體不是預(yù)期結(jié)構(gòu) / 模型名寫錯(cuò) / 流式解析錯(cuò)位打印原始 response確認(rèn)choices字段存在OAuth 相關(guān)報(bào)錯(cuò)工具走了 OAuth 流程但回調(diào)地址沒(méi)配檢查工具配置里的回調(diào) URL 和端口占用重點(diǎn)說(shuō)三個(gè)。401 的排查先別懷疑框架。在項(xiàng)目根目錄跑一段最小驗(yàn)證import os, requests r requests.post( f{os.getenv(TAOTOKEN_BASE_URL)}/v1/chat/completions, headers{Authorization: fBearer {os.getenv(TAOTOKEN_API_KEY)}}, json{model: os.getenv(TAOTOKEN_MODEL_ID), messages: [{role: user, content: ping}]} ) print(r.status_code, r.text[:300])如果這里 401問(wèn)題在 Key 或環(huán)境變量如果這里 200 但框架里 401問(wèn)題在框架讀取配置的方式——很多框架有自己的配置優(yōu)先級(jí)會(huì)覆蓋你設(shè)的環(huán)境變量。local proxy failed 的排查這個(gè)報(bào)錯(cuò)通常和本地網(wǎng)絡(luò)環(huán)境有關(guān)。檢查env | grep -i proxy如果有殘留的代理變量在啟動(dòng) Agent 前unset掉。另外有些框架會(huì)讀~/.netrc或系統(tǒng)級(jí)代理設(shè)置也要一并檢查。reading choices 的排查這個(gè)報(bào)錯(cuò)說(shuō)明代碼在解析返回體時(shí)找不到choices字段。三種可能一是模型名寫錯(cuò)服務(wù)端返回了錯(cuò)誤對(duì)象二是流式和非流式解析混用三是返回體被中間層包裝過(guò)。最直接的排查是打印原始返回resp llm_client.invoke(prompt) print(type(resp), resp)看到原始結(jié)構(gòu)問(wèn)題基本就清楚了。OAuth 相關(guān)報(bào)錯(cuò)如果你接的工具走 OAuth報(bào)錯(cuò)多半是回調(diào)地址和實(shí)際監(jiān)聽端口不一致。檢查工具配置里的redirect_uri確認(rèn)端口沒(méi)被占用本地防火墻沒(méi)攔。排查完這些如果還卡著去https://taotoken.net/api-keys重新生成一個(gè) Key 試試排除 Key 本身的問(wèn)題。文檔在https://taotoken.net/doc里面有各框架的接入示例。6. 選型對(duì)照表與下一步把工具層先標(biāo)準(zhǔn)化把前面的內(nèi)容收成一張可復(fù)制的選型對(duì)照表你可以在項(xiàng)目評(píng)審時(shí)直接拿去用維度LangGraphMCP 協(xié)議多 Agent 協(xié)作抽象層級(jí)編排層工具接口層架構(gòu)模式層核心優(yōu)勢(shì)狀態(tài)可控、可快照、可觀測(cè)工具標(biāo)準(zhǔn)化、跨框架復(fù)用上下文隔離、角色專業(yè)化主要成本圖拓?fù)湫杼崆霸O(shè)計(jì)需額外維護(hù) Server通信稅、協(xié)調(diào)復(fù)雜度適用場(chǎng)景步驟可提前畫出的任務(wù)工具需多框架復(fù)用角色差異大且上下文隔離收益高失敗模式圖設(shè)計(jì)不合理導(dǎo)致死循環(huán)Server 崩潰導(dǎo)致工具不可用Agent 間消息丟失難排查我的建議默認(rèn)首選編排層工具層現(xiàn)在就上超過(guò) 3 個(gè) Agent 先重新審視選型的順序我再說(shuō)一遍先 MCP 定工具、再 LangGraph 定編排、最后按需拆多 Agent。這個(gè)順序的好處是每一層都能獨(dú)立驗(yàn)證、獨(dú)立替換。工具層標(biāo)準(zhǔn)化之后你換編排框架的成本幾乎為零編排層穩(wěn)定之后你加 Agent 的風(fēng)險(xiǎn)也可控。如果你還在猶豫從哪開始我的建議是先用 LangGraph 搭一個(gè)最簡(jiǎn)單的單 Agent——一個(gè) LLM 節(jié)點(diǎn)加兩個(gè)工具節(jié)點(diǎn)跑通完整鏈路把狀態(tài)快照和可觀測(cè)性做起來(lái)。然后再考慮是否需要多 Agent。工程世界里“夠用”比“先進(jìn)”有更大的生存概率。模型接入這層用 TaoToken 把三件套對(duì)齊https://taotoken.net/api作為 Base URLKey 在控制臺(tái)生成Model ID 按節(jié)點(diǎn)需要選。想先驗(yàn)證模型對(duì)話效果可以去模型對(duì)話頁(yè)面試幾輪準(zhǔn)備長(zhǎng)期做編碼和 Agent 的可以看 Coding Plan需要生成和管理 Key 的直接進(jìn) API Keys 頁(yè)面。文檔里有各框架的接入片段照著改base_url和api_key就能接上。最后留一個(gè)我踩過(guò)的坑作為收尾Agent 項(xiàng)目里可觀測(cè)性比 prompt 優(yōu)化更優(yōu)先。你不知道 Agent 在做什么就不知道要優(yōu)化什么。LangGraph 的 checkpoint 加上一層 trace能讓你在出問(wèn)題時(shí)從“猜”變成“看”。這一步投入的時(shí)間會(huì)在第一次線上事故時(shí)全部賺回來(lái)。