☰
从零上手大模型Agent开发:MCP协议实战指南,让AI工具调用变得简单!TaoToken统一Key接入实战
2026/10/7 7:09:43 网站建设 项目流程

1. 为什么你的 Agent 总是“只会聊天不会干活”

很多人第一次做大模型 Agent 开发,卡住的地方不是模型不够聪明,而是模型根本“碰不到”外部世界。你问它今天北京天气,它只能凭训练数据瞎编;你让它查一下数据库里某个订单状态,它一脸茫然。这不是模型的问题,是缺少一套让模型调用外部工具的机制。

早期大家用 Function Calling 硬写。每个工具都要手写函数、配 JSON Schema、调提示词,一个天气查询能写上百行。工具一多,维护成本爆炸,而且换个模型就得重写一遍适配层。我试过在一个项目里接了 7 个工具,光是 Schema 描述和参数校验就占了半个文件,改一个参数名要同步改三处,非常痛苦。

MCP(Model Context Protocol,模型上下文协议)就是来解决这个问题的。它把“模型环境”和“工具运行环境”拆成两个角色:MCP Client 负责和模型对话、决定调哪个工具;MCP Server 负责真正执行工具、返回结果。两者之间用一套固定的 JSON-RPC 2.0 消息格式通信,工具的描述、参数、返回值都有统一规范。你写一次 Server,任何支持 MCP 的 Client 都能直接接。

这篇文章面向的是刚接触大模型 Agent 开发、想跑通“模型调用工具”最小闭环的开发者。我会带你从零搭一个 MCP Server(天气查询工具),再写一个 Agent 侧 Client,通过 TaoToken 统一 Key 接入模型,最后完成一次真实的工具调用验证。全程可复制,代码能直接跑。核心检索词就三个:MCP 协议、AI 工具调用、大模型 Agent 开发。读完你能得到一个能用的本地 Agent 骨架,后面加工具只是往 Server 里注册新函数的事。

适合谁:会一点 Python、听说过大模型 API 但没真正接过工具调用的同学;或者已经用 LangChain 写过 Agent、但被 Function Calling 的样板代码折磨过的同学。不需要你有 GPU,不需要本地部署模型,一个 API Key 就够。

2. TaoToken 统一 Key:Agent 开发的模型接入前置

在写 MCP 代码之前,先把模型通道搞定。Agent 开发最烦的事情之一是模型切换:今天用这个模型测效果,明天换那个模型比价格,每换一次就要改 base_url、改 key、改模型名,散落在 .env、代码、配置文件里到处都是。TaoToken 的思路是给你一个统一的 API 入口和一把 Key,OpenAI 兼容格式,换模型只改一个 model 字段。

TaoToken 是什么:一个面向开发者的模型 API 聚合通道,提供 OpenAI 兼容的接口。你可以把它理解成一个“模型插座”,Agent 代码里只认一个 base_url 和一把 Key,背后接哪个模型由你配置决定。对 MCP 实战来说,这意味着 Client 侧的模型调用代码可以写得非常干净,不用为每个模型厂商写适配。

能做什么:支持对话补全、流式输出、工具调用(Function Calling / Tool Use)等标准能力。MCP Client 需要模型具备工具调用能力,TaoToken 的接口在这一点上和 OpenAI 的 tools 参数格式一致,所以 MCP 的工具注册逻辑可以直接复用。

适合谁:正在做 Agent 开发、需要频繁切换模型对比效果的个人开发者和小团队;不想在多个厂商控制台之间来回注册、管理多把 Key 的人。

接入前你需要准备两样东西:一把 TaoToken 的 API Key,以及确认你要用的模型 ID。Key 在控制台的 API Keys 页面创建,模型 ID 在文档的模型列表里查。这两个信息后面会写进 .env 文件。

这里要强调一个概念:MCP 协议本身不绑定任何模型厂商。MCP Client 负责的是“把工具列表告诉模型、解析模型的工具调用请求”,至于模型是谁、跑在哪里,是 Client 内部的事。所以你可以用 TaoToken 接云端模型,也可以换成任何 OpenAI 兼容的本地服务,MCP 层代码一行不用改。这就是分层的好处。

获取 Key 的入口我放在这里,方便你直接跳转:API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。先把 Key 拿到手,下一步我们就开始写配置。

3. 可复制配置:MCP Server 与 Agent Client 的完整片段

这一节是全文的核心,给你三份可以直接复制的配置和代码:环境变量文件、MCP Server 的注册片段、Agent Client 的工具注册代码。路径和字段名我都按实际能跑通的版本来写,你照着填就行。

3.1 环境变量 .env 文件

在项目根目录建一个 .env,内容如下。注意 base_url 用 TaoToken 的 API 地址,不要加多余路径:

# .env TAOTOKEN_API_KEY=sk-你的Key粘贴在这里 TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_MODEL=claude-sonnet-4-5

模型 ID 这一行你可以换成文档里任意支持工具调用的模型。我实测下来,带 tool use 能力的模型在 MCP 场景下表现更稳,不会出现“该调工具却直接编答案”的情况。

3.2 MCP Server 配置片段(JSON 格式)

MCP Server 的注册信息通常写在 Client 的配置文件里,不同 Client 路径不一样。以常见的 mcp.json 为例,放在项目根目录:

{ "mcpServers": { "weather-server": { "command": "uv", "args": ["run", "server.py"], "env": { "OPENWEATHER_API_KEY": "你的天气APIKey" } } } }

这段配置告诉 Client:有一个叫 weather-server 的 MCP Server,用 uv run server.py 启动,启动时注入环境变量。command 和 args 是 Server 的启动方式,stdio 模式下 Server 作为子进程运行,通过标准输入输出和 Client 通信。

如果你用的是 TOML 格式的配置(部分工具用这种),等价写法是:

[mcp_servers.weather-server] command = "uv" args = ["run", "server.py"] [mcp_servers.weather-server.env] OPENWEATHER_API_KEY = "你的天气APIKey"

3.3 Agent Client 工具注册代码

Client 侧的核心是把 MCP Server 暴露的工具列表转成模型能理解的 tools 参数。下面这段是精简后的关键逻辑,完整文件你可以直接建 client.py:

import asyncio import json import os from dotenv import load_dotenv from openai import OpenAI from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client load_dotenv() class MCPAgent: def __init__(self): self.client = OpenAI( api_key=os.getenv("TAOTOKEN_API_KEY"), base_url=os.getenv("TAOTOKEN_BASE_URL"), ) self.model = os.getenv("TAOTOKEN_MODEL") self.session = None self.tools = [] async def connect(self, server_script: str): params = StdioServerParameters(command="uv", args=["run", server_script]) self._read, self._write = await stdio_client(params).__aenter__() self.session = await ClientSession(self._read, self._write).__aenter__() await self.session.initialize() resp = await self.session.list_tools() self.tools = [ { "type": "function", "function": { "name": t.name, "description": t.description, "parameters": t.inputSchema, }, } for t in resp.tools ] print(f"已注册工具: {[t['function']['name'] for t in self.tools]}") async def chat(self, user_input: str): messages = [{"role": "user", "content": user_input}] resp = self.client.chat.completions.create( model=self.model, messages=messages, tools=self.tools, tool_choice="auto", ) msg = resp.choices[0].message if msg.tool_calls: for call in msg.tool_calls: args = json.loads(call.function.arguments) result = await self.session.call_tool(call.function.name, args) print(f"工具 {call.function.name} 返回: {result.content[0].text}") else: print("模型直接回复:", msg.content) async def main(): agent = MCPAgent() await agent.connect("server.py") await agent.chat("北京今天天气怎么样?") if __name__ == "__main__": asyncio.run(main())

这段代码里,connect 方法做了三件事:启动 Server 子进程、初始化会话、把 Server 的工具列表转成 OpenAI tools 格式。chat 方法把用户输入发给模型,如果模型返回 tool_calls,就通过 session.call_tool 真正执行工具。注意 tool_choice 设为 auto,让模型自己决定要不要调工具。

三件套在这里齐了:Base URL 是 https://taotoken.net/api ,Key 是 .env 里的 TAOTOKEN_API_KEY,Model ID 是 TAOTOKEN_MODEL。这三个值贯穿整个链路,缺一个都跑不通。

4. 验证请求:跑通一次完整的工具调用闭环

配置写完了,现在来验证。这一步的目标是看到模型主动调用工具、工具返回真实数据、模型基于数据给出回答。整个过程分三个动作:启动 Server、发起对话、观察日志。

4.1 先单独测 Server 能不能起来

在写 Client 之前,先确认 Server 本身没问题。MCP 官方提供了一个 Inspector 工具,可以可视化查看 Server 暴露了哪些工具。装好 Node.js 后运行:

npx -y @modelcontextprotocol/inspector uv run server.py

浏览器打开 http://127.0.0.1:5173/ ,你能看到 weather-server 下面列出了一个 query_weather 工具,点进去能看到它的参数 schema(city 字段,字符串类型)。如果这里能看到工具,说明 Server 注册没问题,问题只可能在 Client 侧。

4.2 跑 Client 发起真实对话

回到项目目录,运行:

uv run client.py

预期输出分三段。第一段是工具注册日志:

已注册工具: ['query_weather']

第二段是模型返回的 tool_calls,说明模型决定调用工具。第三段是工具执行结果:

工具 query_weather 返回: {"city": "Beijing", "temp": 24, "humidity": 56, "desc": "晴"}

如果模型没有调工具而是直接回复,通常是两个原因:模型不支持 tool use,或者工具描述不够清楚。换一个带工具调用能力的模型 ID 再试。

4.3 把结果回灌给模型生成最终回答

上面代码里我省略了一步:工具返回结果后,应该把结果作为 tool 角色消息追加到 messages,再请求一次模型,让它用自然语言总结。完整流程是:

messages.append(msg) # 模型的 tool_calls 消息 for call in msg.tool_calls: result = await self.session.call_tool(call.function.name, json.loads(call.function.arguments)) messages.append({ "role": "tool", "tool_call_id": call.id, "content": result.content[0].text, }) final = self.client.chat.completions.create(model=self.model, messages=messages) print(final.choices[0].message.content)

这样模型会输出类似“北京今天晴,气温 24 摄氏度,湿度 56%,适合出门”的回答。到这里,从 MCP 协议对接到工具响应的最小闭环就跑通了。

4.4 观察一次完整调用的时序

把日志打开,一次完整调用是这样的:Client 启动 Server 子进程 → 发送 initialize 请求 → 调用 list_tools 拿到工具列表 → 用户输入问题 → Client 把问题和工具列表发给模型 → 模型返回 tool_calls → Client 通过 session.call_tool 发 JSON-RPC 请求给 Server → Server 执行 query_weather 返回结果 → Client 把结果回灌模型 → 模型生成最终回答。整条链路里,MCP 负责 Client 和 Server 之间的通信,TaoToken 负责 Client 和模型之间的通信,两者解耦,各管一段。

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

跑不通是常态,我把几个高频报错和对应排查路径列出来,你对照日志定位。

5.1 401 Unauthorized

最常见。日志里出现Error code: 401或invalid api key,说明模型通道的 Key 有问题。排查顺序:第一,检查 .env 里 TAOTOKEN_API_KEY 有没有多余空格或引号;第二,确认 Key 没有过期或被删除,去控制台 API Keys 页面核对;第三,确认 base_url 写的是 https://taotoken.net/api ,不要多加 /v1 或结尾斜杠,路径错了也会返回 401 或 404。改完 .env 记得重启进程,环境变量是启动时加载的。

5.2 local proxy failed / connection error

日志里出现local proxy failed或APIConnectionError,通常是网络层问题。先确认你的运行环境能正常访问外网 API 地址,用 curl 测一下:

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

如果 curl 通但代码不通,检查代码里 base_url 是不是被某个全局代理配置覆盖了。另外,MCP Server 作为子进程启动时,环境变量可能没继承,导致 Server 侧读不到 Key。解决办法是在 mcp.json 的 env 字段里显式传入,别依赖父进程环境。

5.3 reading 'choices' 报错

TypeError: Cannot read properties of undefined (reading 'choices')或 Python 侧KeyError: 'choices',说明模型返回的响应结构和你预期的不一样。两种可能:一是请求根本没成功,返回的是错误对象,你直接取了 choices;二是模型 ID 写错了,接口返回了非预期格式。排查方法:在请求后先打印完整响应:

resp = self.client.chat.completions.create(...) print(resp.model_dump_json(indent=2))

看返回里有没有 error 字段。如果有,错误信息会告诉你具体原因,通常是模型 ID 不存在或参数不合法。

5.4 OAuth / 认证相关报错

如果你用的是某些需要 OAuth 的 Client(比如 Claude Code 类工具),日志里可能出现OAuth token expired或authentication failed。这类报错和模型 API Key 是两套东西:OAuth 是 Client 工具自身的登录态,API Key 是模型通道的凭证。先确认 Client 工具本身登录正常,再检查模型通道的 Key。两者不要混在一起排查。

5.5 工具被调用但参数为空

模型返回了 tool_calls,但 arguments 是空对象或缺少 city 字段。这通常是工具描述写得不够明确。在 Server 里注册工具时,description 要写清楚“这个工具做什么、参数是什么格式”。比如 query_weather 的描述写成“查询指定城市的当前天气,city 参数为城市英文名,如 Beijing”,模型就能正确填参。参数 schema 里给 city 加上 description 也有帮助。

5.6 Server 启动即退出

运行 client.py 后立刻报Connection closed或 Server 进程退出。先用 Inspector 单独跑 Server,看有没有 Python 异常。常见原因是依赖没装全(缺 mcp 或 httpx),或者 server.py 里有语法错误。stdio 模式下 Server 的任何 stderr 输出都可能干扰协议通信,调试信息尽量写到文件而不是 print 到 stdout。

6. 继续往下走:把 MCP 用到真实 Agent 项目里

跑通天气查询这个最小闭环之后,你会发现 MCP 的扩展方式非常线性:加一个新工具,就是在 Server 里多注册一个函数,Client 侧一行不用改,工具列表会自动同步。这意味着你可以把数据库查询、文件操作、网页抓取、命令行执行分别做成独立的 MCP Server,Agent 按需连接。

如果你打算长期做 Agent 开发,建议把模型通道固定下来,用 TaoToken 的 Coding Plan 管理调用额度和模型切换,避免每次换模型都改代码。入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。想先在线试一下模型对话效果的,可以去 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。接入过程中遇到报错,先查文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,大部分配置问题里面都有说明。

最后给一个实用建议:MCP Server 的调试不要靠猜,Inspector 是你的第一工具。每次改完 Server 先用 Inspector 确认工具列表和 schema 正确,再跑 Client。这样能把问题范围缩小到“协议层”还是“模型层”,排查效率高很多。工具调用的稳定性,八成取决于工具描述写得清不清楚,而不是模型有多强。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询