news 2026/10/8 8:57:18

从MCP握手到LangGraph多Server调用:完整实操指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从MCP握手到LangGraph多Server调用:完整实操指南

最近圈子里聊 MCP(Model Context Protocol)的人明显多了起来。不管是 IDE 里接数据库、让 Agent 调 Figma,还是用 LangGraph 编排所谓“能让 AI 下地干活”的多工具链路,MCP 几乎已经成了接入外部工具时的默认协议。网上讲概念的帖子很多,但真正从协议层握手讲到LangGraph 里多 Server 调用的实操文章并不多。这篇就把我实际跑通的一条完整链路分享出来,从最底层的 initialize 握手开始,到用 LangChain 生态把多个 MCP Server 接进 LangGraph Agent,每一步都附代码和踩坑记录。想搞懂 MCP 不只是在文档里复制粘贴配置的人,这篇文章应该能帮你省不少时间。

1. MCP 到底是什么,它到底解了什么结

1.1 从“工具接口混乱”说起

先说一个最现实的问题:在没有 MCP 之前,如果我想让 AI 帮忙查数据库、搜文件、调用公司内部 API,需要给每个数据源单独写一套“连接器”。LLM 本身只认识文本,要让它操作外部系统,要么走 Function Calling,要么自己在代码里拼 prompt 和工具调用。Function Calling 解决的是“模型如何决定调什么函数”,但函数从哪来、怎么规范化、怎么让多个不同应用共用一个工具能力,它管不到。

结果就是每个项目都在重复写类似的胶水代码:数据库一个封装、文件系统一个封装、第三方 API 一个封装,换个模型厂商可能还要再适配一遍。MCP 的诞生就是为了终结这种混乱。它把“AI 应用(客户端)”和“外部工具/数据源(服务端)”之间的通信方式标准化了。协议说白了就是一套规则:客户端怎么连服务器、怎么列工具、怎么调用工具、怎么拿结果。一旦大家都遵守这套规则,同一个 MCP Server 可以被任意支持 MCP 的客户端复用,客户端也不用关心 Server 内部是 Java、Python 还是 Node 写的。

MCP 的官方定位是“上下文协议”,因为它不只是可以暴露函数(Tools),还可以暴露资源(Resources)和提示词(Prompts)。信息源和数据访问方式统一了,模型拿到的“上下文”就不再只是一段文本,而是整个可编程的工具环境。这个定位,和单纯做“远程函数调用”的 RPC 本质上拉开了距离。

1.2 协议组成和运行模式

从部署模式看,MCP 目前主力有两种 Transport(传输方式):

  • stdio:MCP Server 以子进程方式启动,客户端通过标准输入输出和它交换 JSON 消息。本地开发最常用,LangGraph 本地集成、FastMCP 默认模式基本都是这种。
  • HTTP/SSE:MCP Server 作为独立服务暴露 HTTP 接口,客户端通过网络访问,适合跨机器、跨容器的部署。

一个 MCP 连接里有三个角色:

角色说明例子
Host承载 AI 应用的进程,通常是你写的 Agent 主程序LangGraph 应用、Claude Desktop、IDE 插件
Client负责和 Server 建连、收发协议消息的组件MCP Python SDK 里的 ClientSession
Server暴露能力的一方,提供方法供 Client 调用写好的文件搜索服务、数据库查询服务

运行模式上,客户端先启动传输层,然后发起握手、能力协商,最后进入正常工作状态。理解这个时序非常关键,很多人用 LangGraph 接入 MCP 时遇到的怪问题,有相当一部分就出在“没按协议时序来”这上面。

2. 协议握手:MCP 连接的第一步

2.1 通信基础:JSON-RPC 2.0

MCP 协议的消息层建立在 JSON-RPC 2.0 之上,也就是客户端和服务器之间发的是一个又一个 JSON 对象,包含 jsonrpc 字段、method、params。请求需要带 id,响应需要匹配这个 id,通知则不需要响应。这套规范非常成熟,理解起来也简单:它就像两个人约定好对话格式,一个人开口说“帮我做什么”,另一个按编号回复“结果是什么”。

MCP 的每一次方法调用最终都落到 JSON-RPC 的三大类消息上。比如客户端发:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0.0"}}}

服务端回:

{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"sql-server","version":"0.1.0"}}}

很多读者看到这种 JSON 就头疼,但实际开发中,官方 SDK / FastMCP 已经帮你把这些底层逻辑封装好了,你不需要手工编 JSON。但为什么我还要强调理解 JSON-RPC?因为排查问题时要看协议日志,一旦看到Method not found、Invalid params、JSON-RPC error,你必须能立刻反应过来是协议层出了问题,而不是业务代码出错。

2.2 initialize 握手的完整过程

MCP 的连接不是建立 TCP 连接就算完事,它有一个类似 TCP 三次握手的“协议握手”过程。流程是这样的:

  1. 客户端发起initialize请求,带上自己支持的协议版本和能力描述。
  2. 服务端返回自己的协议版本、能力列表(支持 tools、resources、prompts 中的哪些)和服务标识。
  3. 客户端收到后,发送一个notifications/initialized通知,表示“我知道你的能力了”。
  4. 服务端收到通知后,双方才进入“正常工作状态”。

这里有个很关键的细节:在notifications/initialized发出之前,客户端是不允许调用tools/list、tools/call等业务方法的。协议上管这叫生命周期状态机。我在项目里看到过一种低级错误:刚连上 server 就立刻调工具,结果请求被 server 端无情拒绝,报错是Connection not initialized。原因就是 SDK 的initialize在后台异步进行,而你用asyncio.create_task提前并发发请求了。解决方法是确保连接流程走完,再进入业务逻辑。

版本协商也是一个重点。MCP 的 protocolVersion 是带日期的字符串,比如 2025-06-18。client 和 server 各自声明版本,最终协商后的版本取两者的交集。如果你手写协议对接,一定要处理好版本不一致的情况,别直接把服务端版本原样当成本地版本,否则后续能力解析会错位。用官方 SDK 则不用太担心,它会自动按兼容策略处理。

2.3 能力发现:tools/list 和 tools/call

握手结束,第一件真正意义上的“业务”操作就是能力发现。客户端发tools/list,服务端返回工具列表。每个工具包含名称、描述、输入参数 schema。这个 schema 是 JSON Schema 格式,也就是模型端做 Function Calling 时最需要的那份“说明书”。

拿 SQL 查询服务举例,工具返回会长这样:

{"tools":[{"name":"query_order","description":"按订单号查询订单信息","inputSchema":{"type":"object","properties":{"order_id":{"type":"string"},"status":{"type":"string","description":"订单状态,默认全部"},"page":{"type":"integer"}},"required":["order_id"]}}]}

LangGraph 里的模型看到这份 schema 后,会决定“什么时候调用工具、传什么参数”。工具描述写得好不好,直接影响模型的理解准确率。MCP Server 把工具描述写清楚,比在 Agent 代码里再加一堆 prompt 更管用。

真正的执行动作是tools/call。客户端发:

{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"query_order","arguments":{"order_id":"SO-001"}}}

服务端返回的结构里包含content数组,内容类型默认是文本,也支持图片等资源。SDK 里这个返回会被封装成工具调用结果,LangGraph 的 ToolNode 拿到后直接写回状态。

除了 tools,MCP 还有 resources 和 prompts 两类能力:

能力用途类比
tools可执行的函数,模型自主选择调用给 AI 配的“手”
resources暴露只读数据,比如配置文件、数据库记录给 AI 看的“资料室”
prompts预置的提示词模板,客户端可调用给 AI 准备的“话术库”

这三样组合起来,MCP Server 才能被称为“完整的上下文提供者”,而不仅仅是“工具仓库”。

2.4 心跳与连接生命周期

建立连接后,MCP 还有一套心跳机制。客户端可以对 server 发ping请求,server 必须响应;反过来 server 也可以 ping 客户端。目的是探测连接是否健康,尤其在 HTTP/SSE 模式下,长时间不通信可能导致网络中间设备把连接回收。我遇到过一个现象:MCP 服务部署在远程服务器上,Agent 空闲几分钟后调用工具失败,排查发现连接已经被中间代理超时断开了。解决办法是在 Agent 的空闲分支里加定期 ping,保持活跃。

协议层面的连接状态还有 shutdown、aborting 等。正常关闭时双方会走shutdown流程,释放资源。在 LangGraph 的异步环境里,如果直接杀进程而不走关闭流程,可能留下僵尸子进程,尤其是 stdio transport 下,子进程没有父进程回收,会一直赖在系统进程列表里。

3. LangGraph 里多 Server 到底怎么玩

3.1 语言模型框架为什么需要 MCP

LangGraph 本身的定位,是用图的方式编排 Agent 的工作流:节点负责执行,边负责决定下一步去哪儿,状态在节点之间流转。它并不关心工具怎么连外部的数据库、文件、API,它只知道“模型说要调一个叫 xx 的工具,参数是 yy,谁来执行?”这个执行者可以是任意实现了 Tool 接口的对象。

MCP 接入 LangGraph,最直接的好处是:你不必为每个外部系统写 LangChain 专属的 Tool 封装,只需写一个 MCP Server,LangGraph 应用通过 MCP 客户端就能使用它的能力。工具维护、数据源管理、权限控制都下沉到 Server 侧,模型层和应用层保持干净。

实际项目里,我更推荐把 MCP Server 当成“一个 AI 可操作的能力单元”。比如你这个 Agent 需要两件事:查数据库和操作文件系统,那分别在两个 MCP Server 里实现。这样一来,别人想复用时直接起 Server,你想替换实现时也不用动 Agent 代码。

3.2 多 Server 架构选择的三种方式

当你有多个 MCP Server,怎么设计 LangGraph 的调用策略?我总结了三种常见方案。

方案一:全量工具注入。把所有 Server 的 tools 拉出来,一股脑交给 LLM。简单粗暴,适合工具总数量少(10 个以内)、且彼此功能独立的情况。缺点是当工具数量多、prompt 变长后,模型的工具选择准确率会下降,token 成本也高。

方案二:按子 Agent 分组。每个子 Agent 绑定一个 MCP Server,再定义一个 Supervisor 路由节点,根据用户意图决定调用哪个子 Agent。适合企业级的复杂场景,比如“文件管理子 Agent”“数据库操作子 Agent”。代价是实现复杂度上了一个数量级,需要设计拓扑和路由逻辑。

方案三:LLM 路由 + 动态加载。先用一个轻量分类器判断当前任务属于哪个领域,然后在状态里动态挂载对应 Server 的工具。LangGraph 的状态持久化让这一招变得更加可行:可以把已加载的工具缓存到状态中,避免每次重复握手。

我自己在中等复杂度的项目里,默认先做方案一,跑通后再按工具调用冲突或效果衰减来决定要不要升级到方案二。过早容器化、过早拆图,都会让你在调试时多痛十倍。

3.3 两条集成路线:直接接 Protocol 还是用 Adapter

接入 LangGraph 有两条主流路线,很多人纠结选哪个:

  • 路线 A:自己写 MCP Client 封装。用官方mcpPython SDK,创建ClientSession,list_tools()拿工具,然后在 LangGraph 的节点里直接调call_tool()。优点是完全掌控协议细节,适合需要在调用前后加日志、鉴权、限流的场景;缺点是一切手写,代码量明显增加。
  • 路线 B:使用langchain-mcp-adapters。这个库提供了MultiServerMCPClient,能把多个 MCP Server 统一聚合成 LangChain Toolkit,然后直接传给 LangGraph 的 ToolNode。优点是省心,几行代码就连上了;缺点是屏蔽了底层细节,出问题时需要翻库源码。

选哪种,取决于你的项目阶段。如果是验证 demo,绝对选 B;如果要做生产级系统,我的建议是先用 B 跑通,然后把关键的 Server 调用改造成 A,给自己留调试入口。

4. 实操:一个双 Server 调用的 LangGraph Agent

4.1 环境准备与服务端编写

我用一个典型例子来演示:一个 Server 负责查 SQL 数据库里的订单,另一个 Server 负责读取本地文件模板。Agent 的最终任务是这样的:用户输入“查询订单 SO-001 的状态,并用模板 A 生成摘要”,Agent 需要先调数据库 Server 拿订单数据,再调文件 Server 读模板,最后合成为一段话。

先安装环境依赖:

pip install mcp langchain langgraph langchain-mcp-adapters langchain-anthropic

我这里的 LLM 用的是 Claude 模型,你换成 OpenAI、通义千问等模型只需要替换 ChatModel 初始化,对 MCP 链路没有影响。

数据库查询 Server(sql_server.py):

from mcp.server.fastmcp import FastMCP mcp = FastMCP("OrderQueryServer") @mcp.tool() def query_order(order_id: str) -> str: """按订单ID查询订单状态,返回订单状态、金额、客户名""" # 这里简化成模拟数据,真实场景连接数据库 mock_db = { "SO-001": {"status": "shipped", "amount": 199.0, "customer": "张三"}, "SO-002": {"status": "pending", "amount": 89.0, "customer": "李四"}, } order = mock_db.get(order_id) if not order: return f"订单 {order_id} 不存在" return f"订单 {order_id} 状态: {order['status']}, 金额: {order['amount']}, 客户: {order['customer']}" if __name__ == "__main__": mcp.run(transport="stdio")

文件 Server(file_server.py):

import os from mcp.server.fastmcp import FastMCP import pathlib mcp = FastMCP("FileTemplateServer") @mcp.tool() def read_template(template_name: str) -> str: """读取指定名称的模板文件内容,模板位于当前目录 templates 文件夹下""" base_dir = pathlib.Path("./templates") safe_name = os.path.basename(template_name) # 防目录穿越 file_path = base_dir / safe_name if not file_path.exists(): raise FileNotFoundError(f"模板 {template_name} 不存在") return file_path.read_text(encoding="utf-8") if __name__ == "__main__": mcp.run(transport="stdio")

写完后,在项目里建一个 templates 目录,放一个 template_a.txt,内容写上“客户 {customer} 的订单 {order_id} 当前状态为 {status}”。两个 Server 分别在不同终端启动:

python sql_server.py python file_server.py

启动后终端会一直不返回,等待标准输入收到协议消息。stdio transport 的特点就是这样,一切交流都在标准输入输出里进行,日志要输出到 stderr,以免污染协议流。第一次写 FastMCP 的人很容易在这里翻车:在工具函数里随手print(),结果把调试信息打到了 stdout,客户端解析直接失败。后面我会再强调一次这个坑。

4.2 用 MultiServerMCPClient 连接两个 Server

现在写主程序,核心是配置两个 Server 的连接信息,并获取聚合工具:

import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent async def main(): client = MultiServerMCPClient( { "sql-server": { "transport": "stdio", "command": "python", "args": ["sql_server.py"], "encoding": "utf-8", }, "file-server": { "transport": "stdio", "command": "python", "args": ["file_server.py"], "encoding": "utf-8", }, } ) async with client: tools = await client.get_tools() model = ChatOpenAI(model="gpt-4o-mini", temperature=0) agent = create_react_agent(model, tools) result = await agent.ainvoke( {"messages": [("user", "查询订单 SO-001 的状态,并读取模板 template_a 生成摘要")]} ) print(result["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())

这里有几个要点。get_tools()返回的是 LangChain Tool 对象,它们的名字默认会带上 Server 前缀吗?不会,默认就是 MCP Server 侧声明的工具名。如果两个 Server 里都有同名工具,你会得到两个同名 Tool 对象,LangGraph 的 ToolNode 遇到这种情况极大概率会出问题。所以我建议在 Server 侧就把工具名设计得带好前缀,例如sql_query_order、file_read_template,从源头避免冲突。

async with client这一步真的很重要。它负责启动所有子进程、完成协议握手。如果你在循环里反复创建和销毁MultiServerMCPClient,每次开启都会重新做握手,开销很大;更好的做法是复用一个长连接,手动管理生命周期。

4.3 用 ToolNode 构建带路由的 Agent 图

create_react_agent是 LangGraph 预构建的 ReAct Agent,适合快速验证。但如果你想控制多个 Server 的调用顺序、处理多步依赖,我更推荐手写 StateGraph。下面是一个带工具调用的核心图:

from typing import TypedDict, Annotated from langgraph.graph import StateGraph, START, END from langgraph.prebuilt import ToolNode from langchain_core.messages import HumanMessage, BaseMessage from typing import Sequence class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], operator.add] tool_names: list[str] def build_graph(model, tools): tool_node = ToolNode(tools) def agent_node(state: AgentState): # 把当前所有工具绑定到模型,让模型决定调用哪个 model_with_tools = model.bind_tools(tools) response = model_with_tools.invoke(state["messages"]) return {"messages": [response]} def should_continue(state: AgentState): last = state["messages"][-1] if hasattr(last, "tool_calls") and last.tool_calls: return "continue" return "end" graph_builder = StateGraph(AgentState) graph_builder.add_node("agent", agent_node) graph_builder.add_node("tools", tool_node) graph_builder.add_edge(START, "agent") graph_builder.add_conditional_edges( "agent", should_continue, {"continue": "tools", "end": END}, ) graph_builder.add_edge("tools", "agent") return graph_builder.compile()

这套结构里:

  • AgentState里messages是对话历史,所有节点都能读写;
  • agent_node把工具绑定给模型,由模型生成“是否调用工具”的决定;
  • should_continue判断最后一条消息是否包含tool_calls,有就跳进tools节点,没有就收尾;
  • ToolNode收到模型生成的 ToolCall 后,按工具名找到对应的执行函数,执行完把结果作为 ToolMessage 塞回 messages。

多 Server 在这里其实已经被抹平了:不管工具来自哪个 MCP Server,ToolNode 只认工具名。所以真正的多 Server 协调工作发生在工具名设计和模型意图理解上,而不是在图中加特殊节点。

4.4 完整执行流程与验证

把上面代码组装起来,跑一次完整流程。模型的执行逻辑大致是这样的:

  1. 看到用户消息“查询订单 SO-001 的状态,并读取模板 template_a 生成摘要”。
  2. 模型判定需要调sql_query_order(order_id="SO-001"),生成 ToolCall。
  3. Agent 节点返回带 ToolCall 的消息,条件边把它送进 ToolNode。
  4. ToolNode 找到 SQL Server 对应的 MCP Client,通过内部会话发tools/call,拿到订单状态消息。
  5. 状态更新回 messages,再次进入 Agent 节点。
  6. 模型看到订单结果,认为还需要读模板,于是生成第二个 ToolCall:file_read_template(template_name="template_a")。
  7. ToolNode 执行第二个 Server 的调用。
  8. 模型拿到模板内容后,合成最终摘要,输出给用户。

整个过程里,两个 MCP Server 的调用顺序和次数由模型动态决定,这就是 Agent 与普通固定流程最大的区别。实际运行到第三步时,终端里能看到 SQL Server 子进程收到了协议请求,但如果你在工具里放了 debug print,注意它不会出现在主进程终端,而会污染 stdio 通道——这个要在开发早期就养成输出到 stderr 的习惯。

4.5 Supervisor 模式与更大规模的编排

如果你的场景复杂度更高,比如多个 MCP Server 分属不同团队、工具量上百,我会建议再升级一层,用 Supervisor 模式。大致思路是:

  • 每个 MCP Server 对应一个子 Agent,子 Agent 的 model 只绑定该 Server 的工具;
  • 顶层再放一个 Supervisor Agent,它不绑定任何工具,只根据用户指令把任务分配给对应子 Agent;
  • 子 Agent 执行完并把结果汇报给 Supervisor,Supervisor 再做最终整合。

LangGraph 对这种情况的支持非常好,因为它天然支持图编排和多 agent 协作。不过要提醒一句:多 Agent 的调试复杂度远高于单 Agent,工具链路的故障传播也更隐蔽。我的建议是先在单图上跑通所有 MCP 工具,确认没有协议层问题,再按领域拆分。拆分后每个人的 Agent 只负责一种 Server,问题定位起来舒服很多。

5. 常见问题与排查实录

5.1 握手失败与协议版本不一致

现象:启动客户端后,server 子进程正常启动,但客户端在get_tools()时报错或一直卡住。

排查思路:先确认 server 单独跑起来有没有启动成功,有没有语法错误。stdio transport 下,server 因为缺依赖、读不到环境变量等任何原因在启动阶段崩溃,客户端都感知不到,只会看到“连接不上”。我遇到过 Windows 环境下python命令指向了 Microsoft Store 的假 python,server 实际没起来,客户端在那傻等。解决办法:手动在终端执行同样的启动命令,看有没有报错;或者把启动命令具体化,像C:/Python311/python.exe这样写绝对路径。

还有一种很常见的情况:协议握手阶段 client 和 server 的 protocolVersion 不一致。新版 mcp 库和旧版 server 之间容易出现版本协商失败。官方 SDK 一般会做兼容,但如果你用的是自研协议实现,这种版本不匹配就会直接告别。此时看协议日志,找到initialize请求和响应的protocolVersion,确认都到了同一天。

5.2 stdio 污染问题

现象:工具明明执行了,但客户端收到的内容全是乱码或奇怪的字符串。

原因:MCP Server 端代码里用了可以改变 stdout 输出的 API,或者打印了日志信息,导致标准输出里混入了非协议内容。协议解析器看到 JSON 中混着别的内容,自然失败或解析错误。

对策:所有查日志、调试信息输出到sys.stderr,不要用print()。FastMCP 里的日志配置也建议单独设置。这个坑无数次在开发阶段坑人,严格执行“stdout 只跑协议”原则可以帮你避开 90% 的诡异错误。

5.3 工具名冲突和多 Server 同名工具

现象:两个 Server 都提供了search工具,LangGraph 模型选择时犹豫不决,或者 ToolNode 执行时报错找不到唯一工具。

原因:LangChain Tool 集合要求工具名唯一,同名时后加入的会覆盖前面一个,或者抛异常。

对策:在 MCP Server 侧把工具名写得具体化,用{domain}_{action}的模式命名,例如sql_query_order、file_read_template。宁可名字长一点,也别让模型在十万八千里外猜错工具。如果实在改不了 Server 侧名称,可以在拿到 LangChain Tool 后通过tool.name重新复制一个新 Tool 对象改名。

5.4 异步环境下的生命周期管理

现象:在 Jupyter 或 FastAPI 里跑 LangGraph Agent,第一次调用成功,第二次就报“connection closed”或者子进程退出了。

原因:MultiServerMCPClient的生命周期在异步环境里没有正确维护。你可能在某个 request 结束后调用了 client 的关闭方法,或者没有用async with包住整个 Agent 生命周期。stdio transport 下的 MCP Server 是子进程,父进程一关连接它也就退出了。

对策:把 MCP 连接的创建和 Agent 的执行放在同一个上下文里,避免在中间穿插耗时操作导致连接被回收。服务化部署时,建议在应用启动阶段建立 MCP 连接,进程退出时再统一关闭,不要在每次请求里新建连接。

5.5 Windows 和容器环境的问题

搜索热词里能看到一堆 Windows Server、Docker Desktop 下面 MCP/Server 的报错,这确实和现实很贴合。Windows 上最常见的坑是:

  • 命令名问题:python、python3在不同环境指向不一致,建议用绝对路径;
  • 权限问题:某些进程以管理员权限启动,子进程继承权限后访问系统资源受限;
  • Docker 环境:容器里跑 MCP Server,stdio transport 下客户端和 server 必须共享文件描述符,所以通常不适合跨容器直接做 stdio,要走 HTTP/SSE transport。

我个人的经验:本地开发阶段一律 stdio,简单直接;一旦要部署成多服务或者上生产,迁移到 HTTP/SSE transport,并在 Server 侧加上鉴权和日志。晚了会后悔。

以上这些坑,几乎都是我在真实项目里一个个踩出来的。MCP 的连接链路本来就长,客户端、SDK、Server、模型 Agent 四层叠加,任何一层出问题都会表现为“Agent 调不动工具”。我的排查顺序永远是:协议层(握手和工具列表)引发的问题 → 传输层(stdio或网络)问题 → Agent 层(工具绑定和模型决策)问题。顺着这个顺序走,你很快能找到根因。

最后说一点个人体会。MCP 的价值,不在于它能把工具调用写得有多炫,而在于它把外部能力接入这个本来很“脏”的活,变成了可以按标准重复做的事。多 Server 调用只是表面现象,真正的核心是让 Agent 不关心工具在哪儿、服务是谁写的,只关心工具叫什么、能干什么。这套思路一旦建立,你会发现从“为每个系统写适配器”到“为每个能力写 Server”,就像是把一堆乱七八糟的充电线换成了统一的 USB-C 口,连接成本一下子就下来了。

后续如果要扩展,我建议从这个方向继续深入:给 MCP Server 加完善的鉴权和审计日志,定义更严格的工具描述规范,再用 LangGraph 的检查点机制把多轮调用状态持久化。我个人在实际操作中的体会是,工具描述写得越接近“人的指令”,模型的理解越准确,比在 Agent 层堆 prompt 有效得多。这篇就当个起点,接下来你大可以自己动手,把第一个 SQL Server 和一个文件 Server 接进你的 LangGraph Agent,跑通之后再谈规模化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 6:21:00

NVIDIA OpenShell实战:驱动、CUDA与本地终端AI助手部署指南

昨天(10 月 1 日)刷 GitHub 热榜时,我被 NVIDIA/OpenShell 这个项目吸引住了。不是因为它的 UI 有多漂亮,而是因为这次把“终端里的自然语言助手”推向主流的不是某个个人开发者,而是 GPU 硬件厂商本身。我把这个项目从…

作者头像 李华
网站建设 2026/10/7 6:21:00

DeepSeek Harness桌面端实测:工作流编排可视化与内网部署全解析

看到“DeepSeek Harness 出了桌面端”这个消息时,我的第一反应不是激动,而是怀疑。这个工具在圈子里一直以“终端党专属”著称,命令行里敲熟的人根本不需要图形界面,反而是那些刚入门、被 YAML 配置和依赖环境劝退的新手&#xff…

作者头像 李华
网站建设 2026/10/7 6:20:16

硬件工程师成长路径:从理论到实践,用项目驱动能力跃迁

硬件工程师这个行当,有个很有意思的现象:科班出身、成绩不错的人,刚进公司头一年往往会被现实教育得很惨;反而是一些在学校“不务正业”、整天泡实验室折腾板子的人,能很快上手干活。这不是个别现象,而是这…

作者头像 李华
网站建设 2026/10/7 6:18:18

AI Agent外挂记忆实战:Rust接入mem0实现跨会话记忆

跟Agent打交道最让人崩溃的瞬间,不是它没算对结果,而是我昨天刚跟它讨论完一套方案,今天打开对话窗口它反问我“您指的是哪个方案”。大模型本身是无状态的,每一次调用都是从头开始,对话历史的重量全压在外面的编排层上…

作者头像 李华
网站建设 2026/10/7 6:17:57

人体摔倒姿态检测实战:从数据集选型到时序模型部署的避坑指南

简介:这份人体摔倒姿态检测数据集面向计算机视觉与深度学习方向的开发者、学生及安全监控领域研究人员,用于训练和评估人体摔倒识别算法,可应用于智能家居看护、医疗健康监测与安防预警等场景。压缩包共约2000个文件,以7782个jpg图…

作者头像 李华
网站建设 2026/10/7 6:16:34

HTML+CSS+JS期末项目模板:本地预览+GitHub Pages一键部署

简介:这是一份面向高校计算机专业学生及网页设计初学者的HTML/CSS个人博客网站源码包,专为Web前端课程期末作业或网页设计实训项目打造。资源包含完整的四页面结构(首页、信息页、列表页、关于页),采用语义化HTML5标签…

作者头像 李华