拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

LangGraph 接入 MCP:把工具调用链路改到 TaoToken 的配置与验证

LangGraph 接入 MCP:把工具调用链路改到 TaoToken 的配置与验证

1. 为什么要把 LangGraph 的 MCP 工具链路统一到 TaoToken

如果你已经在本地跑通了 LangGraph + MCP,大概率会遇到一个很现实的问题:模型请求散落在好几个地方。ChatOpenAI里写一个base_url,ChatTongyi里写一个api_key,MCP server 的env里又塞一个第三方 key,最后排查一次工具调用失败,得翻四五个文件才能确认请求到底从哪个通道发出去的。

LangGraph 的多智能体工作流本身是「编排层」,它不关心模型请求走哪条路,只关心节点之间的状态流转。MCP 是「工具层」,负责把外部能力以统一协议暴露给 Agent。真正决定请求发往哪里的,是模型客户端初始化时那个base_url和api_key。所以「把工具调用链路改到 TaoToken」这件事,本质上是把 LangGraph 里所有模型客户端的 endpoint 收敛到同一个通道,同时让 MCP server 侧的环境变量也指向同一套凭证。

TaoToken 在这里扮演的角色是统一的模型请求入口。它提供 OpenAI 兼容的/v1/chat/completions接口,意味着你原来用ChatOpenAI、langchain_openai写的代码几乎不用改,只需要替换base_url和api_key。对于 LangGraph 这种重度依赖 LangChain 生态的框架来说,兼容性就是最大的省事。

适合谁看这篇:已经在本地用langchain-mcp-adapters跑通过至少一个 MCP server、能成功调用get_tools()并让 Agent 执行工具、但模型请求还指向各家厂商分散 endpoint 的开发者。如果你还没跑通 MCP 基础链路,建议先把MultiServerMCPClient和create_react_agent的最小例子跑起来,再回来看统一通道的配置。

我试过把三个不同厂商的模型客户端混在一个 LangGraph 图里,结果一次工具调用超时,日志里三个 endpoint 交替出现,定位花了半小时。从那以后我就把所有模型请求收敛到一个通道,排查成本直接降下来。下面按「前置准备 → 可复制配置 → 验证请求 → 排错」的顺序展开,每一步都给完整代码。

2. TaoToken 前置准备:Key、Base URL 与 MCP 环境变量

在改 LangGraph 代码之前,先把凭证和地址准备好。TaoToken 的 API 地址是https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI 兼容的base_url使用。很多人在这一步会多写一个/v1,结果请求变成/v1/v1/chat/completions直接 404,后面排错章节会专门讲这个。

Key 的获取走控制台,登录后在 API Keys 页面创建。创建时建议按用途命名,比如langgraph-mcp-dev,这样后面在 MCP server 的env里看到这个 key 就知道是哪个项目在用。Key 只在创建时完整显示一次,复制后存到本地.env文件,不要硬编码进代码提交到仓库。

MCP server 侧的环境变量写法是这次改造的关键点之一。MCP 的 stdio 方式通过env字段把环境变量传给子进程,SSE 方式则通过 URL 参数或请求头传递。如果你用的是自己手写的 FastMCP server,模型请求发生在 server 内部的 sampling 回调里,这时候 server 需要知道往哪个 endpoint 发请求。所以要在 server 启动时注入TAOTOKEN_API_KEY和TAOTOKEN_BASE_URL两个环境变量。

一个容易忽略的点:LangGraph 主进程和 MCP server 子进程是两套环境。主进程里ChatOpenAI用的 key,和 MCP server 里 sampling 用的 key,可以是同一个,但必须都显式配置。只配了主进程,工具调用时 server 侧 sampling 会因为拿不到 key 而失败,报错信息通常是401或missing api key,但堆栈会指向 MCP 通信层,容易误判成协议问题。

建议在项目根目录建一个.env:

TAOTOKEN_API_KEY=sk-你的key TAOTOKEN_BASE_URL=https://taotoken.net/api

然后在 LangGraph 主进程和 MCP server 启动脚本里都用python-dotenv或os.environ读取。这样只有一处凭证来源,改 key 的时候不用满项目搜。

3. 可复制配置:LangGraph 客户端与 MCP server 的 endpoint 写法

这一节给完整可复制的配置片段。先看 LangGraph 主进程里的模型客户端。原来你可能写的是:

from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-4o-mini", base_url="https://api.openai.com/v1", api_key="sk-xxx", )

改成 TaoToken 通道后:

import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() llm = ChatOpenAI( model="gpt-4o-mini", base_url=os.environ["TAOTOKEN_BASE_URL"], api_key=os.environ["TAOTOKEN_API_KEY"], streaming=True, )

注意base_url直接用https://taotoken.net/api,不要手动拼/v1。LangChain 的 OpenAI 客户端会自动补/chat/completions,如果你在base_url里已经带了/v1,最终路径会重复。Model ID 按你实际要用的填,TaoToken 支持多个模型,具体列表在模型对话页面能看到。

接下来是 MCP server 侧的配置。以 stdio 方式为例,MultiServerMCPClient的配置里env字段要注入 TaoToken 的凭证:

from langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient( { "my-local-mcp": { "command": "python", "args": ["mcp_server.py"], "env": { "TAOTOKEN_API_KEY": os.environ["TAOTOKEN_API_KEY"], "TAOTOKEN_BASE_URL": os.environ["TAOTOKEN_BASE_URL"], }, "transport": "stdio", } } )

如果你用的是 SSE 方式连接远程 MCP server,配置改成:

client = MultiServerMCPClient( { "my-remote-mcp": { "url": "http://127.0.0.1:8000/sse", "transport": "sse", } } )

SSE 方式下,模型请求发生在 server 内部,所以 server 启动时必须自己读到TAOTOKEN_API_KEY。手写 FastMCP server 时,在 sampling 回调里构造模型客户端:

import os from mcp.server.fastmcp import FastMCP from langchain_openai import ChatOpenAI mcp = FastMCP("taotoken-mcp-demo") @mcp.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" return a + b if __name__ == "__main__": mcp.run(transport="sse")

如果 server 内部需要调用模型做 sampling,就在回调里用os.environ["TAOTOKEN_BASE_URL"]和os.environ["TAOTOKEN_API_KEY"]初始化ChatOpenAI。这样主进程和 server 进程用的是同一套凭证,请求都从 TaoToken 通道出去。

一个完整的settings风格配置片段,方便你对照检查:

{ "model_client": { "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "model": "gpt-4o-mini" }, "mcp_servers": { "my-local-mcp": { "transport": "stdio", "command": "python", "args": ["mcp_server.py"], "env": { "TAOTOKEN_API_KEY": "${TAOTOKEN_API_KEY}", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" } } } }

三件套记牢:Base URL 是https://taotoken.net/api,Key 从环境变量读,Model ID 按实际填。任何一处缺失,工具调用链路都会断。

4. 验证请求:一次完整的 MCP 工具调用链路确认

配置改完不能只看代码,要跑一次完整链路确认请求确实从 TaoToken 发出。验证思路是:让 Agent 调用一个 MCP 工具,同时在 TaoToken 控制台的用量日志里看到这次请求。

先写一个最小验证脚本:

import asyncio import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.prebuilt import create_react_agent load_dotenv() async def main(): llm = ChatOpenAI( model="gpt-4o-mini", base_url=os.environ["TAOTOKEN_BASE_URL"], api_key=os.environ["TAOTOKEN_API_KEY"], ) client = MultiServerMCPClient( { "demo-mcp": { "command": "python", "args": ["mcp_server.py"], "env": { "TAOTOKEN_API_KEY": os.environ["TAOTOKEN_API_KEY"], "TAOTOKEN_BASE_URL": os.environ["TAOTOKEN_BASE_URL"], }, "transport": "stdio", } } ) tools = await client.get_tools() print("已加载工具:", [t.name for t in tools]) agent = create_react_agent(model=llm, tools=tools) response = await agent.ainvoke( {"messages": [{"role": "user", "content": "帮我算一下 3 加 5 等于几"}]} ) print("最终回复:", response["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())

运行后你会看到两段输出:第一段是已加载工具,确认 MCP server 成功暴露了add工具;第二段是最终回复,确认 Agent 调用了工具并拿到结果。如果已加载工具是空列表,说明 MCP server 没启动成功,跟 TaoToken 无关,先查 server 脚本。

确认工具加载成功后,打开 TaoToken 控制台的用量日志页面,刷新一下,应该能看到刚才这次请求的记录,包含 model、token 数和时间戳。这一步是「确认请求确实经由统一通道发出」的关键证据。如果日志里没有记录,但 Agent 又返回了结果,说明请求走了别的 endpoint,回去检查base_url是不是被某个环境变量覆盖了。

再补一个更严格的验证:在 MCP server 的add工具里加一行打印,确认工具真的被执行了:

@mcp.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" print(f"[MCP SERVER] add called with {a}, {b}") return a + b

运行验证脚本时,终端会先打印[MCP SERVER] add called with 3, 5,再打印最终回复。这条日志证明工具调用链路是通的:LangGraph 主进程 → MCP 协议 → server 工具执行 → 结果回传 → 模型生成最终回复。整条链路上模型请求都从 TaoToken 走。

如果你用的是 SSE 方式,验证方法一样,只是MultiServerMCPClient配置换成url字段,server 侧用mcp.run(transport="sse")启动,然后访问http://127.0.0.1:8000/sse确认服务在监听。

5. 本篇常见错排查:401、local proxy failed 与 reading choices

改 endpoint 的过程中,报错基本集中在几个固定位置。下面按真实报错逐个拆。

401 Unauthorized。最常见的原因是 key 没读到。检查.env文件是否在项目根目录、load_dotenv()是否在读取环境变量之前调用、MCP server 的env字段是否真的把 key 传进去了。stdio 方式下,env字段是显式传递的,不会自动继承主进程环境,所以必须手动写。如果主进程能跑通但工具调用报 401,八成是 server 侧env漏了TAOTOKEN_API_KEY。

local proxy failed / connection refused。这个报错通常出现在base_url写错或网络不通时。先确认base_url是https://taotoken.net/api,没有多余路径。然后用 curl 直接测一下:

curl -X POST https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}'

如果 curl 能通但 Python 报连接失败,检查是不是有全局代理环境变量干扰,比如HTTP_PROXY、HTTPS_PROXY被设成了本地某个端口。清掉这些变量再跑。

reading choices 相关报错。典型信息是KeyError: 'choices'或list index out of range,说明返回的 JSON 结构里没有choices字段。这通常是因为base_url多写了/v1,请求打到了错误路径,返回的是 404 页面而不是模型响应。把base_url改成https://taotoken.net/api即可。另一个可能是 Model ID 写错,服务端返回了错误对象,解析时找不到choices。对照模型对话页面确认 Model ID 拼写。

OAuth 相关报错。如果你在 MCP 配置里用了需要 OAuth 的远程 server,而凭证又指向 TaoToken,会出现 OAuth 流程和 API Key 混用的情况。TaoToken 走的是 API Key 认证,不需要 OAuth。把 MCP 配置里的 OAuth 字段去掉,统一用env里的TAOTOKEN_API_KEY。

工具加载为空。get_tools()返回空列表,先看 MCP server 进程有没有正常启动。stdio 方式下,command和args要能独立在终端跑通。比如python mcp_server.py手动执行,看有没有报错。server 启动失败时,MultiServerMCPClient不会抛异常,只是拿不到工具,容易误判成 TaoToken 问题。

请求发出但控制台无记录。Agent 返回了结果,但 TaoToken 用量日志里没有。检查是不是有多个模型客户端实例,其中一个还指向旧 endpoint。LangGraph 图里如果有多个节点各自初始化了ChatOpenAI,要确保每一处都改了base_url。用grep -r "base_url" .搜一遍项目,把所有旧地址替换掉。

排查顺序建议:先 curl 测通道 → 再手动跑 MCP server → 再跑最小 Agent 脚本 → 最后看控制台日志。每一步确认通过再往下走,比一次性跑完整图再猜哪里出错快得多。

6. 把统一通道固化到你的 LangGraph 工作流

配置改完、验证跑通之后,建议把凭证读取和客户端初始化抽成一个公共模块,比如llm_factory.py,所有节点都从这里拿模型实例。这样以后换通道只改一个文件,不用满项目搜base_url。

import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() def get_llm(model: str = "gpt-4o-mini") -> ChatOpenAI: return ChatOpenAI( model=model, base_url=os.environ["TAOTOKEN_BASE_URL"], api_key=os.environ["TAOTOKEN_API_KEY"], streaming=True, )

MCP server 的env也从这个模块的同一套环境变量读,保证主进程和子进程凭证一致。长期跑编码类 Agent 的话,可以把这套配置和 Coding Plan 结合,让多智能体工作流的模型请求都走统一通道,用量和排查都在一个地方看。

最后留一个实用习惯:每次改完 endpoint,先跑第 4 节那个最小验证脚本,确认工具加载和最终回复都正常,再去跑完整的 LangGraph 图。最小脚本 10 秒能跑完,完整图可能要几分钟,先用小成本确认通道通,再上大流程。

返回列表