📞 Function Calling 原理与实战
Function Calling(函数调用)是 LLM 与外部世界交互的核心机制。模型不再只生成文本,而是输出结构化的函数调用请求,由应用程序执行后将结果返回模型,形成感知—决策—执行的闭环。
🔄 工作流程
Function Calling 完整生命周期
用户消息 → 模型判断 → 输出工具调用 → 执行 → 返回结果 → 最终回复
1
用户输入 + 可用工具列表(含 Schema)发送给 LLM
▼
2
LLM 决策:判断是否需要调用工具,如需则输出
tool_calls(含函数名 + 参数 JSON)▼
3
应用程序执行:解析 LLM 输出,调用对应的本地函数 / API
▼
4
结果回传:将函数执行结果以
tool role 消息追加到对话上下文▼
5
LLM 整合:基于工具返回结果生成最终自然语言回复
📋 工具定义规范
不同模型厂商的 Function Calling 格式存在差异,但核心概念一致:名称、描述、参数 Schema。
OpenAI vs Anthropic 格式对比
🤖 OpenAI (GPT-4o)
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
}
🧠 Anthropic (Claude)
{
"name": "get_weather",
"description": "获取指定城市的天气",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
| 特性 | OpenAI | Anthropic |
|---|---|---|
| 工具定义键 | type: "function" + function | 直接 name + input_schema |
| 参数 Schema 键 | parameters | input_schema |
| 多工具支持 | tools 数组 | tools 数组 |
| 并行调用 | ✅ 原生支持 | ✅ 原生支持 |
| 强制调用 | tool_choice: "required" | 通过 system prompt 约束 |
| 流式输出 | ✅ 增量 tool_calls | ✅ 增量 content blocks |
📐 参数 Schema 最佳实践
1
描述要清晰具体
描述不仅仅是给人看的,模型也依赖描述来理解工具的用途。避免模糊描述,给出使用场景和约束条件。
2
使用 enum 约束可选值
对有限选项的参数使用 enum,减少模型的幻觉和格式错误。
3
合理设置 required
只将真正必需的参数标记为 required。过多的必需参数会降低调用成功率,过少则可能导致关键信息缺失。
4
遵循 JSON Schema 规范
使用标准 JSON Schema (Draft 7+),支持 oneOf、anyOf、嵌套对象等高级结构。避免使用不兼容的自定义扩展。
5
控制工具数量
每次请求的工具数量建议控制在 10-20 个以内。工具过多会增加模型选择错误的风险,可采用分步路由策略。
🎯 工具选择策略
| 策略 | 原理 | 适用场景 | 优缺点 |
|---|---|---|---|
| 全量注册 | 将所有工具一次性发给模型 | 工具数 < 20 | ✅ 简单直接 ⚠️ 工具多时性能下降 |
| 分类路由 | 先让模型选择工具类别,再下发该类工具 | 工具数 20-100 | ✅ 减少上下文消耗 ⚠️ 需要额外的分类步骤 |
| 语义检索 | 用 embedding 匹配用户意图最相关的 top-K 工具 | 工具数 > 100 | ✅ 可扩展性强 ⚠️ 检索质量依赖 embedding |
| 混合策略 | 语义检索 + LLM 筛选 | 大规模工具库 | ✅ 兼顾精度与效率 ⚠️ 系统复杂度高 |
🛡️ 错误处理与重试
常见错误类型
| 错误类型 | 表现 | 处理方式 |
|---|---|---|
| 参数格式错误 | JSON 解析失败、类型不匹配 | 将错误信息反馈给 LLM,让其修正后重试 |
| 必填参数缺失 | 缺少 required 字段 | 提示 LLM 缺少哪个参数,请求补全 |
| 函数执行异常 | 运行时错误、超时 | 返回友好错误消息给 LLM,让其换用其他工具或降级处理 |
| 幻觉调用 | 调用了不存在的工具或参数 | 校验工具名称和参数 Schema,拒绝执行并提示 |
| 无限循环 | LLM 反复调用同一工具无进展 | 设置最大调用轮次(通常 5-10 轮) |
推荐重试策略
1
解析重试
参数 JSON 解析失败 → 将原始输出 + 错误信息反馈 LLM,最多重试 2 次
2
执行重试
函数执行超时或临时故障 → 指数退避重试(1s, 2s, 4s),最多 3 次
3
降级策略
重试全部失败 → 告知用户当前无法完成该操作,并提供替代方案
📊 主流模型 Function Calling 能力对比
| 模型 | FC 支持 | 并行调用 | 强制调用 | 流式 FC | 严格模式 | 多轮对话 | 典型准确率 |
|---|---|---|---|---|---|---|---|
| GPT-4o | ✅ 原生 | ✅ | ✅ | ✅ | ✅ strict 模式 | ✅ | ~95% |
| GPT-4.1 | ✅ 原生 | ✅ | ✅ | ✅ | ✅ strict 模式 | ✅ | ~96% |
| Claude 3.5 / 4 | ✅ 原生 | ✅ | ⚠️ system prompt | ✅ | ❌ | ✅ | ~93% |
| Gemini 2.5 Pro | ✅ 原生 | ✅ | ✅ | ✅ | ❌ | ✅ | ~91% |
| DeepSeek-V3 | ✅ 原生 | ✅ | ⚠️ prompt 引导 | ✅ | ❌ | ✅ | ~90% |
| Qwen-Max | ✅ 原生 | ✅ | ⚠️ prompt 引导 | ✅ | ❌ | ✅ | ~88% |
| LLaMA 4 | ✅ 原生 | ✅ | ⚠️ prompt 引导 | ⚠️ 部分支持 | ❌ | ✅ | ~87% |
* 准确率为综合评测估算值,实际表现因具体场景和 prompt 设计而异。建议针对自身业务场景做专项评测。
💡 实战建议
从 GPT-4o 或 Claude 开始验证 FC 可行性,然后根据需要适配多模型。使用统一的工具定义中间层(如 LangChain 的 Tool 接口),便于在不同模型间切换。