Function Calling(函数调用)
让大模型根据用户意图自动选择并调用外部函数/API 的能力。是 AI Agent 的基础能力,让模型从对话进化到行动。
什么是 Function Calling
Function Calling(函数调用)是让大模型调用外部函数的能力。你告诉模型有哪些函数可用,模型根据用户意图决定调用哪个函数、传什么参数。
用户:"上海今天天气怎么样?"
↓
模型分析:需要查天气 → 调用 get_weather("上海")
↓
你的代码执行 get_weather("上海") → 返回 { temp: 28°C, condition: "多云" }
↓
模型基于返回结果生成回答:"上海今天 28°C,多云,适合出行。"
注意:模型自己不能执行函数。它只输出"我想调 get_weather('上海')"这条意图,真正的 HTTP 请求 / DB 查询是你的应用代码去跑。这是 Function Calling 最容易被误解的一点。
工作原理
1. 定义函数
你向模型提供函数的 JSON Schema 描述:
{
"name": "get_weather",
"description": "查询指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名,如'上海'"
}
},
"required": ["city"]
}
}
2. 模型决定调用
模型根据用户输入,决定是否需要调用函数:
{
"function_call": {
"name": "get_weather",
"arguments": "{\"city\": \"上海\"}"
}
}
3. 你执行函数
你的代码执行实际函数调用,返回结果给模型:
result = get_weather("上海") # { temp: 28, condition: "多云" }
4. 模型生成最终回答
模型基于函数返回结果生成自然语言回答。
Parallel Tool Calls:一次调多个
现代模型(GPT-4o+、Claude Sonnet 4+、Gemini 2.5+)支持单轮内发起多个工具调用。例如用户问"对比上海和北京的天气":
// 模型一次返回两个 tool_call
{
"tool_calls": [
{ "id": "t1", "function": { "name": "get_weather", "arguments": "{\"city\":\"上海\"}" } },
{ "id": "t2", "function": { "name": "get_weather", "arguments": "{\"city\":\"北京\"}" } }
]
}
应用层可以并行执行这两个调用,把两个结果一起回给模型。能省一轮往返延迟。陷阱:
- 不是所有 API 都默认开启,要看
parallel_tool_calls参数 - 并行的调用之间不能有依赖(B 的输入需要 A 的输出就不能并行)
- 模型对"什么时候适合并行"判断不总是对——独立查询通常 OK,有顺序的操作(先建用户再发通知)会被错并行
Structured Outputs / JSON Mode
Function Calling 的近亲:让模型保证按指定 JSON Schema 输出。区别是不调函数、就要结构化数据本身。
| 平台 | 机制 |
|---|---|
| OpenAI Structured Outputs | response_format: { type: "json_schema", strict: true },保证 100% 符合 schema |
| OpenAI JSON Mode | response_format: { type: "json_object" },只保证是合法 JSON,结构不保证 |
| Anthropic Tool Use | 用 tool definition 当 schema 模板,模型一定按 schema 填 |
| Google Gemini | responseMimeType: "application/json" + responseSchema |
用途:从非结构化文本提取结构化信息(PDF 解析、表单填写、分类打标),比 prompt 让模型"输出 JSON"稳定得多。
与 MCP 的关系
Function Calling 是模型层面的能力(模型决定调什么函数)。 MCP 是协议层面的标准(标准化函数发现和调用的方式)。
MCP 底层依赖 Function Calling,但提供了更完整的生态:
- 动态发现 Server 能力
- 标准化的工具/资源/提示词暴露方式
- 跨工具复用
主流模型 FC 能力对比
| 模型 | Parallel | Strict Schema | 工具数上限(实测稳定) |
|---|---|---|---|
| GPT-5 / GPT-4o | ✅ | ✅ Structured Outputs | 100+ |
| Claude Sonnet 4 | ✅ | ✅ Tool Use | 50-100 |
| Gemini 2.5 Pro | ✅ | ✅ responseSchema | 50+ |
| DeepSeek V3 | ✅ | 部分 | 30-50 |
| GLM-5 | ✅ | 部分 | 30-50 |
| 早期开源(Llama 3 等) | ⚠️ 部分 | ❌ | 10-20 |
经验:工具列表超过 20-30 个,所有模型的选择准确率都会下降。这是为什么 Agent 设计里常见"工具分组 / 分层路由"——先选一个工具组,再在组内选具体工具。
实际应用
AI 编程工具
Cursor / Claude Code 的 function calling:
{
"name": "read_file",
"description": "读取文件内容",
"parameters": { "path": "string" }
}
{
"name": "edit_file",
"description": "编辑文件",
"parameters": { "path": "string", "old": "string", "new": "string" }
}
{
"name": "run_terminal",
"description": "执行终端命令",
"parameters": { "command": "string" }
}
Agent 平台
Coze / Dify 中的自定义工具就是 function calling:
- 定义工具的输入输出
- 模型自动编排调用顺序
- 支持多步工具链
企业应用
- 查询数据库(自然语言 → SQL → 结果)
- 调用内部 API(ERP/CRM/OA)
- 发送通知(邮件/钉钉/飞书)
最佳实践
1. 描述要清晰
// ❌ 模糊
{ "name": "search", "description": "搜索" }
// ✅ 清晰
{ "name": "search_docs", "description": "在企业知识库中全文搜索文档,返回最相关的 5 条结果" }
2. 参数类型要明确
// ❌ 不清楚枚举值
{ "unit": { "type": "string" } }
// ✅ 明确枚举
{ "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] } }
3. 提供错误处理
函数执行失败时,返回结构化错误让模型理解:
{ "error": "city_not_found", "message": "找不到城市'上海'" }
模型看到结构化错误能自己修正(比如把"上海"补成"上海市"再试一次);看到 HTTP 500 那种 stack trace 反而容易卡住。
4. 限制函数数量
一次提供太多函数会让模型困惑。建议:
- 核心函数 5-10 个
- 用 Agent 模式分步调用
- 或用 MCP 动态发现
调试技巧
线上 FC 不稳定时,按顺序排查:
- 看模型是不是真选错了——把
tool_calls完整 dump 出来。常见情况是模型选对了但参数错了。 - 降温到 0——把
temperature设 0 让结果可复现,再调 prompt 和 schema。详见 Temperature 与 Top-P。 - 加 description 到具体例子——
"description": "查城市天气。例:city='上海' → 返回 {temp, condition}"。模型对例子比对类型描述敏感。 - 检查 strict mode——如果用了
strict: true但 schema 写得不严(少 required、字段类型 union),模型会被卡住一直生成不合规的输出。 - 看模型有没有"幻觉调用"——明明没给的工具名也敢叫。这是 prompt 里历史轮里残留了不存在的工具描述,清掉。
延伸阅读
- 协议层:MCP——把工具描述从应用搬到 Server
- Agent 视角:AI Agent——FC 是 Agent 闭环里的「Action」环节
- 控制随机性:Temperature 与 Top-P