Stories

Detail Return Return

打造自己的 Claude Code:LangGraph + MCP 搭建一個極簡的 AI 編碼助手 - Stories Detail

實踐是最好的學習方式。為了深入理解 LangGraph 和模型上下文協議(MCP)服務器的生態,我們來從零開始構建一個 CLI 編碼代理。我們的目標是,拋開 Claude Code 那些花裏胡哨的功能,看看最基礎的編碼代理能做到什麼程度。

那些商業編碼代理往往會添加各種專有的"秘密配方"——特殊的上下文管理、精心設計的提示策略、優化過的工具選擇算法。這些技術細節被包裝得嚴嚴實實,很難搞清楚哪些是必需的,但對於學習來説,那些只是錦上添花。我們這篇文章的目標是驗證一個問題:用最簡單的方式讓 LLM 在無限循環裏不斷調用工具,這樣的"裸機"代理到底行不行?那些複雜的技術棧是真的必要嗎,還是過度設計了?

核心功能設計

這個代理的設計理念就是極簡但實用。整個系統由一個交互式狀態圖驅動,信息流向很清晰:用户輸入 → 模型響應 → 工具調用 → 回到用户,形成持續的對話循環。本地功能實現了文件讀取器和 pytest 的封裝用於單元測試。MCP 集成覆蓋了幾個關鍵場景:Desktop Commander 負責文件系統操作,Pydantic AI 的沙箱 Python MCP 跑在 Deno Docker 容器裏,DuckDuckGo 提供網絡搜索,還有官方的 GitHub MCP。為了提升可觀測性,還加入了豐富的終端界面和 Mermaid 工作流可視化,可以清楚地看到代理的思考和執行過程。

下面是幾個使用示例:

通過 Desktop Commander MCP 的 read_file 工具讀取 TypeScript 文件並展示內容

利用 DuckDuckGo 的 MCP 執行網絡搜索

快速上手

環境準備很簡單,只需要對 Docker 和命令行有基本瞭解就夠了。啓動主代理的命令:

 uv run main.py

如果要用沙箱 Python 執行功能,需要先構建 Deno MCP 的 Docker 鏡像:

 docker build -t deno-docker:latest -f ./mcps/deno/Dockerfile .

啓動後可以試試這些指令來體驗功能:

  • "Show me the content of main.py"
  • "What tools do you have?"
  • "Read requirements.txt"

狀態持久化與調試

集成了

langgraph-checkpoint-sqlite

來跟蹤對話歷史,方便調試。這個基於 SQLite 的檢查點機制很適合本地實驗場景。可以直接從終端查看代理狀態:

 sqlite3 checkpoints.db "SELECT * from writes LIMIT 2"
 sqlite3 checkpoints.db "SELECT * from checkpoints LIMIT 2"

Pytest 集成

傳統做法是寫完代碼手動跑測試,而現在代理會自動執行這個流程。體驗提升非常明顯:只需要説"X 功能有問題",代理就會跑測試套件,定位失敗的測試用例,分析錯誤信息,然後給出針對性的修復方案。這徹底省掉了盯着測試日誌發呆或者把終端輸出複製到 ChatGPT 的繁瑣操作,代理拿到的上下文信息足夠精確,診斷效率大幅提高。

代碼實現

這套代碼基於 LangChain 框架,使用 Claude 3 Sonnet 作為核心模型來處理代碼庫的維護和開發任務。整個系統採用 StateGraph 工作流,包含三個關鍵節點:user_input、model_response 和 tool_use。

流程在節點間流轉,模型響應決定下一步是調用工具還是回到用户輸入環節。AgentState 負責維護狀態,跟蹤完整的消息歷史。

核心工作流的實現代碼:

 # Key workflow setup from the Agent class
def __init__(self):
    self.workflow = StateGraph(AgentState)
    
    # Register the three main nodes
    self.workflow.add_node("user_input", self.user_input)
    self.workflow.add_node("model_response", self.model_response)
    self.workflow.add_node("tool_use", self.tool_use)
    
    # Define the flow
    self.workflow.set_entry_point("user_input")
    self.workflow.add_edge("user_input", "model_response")
    self.workflow.add_edge("tool_use", "model_response")
    
    # Conditional routing based on tool usage
    self.workflow.add_conditional_edges(
        "model_response",
        self.check_tool_use,
        {
            "tool_use": "tool_use",
            "user_input": "user_input",
        },
     )

代理同時加載本地工具(比如單元測試)和運行在 Docker 容器裏的 MCP 工具。工具設計上返回結構化的 ToolMessages,讓 StateGraph 能夠正確路由響應回模型。這些工具支持運行 Python 代碼、搜索 DuckDuckGo、與 GitHub 交互等操作。狀態在交互間通過 SQLite 檢查點機制保持。

LangGraph 的狀態架構解析

StateGraph 工作流機制值得單獨拿出來細説,因為它是整個代理模式的基礎支撐。LangGraph 的 StateGraph 實現了帶持久化狀態管理的有向圖工作流。架構分解如下:

狀態管理

 class AgentState(BaseModel):
     messages: Annotated[Sequence[BaseMessage], add_messages]

AgentState 類基於 Pydantic 的 BaseModel,維護完整對話歷史,在圖遍歷過程中追蹤所有消息類型(系統消息、用户消息、助手消息和工具消息)。

圖結構設計

工作流由三個節點構成有向圖:

  1. 用户輸入節點:入口點,收集用户輸入
  2. 模型響應節點:用 Claude 處理輸入並決策下一步行動
  3. 工具使用節點:響應模型請求執行具體工具

圖的配置代碼:

 # Core graph structure
workflow = StateGraph(AgentState)

# Node registration
workflow.add_node("user_input", self.user_input)
workflow.add_node("model_response", self.model_response)
workflow.add_node("tool_use", self.tool_use)

# Edge connections
workflow.set_entry_point("user_input")
workflow.add_edge("user_input", "model_response")
 workflow.add_edge("tool_use", "model_response")

流程控制邏輯

圖通過 check_tool_use 實現條件路由:

  1. user_input → model_response(固定路徑)
  2. model_response → tool_use(存在工具調用時)
  3. model_response → user_input(無工具調用時)
  4. tool_use → model_response(固定路徑)

持久化機制

通過 AsyncSqliteSaver 使用 SQLite 做檢查點:

 db_path = os.path.join(os.getcwd(), "checkpoints.db")
 self._checkpointer_ctx = AsyncSqliteSaver.from_conn_string(db_path)
 self.checkpointer = await self._checkpointer_ctx.__aenter__()
 self.agent = self.workflow.compile(checkpointer=self.checkpointer)

這套機制讓對話狀態能跨會話保持遇到中斷也能恢復。整個工作流構成一個持續循環,代理可以處理用户輸入、生成響應、按需調用工具,在整個交互過程中保持上下文連貫性。

MCP 服務器的模塊化實踐

MCP 的配置用到了 LangChain 的 mcp-adapters 庫,調用

get_tools

方法。某些工具需要在系統提示詞裏補充描述信息,確保檢索準確性。把 MCP 服務器打包成容器解決了環境衝突和依賴管理,用户直接跑容器就行,不用折騰安裝配置。

Desktop Commander MCP 提供的能力和 Claude Code 基本重合:完整的文件系統操作、代碼編輯、審計日誌。通過 Docker bind mount 配置把訪問範圍限制在測試文件夾,保持嚴格隔離。這種方式讓實驗變得安全,不用擔心搞壞實際文件系統。

Pydantic AI 的 run-python MCP 基於 Deno 容器創建沙箱 Python 執行環境。Deno 在 WebAssembly 沙箱裏跑 Pyodide,阻止 LLM 在系統上執行危險代碼或任意操作。雖然本地安裝 Deno 也相對安全,但這會讓本地環境變得臃腫。

DuckDuckGo MCP 提供互聯網搜索能力,需要實時信息檢索時調用。

官方 GitHub MCP 負責代碼倉庫搜索和管理功能。

實踐中的幾個關鍵問題

安全性和可配置性。 對代理的訪問權限必須有精細控制。可以給 MCPs 做 Docker 化並設置特定權限——比如 GitHub MCP 用只讀標誌運行來保護倉庫。文件系統訪問權限需要有簡單的開關機制。,用

gh

CLI 工具替代 GitHub MCP 可能更合理,能減少 token 消耗,和 Desktop Commander MCP 的終端訪問配合也更流暢。隨着 Claude Skills 推出和 CLI 工具鏈成熟,現在有更好的方案來防止上下文污染且保持功能完整。

工具過載是個要注意的問題。 MCPs 加載所有工具及其描述至少兩次:初始化代理時一次,每次用户查詢時又一次。如果各種 MCPs 加起來有 40 多個工具,LLM 上下文很快就被塞滿了。必須仔細檢查每個 MCP 的工具命名,避免不同來源的函數名衝突。

MCP 資源使用是事件驅動的。 MCPs 按需啓動,資源消耗呈現尖峯特徵,但在 MacBook Pro 上不算過分。編碼代理需要從 MCP 客户端調用工具時,會用指定命令生成 MCP 進程——通常是

docker run

,也可能是

npx

或 Python 命令。容器運行時長剛好夠執行工具調用,然後自行終止和清理,釋放 CPU 資源給下一個操作。這種按需架構讓代理保持輕量,同時能訪問豐富的工具生態。

MCP 分發方式。 很多 MCPs 提供預配置的 Docker 鏡像,可以快速集成到編碼代理裏,方便維護隔離性。不過很多 MCPs 也有 Python 庫版本,想要更緊密的集成且不需要額外安全層的話可以本地安裝。

langchain-mcp 還不錯。

langchain-mcp-adapters

庫可以把 MCPs 橋接到 LangGraph,和 FastMCP 庫的類似功能差不多水平。但它提供了平滑的接口,把 MCP 工具轉換成 LangChain/LangGraph 兼容的工具,改動代碼不多。

工具調用前的用户許可機制需要完善。 代理執行工具不會提前徵求許可,感覺風險挺大。可以在工具調用前加入人機交互環節能解決這個問題,但代價是會打斷代理的執行流程。

後續改進方向

幾個增強功能值得考慮。跨個人筆記的綜合 RAG 工具——覆蓋 Notion、Obsidian、文本文件、markdown——能讓代理訪問多年積累的知識和研究資料。加入 Confluence MCP 或內部文檔 CLI 可以快速搜索公司特定的實踐規範。如前面提到的,換成

gh

CLI 替代 GitHub MCP 能節省 token。最後在每次工具調用前實現人機交互中斷會增加關鍵的安全層,不過希望做成可配置的,避免在可信操作時頻繁打斷代理。

本文代碼

https://avoid.overfit.cn/post/790f9ab797bc4cf8bdfeb0c7ac58ae83

Lorre Atlan, PhD

user avatar myskies Avatar cixiangdeboluo Avatar qiyuxuanangdechuangkoutie Avatar
Favorites 3 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.