跳到主要内容

Function Calling 与结构化输出

🔧 Function Calling 让 LLM 从文本生成器变成行动执行者;结构化输出确保返回结果机器可解析。

Function Calling 基础

工作流程

1. 定义工具(名称、描述、参数 Schema)
2. 用户提问 → LLM 决策是否调用工具
3. LLM 返回 tool_call(工具名 + 参数 JSON
4. 应用层执行工具,获取结果
5. 将结果放回对话,LLM 生成最终回答

工具定义示例

{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["city"]
}
}
}

结构化输出

方式一:JSON Mode

强制模型输出合法 JSON,但不保证字段结构:

response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[{"role": "user", "content": "..."}]
)

方式二:Structured Outputs(推荐)

提供 JSON Schema,模型严格遵守结构:

from pydantic import BaseModel

class NewsItem(BaseModel):
title: str
summary: str
tags: list[str]
sentiment: Literal["positive", "negative", "neutral"]

response = client.beta.chat.completions.parse(
model="gpt-4o",
response_format=NewsItem,
messages=[...]
)
news = response.choices[0].message.parsed

并行工具调用

一次对话可以同时调用多个工具:

# LLM 可能返回多个 tool_calls
for tool_call in response.choices[0].message.tool_calls:
result = execute_tool(tool_call.function.name,
json.loads(tool_call.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})

工具设计原则

原则说明
单一职责每个工具只做一件事
幂等性相同输入多次调用结果一致
描述精确description 比 name 更影响调用决策
参数精简参数越少,模型越准确
返回简洁只返回模型需要的信息

常见问题排查

问题原因解决
模型不调用工具description 不够清晰改进工具描述,加示例
参数填错参数名歧义用 description 详细说明每个参数
重复调用工具结果没有放回对话确保 tool result 正确追加
JSON 解析失败模型输出不合规用 Structured Outputs 或加校验重试

常见误区

  • Function Calling 不是自动执行,需要应用层手动调用并将结果传回
  • tool_choice: "auto" 时模型可能选择不调用工具,需要在对话设计上引导
  • 工具数量超过 15-20 个时,模型选择准确率下降,考虑分类或动态注入