主题
Function Calling:让模型真的能调你的函数
模型本身只会输出文本,读不了数据库、查不了天气、更下不了单。Function Calling(函数调用,现在也常叫 Tool Calling/工具调用)就是给它一份可调用的接口清单,让它用结构化 JSON 告诉你:我要调哪个函数、传什么参数。真正执行的人是你。这篇把这条链路完整走一遍。
模型只负责说,不负责做
关键认知:它没有任何执行能力。模型见过海量函数签名,于是学会按 JSON Schema(一种描述 JSON 结构的规范)的约束输出调用意图。你可以把它理解成:把 TypeScript 函数的签名和注释改写成接口文档递给它,它回你一段能直接当参数用的 JSON 字符串。参数是字符串形式的 JSON,不是对象,必须自己解析,而且可能失败——官方文档明确说模型不保证输出合法 JSON,也可能编造你没定义的字段,执行前务必校验。
一次完整回合:五步走
- 带着
tools数组发请求,里面是允许它调用的函数; - 模型返回
tool_calls,含函数名和参数; - 你在本地真正执行这个函数;
- 把结果作为一条 tool 消息回填进对话历史;
- 再发一次请求,模型读完结果给出最终回答。
第 4 步最容易翻车:回填的消息必须带上 tool_call_id,和上一步的 id 对上号。注意各家叫法不同,OpenAI 风格里旧的 function_call 字段已标记废弃,官方建议用 tool_calls;Anthropic 风格差别更大,工具定义是扁平的 name/description/input_schema(不是 parameters),响应里是 content 中的 tool_use 块,回填则是带 tool_use_id 的 tool_result。字段名以各家官方文档为准,别混着抄。
json
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询某个城市当前的天气。用户问天气、温度、是否下雨时使用。",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名,中文或拼音,例如「杭州」"
},
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
}描述写得清楚,模型才不乱填
description 是给模型看的注释,写它就等于写函数文档。要点:说清「什么时候该用」,而不只是「这是干嘛的」;每个参数都补说明和例子,「城市名,中文或拼音」比干巴巴一个 string 有用得多;能枚举就用 enum 收窄取值;必填项老实标进 required。官方的验收标准可以借用:实习生只拿到这些信息能正确调用吗?不能就把缺的补进描述里。
并行调用与多轮循环
模型一次可能返回多个 tool_calls,比如同时查北京和上海,这就是并行调用(parallel tool calling)——能并发就并发,再把多条结果一起回填。任务也可能需要好几轮:先查接口拿 id,再拿 id 查详情,直到模型不再返回工具调用、而是给出一段人话。所以外面通常要包一个循环,并设最大轮数上限,防止它绕不出来。
出错也要回填,别直接抛给用户
接口超时、参数没过、权限不足都很常见。正确做法是把错误当成一种结果回填给模型,比如告知「调用失败:city 参数为空」,让它换参数重试或改用别的工具,必要时再向用户解释。直接把原始报错甩到用户脸上,等于浪费了它的纠错能力。Anthropic 的 tool_result 还支持 is_error 标记,可以显式标错。
ts
const result = await runTool(call);
history.push({
role: "tool",
tool_call_id: call.id,
content: result.ok ? JSON.stringify(result.data) : `调用失败:${result.error}`,
});危险操作必须人工确认
读写要分开看。查天气、查列表这类只读工具可以放开自动执行;删数据、转账、发邮件、改线上配置这类有副作用的操作,执行前一定要人工确认:把模型想调的函数和参数摊开给人看,人点了同意再执行。别把「模型说的」当成「已授权的」。
常见坑
- 参数是字符串不是对象,直接当对象用会炸,记得
JSON.parse并包 try/catch。 - 忘了回填
tool_call_id/tool_use_id,模型对不上号会直接报错。 - 工具描述含糊,模型就会乱调,或者该调的时候不调。
- 一次暴露几十个工具,选择准确率明显下降,官方建议单轮控制在 20 个以内(软建议)。
- 各家字段名不通用,复制代码前先看对应厂商的官方文档。