1. 为什么你的 Agent 工具链总在“最后一公里”卡住
如果你正在用 LangChain 做 Agent 开发,大概率遇到过这种场景:Tool 的name、description、func都写好了,本地单测也能跑通,但一旦把 Agent 接上真实模型,要么工具压根不被调用,要么调用时参数传错,要么 MCP 服务返回 401 直接中断整条链路。问题往往不在 Tool 本身,而在“模型通道”和“工具注册”这两层没有对齐。
LangChain Tool 开发与接入 MCP 这件事,本质上是把三样东西串起来:一个能被模型理解的工具描述、一个能被 Agent 发现的注册入口、一条稳定的模型调用通道。前两者是 LangChain 的强项,第三者却经常被忽略——很多人把 Key 散落在各个脚本里,OpenAI 一个、Claude 一个、本地模型又一个,结果 Agent 在 ReAct 循环里切换模型时直接报local proxy failed或者401。
这篇内容面向正在做 Agent 工具链的开发者,给你一套可复制的 Tool 定义模板、MCP 服务注册配置,以及用 TaoToken 统一 Key 打通模型通道的接入参数。核心检索词就是 LangChain Tool 开发与 MCP 接入,适合已经写过一两个 Tool、但还没把整条链路跑顺的人。读完之后,你应该能独立完成一次端到端的工具发现与执行验证。
我试过把天气查询、邮件发送、内部 API 调用都封装成 Tool,踩过的坑集中在两处:一是description写得太模糊导致模型不选这个工具,二是 MCP 服务的 Base URL 和模型通道的 Base URL 混在一起配置,排查了半天才发现是 Key 用错了地方。下面按可跟做的顺序拆开讲。
2. TaoToken 前置:统一 Key 与 API 通道怎么准备
在写 Tool 之前,先把模型通道固定下来。Agent 的 ReAct 循环会反复调用模型,如果每次调用都换 Key、换 Base URL,调试成本会指数级上升。TaoToken 在这里的角色是提供一个统一的 API 入口,让你用同一个 Key 访问不同模型,Tool 开发和 MCP 接入阶段不用再为模型切换改代码。
你需要准备的东西只有两样:一个 API Key,一个 Base URL。API 地址是https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI 兼容接口的base_url使用。Key 在控制台的 API Keys 页面生成,地址是https://taotoken.net/console/api-keys,生成后复制保存,后面配置里会反复用到。
如果你用的是 Claude Code 或者需要 Anthropic 兼容格式,TaoToken 也提供了对应的接入文档,地址是https://taotoken.net/doc。对于 LangChain 的ChatOpenAI来说,你只需要把openai_api_base指向 TaoToken 的 API 地址,openai_api_key填生成的 Key,模型名按你实际要用的填,比如gpt-4o-mini或者claude-3-5-sonnet这类。
这里有个容易忽略的点:LangChain 的ChatOpenAI默认会去读环境变量OPENAI_API_KEY和OPENAI_API_BASE。如果你本地已经有一份指向别处的配置,建议在代码里显式传参,不要依赖环境变量,否则会出现“本地单测通、Agent 跑不通”的诡异现象。显式传参的写法在下一节的配置片段里会给全。
另外,MCP 服务的 Base URL 和模型通道的 Base URL 是两个独立的东西。MCP 服务是你自己部署或第三方提供的工具服务地址,模型通道是 TaoToken 的 API 地址。很多人把这两个混在一个配置里,结果 MCP 请求发到了模型通道上,返回一堆看不懂的 JSON。记住:MCP 管工具,TaoToken 管模型,两者通过 LangChain 的 Tool 层解耦。
如果你打算长期跑编码类 Agent,或者需要多轮工具调用,可以了解一下 Coding Plan,地址是https://taotoken.net/coding-plan,它更适合高频、长链路的场景。不过对于本篇的验证流程,普通的 API Key 就够了。
3. 可复制配置:Tool 定义模板与 MCP 注册片段
这一节给的是可以直接粘贴进项目的配置。先看 Tool 定义模板,我把它拆成“功能函数 + Tool 对象 + 注册到 Agent”三段,每段都能单独替换。
功能函数部分,重点是错误处理和返回值格式。Agent 对返回值的解析能力有限,尽量返回字符串,结构化数据先序列化成 JSON 字符串再返回。下面这个模板可以直接改:
import json import requests from langchain.agents import Tool def call_mcp_service(query: str) -> str: """调用 MCP 服务的功能函数,输入为字符串,输出为格式化后的字符串""" try: if not query or not isinstance(query, str): raise ValueError("输入必须是非空字符串") payload = {"parameters": {"query": query}} resp = requests.post( "http://localhost:8000/mcp/weather", json=payload, headers={"Content-Type": "application/json"}, timeout=10, ) if resp.status_code != 200: return f"调用失败,状态码:{resp.status_code},详情:{resp.text}" data = resp.json() return json.dumps(data, ensure_ascii=False) except requests.Timeout: return "调用 MCP 服务超时,请稍后重试" except Exception as e: return f"调用 MCP 服务异常:{str(e)}"Tool 对象定义时,description要写清楚“什么时候用、输入长什么样、输出是什么”。模型选不选这个工具,八成看 description。模板如下:
weather_tool = Tool( name="WeatherService", func=call_mcp_service, description=( "用于查询指定城市的天气信息。" "输入应为城市名称,例如'北京'、'上海'。" "输出为包含天气状况、温度、湿度的 JSON 字符串。" ), )接下来是模型通道的配置。用 TaoToken 统一 Key,显式传参,避免环境变量干扰:
from langchain.chat_models import ChatOpenAI llm = ChatOpenAI( temperature=0, model_name="gpt-4o-mini", openai_api_base="https://taotoken.net/api", openai_api_key="你的_TaoToken_API_Key", )如果你更习惯用配置文件管理,可以写一个settings.json,路径放在项目根目录的config/下:
{ "model": { "base_url": "https://taotoken.net/api", "api_key": "你的_TaoToken_API_Key", "model_id": "gpt-4o-mini" }, "mcp": { "weather_service": "http://localhost:8000/mcp/weather", "email_service": "http://localhost:8000/mcp/email" } }注意这里的三件套:Base URL、Key、Model ID 必须同时出现,缺一个都会在 Agent 初始化时报错。如果你用的是 Cline MCP 或者 Codex 的auth.json,配置逻辑是一样的,把base_url指向 TaoToken 的 API 地址,api_key填生成的 Key,model_id填你要用的模型。
MCP 服务注册到 Agent 的工具列表时,直接传 Tool 对象数组:
from langchain.agents import initialize_agent, AgentType agent = initialize_agent( tools=[weather_tool], llm=llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True, )到这里,配置层就齐了。下一节跑一次真实请求,确认工具能被发现和执行。
4. 验证请求:一次端到端调用确认工具被发现
配置写完不验证,等于没写。这一节用一个最小请求确认三件事:模型通道通、Tool 被注册、MCP 服务被调用。
先跑一个不涉及 MCP 的纯模型请求,确认 TaoToken 通道正常:
from langchain.chat_models import ChatOpenAI llm = ChatOpenAI( temperature=0, model_name="gpt-4o-mini", openai_api_base="https://taotoken.net/api", openai_api_key="你的_TaoToken_API_Key", ) print(llm.invoke("用一句话说明什么是 LangChain Tool").content)如果这一步返回正常文本,说明 Base URL 和 Key 没问题。如果报401,去控制台确认 Key 是否复制完整;如果报local proxy failed,检查是不是本地有代理配置干扰了请求。
接着跑 Agent 调用,观察 verbose 输出里有没有Action: WeatherService这一行:
result = agent.run("北京今天的天气怎么样?") print(result)正常情况下,verbose 日志会显示 Agent 先思考、再选择WeatherService、传入北京、拿到 MCP 返回的 JSON、最后生成自然语言回答。如果 Agent 直接回答而没有调用工具,说明description没写清楚,模型不知道这个工具能查天气。把 description 改成“当用户询问任何城市天气时使用此工具”再试。
如果日志里出现Action: WeatherService但紧接着报错,重点看 MCP 服务的返回。常见的是 MCP 服务没启动,或者端口写错。用 curl 单独测一下 MCP 服务:
curl -X POST http://localhost:8000/mcp/weather \ -H "Content-Type: application/json" \ -d '{"parameters": {"query": "北京"}}'这个请求能返回 JSON,说明 MCP 服务本身没问题,问题在 LangChain 的 Tool 封装层。检查func的入参名是否和 Agent 传入的一致,query和input混用是高频错误。
验证通过的标准是:Agent 输出里包含真实天气数据,且 verbose 日志完整展示了“思考 → 选工具 → 传参 → 拿结果 → 生成回答”这条链路。到这一步,LangChain Tool 开发与 MCP 接入就算跑通了。
5. 本篇常见错排查:401、local proxy failed 与 reading choices
这一节对照真实报错给排查路径。以下四个是我在 Tool 开发和 MCP 接入阶段遇到频率最高的。
401 Unauthorized:出现在模型调用或 MCP 调用两处。如果是模型调用报 401,检查 TaoToken 的 Key 是否填对,注意不要有多余空格。如果是 MCP 调用报 401,检查 MCP 服务自己的鉴权头,和 TaoToken 的 Key 是两回事。排查命令:把 Key 单独拿出来请求一次https://taotoken.net/api下的模型列表接口,确认 Key 有效。
local proxy failed:这个报错通常和本地网络配置有关。LangChain 底层用的requests或httpx会读取系统代理设置,如果本地有残留的代理配置,请求会先走代理再失败。解决办法是在代码里显式禁用代理:
import os os.environ["NO_PROXY"] = "taotoken.net,localhost,127.0.0.1"或者在requests.post里加proxies={"http": None, "https": None}。注意不要用任何非正规的网络工具,这里只是清理本地环境变量。
reading choices 报错:完整报错通常是KeyError: 'choices'或reading 'choices'。这说明模型返回的 JSON 结构里没有choices字段,常见原因是 Base URL 指向了一个非 OpenAI 兼容的接口,或者模型名写错导致服务端返回了错误对象。检查openai_api_base是否严格等于https://taotoken.net/api,以及model_name是否是服务端支持的模型 ID。
OAuth 相关报错:如果你用的是 Claude Code 或 Codex 这类工具,可能会遇到 OAuth token 过期。这类工具的配置里同样需要 Base URL、Key、Model ID 三件套。OAuth 报错时,先确认是不是把 TaoToken 的 Key 填到了 OAuth 字段里,两者不能混用。正确的做法是在工具的模型配置里填 TaoToken 的 API Key,OAuth 流程交给工具自身处理。
Tool 不被调用:不是报错但更常见。排查顺序是:先看description是否包含用户问题的关键词,再看name是否和 Agent 的 prompt 冲突,最后看func的返回值是否是字符串。如果返回了 dict,Agent 可能解析失败而跳过这个工具。
MCP 服务超时:在requests.post里加timeout=10,并在异常处理里捕获requests.Timeout。超时后返回明确的错误字符串,Agent 会把这个字符串当作工具结果继续处理,而不是直接崩溃。
6. 把工具链跑顺之后,下一步做什么
工具链跑通之后,你会发现真正的瓶颈从“能不能调用”变成了“调用得准不准”。description的措辞、MCP 服务的返回格式、Agent 的 prompt 模板,这三者需要反复调。我的经验是先把一个 Tool 调到 90% 准确率,再复制这套模板去加第二个、第三个,不要一次性注册一堆工具然后逐个排查。
如果你需要频繁验证不同模型对同一个 Tool 的调用效果,可以用模型对话页面快速切换模型,地址是https://taotoken.net/model-chat,不用改代码就能对比。长期跑编码类 Agent 的话,Coding Plan 的通道更适合高频调用场景。接入文档在https://taotoken.net/doc,遇到配置问题先翻文档,大部分报错都有对应说明。
最后留一个可跟做的练习:把本篇的天气 Tool 换成邮件发送 Tool,MCP 服务用一个本地 Flask 接口模拟,跑通“Agent 发现工具 → 调用 → 返回发送结果”这条链路。做完这个练习,你对 LangChain Tool 开发与 MCP 接入的理解会从“知道”变成“能改”。