Function Calling 原理与实践
什么是 Function Calling
Function Calling(工具调用)是 LLM 选择调用外部函数的机制。模型不直接执行函数,而是输出一个结构化的调用请求,由应用层执行并将结果返回给模型继续推理。
底层机制
用户: "北京的天气怎么样?"
系统隐藏过程:
1. 应用层将工具定义(JSON Schema)注入到 LLM 请求中
2. LLM 判断需要调用 get_weather 工具
3. LLM 返回: {"name": "get_weather", "arguments": {"city": "北京"}}
4. 应用层执行 get_weather("北京") → {"temp": 28, "condition": "晴"}
5. 应用层将结果作为新的 tool_result 消息传回 LLM
6. LLM 生成最终回答: "北京今天 28°C,晴"
工具定义(JSON Schema)
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名,如北京"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}
并行调用(Parallel Tool Calls)
OpenAI 和 Claude 等模型支持单次 Response 返回多个工具调用,用唯一 ID 匹配结果:
LLM 返回:
- tool_call_1: get_weather("北京")
- tool_call_2: get_weather("上海")
- tool_call_3: get_weather("广州")
应用层并行执行 3 个函数,结果按 ID 分别传回
注意:并行调用的工具间不应有依赖关系。有依赖的调用需用 ReAct 循环分步执行。
常见问题
参数幻觉
模型可能编造不存在的参数名或非法值。解决:严格定义 JSON Schema,使用枚举约束,在应用层做参数校验。
循环陷阱
Agent 可能陷入 Thought→Action→Observation→Thought 的死循环。设定最大工具调用轮数(如 5-10 次)作为安全网。
错误恢复
工具调用可能失败(网络超时、参数错误)。设计错误处理链:重试 → 降级 → 告知用户。
工具描述的最佳实践
- description 用清晰的自然语言描述工具的用途和边界
- 参数描述中说明值的格式和约束("日期格式:YYYY-MM-DD")
- 相近的工具给出区分说明,减少模型选错
- 使用 `required` 明确标记必需参数