结构化输出与 Function Calling
摘要:这是 AI Agent 从“聊天机器人”进化为“智能助理”的关键一步。本章将讲解如何让 LLM 不再只会闲聊,而是能够精准地输出 JSON 数据,甚至主动请求执行代码函数。
1. 结构化输出 (Structured Output)
默认情况下,LLM 输出的是非结构化的文本。但这对于程序来说是难以处理的。我们需要 JSON。
1.1 JSON Mode
这是最简单的让模型输出 JSON 的方式。
在请求中设置 response_format: { "type": "json_object" },并在 System Prompt 中明确要求“输出 JSON”。
适用场景:数据提取、文本分类、生成配置文件。
1.2 Function Calling (函数调用)
JSON Mode 只是让输出格式变了,而 Function Calling 则是让模型具备了意图识别和参数构造的能力。
核心流程:
- 定义工具:你告诉模型:“我有这几个函数(工具),如果你需要用,就告诉我。”
- 模型决策:模型根据用户的问题,判断是否需要调用工具。如果需要,它不返回文本,而是返回一个
tool_calls对象,包含函数名和参数(JSON 格式)。 - 执行工具:你的代码捕获到这个请求,执行真正的本地函数(如查询数据库、调用天气 API)。
- 回传结果:你把函数执行的结果(Result)再次发给模型。
- 最终回答:模型结合函数结果,生成给用户的最终自然语言回复。
2. 协议详解
2.1 定义 Tools Schema
你需要在 API 请求中增加 tools 字段。每个 tool 都是一个函数描述。
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取某个城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如 Beijing, Shanghai"
}
},
"required": ["city"]
}
}
}
]
2.2 处理 Tool Calls
当模型决定调用工具时,返回的 JSON 结构如下:
{
"choices": [
{
"message": {
"role": "assistant",
"content": null, // 注意这里内容为空
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Beijing\"}" // 模型生成的参数
}
}
]
},
"finish_reason": "tool_calls"
}
]
}
2.3 提交 Tool Outputs
执行完函数后,你需要构造一个新的 message 发回给模型。注意:必须带上 tool_call_id,这样模型才知道这个结果对应哪次调用。
{
"role": "tool",
"tool_call_id": "call_abc123",
"name": "get_weather",
"content": "{\"temperature\": 22, \"condition\": \"Sunny\"}"
}
3. 为什么不直接用正则提取参数?
早期的开发者确实是用正则去匹配用户输入(例如匹配 "查询天气"),但这非常脆弱。Function Calling 的优势在于:
- 语义理解:模型能理解复杂的自然语言意图(例如“帮我看看那个有鸟巢的城市今天下雨吗” -> 提取出 Beijing)。
- 鲁棒性:模型经过专门训练,生成的 JSON 参数非常稳定,甚至能自动纠正拼写错误。
- 多步推理:模型可以一次性决定调用多个工具,或者根据前一个工具的结果决定下一步操作(Agent 雏形)。
下一步:我们将编写一段代码,亲手实现这个“模型 -> 代码 -> 模型”的闭环。