ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MCP + LangChain 实战:从零搭建 Agent 工具调用链路

MCP + LangChain 实战:从零搭建 Agent 工具调用链路 在真实业务里Agent 最大的痛点往往不是“模型不够聪明”而是工具接得太痛苦。OpenAI Function Calling 刚出来时大家都觉得方便可真要在一个项目里接五六个外部系统函数定义、参数校验、鉴权、错误处理全都堆在业务代码里越写越重。MCP 的出现在很大程度上改变了这个局面它给“大模型调用工具”这件事提供了一套统一协议而 LangChain 作为最常用的 Agent 编排框架也很快补齐了对 MCP 的原生支持。这篇文章会从概念讲起一步步带你把 MCP Server 搭起来再通过 LangChain 把它加载成 Agent 工具最终跑通调用链路。文章会覆盖 LangGraph 与 LangChain 的关系、DeepSeek API 接入、Claude Code 配置 MCP 等内容适合正在做 AI Agent 开发、想统一工具接入方式的开发者阅读。1. 背景与核心概念1.1 从“大模型生成文本”到“大模型动手干活”先回顾一下 Agent 进化的路径。最初我们使用大模型只是做文本生成、摘要、翻译模型和外部世界没有任何交互。后来出现了 Function Calling模型可以根据用户问题输出一个结构化的工具调用指令再由代码去执行真实函数。再往后LangChain 等框架把这些调用逻辑包装成 Agent让模型能够在一次对话中多次决策是否需要调用工具、调用哪个工具、拿到结果之后如何继续推理。但这套流程有一个很现实的问题不同框架、不同模型、不同工具库Function Calling 的格式都不一样。你写了一个 Python 函数想在 Claude Code 里用又想在另一个 Agent 框架里用就得分别适配。每接一个新的外部系统都要重新做一遍胶水代码而且这些代码往往没有复用价值。MCP 就是在这个背景下产生的。1.2 什么是 MCPMCPModel Context Protocol模型上下文协议是 Anthropic 在 2024 年底提出的一种开放协议目标是为大模型应用提供一套标准化的“工具接入”方式。它的核心设计思路是把“能力提供方”和“能力消费方”解耦。你可以把 MCP 理解为 AI 应用世界的 USB-C 接口。一个 MCP Server 对外暴露工具、资源、提示词三种能力任何支持 MCP 的客户端Host都可以通过标准协议去发现并调用这些能力而不需要关心对方是用什么语言写的、部署在哪台机器上。目前主流的 Agent 客户端比如 Claude Desktop、Claude Code、LangChain、Cursor 等都已经支持 MCP 协议。1.3 LangChain 和 LangGraph 分别是什么很多新手容易把 LangChain 和 LangGraph 搞混这里做个梳理。LangChain 是一个生态型的开发框架提供大模型调用、提示词管理、文档加载、向量检索、工具调用、Agent 编排等模块。它的主要价值是让开发者不用重复造轮子把常用能力都封装好了。LangGraph 是 LangChain 团队推出的一个低层编排框架它把 Agent 的执行流程建模成一张图节点Node表示计算单元边Edge表示流转逻辑。相比 LangChain 早期版本的 AgentExecutorLangGraph 提供了更好的可控性、可观测性和状态管理能力适合生产级 Agent 开发。在 LangGraph 中有一个非常实用的预置组件叫 create_react_agent它用 ReAct 范式实现了一个完整的 Agent 循环模型接收输入、判断是否调用工具、执行工具、返回结果、再交给模型推理。本文的实战部分就会基于这个组件来实现。1.4 为什么需要 MCP LangChainLangChain 支持 Agent 工具已经不是新鲜事关键是如何把 MCP 生态中的工具加载进来。通过 langchain-mcp-adaptersLangChain 可以直接加载远程或本地的 MCP Server 暴露的工具自动把 MCP 工具转换为 LangChain 的 Tool 对象然后交给 Agent 调用。这样一来工具侧只需要开发一次 MCP Server就能同时服务于 LangChain Agent、Claude Code、Claude Desktop 等多个客户端。这也是为什么 MCP 被很多人视为 Agent 时代的“标准基础设施”。本文的完整实战链路如下图用户提问 ↓ LangChain Agentcreate_react_agent ↓ 模型决定调用工具 langchain-mcp-adapters ↓ 通过 MCP 协议通信 MCP ServerFastMCP ↓ 执行真实逻辑 外部系统 / 业务函数 / 数据库2. 环境准备与版本说明2.1 基础环境本文的代码以 Python 3.10 及以上版本为例操作系统没有特殊限制Windows、macOS、Linux 均可。建议新建一个独立的 Python 虚拟环境避免把依赖装到全局环境里造成冲突。python -m venv .venv source .venv/bin/activate # Windows 下使用 .venv\Scripts\activate2.2 安装依赖需要安装的核心依赖如下pip install langchain langchain-openai langgraph langchain-mcp-adapters mcp fastmcp各包的作用如下依赖包用途langchainLangChain 核心库langchain-openai通过 OpenAI 兼容接口调用模型DeepSeek 也走这个方式langgraphAgent 编排框架提供 create_react_agentlangchain-mcp-adapters将 MCP 工具加载为 LangChain Tool 的适配层mcpMCP 官方 Python SDKfastmcp快速开发 MCP Server 的高层框架这里要特别提醒MCP 协议和相关 SDK 更新速度很快不同版本之间 API 可能会有差异。上面命令安装的是当前 PyPI 上的最新稳定版本。如果你在运行时遇到模块找不到或参数不兼容的问题优先检查各包的版本并根据项目实际情况调整。2.3 项目结构为了演示方便本文使用下面的项目结构langchain-mcp-demo/ ├── .venv/ # 虚拟环境 ├── mcp_server.py # MCP Server 定义 ├── agent_client.py # LangChain Agent 客户端 ├── deepseek_agent.py # DeepSeek MCP 示例 └── requirements.txt # 依赖清单3. 核心原理拆解3.1 Agent 调用工具的本质一个 Agent 调用工具本质上是在“模型推理”和“外部执行”之间循环。以 ReAct 范式为例完整的循环是用户输入问题。模型根据当前对话内容和可用工具的描述决定下一步动作。如果模型认为需要调用工具就输出一个结构化动作包含工具名和参数。框架执行对应工具并把执行结果返回给模型。模型根据工具结果继续推理可能再次调用工具也可能直接输出最终答案。在这个过程中模型的“工具感知”完全依赖工具的描述信息。工具名、参数说明、返回值格式写得越清晰模型就越不容易调用错。这也是为什么在 MCP Server 里tool 的 docstring 和参数注解非常重要。3.2 MCP 协议的核心模型MCP 采用客户端—服务器架构涉及三个角色Host宿主程序通常就是用户正在使用的 AI 应用比如 Claude Desktop、Claude Code或者你自己开发的 Agent 程序。ClientHost 内部的连接组件负责与 Server 建立会话、发起请求。Server能力提供方暴露工具、资源、提示词。通信层支持两种传输方式stdioServer 作为子进程启动和客户端之间通过标准输入输出通信适合本地开发。Streamable HTTPServer 作为一个 HTTP 服务运行客户端通过网络请求调用适合远程部署。在 LangChain 集成中两种方式都支持。本文的示例先走 stdio因为最简单后面会给出远程 Mode 的思路。3.3 MCP 与 Function Calling 的区别很多读者会问我直接用 Function Calling 不就行了吗为什么要引入 MCP两者的定位并不冲突。Function Calling 是模型的一种输出能力它解决的是“模型如何表达调用意图”的问题但表达之后谁来解析、谁来执行、执行结果如何传回每个框架都不一样。MCP 解决的是“工具能力如何标准化暴露和发现”的问题。换句话说Function Calling 是模型侧的功能而 MCP 是工具侧的协议。用 MCP Server 暴露工具之后到了执行层仍然需要底层模型具备 Function Calling 能力只是框架层面的适配被统一了。3.4 MCP Server 中 Tool 的定义方式使用 FastMCP 框架定义一个工具非常简单只需要在函数上加上装饰器并写清楚 docstring 和类型注解。FastMCP 会根据函数的签名自动生成工具的描述信息包括参数的 JSON Schema。from fastmcp import FastMCP mcp FastMCP(demo-server) mcp.tool() def get_order_status(order_id: str) - str: 查询订单状态 Args: order_id: 订单号 # 这里只是示例实际项目中替换为真实业务逻辑 orders { 1001: 已发货, 1002: 待支付, 1003: 已完成, } return orders.get(order_id, 订单不存在)工具注册好之后MCP Server 会在握手阶段把所有工具列表返还给客户端客户端再把这些工具交给模型供其决策。4. 完整实战手写 MCP Server 并接入 LangChain Agent4.1 创建 MCP Server我们写一个简单的“订单查询 天气查询”服务虽然业务逻辑是模拟的但完整覆盖了 MCP Server 的注册、启动、与客户端交互的关键步骤。# 文件路径mcp_server.py from fastmcp import FastMCP mcp FastMCP(order-server) mcp.tool() def get_order_info(order_id: str) - str: 查询订单基本信息 Args: order_id: 订单号例如 1001 orders { 1001: 用户A 的订单金额 199 元状态已发货, 1002: 用户B 的订单金额 59 元状态待支付, 1003: 用户C 的订单金额 899 元状态已完成, } return orders.get(order_id, 未查询到该订单) mcp.tool() def get_weather(city: str) - str: 查询指定城市的当前天气 Args: city: 城市名称例如 北京、上海 weather_map { 北京: 晴25℃, 上海: 多云28℃, 广州: 雷阵雨30℃, } return weather_map.get(city, 暂未收录该城市的天气数据) if __name__ __main__: mcp.run(transportstdio)按 CtrlC 结束服务即可。这个文件单独运行不会打印任何业务输出因为 stdio 模式下它是在等待客户端的握手和数据请求。4.2 验证 MCP Server 能正常工作在写客户端之前可以先用 mcp 官方提供的集成测试方式做一次快速验证。更常见的做法是直接通过 MCP Inspector 调试mcp dev mcp_server.pyMCP Inspector 会在浏览器里打开一个调试面板你可以直接查看 Server 暴露了哪些工具并手动输入参数进行调用测试。生产开发中这一步非常推荐它能让你在接入 Agent 之前先确认工具本身没有逻辑错误。4.3 在 LangChain 中加载 MCP 工具接下来是整体链路中最关键的一步使用 langchain-mcp-adapters 连接 MCP Server并获取工具列表。这里有两种常见用法使用MultiServerMCPClient同时连接多个 MCP Server。使用load_mcp_tools针对单个 Server 做快速加载。下面我们以 MultiServerMCPClient 为例因为在一个真正的 Agent 项目中通常会有多个 MCP Server 提供不同领域的能力。以 stdio 方式启动时需要指定命令和参数# 文件路径agent_client.py import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent async def main(): # 1. 创建 MCP 客户端 async with MultiServerMCPClient( { order: { command: python, args: [mcp_server.py], transport: stdio, } } ) as client: # 2. 获取 MCP Server 暴露的工具 tools client.get_tools() print( 可用工具 ) for tool in tools: print(f- {tool.name}: {tool.description[:50]}) print(\n) # 3. 创建模型实例 model ChatOpenAI( modeldeepseek-chat, api_keyyour-api-key, base_urlhttps://api.deepseek.com, ) # 4. 创建 Agent agent create_react_agent(model, tools) # 5. 测试对话 result await agent.ainvoke( {messages: [{role: user, content: 订单1001现在是什么状态}]} ) print( 回复内容 ) print(result[messages][-1].content) if __name__ __main__: asyncio.run(main())4.4 运行与验证先确保 mcp_server.py 和 agent_client.py 在同一个目录下然后运行python agent_client.py如果一切正常你会看到控制台先打印出从 MCP Server 拉取到的工具列表 可用工具 - get_order_info: 查询订单基本信息 - get_weather: 查询指定城市的当前天气 回复内容 订单 1001 当前状态为已发货。该订单属于用户A金额为199元。到这里一条完整的链路已经跑通了用户问题 → DeepSeek 模型 → 判断需要调用工具 → LangChain Agent → langchain-mcp-adapters → MCP Server → 返回结果 → 模型组织最终回答4.5 远程 MCP Server 怎么连除了本地 stdio 方式MCP Server 也可以作为一个 HTTP 服务启动部署在远程服务器上。FastMCP 只需切换 transport 即可# 远程部署模式 if __name__ __main__: mcp.run(transporthttp, host0.0.0.0, port8000)客户端连接方式需要调整async with MultiServerMCPClient( { order: { url: http://localhost:8000/mcp, transport: streamable-http, } } ) as client: tools client.get_tools()需要说明的是不同版本的 langchain-mcp-adapters 对远程 HTTP 传输的参数写法可能有差异。如果你的版本不识别transport参数请以对应版本文档为准。远程部署时还需要考虑鉴权、HTTPS、超时设置等问题建议先在本地跑通再部署到线上。5. 实战进阶DeepSeek 接入与 Claude Code 配置5.1 DeepSeek API 的基础调用方式DeepSeek 的 API 采用了 OpenAI 兼容格式这是它接入 LangChain 很方便的原因。在 LangChain 中不需要额外安装 deepseek 专用包直接用 ChatOpenAI 并指定 base_url 即可。核心参数有三个model模型名称常用的是 deepseek-chat 和 deepseek-reasoner。api_key从 DeepSeek 开放平台申请。base_urlhttps://api.deepseek.com一个最小调用示例如下from langchain_openai import ChatOpenAI model ChatOpenAI( modeldeepseek-chat, api_keyyour-api-key, base_urlhttps://api.deepseek.com, ) resp model.invoke(你好请介绍一下你自己) print(resp.content)在本文的 Agent 示例中DeepSeek 扮演的是“决策大脑”的角色它负责理解用户意图、决定是否调用 MCP 工具、解析工具返回的结果。因为 Agent 循环需要多次调用模型所以建议把超时时间和最大重试次数配置得合理一些。5.2 使用 DeepSeek 的实用建议在实际项目里把 DeepSeek 接进 Agent 时建议考虑以下几点第一system prompt 要写清楚工具使用的边界。比如“查询订单信息时必须优先使用 get_order_info 工具”“如果工具返回‘未查询到’不要编造订单状态”。在 Agent 应用中模型幻觉的风险依然存在尤其是工具返回空结果时模型倾向于凭想象补全答案。第二deepseek-reasoner 适合需要深度推理的场景但在 Agent 循环中它的响应时间更长、token 消耗也更大。如果只是做工具调用deepseek-chat 往往性价比更高。第三为了节约 API 调用可以在 prompt 里要求模型只在必要时调用工具。对于简单问候不需要走工具链路。5.3 Claude Code 如何配置 MCPClaude Code 是 Anthropic 推出的命令行 AI 编程助手它支持配置 MCP Server。安装好 Claude Code 之后可以通过 MCP 相关命令来添加工具。常见的命令格式是claude mcp add demo-server -- python mcp_server.py执行后在 Claude Code 会话中你就可以直接让 AI 使用这个 MCP Server 暴露的工具。这里要提醒一点Claude Code 的 CLI 版本更新非常频繁MCP 命令的参数格式在过去几次版本中都有调整。如果你使用的版本提示命令不存在或参数不合法优先运行 claude mcp --help 查看当前版本支持的命令。另外生产项目中不要直接在全局配置里乱加 Server建议每个 Server 使用独立的配置文件并在项目级目录下管理。5.4 关于“无法识别模型”类报错在配置 Claude Code 接入第三方模型时可能会遇到类似下面的报错deepseek-v4-pro is not a model this version of claude code recognizes这类问题的根本原因通常有两类一种是模型名称在当前 Claude Code 版本中还未被内置识别。Claude Code 会对模型白名单做校验如果录入的模型名不在列表里就会报错。针对这种情况需要先确认 Model Name 是否填写正确再确认 CLI 版本是否需要更新。另一种是第三方模型的接入方式本身需要额外的兼容配置而不只是改一个模型名。不同版本的 Claude Code 对第三方模型的环境变量要求不同建议仔细阅读对应版本的官方配置说明不要轻易相信社区的“更名大法”。5.5 VSCode 中配置 Claude Code 的 MCP很多开发者会把 Claude Code 集成到 VSCode 中使用。基本思路是在 VSCode 的终端里启动 Claude Code然后在项目根目录维护 MCP 配置文件。当 MCP Server 发生变化时需要在 Claude Code 中重新加载配置。到这一步你已经掌握了 Claude Code 使用 MCP 的最基本流程。更复杂的多 Server 管理、权限控制等能力需要结合具体项目进一步探索。6. 常见问题与排查思路MCP LangChain 的组合虽然简化了工具接入但涉及 Agent、协议、模型三层依赖踩坑点依然很多。下面整理几个高频问题。问题现象常见原因解决思路Agent 提示没有可用工具MCP Server 启动失败或握手超时先单独运行 MCP Server确认没有异常再通过 MCP Inspector 验证工具列表模型一直不调用工具工具描述不清晰或模型不支持 function calling优化工具 name 和 docstring确认模型参数支持工具调用工具执行成功但回答不准确工具返回结果被模型忽略检查工具返回格式尽量返回结构化文本并在 prompt 中强制要求结合工具结果回答the agent execution provider did not respond in timeAgent 执行环境超时模型响应过慢检查模型 API 延迟适当调大超时时间必要时更换更快的模型MCP Server 使用 stdio 时客户端卡住Server 进程输出非协议内容到 stdout确保 Server 中不要随意 print 内容所有日志走 stderr远程 MCP 连接失败URL 不正确、端口未开放、鉴权缺失用 curl 或浏览器访问 Server 端点验证联通性DeepSeek 报模型不存在模型名拼写错误或账号无权限从平台确认实际可用的模型名不要使用推测的名称下面针对几个重点问题展开说明。6.1 模型不调用工具这是 Agent 开发中最常见的问题。出现这个现象时可以按顺序排查第一步检查工具是否真的被加载到了 Agent 中。在代码里打印 tools 列表确认 get_tools() 返回了预期的 Tool 对象。第二步检查模型是否支持 Function Calling。目前主流的 OpenAI 兼容模型基本都支持但一些轻量模型表现不稳定。第三步检查工具的 description 是否足够具体。模型是通过描述来判断“什么时候用这个工具”的如果你写的是“订单工具”模型根本不知道里面到底是什么逻辑。更合理的写法是“当用户询问订单状态、订单金额、发货进度时使用该工具查询订单信息”。6.2 Agent 循环超时在 langgraph 的 create_react_agent 中超时问题通常表现为整个节点长时间没有返回。可能是模型 API 响应慢也可能是工具本身执行慢。常用做法有给模型配置合理超时比如 ChatOpenAI 中的 timeout 参数。在工具函数内部实现自己的超时保护避免外部服务无响应拖垮整个 Agent。给 Agent 节点设置最大执行步数防止模型陷入反复调用工具的循环。6.3 MCP Client 获取不到工具如果你确认 MCP Server 本身能跑通但客户端始终拿不到工具优先检查两件事一是 transport 类型是否匹配。Server 端用的 stdio客户端却配置成 streamable-http那一定连不上。二是 Python 环境是否一致。使用 stdio 方式时客户端会尝试用python命令启动 Server。如果客户端运行在虚拟环境 A而命令python指向的是全局环境 B且 B 中没有安装 fastmcpServer 就会启动失败。解决方案是在客户端配置中把 command 写清楚。例如在虚拟环境中可以使用/path/to/.venv/bin/python这样的绝对路径而不是笼统的 python。7. 最佳实践与工程建议7.1 工具设计要“小而专”一个工具只做一件事工具名和描述要面向“模型能理解”来写。不要写一个“万能工具”参数又长又复杂模型很容易给错参数。比如把“查订单”和“退款”拆成两个工具就比定义一个带 action 参数的 order_tool 更可靠。在 MCP Server 中docstring 的写法直接影响工具描述质量。建议采用以下模板mcp.tool() def cancel_order(order_id: str, reason: str ) - str: 取消一个尚未发货的订单 Args: order_id: 需要取消的订单号 reason: 取消原因可选 # 业务逻辑 ...7.2 日志与错误处理MCP Server 在 stdio 模式下所有标准输出都会被协议消息占用所以日志只能输出到 stderr。你在 Server 内部使用 print 调试时可能不会立刻报错但会导致客户端解析消息异常。建议统一使用 logging 模块输出到 stderr。工具函数内部要做好异常捕获把错误信息转换成模型能读懂的描述。不要直接把 Python traceback 返回给模型模型会被大量报错信息干扰正确的做法是返回一句话描述比如“订单取消失败订单状态不允许取消”。7.3 环境变量与密钥管理在 Agent 项目中模型 API Key、MCP Server 的访问密钥都属于敏感信息不要硬编码在代码里。可以使用 .env 文件配合 dotenv 管理也可以通过 CI/CD 的密钥管理能力注入。# .env DEEPSEEK_API_KEYsk-xxxx MCP_SERVER_TOKENxxxx在实际项目中要遵循最小权限原则Agent 能访问的工具范围必须控制不是所有工具都应该暴露给所有用户。尤其是在涉及支付、退款、删除类操作时必须在上层做鉴权和二次确认。7.4 LangGraph 状态与可观测性相比简单的 AgentExecutorLangGraph 最大的优势是状态可观测。你可以给 Agent 加上节点级别的日志打印每次模型输出和工具调用结果这样定位“模型为什么答错”会容易很多。生产环境建议把工具调用的入参和出参记录到日志平台方便后续分析和审计。7.5 多 MCP Server 的隔离管理当一个项目需要连接多个 MCP Server 时不要把所有工具一股脑塞给 Agent。工具越多模型选择错误工具的概率越大。建议按 Domain 拆分组比如“订单域一组”“用户域一组”然后根据具体任务决定加载哪些组。LangChain 的 MultiServerMCPClient 天然支持这种隔离方式每个 Server 对应一个命名空间。8. 总结与学习路线写完这个 Demo你已经完整走过了 MCP Server 开发、LangChain Tool 加载、Agent 编排、第三方模型接入、客户端配置这几条核心路径。回顾一下本文的价值不在于那几十行代码而在于把 MCP 放进了一个真实的 Agent 体系中让你知道每一步发生在协议层的哪个位置。如果你接下来要继续深入建议按这个顺序学习第一把 MCP Server 接入真实业务系统。替换订单查询里的模拟数据接入数据库或后端 API试试鉴权、超时、错误恢复这些真实场景。第二深入研究 LangGraph 的复杂编排。create_react_agent 只是起点生产中会遇到多轮工具调用、条件分支、状态持久化等问题这些都需要对 LangGraph 的图结构有更深入的理解。第三对比不同 Agent 框架的设计取舍。你可以在 LangChain、Claude Code、自研 Agent 框架中各自接入同一个 MCP Server体会协议带来的“一次开发、多处复用”这一核心价值。最后分享一个落地经验在把 Agent 接入线上系统之前一定要先为每个工具写好边界说明和失败预案。Agent 的能力上限由模型决定但可靠性下限由工具设计和异常处理决定。把工具这层做扎实了后续换模型、加能力都会轻松很多。如果这篇文章对你有帮助欢迎收藏备用也欢迎在评论区聊聊你在 MCP 接入过程中遇到的具体问题。
返回列表