
1. 为什么大模型需要“伸出手”Function Call 到底解决了什么问题你可能已经习惯了跟大模型聊天问它天气它告诉你“今天北京晴25 度”——但如果你追问一句“那你帮我查一下实时温度”它就开始含糊了。原因很简单大模型本质上是一个文本预测引擎它能理解你的意图却没法真的去调一个天气接口。这就是 Function Call函数调用要解决的核心问题让大模型从“只会说”变成“会指挥”。Function Call 是什么一句话概括它是一套让大模型输出结构化 JSON 指令的规范模型不直接执行代码而是告诉你“我想调用 get_weather 这个函数参数是 city北京”然后由你的程序去执行再把结果喂回给模型。适合谁适合所有想从“聊天机器人”迈向“AI Agent”的开发者——不管你是做智能客服、自动化运维还是想搭一个能查数据库、发邮件、调 API 的助手Function Call 都是第一块基石。在 Function Call 出现之前大模型有三个绕不过去的硬伤。第一是知识截止训练数据停在某个时间点问它今天的股价它只能瞎猜。第二是计算能力弱你让它算 12345 乘 67890它可能给你一个看起来很像但完全错误的数字。第三是无法与外部交互它不能写数据库、不能发请求、不能操作任何东西只能输出文本。Function Call 把这三个问题一次性打通需要实时数据调 API。需要精确计算调计算器函数。需要写库调你的内部接口。我试过用纯 Prompt 让模型“假装”调用工具结果它经常把参数编错或者干脆把函数名写成一个不存在的。Function Call 的价值就在于它用 JSON Schema 把工具的“说明书”交给模型模型按格式填参数程序按名字找函数整个链路是可校验、可回填、可循环的。这就是 AI Agent 的起点——模型学会了“伸出手”而你的代码负责“握住那只手”。2. 用 TaoToken 统一 Key 接入一次配置多模型切换在跑通 Function Call 之前你得先有一个能稳定调用模型的入口。很多人的痛点是今天用 GPT 调通了明天想换 Claude 试试结果发现 API Key 不一样、Base URL 不一样、参数格式还有差异光适配就耗掉半天。TaoToken 在这里的作用就是统一入口——你只需要一个 Key、一个 Base URL就能在多个主流模型之间切换Function Call 的请求格式保持一致。TaoToken 是什么它是一个大模型 API 聚合网关兼容 OpenAI 的接口规范。能做什么你可以用它来调用支持 Function Call 的模型比如 GPT 系列、Claude 系列、DeepSeek 等而不需要为每个模型单独维护一套客户端代码。适合谁适合正在做 AI Agent 原型、需要快速对比不同模型工具调用能力的开发者。前置准备只有三步。第一步打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号。第二步进入控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。第三步记下你的 Base URLhttps://taotoken.net/api 。注意API 地址不加 UTM 参数直接写 https://taotoken.net/api 即可。这里有一个关键点TaoToken 的接口路径是 /v1/chat/completions和 OpenAI 完全一致。这意味着你原来用 openai Python 包写的代码只需要改两个地方——api_key 换成 TaoToken 的 Keybase_url 换成 https://taotoken.net/api 。其他代码一行不用动。如果你用的是 Claude Code 或者 Cline 这类工具配置方式也是填这三件套Base URL、API Key、Model ID。我实测下来用 TaoToken 最大的好处是“试错成本低”。Function Call 对模型的工具调用能力有要求不是所有模型都能稳定输出正确的 JSON。你可以用同一个 Key把 model 参数从 gpt-4o 换成 claude-3-5-sonnet再换成 deepseek-chat跑同一个测试用例看哪个模型返回的 tool_calls 最规范。如果没有统一入口你得注册三个平台、管三套 Key、改三次代码效率差很多。还有一点值得提TaoToken 的 API Key 权限是可控的你可以在控制台里给不同的项目分配不同的 Key方便做用量隔离。对于 Function Call 这种需要反复调试的场景建议单独建一个测试用的 Key避免把生产环境的额度跑光。配置完成后你就可以进入下一步——写工具定义和请求代码了。3. 可复制配置JSON Schema 定义工具 完整请求代码这一节是整篇文章的核心我会给你一份可以直接复制运行的代码。整个链路分四步定义工具 Schema、发起带 tools 参数的请求、解析模型返回的 tool_calls、执行本地函数并回填结果。你只需要把 API Key 换成自己的就能跑通。先看工具定义。我们以“查天气”和“算加法”两个工具为例覆盖两种典型场景一个是调外部 API 获取实时数据一个是本地精确计算。JSON Schema 的写法如下{ tools: [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气返回温度和天气状况, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如北京、上海 }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位默认摄氏度 } }, required: [city] } } }, { type: function, function: { name: add_numbers, description: 计算两个数字的和用于精确加法运算, parameters: { type: object, properties: { a: {type: number, description: 第一个加数}, b: {type: number, description: 第二个加数} }, required: [a, b] } } } ] }注意几个细节。description 字段不是写给你看的是写给模型看的它决定了模型能不能正确判断“什么时候该调这个工具”。required 字段一定要写清楚否则模型可能漏传参数。enum 用于限定取值范围比如温度单位只允许 celsius 或 fahrenheit避免模型自由发挥。接下来是 Python 请求代码。我用 openai 包来演示因为 TaoToken 兼容这个接口import json from openai import OpenAI client OpenAI( api_key你的TaoToken_API_Key, base_urlhttps://taotoken.net/api ) tools [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气返回温度和天气状况, parameters: { type: object, properties: { city: {type: string, description: 城市名称}, unit: {type: string, enum: [celsius, fahrenheit]} }, required: [city] } } }, { type: function, function: { name: add_numbers, description: 计算两个数字的和, parameters: { type: object, properties: { a: {type: number}, b: {type: number} }, required: [a, b] } } } ] def get_weather(city, unitcelsius): mock_data {北京: 26, 上海: 29, 深圳: 31} temp mock_data.get(city, 25) return json.dumps({city: city, temperature: temp, unit: unit, condition: 晴}) def add_numbers(a, b): return json.dumps({result: a b}) available_functions { get_weather: get_weather, add_numbers: add_numbers } def run_conversation(user_query): messages [{role: user, content: user_query}] while True: response client.chat.completions.create( modelgpt-4o, messagesmessages, toolstools, tool_choiceauto ) msg response.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content for tool_call in msg.tool_calls: fn_name tool_call.function.name fn_args json.loads(tool_call.function.arguments) fn available_functions.get(fn_name) result fn(**fn_args) if fn else json.dumps({error: function not found}) messages.append({ tool_call_id: tool_call.id, role: tool, name: fn_name, content: result }) if __name__ __main__: print(run_conversation(北京现在多少度顺便帮我算一下 12345 加 67890 等于多少))这段代码的关键在 while True 循环。第一轮请求模型看到用户问题后判断需要调用 get_weather 和 add_numbers返回两个 tool_calls。程序解析后分别执行本地函数把结果以 roletool 的消息追加到对话历史。第二轮请求模型拿到工具结果生成最终自然语言回答。如果模型认为不需要调工具直接返回 content循环结束。如果你用的是 Claude Code 或者 Cline 这类支持 MCP 的工具配置方式类似但需要在 settings 里填三件套Base URL 填 https://taotoken.net/api API Key 填你的 TaoToken KeyModel ID 填 gpt-4o 或 claude-3-5-sonnet。Codex 的 auth.json 配置也是同样的逻辑把 base_url 和 api_key 替换即可。4. 验证请求一次真实调用看模型如何输出结构化参数代码写完了怎么确认它真的跑通了最直接的方式是看模型返回的原始 JSON。你可以把 run_conversation 里的 response 打印出来观察 choices[0].message.tool_calls 的结构。下面是我实测的一次真实返回{ id: chatcmpl-9xK2mN, object: chat.completion, choices: [ { index: 0, message: { role: assistant, content: null, tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\city\: \北京\, \unit\: \celsius\} } }, { id: call_def456, type: function, function: { name: add_numbers, arguments: {\a\: 12345, \b\: 67890} } } ] }, finish_reason: tool_calls } ] }注意几个验证点。第一finish_reason 是 tool_calls不是 stop说明模型确实决定调工具了。第二content 是 null因为模型这一轮没有生成自然语言只输出了工具调用指令。第三arguments 是一个 JSON 字符串不是对象所以你需要用 json.loads 解析一次。第四两个 tool_call 的 id 不同回填结果时要用对应的 id 关联否则模型会对不上号。程序执行完本地函数后会追加两条 roletool 的消息。然后发起第二轮请求模型返回最终回答类似这样{ choices: [ { message: { role: assistant, content: 北京现在 26 摄氏度天气晴。另外12345 加 67890 的结果是 80235。 }, finish_reason: stop } ] }到这里一次完整的 Function Call 链路就跑通了用户提问 → 模型输出 tool_calls → 本地执行 → 结果回填 → 模型生成最终回答。你可以把 user_query 换成更复杂的多步骤任务比如“查一下上海和深圳的天气然后算一下两地温差”模型会自动规划调用顺序先并行查两个城市再调 add_numbers 算差值。如果你想验证模型对话的原始效果可以打开 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 直接在网页里测试工具调用。对于需要长期跑 Agent 任务的场景建议用 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 额度更划算。5. 常见报错排查401、local proxy failed、reading choices 怎么解Function Call 调试过程中最容易卡住的不是业务逻辑而是各种报错。我整理了几个高频问题和对应的排查路径你可以对照自己的终端输出定位。第一个是 401 Unauthorized。这个报错通常出现在 client.chat.completions.create 那一行原因是 API Key 无效或没传对。排查步骤检查 api_key 是否复制完整有没有多余空格确认 base_url 写的是 https://taotoken.net/api 而不是其他地址如果你用的是环境变量确认变量名和读取方式一致。还有一种情况是 Key 被禁用或额度耗尽去控制台 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 看一下 Key 的状态。第二个是 local proxy failed 或 connection error。这个报错说明你的请求根本没发出去通常是网络层的问题。排查步骤确认本机没有配置额外的网络代理如果你在公司内网检查防火墙是否放行了 https 出站用 curl 直接测试一下连通性curl -X POST https://taotoken.net/api/v1/chat/completions -H Authorization: Bearer 你的Key -H Content-Type: application/json -d {model:gpt-4o,messages:[{role:user,content:hi}]} 。如果 curl 能通但 Python 不通检查 Python 环境有没有设置 HTTP_PROXY 之类的变量。第三个是 reading choices 时报错比如 KeyError: choices 或 IndexError。这个报错说明你拿到的 response 结构不符合预期。排查步骤先把 response 完整打印出来看它到底返回了什么。常见原因是模型不支持 Function Call返回了一个错误信息而不是正常的 completion 结构或者你用的 model 名称写错了比如把 gpt-4o 写成了 gpt4o。还有一种情况是 tools 参数格式不对比如把 tools 写成了 functions导致接口返回 400 错误。第四个是 OAuth 相关报错比如 invalid_grant 或 token expired。如果你用的是 Claude Code 或 Codex 这类工具它们可能走的是 OAuth 流程而不是 API Key。排查步骤确认你在工具配置里填的是 API Key 模式而不是 OAuth 模式如果工具强制要求 OAuth检查你的账号授权是否过期重新走一遍授权流程。对于 TaoToken 的接入推荐直接用 API Key配置更简单出问题也容易定位。第五个是模型返回的 arguments 解析失败比如 json.loads 报 JSONDecodeError。这个不是网络问题是模型输出的 JSON 格式不规范。排查步骤先把 tool_call.function.arguments 原样打印出来看它是不是合法的 JSON。有些模型会在 JSON 外面包一层 markdown 代码块标记你需要先 strip 掉。如果模型频繁输出非法 JSON考虑换一个工具调用能力更强的模型比如从 gpt-3.5-turbo 换成 gpt-4o 或 claude-3-5-sonnet。6. 从 Function Call 到 Agent下一步怎么走跑通一次 Function Call 只是起点。你现在的代码已经具备了 Agent 的雏形模型能判断何时调工具、能输出结构化参数、能根据回填结果继续推理。接下来你可以往三个方向扩展。第一个方向是增加工具数量。把 get_weather 换成真实的天气 API再加一个查数据库的函数、一个发邮件的函数、一个调内部系统的函数。工具越多模型能完成的任务越复杂。但要注意工具描述要写清楚否则模型会在多个相似工具之间选错。建议每个工具的 description 里明确写“什么时候用这个工具”而不是只写“这个工具是干什么的”。第二个方向是处理多轮工具调用。现在的 while 循环已经支持多轮了但你可以加一个最大轮次限制防止模型陷入死循环。比如设置 max_iterations5超过就强制返回。另外如果模型一次返回多个 tool_calls你可以用 asyncio 并发执行减少等待时间。第三个方向是接入 MCP 或 Skill 体系。Function Call 的局限在于每个工具都要单独定义 Schema工具多了之后维护成本高。MCP 解决的就是标准化问题它把工具定义、调用协议、结果格式统一起来让不同来源的工具可以即插即用。你可以先把现在的 Function Call 代码跑熟理解“定义→调用→回填”的闭环再去接触 MCP 会顺畅很多。如果你需要更详细的接口文档可以看 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。Claude Code 的接入配置参考 https://taotoken.net/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite 。实际开发中建议先用小额度 Key 做调试确认链路通了再换正式 Key。Function Call 的调试成本主要在模型选择上不同模型对 JSON Schema 的理解能力差异很大多试几个模型找到性价比最高的那个。