ARTICLE DETAIL

资讯详情

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

Function Calling 原理与 Tool 设计——教 LLM 学会「点菜」:用 TaoToken 统一 Key 跑通 MCP 工具调用

Function Calling 原理与 Tool 设计——教 LLM 学会「点菜」:用 TaoToken 统一 Key 跑通 MCP 工具调用 1. 从「用嘴点单」到「填单子」Function Calling 到底解决了什么如果你正在做 Agent 相关开发大概率遇到过这样的场景让模型输出一段文本然后用正则去匹配Action: xxx再手动解析后面的 JSON 参数。Demo 阶段跑得挺欢一旦工具数量上到十几个或者模型多打了几个字、换了个格式整条链路就开始飘。这就是 Function Calling函数调用要解决的核心问题。用餐厅点菜来类比特别直观顾客LLM负责看菜单、做决定菜单tools 定义写清每道菜的名字、做法、配料服务员你的 Agent 代码负责把顾客的要求记成标准单子然后去后厨真实业务系统执行。没有 Function Calling 的时候顾客只能用嘴描述“我要那个鸡蛋炒番茄、不放糖、加葱花的菜。”服务员听着容易误解。有了 Function Calling菜单上写着“番茄炒蛋含配料说明”顾客只需要说“番茄炒蛋一份不放辣”服务员把这句话记成一张标准单子后厨照单做菜。一句话定义Function Calling 模型根据工具菜单tools输出结构化的调用意图 {工具名 参数}真正执行的是你的代码。关键在“意图”两个字——模型只是“说它想调 get_weather”它不会真的去调。就像顾客只负责点菜绝不进后厨。一次完整的 FC 调用包含四个角色、五个回合用户提问 → Agent 把对话历史 工具菜单发给 LLM → LLM 输出结构化 tool_calls工具名 参数→ Agent 执行工具 → 工具结果以 roletool 放回对话 → LLM 输出最终回答。注意第 3 步和第 6 步LLM 永远只输出“意图”调不调、调的结果什么样LLM 一概不碰动手的全是 Agent 代码。这套机制适合谁任何在做 Agent、智能助手、自动化工作流的开发者。无论你用的是 OpenAI 兼容接口、Claude 系列还是国内模型Function Calling 都是标配能力。而要把这套链路稳定跑通一个统一的 API Key 管理入口能省掉大量切换成本——TaoToken 就是干这个的后面会给出具体配置。2. TaoToken 前置准备统一 Key 与 MCP 工具调用环境搭建在动手写 Function Calling 代码之前先把“服务员”的工牌办好。TaoToken 的作用是提供一个统一的 API 入口让你用同一个 Key 访问多种模型不用在多个平台之间来回切换 Key 和 Base URL。对于 Function Calling 这种需要反复调试工具定义、对比不同模型表现的场景统一 Key 能显著降低环境切换的摩擦。2.1 获取 API Key 与确认 Base URL第一步打开 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册后在控制台创建 API Key。拿到 Key 之后记下两个关键信息Base URLhttps://taotoken.net/apiAPI Key形如sk-xxxxxxxx的字符串这两个值后面会写进环境变量和配置文件。注意 Base URL 不要加 UTM 参数直接用https://taotoken.net/api即可。2.2 环境变量配置推荐用环境变量管理 Key避免硬编码到代码里。Linux/macOS 下在~/.bashrc或~/.zshrc追加export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell 用$env:TAOTOKEN_API_KEYsk-你的Key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api配置完执行source ~/.bashrc或重开终端用echo $TAOTOKEN_API_KEY确认生效。2.3 安装依赖Python 环境下安装 OpenAI SDKTaoToken 兼容 OpenAI 接口格式pip install openai如果你打算用 MCP 协议接入工具再装一个 MCP 客户端库pip install mcp2.4 验证 Key 是否可用写一个最小脚本确认 Key 和 Base URL 能通import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 回复 OK 两个字母}], ) print(resp.choices[0].message.content)如果输出OK说明前置环境已经就绪。如果报 401检查 Key 是否复制完整、有没有多余空格如果报连接错误检查 Base URL 是否写成了https://taotoken.net/api不要带路径后缀。2.5 关于模型选择Function Calling 对模型的指令遵循能力有要求。实测下来gpt-4o-mini、gpt-4o、claude-3.5-sonnet 这类模型在工具选择准确率上表现稳定。你可以通过 TaoToken 的模型对话页面https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite先手动测试几个模型对同一组工具定义的反应再决定生产用哪个。3. 可复制配置MCP 工具定义 JSON 与统一 Key 接入片段这一节给出可以直接复制运行的配置。核心是三件套Base URL、API Key、Model ID。无论你是在 Cline、Claude Code 还是自己写的 Agent 里接入这三个值都是必须的。3.1 工具定义 JSON菜单先定义一个天气查询工具这是最经典的 Function Calling 示例{ type: function, function: { name: get_weather, description: 查询指定城市、指定日期的天气。当用户询问天气、气温、是否下雨、要不要带伞时使用。, parameters: { type: object, properties: { city: { type: string, description: 城市名例如北京、上海 }, date: { type: string, description: 日期例如明天、2026-08-07 } }, required: [city, date] } } }这段 JSON 就是“菜单”一道菜叫get_weatherdescription 写清了“什么情况下点这道菜”parameters 写明了每个参数叫什么、什么类型、必不必须。description 里给例子“例如北京”比写一百字抽象说明都管用模型是看例子猜意图的。3.2 MCP 工具定义格式如果你用 MCP 协议工具定义会包一层 server 配置。以 Cline 的 MCP 配置为例在cline_mcp_settings.json中{ mcpServers: { weather-server: { command: python, args: [-m, weather_mcp_server], env: { TAOTOKEN_API_KEY: sk-你的Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }MCP Server 内部再暴露 tools 列表格式和上面的 JSON 一致。MCP 的价值在于标准化——机票系统、酒店系统、支付系统各自实现一个 MCP Server任何支持 MCP 的 Agent 都能直接调用不用各写各的适配。3.3 Claude Code / Codex 的 settings 配置如果你用 Claude Code 或 Codex 这类编码 Agent配置文件通常在~/.claude/settings.json或项目根目录的.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-3.5-sonnet } }Codex 的auth.json格式{ openai_api_key: sk-你的Key, openai_base_url: https://taotoken.net/api, model: gpt-4o-mini }三件套齐全Base URL 指向 TaoTokenKey 用统一 KeyModel ID 按需选择。这样无论你切到哪个 Agent 工具配置逻辑都是一致的。3.4 完整可运行的 Function Calling 代码把上面的工具定义和 Key 配置串起来import json import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) def get_weather(city: str, date: str) - str: mock {北京: {明天: 小雨18~25℃}} return mock.get(city, {}).get(date, 查不到该城市天气) tools [{ type: function, function: { name: get_weather, description: 查询指定城市、指定日期的天气。当用户询问天气、气温、是否下雨、要不要带伞时使用。, parameters: { type: object, properties: { city: {type: string, description: 城市名例如北京}, date: {type: string, description: 日期例如明天}, }, required: [city, date], }, }, }] messages [{role: user, content: 北京明天天气怎么样}] for _ in range(5): resp client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, ) msg resp.choices[0].message if not msg.tool_calls: print(最终回答, msg.content) break messages.append(msg) for tc in msg.tool_calls: args json.loads(tc.function.arguments) result get_weather(**args) messages.append({ role: tool, tool_call_id: tc.id, content: str(result), })对比之前用正则解析文本协议的写法这里tool_calls是接口返回的强类型字段永远不可能“格式不对”。参数通过arguments字段传递标准 JSON不用手写字符串拼接。4. 验证请求与成功结果一次完整工具调用与结果回填配置写好了接下来验证整条链路是否跑通。这一节把每一步的请求和响应都摊开看方便你对照排查。4.1 第一次请求发菜单Agent 把对话历史和工具菜单一起发给模型。请求体核心部分{ model: gpt-4o-mini, messages: [ {role: user, content: 北京明天天气怎么样} ], tools: [ { type: function, function: { name: get_weather, description: 查询指定城市、指定日期的天气。当用户询问天气、气温、是否下雨、要不要带伞时使用。, parameters: { type: object, properties: { city: {type: string, description: 城市名例如北京}, date: {type: string, description: 日期例如明天} }, required: [city, date] } } } ] }4.2 模型返回收单子模型不会返回一大段话而是返回结构化 tool_calls{ role: assistant, tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\city\: \北京\, \date\: \明天\} } } ] }拿到tool_calls字段就说明模型想调工具。arguments是个字符串里面是 JSON 参数用json.loads解析即可。4.3 执行工具并回填结果Agent 代码执行get_weather(city北京, date明天)得到结果“小雨18~25℃”然后以roletool放回对话{ role: tool, tool_call_id: call_abc123, content: 小雨18~25℃ }tool_call_id必须和上面单子的id对上模型才知道“我那张单子出菜了结果是这样”。4.4 第二次请求模型生成最终回答把 assistant 的 tool_calls 消息和 tool 结果消息都追加到 messages再次请求模型。这次模型不再调工具直接输出北京明天有小雨气温 18~25℃记得带伞。4.5 成功标志整条链路跑通的标志是控制台打印出“最终回答北京明天有小雨气温 18~25℃记得带伞。”如果卡在某一步对照下一节的排查清单。4.6 多工具并行验证Function Calling 的一个隐藏福利是原生支持多工具并行。模型可以一次性输出两个 tool_calls比如同时查航班和酒店{ role: assistant, tool_calls: [ {id: call_1, type: function, function: {name: search_flight, arguments: {\departure_city\:\上海\,\arrival_city\:\北京\,\date\:\2026-08-07\}}}, {id: call_2, type: function, function: {name: search_hotel, arguments: {\city\:\北京\,\checkin\:\2026-08-07\}}} ] }你的代码可以并行执行这两个工具省一半时间。验证时可以在工具定义里加一个search_hotel观察模型是否会在合适场景下同时输出两个 tool_calls。5. 本篇常见错误排查401、local proxy failed、reading choices、OAuth这一节对照真实报错给出排查路径。大部分问题集中在 Key 配置、Base URL 格式、模型返回解析这三类。5.1 401 Unauthorized报错信息openai.AuthenticationError: Error code: 401 - {error: {message: Invalid API key, type: invalid_request_error}}原因通常是 Key 没配好。排查顺序第一确认环境变量是否生效。执行echo $TAOTOKEN_API_KEY如果输出为空说明source没执行或写错了文件。Windows 下用echo $env:TAOTOKEN_API_KEY。第二确认 Key 没有多余空格或换行。从控制台复制时容易带上尾部空格用repr(os.environ[TAOTOKEN_API_KEY])打印出来看。第三确认 Base URL 写对了。必须是https://taotoken.net/api不要写成https://taotoken.net/api/v1或带其他路径。如果 Base URL 错了请求会打到错误端点也可能返回 401。5.2 local proxy failed / Connection error报错信息openai.APIConnectionError: Connection error.或者httpx.ConnectError: [Errno 111] Connection refused这类错误通常是本地网络配置问题。排查第一确认 Base URL 是https://taotoken.net/api不是http://也不是localhost。如果你之前配过其他工具的代理设置检查环境变量HTTP_PROXY、HTTPS_PROXY是否指向了不可用的地址临时unset HTTP_PROXY HTTPS_PROXY再试。第二确认 DNS 能解析。ping taotoken.net看是否通。第三如果公司网络有防火墙确认 443 端口出站没有被拦。5.3 reading choices / KeyError: choices报错信息KeyError: choices或者AttributeError: NoneType object has no attribute choices这通常说明响应体不是预期的 OpenAI 格式。原因可能是第一Base URL 配错了请求打到了某个返回 HTML 的端点。检查resp的原始内容print(resp)或print(response.text)。第二模型名写错了。如果 model ID 不存在某些网关会返回错误结构。确认你用的 model ID 在 TaoToken 的模型列表里存在。第三请求被中间层拦截返回了错误页。检查是否有额外的 header 或参数不被支持。5.4 OAuth / token 过期报错信息Error: OAuth token expired或者invalid_grant如果你用的是 Claude Code 或 Codex 这类带 OAuth 流程的工具报这个错说明登录态过期了。解决方式是重新走一遍登录流程或者在 settings.json 里改用 API Key 方式ANTHROPIC_API_KEY/openai_api_key而不是 OAuth token。用 TaoToken 的统一 Key 可以绕过 OAuth 过期问题因为 Key 是长期有效的。5.5 模型不调工具直接回答现象模型没有返回 tool_calls而是直接输出了一段文本回答。排查第一检查 tools 参数是否真的传进去了。打印请求体确认。第二检查工具 description 是否写得太模糊。如果 description 是“查询数据”这种模型不知道什么时候该用可能选择不调。改成“当用户询问天气、气温、是否下雨时使用”这种明确场景描述。第三检查用户提问是否真的需要工具。如果用户问“你好”模型不调工具是正常的。第四换一个指令遵循能力更强的模型试试。有些小模型对 Function Calling 支持不完整。5.6 参数幻觉出发到达填反现象用户说“上海飞北京”模型输出departure_city: 北京, arrival_city: 上海。这是 LLM 的概率本质决定的它是在预测下一个词不是查数据库。对策分三层第一层schema 兜底。在 parameters 里用enum限定可选值模型填错直接校验失败。第二层业务校验。执行前检查“出发地≠到达地”“日期在今天之后”。第三层反馈修正。校验失败时把错误信息以 tool 结果喂回模型{error: 出发城市不能等于到达城市请重新确认用户意图}模型收到错误后会重新生成参数。6. 稳定跑通链路之后从 Function Calling 到生产级 Agent把上面的配置和代码跑通你已经掌握了 Function Calling 的核心链路。但要从 Demo 走到生产还有几件事必须做。6.1 写操作二次确认工具分两类读操作查天气、查航班无害和写操作下单、付款、删数据有后果。写操作绝不能“模型说调就调”——模型可能被 prompt 注入诱导也可能真的理解错。生产里的下单流程长这样模型请求调用下单工具 → 参数校验必填项齐不齐、金额合不合理→ 用户二次确认“确认订东航 MU5101¥890 吗”→ 执行下单。两道闸参数校验 用户确认。读操作放行写操作必过闸。6.2 工具粒度设计工具该拆多细两个极端都是坑。太细的例子search_flight_early早班机、search_flight_late晚班机、search_flight_direct直飞、search_flight_transfer中转。功能上和search_flight 参数time_range早完全一样但工具列表会爆炸模型每次都要在一堆相似工具里挑更容易挑错。太粗的例子一个query工具想干所有事。模型不知道啥时候用参数也没法定义清楚。经验法则一个函数只干一件完整的事把变化留给参数。航班查询是一个完整的事一个工具早/晚班交给time_range参数天气查询是另一件事另一个工具。两个工具之间职责不重叠模型就不会纠结。6.3 工具结果也是上下文工具返回的结果是放回对话的一个复杂接口可能返回几百个字段的 JSON直接把上下文撑爆。这是上下文管理要解决的问题——滑动窗口、摘要压缩、截断策略。你在 Function Calling 阶段就要有这个意识工具返回值尽量精简只保留模型决策需要的字段不要把整个 API 响应原样塞回去。6.4 用 TaoToken 统一管理多模型生产环境往往需要对比不同模型的表现或者按成本/延迟做路由。TaoToken 的统一 Key 让你不用为每个模型单独配 Key 和 Base URL切换模型只需要改model参数。对于 Function Calling 这种需要反复调试工具定义、对比工具选择准确率的场景这个便利性很实在。如果你在搭长期运行的编码 Agent 或自动化工作流可以看看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite按需选择模型和配额。API Key 管理在控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite接入文档在文档页https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。6.5 下一步上下文管理聊了这么久Agent 的“手”工具基本讲透了。但你可能已经注意到一个问题每次工具结果都要放回对话对话会越来越长模型早晚“失忆”。下一篇讲 Agent 落地第一天敌——上下文管理滑动窗口、摘要压缩、截断策略让 Agent 记住该记住的、忘掉该忘掉的。在那之前先把这篇的 Function Calling 链路跑通。把工具定义 JSON 复制到你的项目里用 TaoToken 的统一 Key 配好环境变量跑一次完整的“发菜单 → 收单子 → 执行 → 回填”流程。跑通之后你再看任何 Agent 框架的工具调用部分都会觉得清晰很多。
返回列表