1. 从一次 Agent 翻车说起:四种工具调用形态的边界到底在哪
上周帮朋友排查一个 Agent 项目,现象很典型:模型能正确识别用户意图,但一到执行环节就开始乱套——该查数据库的时候去调了本地脚本,该读文件的时候把整个 CSV 塞进了上下文,最后 token 爆掉直接报错。翻代码发现,他把 Function Calling、MCP、Skill、CLI 四种东西混在一起用,没有区分各自的职责边界。
这个问题在搭建 Agent 或 AI 工作流的开发者里非常普遍。LLM 工具调用(Tool Use)这个概念被讲得很多,但真正落到选型时,很多人分不清 Function Calling、MCP、Skill、CLI 到底该在什么场景用哪个。有人觉得 MCP 是 Function Calling 的升级版,有人把 Skill 当成保存好的 Prompt,还有人用 CLI 脚本硬扛所有数据查询任务,结果上下文窗口被撑爆。
我试过把这四种形态拆开对照,发现它们其实处在工具调用链路的不同层次上。Function Calling 解决的是「模型怎么输出结构化的调用请求」,MCP 解决的是「工具怎么标准化接入并复用」,Skill 解决的是「拿到工具后该按什么流程执行」,CLI 解决的是「本地命令怎么被安全地触发」。四者不是替代关系,而是互补关系。
这篇文章面向正在搭建 Agent 或 AI 工作流的开发者,目标很明确:给出一张可对照的选型表,配一套最小验证路径。你会看到每种形态的可复制配置片段、逐项验证动作,以及如何通过 TaoToken 统一 Key 和 API 通道管理多工具调用时的鉴权与端点配置。读完你可以直接对照自己的项目做选型,不用再靠猜。
2. TaoToken 统一 Key 前置:多工具调用时的鉴权与端点管理
在展开四种形态的配置之前,先解决一个绕不开的前置问题:多工具调用场景下,鉴权和端点配置怎么管。
假设你的 Agent 同时要用 Function Calling 调模型、用 MCP 接外部工具、用 CLI 执行本地命令。如果每个环节都单独配一套 Key 和 Base URL,维护成本会很高,而且容易出现某个环节 Key 过期导致整条链路断掉的情况。TaoToken 在这里的作用是提供一个统一的 API 通道,让你用同一个 Key 管理多工具调用时的模型请求。
具体来说,TaoToken 的 API 端点统一为https://taotoken.net/api,你只需要在环境变量里配一次 Key,所有走 OpenAI 兼容协议的工具调用请求都可以复用这个配置。对于 Function Calling 这种需要模型原生支持结构化输出的场景,统一端点意味着你不用为每个模型单独改 Base URL。
配置方式很简单,在项目根目录建一个.env文件:
TAOTOKEN_API_KEY=sk-your-key-here TAOTOKEN_BASE_URL=https://taotoken.net/api然后在代码里读取:
import os from openai import OpenAI client = OpenAI( api_key=os.getenv("TAOTOKEN_API_KEY"), base_url=os.getenv("TAOTOKEN_BASE_URL") )这样配置的好处是,当你从 Function Calling 切换到 MCP 客户端配置时,Base URL 和 Key 不用改,只需要在 MCP 的配置文件里引用同一组环境变量。对于 CLI 脚本,你也可以在脚本开头 source 这个.env,保证鉴权信息一致。
需要提醒的是,TaoToken 的 API Key 管理页面在https://taotoken.net/api-keys,你可以在这里生成和轮换 Key。如果你用的是 Claude Code 这类工具,接入文档在https://taotoken.net/doc,里面有针对不同客户端的配置示例。
统一 Key 的另一个价值在于排查问题。当工具调用失败时,你可以先确认是不是 Key 或端点的问题,排除掉鉴权因素后再去看工具本身的配置。这个排查顺序能省很多时间。
3. 四种形态的可复制配置:Function Calling、MCP、Skill、CLI 对照
这一节给出四种形态的最小可复制配置,你可以直接拿去改。每种配置我都会标注关键参数和路径,保证和原文一致。
3.1 Function Calling 的 JSON Schema 配置
Function Calling 的核心是工具定义。模型通过description字段判断是否调用某个工具,所以描述要写清楚「这个工具做什么、什么时候用」。
{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的实时天气。当用户询问天气、温度、是否下雨时调用此工具。", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如:北京、上海" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位,默认摄氏度" } }, "required": ["city"] } } }运行时是「两轮对话 + 中间执行」的闭环:第一轮你把工具定义和用户问题一起发给模型,模型返回finish_reason为tool_calls的响应;你的代码执行工具,把结果塞回对话;第二轮模型基于工具结果生成最终答案。模型支持一次返回多个tool_calls,可以实现并行调用。
3.2 MCP 的客户端配置
MCP 是 Client-Server 架构,Server 是工具实现方,Client 是 AI 应用侧。底层通信使用 JSON-RPC 2.0,传输层支持 Stdio(本地)和 Streamable HTTP(远程)。
以 Claude Desktop 的 MCP 配置为例,配置文件路径是~/Library/Application Support/Claude/claude_desktop_config.json(macOS):
{ "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/yourname/projects" ] }, "database": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb" ] } } }MCP 的核心能力分三类:Tools 是有副作用的操作(需要授权),Resources 是只读数据(无副作用),Prompts 是可复用的提示词模板。一个 Client 可以连接多个 Server,这就是「一次实现到处复用」的含义。
3.3 Skill 的文件夹结构
Agent Skill 本质是一个包含指令、脚本和资源的文件夹。核心是skill.md指令文件,可以带上脚本、模板、参考文档。
skills/ code-review/ skill.md scripts/ lint.sh templates/ review-template.mdskill.md的内容示例:
# Code Review Skill ## 何时使用 当用户提交代码审查请求,或 Agent 检测到需要审查代码变更时加载。 ## 执行流程 1. 读取变更文件列表 2. 对每个文件运行 scripts/lint.sh 3. 按 templates/review-template.md 格式输出审查意见 ## 注意事项 - 只审查变更部分,不审查整个仓库 - 安全问题优先级最高Skill 具有渐进式加载设计:只读元数据 → 按需加载指令 → 用到时才读取资源。这和 Prompt 的区别在于,Skill 能被 Agent 自动发现和按需加载,不需要每次手动输入。
3.4 CLI 的调用配置
CLI 偏本地命令执行,关键是把数据留在模型外部,只把精简结果返回给模型。
#!/bin/bash # scripts/query-logs.sh # 查询最近错误日志,只返回摘要 LOG_DIR="/var/log/app" RESULT=$(grep -r "ERROR" "$LOG_DIR" | tail -20 | awk '{print $1, $2, $NF}') # 只返回精简摘要,不返回完整日志 echo "最近20条错误摘要:" echo "$RESULT" | head -5 echo "..." echo "完整结果已保存到 /tmp/error-summary.txt"调用时,模型只需要执行bash scripts/query-logs.sh,拿到的是几行摘要,而不是整个日志文件。这就是 CLI 绕过上下文窗口瓶颈的核心机制:数据在模型外部处理,只把精简结果返回。
四种形态的对照表:
| 形态 | 解决的问题 | 配置位置 | 数据流向 | 适用场景 |
|---|---|---|---|---|
| Function Calling | 模型输出结构化调用请求 | 代码内 JSON Schema | 模型决策,代码执行 | 单次工具调用、API 请求 |
| MCP | 工具标准化接入与复用 | 客户端配置文件 | 数据流入模型上下文 | 多工具接入、跨客户端复用 |
| Skill | 可复用能力封装 | skills/ 文件夹 | 按需加载指令和资源 | 复杂流程、团队复用 |
| CLI | 本地命令执行 | 脚本文件 | 数据留在外部 | 大数据处理、本地操作 |
4. 逐项验证请求:从 401 到成功返回的完整路径
配置写完只是第一步,真正跑通才算数。这一节给出四种形态的逐项验证动作,以及常见报错的排查路径。
4.1 Function Calling 验证
用 curl 发一个最小请求,确认模型能返回tool_calls:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "北京今天天气怎么样?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的实时天气", "parameters": { "type": "object", "properties": { "city": {"type": "string"} }, "required": ["city"] } } }], "tool_choice": "auto" }'成功返回的特征是finish_reason为tool_calls,且message.tool_calls数组里有结构化的调用请求。如果返回的是普通文本,说明模型没有识别到工具调用意图,检查description是否写清楚。
4.2 MCP 验证
MCP 的验证分两步。先确认 Server 能启动:
npx -y @modelcontextprotocol/server-filesystem /tmp如果 Server 正常启动,会输出监听信息。然后在客户端里发一个测试请求,比如让 Claude Desktop 读取/tmp下的文件列表。成功的话,客户端会显示工具调用过程。
4.3 Skill 验证
Skill 的验证是确认 Agent 能自动发现并加载。在支持 Skill 的 Agent 里,输入一个需要 code-review 的任务,观察 Agent 是否主动加载了skills/code-review/skill.md。如果 Agent 没有加载,检查 skill 文件夹是否放在 Agent 的扫描路径下。
4.4 CLI 验证
CLI 验证最简单,直接跑脚本:
bash scripts/query-logs.sh确认输出是精简摘要,而不是完整日志。如果输出太长,说明脚本没有做好数据裁剪,需要调整awk或head参数。
4.5 常见报错对照
| 报错信息 | 可能原因 | 排查动作 |
|---|---|---|
| 401 Unauthorized | Key 无效或过期 | 检查TAOTOKEN_API_KEY是否正确,去 api-keys 页面确认 |
| local proxy failed | 本地代理配置冲突 | 检查环境变量里是否有残留的代理设置 |
| reading choices | 响应格式不符合预期 | 确认 Base URL 是https://taotoken.net/api,模型名正确 |
| OAuth error | 客户端鉴权失败 | 重新走一遍客户端授权流程,确认回调地址正确 |
| context length exceeded | 上下文窗口溢出 | 检查是否把大数据直接塞进了模型上下文,改用 CLI 或 MCP 摘要 |
如果报错涉及 Claude Code 的 OAuth 流程,检查~/.claude/settings.json里的配置:
{ "apiKey": "sk-your-key-here", "baseUrl": "https://taotoken.net/api", "model": "claude-3-5-sonnet-20241022" }这三件套(Base URL + Key + Model ID)必须同时正确,缺一个都会导致鉴权失败。
5. 本篇常见错排查:从真实报错反推配置问题
这一节把上一节的报错展开,给出具体的排查步骤。这些是我在实际项目里踩过的坑,你可以直接对照。
5.1 401 报错:Key 配置的三种常见错误
401 是最常见的报错,通常有三种原因。第一种是 Key 写错了,比如复制时多了空格。第二种是 Key 过期了,需要去https://taotoken.net/api-keys重新生成。第三种是环境变量没生效,比如在.env里配了但代码里没 load。
排查顺序:先确认环境变量是否被正确读取,再确认 Key 本身是否有效。可以用一个最小 curl 请求测试:
curl -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ https://taotoken.net/api/v1/models如果这个请求返回模型列表,说明 Key 和端点都没问题,问题在代码里。
5.2 local proxy failed:代理配置冲突
这个报错通常是因为环境里残留了代理设置。检查HTTP_PROXY、HTTPS_PROXY、ALL_PROXY这几个环境变量,如果有值且指向不可用的地址,就会导致请求失败。
echo $HTTP_PROXY echo $HTTPS_PROXY unset HTTP_PROXY HTTPS_PROXY ALL_PROXY清掉之后再试。如果是在 Docker 里跑,检查容器的网络配置。
5.3 reading choices:响应格式解析失败
这个报错说明客户端拿到了响应,但解析时找不到choices字段。常见原因是 Base URL 配错了,比如配成了https://taotoken.net而不是https://taotoken.net/api。另一个原因是模型名写错了,导致服务端返回了错误格式的响应。
确认 Base URL 和模型名:
client = OpenAI( api_key=os.getenv("TAOTOKEN_API_KEY"), base_url="https://taotoken.net/api" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "test"}] ) print(response.choices[0].message.content)5.4 OAuth error:Claude Code 鉴权流程
Claude Code 的 OAuth 流程容易出错的地方在回调地址和配置文件路径。确认~/.claude/settings.json里的baseUrl是https://taotoken.net/api,apiKey是有效的 Key,model是支持的模型 ID。
如果 OAuth 流程卡住,可以尝试重新初始化:
claude config set --global apiKey sk-your-key-here claude config set --global baseUrl https://taotoken.net/api5.5 context length exceeded:上下文窗口溢出
这个报错说明塞进模型上下文的数据太多了。排查方向是检查 MCP 工具返回的数据量,以及 CLI 脚本是否把完整数据返回给了模型。
优化方法有三种。第一种是让 MCP Server 只返回必要字段或摘要。第二种是在 MCP Client 和 Server 之间加代理层,拦截大响应,存到外部存储,只返回摘要和 ID。第三种是把大数据处理改成 CLI 脚本,数据在模型外部处理,只返回精简结果。
核心原则是:不要让模型去「看」数据,而是让模型去「指挥」工具处理数据。把模型当成大脑,把工具当成手脚,大脑不需要记住身体的每一个细胞。
6. 选型建议与统一 Key 的长期价值
回到开头那个 Agent 翻车案例,问题的根源不是某个工具用错了,而是没有区分四种形态的职责边界。Function Calling 负责模型决策,MCP 负责工具接入,Skill 负责流程封装,CLI 负责本地执行。四者各司其职,混用就会出问题。
选型时可以先问三个问题:这个任务需要模型输出结构化调用请求吗?需要跨客户端复用工具吗?需要处理大数据吗?根据答案对照下面的路径:
- 单次 API 调用、需要模型决策 → Function Calling
- 多工具接入、需要跨客户端复用 → MCP
- 复杂流程、需要团队复用 → Skill
- 大数据处理、本地命令执行 → CLI
如果你正在搭建长期运行的 Agent 或 AI 工作流,建议把 TaoToken 的统一 Key 作为基础设施的一部分。这样当你从 Function Calling 扩展到 MCP、从 MCP 扩展到 Skill 时,鉴权和端点配置不用重复折腾。模型对话可以在https://taotoken.net/models验证,接入文档在https://taotoken.net/doc,Coding Plan 适合长期编码和 Agent 场景。
最后留一个实用技巧:在项目里建一个tool-config/目录,把四种形态的配置片段都放进去,用环境变量引用统一的 Key 和 Base URL。这样换模型或换工具时,只需要改一处配置,不用满项目找散落的端点地址。这个习惯能帮你省下大量排查鉴权问题的时间。