2026/5/21 16:17:20
网站建设
项目流程
忻州市中小企业局网站,菜鸟网络属于哪个公司,编程入门先学什么scratch,百度网站名称及网址本文档详细复盘了我们如何基于 LangChain 构建一个能够连接 Model Context Protocol (MCP) Server 的智能 Agent (GithubAgent)。我们的目标是复刻 Cline 等先进 IDE 插件的核心能力#xff1a;自动工具发现、自动规则注入以及智能工具调用。
1. 架构概览#xff1a;GithubAg…本文档详细复盘了我们如何基于 LangChain 构建一个能够连接 Model Context Protocol (MCP) Server 的智能 Agent (GithubAgent)。我们的目标是复刻 Cline 等先进 IDE 插件的核心能力自动工具发现、自动规则注入以及智能工具调用。1. 架构概览GithubAgent 的解剖GithubAgent不仅仅是一个简单的 LLM 包装器它是一个具备完整思考-行动-观察 (ReAct)循环的自治单元。1.1 调用链路图MCP Server (GitHub)Gemini ModelGithubAgent前端用户MCP Server (GitHub)Gemini ModelGithubAgent前端用户初始化阶段循环结束alt[发现 ToolCall][无 ToolCall]loop[ReAct Loop]列出我的 repoSSE Connect HandshakeInitializeResult (含 Instructions)注入 System PromptListToolsTools List (含 Schema Description)转换为 StructuredTool (Pydantic)Bind Tools对话历史 (Messages)AIMessage (含 ToolCall: get_repo_list)yield [Thinking: Calling get_repo_list...]CallTool(get_repo_list, args)ToolResult (Repo JSON)Append ToolMessage to Historyyield Here are your repos... (流式文本)1.2 Agent 内部方法调用流 (Internal Flow)为了更清晰地展示代码结构我们将GithubAgent内部的方法调用关系绘制如下ReAct Loop1. 初始化连接2. 提取指令3. 获取工具4. 转换工具5. 绑定模型6. 进入循环StreamDecideNoYesResultContinue_connect_and_executesession.initialize()_extract_instructions_fetch_mcp_tools_convert_mcp_toolsllm.bind_tools()_agent_loopLLM.astream()Has Tool Calls?Break Loop_execute_tool_callAppend ToolMessage2. 核心方法详解代码:https://github.com/jason-nvd11-org/askc-backend/blob/github-mcp-agent/src/agents/github_agent.py为了实现上述架构我们将GithubAgent拆分为五个单一职责的核心方法。2.1_fetch_mcp_tools: 工具发现代码职责连接 MCP Server拉取原始工具定义。asyncdef_fetch_mcp_tools(self,session:ClientSession)-List[McpTool]:logger.info(Fetching tools from MCP...)resultawaitsession.list_tools()returnresult.tools解释这是实现“自动发现”的第一步。无论 Server 端增加了什么新工具Agent 只要重启就能看到。2.2_convert_mcp_tools: 智能适配器代码职责将 MCP 的 JSON Schema 转换为 LangChainStructuredTool。关键技术pydantic.create_model。def_convert_mcp_tools(self,mcp_tools:List[McpTool],session:ClientSession)-List[StructuredTool]:# ... 遍历 mcp_tools ...args_modelcreate_model(f{tool.name}Schema,**fields)returnStructuredTool.from_function(...,args_schemaargs_model)解决的问题这是解决“参数对齐”的关键。它确保 LLM 知道参数名是owner而不是username。2.3_extract_instructions: 规则注入代码职责从握手响应中提取 Server 端的instructions。def_extract_instructions(self,init_result:Any)-str:ifhasattr(init_result,instructions)andinit_result.instructions:returnf\n\n[Server Instructions]\n{init_result.instructions}return解决的问题Cline 能读懂 Server 的“潜规则”如“别用浏览器”靠的就是这一步。我们将这些规则强行注入到了 System Prompt 中。2.4_agent_loop: 大脑回路代码职责维护 ReAct 循环处理流式输出与工具调用的分流。asyncdef_agent_loop(self,llm_with_tools,session,messages):whileTrue:# 1. 思考 (Think)asyncforchunkinllm_with_tools.astream(messages):# ... 累加 chunk ...ifchunk.content:yieldchunk# 实时输出文本# 2. 决策 (Decide)ifnotgetattr(final_chunk,tool_calls,None):break# 没工具用结束# 3. 行动 (Act)fortool_callinfinal_chunk.tool_calls:yieldAIMessageChunk(contentf[Thinking: Calling{tool_call[name]}...])tool_msgawaitself._execute_tool_call(session,tool_call)messages.append(tool_msg)2.5 流式输出机制 (Streaming Mechanism)Agent 的流式能力不仅仅是简单地调用 LLM 的astream它实现了一个混合流 (Hybrid Stream)透传流 (Pass-through):当 LLM 生成普通文本时Agent 作为中间管道收到一个 chunk 就立刻yield一个 chunk。asyncforchunkinllm_with_tools.astream(messages):ifchunk.content:yieldchunk合成流 (Synthesized):当 Agent 处于“思考”或“执行”状态时LLM 是静默的。为了保持前端连接活跃并提供反馈Agent 会手动构造AIMessageChunk并推送。yieldAIMessageChunk(contentf[Thinking: Calling{tool_name}...])协议一致性:无论是 LLM 生成的还是 Agent 伪造的输出给前端的都是标准的BaseMessageChunk对象。这使得上层调用者如ChatService无需区分数据来源统一处理。3. 关键问题深度解析Q1: Agent 如何像 Cline 一样发现工具A: 通过 MCP 协议的session.list_tools()。MCP 协议标准定义了tools/list接口。只要连接建立Client 就可以询问 Server“你有什么本事” Server 会返回一份详细的清单包含名称、描述、参数结构。我们的 Agent 正是利用这个接口实现了动态发现。Q2: Agent 如何获取 Instructions 并注入 PromptA: 通过session.initialize()的返回值。FastMCP 框架将instructions放在了初始化握手响应InitializeResult中。我们的_extract_instructions方法专门负责捕获这个字段并将其追加到self.system_prompt后。这样LLM 在每一次对话开始前都会先“读”一遍这份说明书。Q3: Agent 如何读懂 Tool 的注解A: 通过全链路透传description字段。Server 代码里的 docstring - MCP Protocol (description字段) -session.list_tools()-GithubAgent-StructuredTool(description...)- LLM Prompt。我们在代码中显式地传递了descriptiontool.description确保 LLM 能看到工具的用途说明。Q4: LLM 的 Tool Call 输出在哪里A: 藏在AIMessage.tool_calls属性里。现代 LLM APIOpenAI/Gemini将“内容”与“指令”分流了。Content: 给用户看的文本。当调用工具时这通常是空的。Tool Calls: 给程序看的指令。LangChain 将其解析并存放在message.tool_calls列表里。我们在_agent_loop中通过检查if final_chunk.tool_calls:来捕捉 LLM 的意图。Q5: 如何实现 “Thinking…” 流式反馈A: 手动 YieldAIMessageChunk。既然 Tool Call 阶段content是空的前端默认看不到任何东西。我们在检测到tool_calls后、执行工具前手动构造了一个包含提示文本的消息块并yield出去yieldAIMessageChunk(contentf\n[Thinking: Calling tool {tool_name}...]\n) 这模拟了类似 ChatGPT 的思考状态展示。## 4. 实战演示 (Sample Output)以下是运行 GithubAgent 时的真实日志输出已脱敏展示了完整的思考与执行过程 text INFO|src.agents.github_agent:_connect_and_execute:110-Connecting to GitHub MCP at https://.../sse...INFO|src.agents.github_agent:_connect_and_execute:116-MCP Session initialized.INFO|src.agents.github_agent:_extract_instructions:44-Loaded server instructions.INFO|src.agents.github_agent:_fetch_mcp_tools:33-Fetched2toolsfromMCP.INFO|src.tools.mcp_tool_converter:convert:13-Converting tool:get_repo_list,Description:Fetches alistof repositories...INFO|src.agents.github_agent:_agent_loop:99-AI requested1tool calls[Thinking:Calling tool get_repo_list...]INFO|src.agents.github_agent:_execute_tool_call:54-Executing tool:get_repo_listwithargs:{limit:3,owner:nvd11}INFO|src.agents.github_agent:_execute_tool_call:64-Tool result:[{name:mail-service,...}]Here are the first3repositoriesforuser nvd11:1.mail-service:https://github.com/nvd11/mail-service2.envoy-config:https://github.com/nvd11/envoy-config3.first-mcp:https://github.com/nvd11/first-mcp