OpenAI 工具调用与 JSON 输出的进化史:从 Prompt 黑魔法到原生支持
前言
在使用大语言模型构建应用时,两个需求几乎无处不在:
- 让模型调用外部工具/函数(比如查数据库、调 API、发邮件)
- 让模型输出结构化的 JSON(方便程序解析和处理)
如今在 OpenAI 的 API 中,这两个需求都有了非常成熟的原生支持。但在早期,开发者们可没这么幸运——我们只能靠 Prompt Engineering(提示词工程) 这种”黑魔法”来碰运气。
这篇文章带你回顾一下这段进化史。
第一阶段:纯 Prompt 时代 —— 在黑暗中摸索
在 GPT-3.5 / GPT-4 刚出来的年代,API 根本没有什么”工具调用”或”结构化输出”的概念。模型就是一个黑箱:你给它文字,它返回文字。
工具调用?全靠 Prompt 描述
如果你想做一个能查天气的 AI 助手,你大概会这样写 system prompt:
1 | 你是一个助手。当用户询问天气时,你需要输出一个 JSON 来调用函数。 |
然后你的代码就得:
- 解析模型返回的文本
- 用正则或者 JSON.parse 试图提取函数调用
- 如果解析失败……重试、换措辞、祈祷 🙏
JSON 输出?更是噩梦
想让模型稳定输出 JSON 就更痛苦了。你能做的只有:
- 在 prompt 里反复强调”你必须返回合法的 JSON”
- 给出一两个示例(few-shot)
- 在 prompt 结尾加上
{来诱导模型开始输出 JSON - 用各种后处理手段修复模型吐出来的”近似 JSON”
即便如此,你还是会经常遇到:
- 模型在 JSON 前后加了一堆废话:
好的,以下是结果:{...} - 多了个 trailing comma:
{"name": "Tom",} - 缺了引号:
{name: "Tom"} - 凭空编造字段、漏掉 required 字段
- JSON 嵌套超过一定深度就开始放飞自我
那时候搞 LLM 应用的开发者,代码里最长的部分往往不是业务逻辑,而是解析和容错逻辑。
第二阶段:JSON Mode —— 一道曙光
OpenAI 后来推出了 JSON Mode,通过在请求中设置:
1 | { |
这让模型保证输出合法的 JSON(不再是”尽力而为”)。
这已经是一个巨大的进步——至少你不用再处理 trailing commas 和缺引号的问题了。但它有一个关键局限:
JSON Mode 只保证合法的 JSON 格式,不保证符合你指定的 Schema。
也就是说,模型输出的确实是合法 JSON,但它依然可能:
- 漏掉你需要的字段
- 多出你不知道的字段
- 字段类型不对(该是 number 的给了 string)
- 枚举值超出范围
你还是需要在代码里做一层校验。
第三阶段:原生工具调用 + Structured Outputs —— 真正的银弹
这才是真正革命性的变化。OpenAI 在 2024 年 8 月前后推出了 Structured Outputs,同时工具调用也进入了原生 + strict mode 时代。
原生工具调用(Function Calling)
现在的工具调用不再是往 prompt 里塞描述文字了,而是通过 tools 参数结构化的方式声明:
1 | tools = [ |
模型返回的不再是需要你手动解析的文本,而是结构化的 function_call 对象,带有 call_id、name 和准确的 arguments JSON。
而你执行完函数后,也只需返回结构化的 function_call_output,整个流程干净、可靠。
Strict mode(strict: true)更是确保了模型传回的参数严格遵循你定义的 Schema,不会多字段、不会少字段、类型也对得上。
详细介绍:OpenAI Tools Guide
结构化输出(Structured Outputs)
对于 JSON 输出的需求,Structured Outputs 是 JSON Mode 的全面进化版:
1 | response = client.responses.create( |
这个方案的核心优势:
| JSON Mode | Structured Outputs | |
|---|---|---|
| 输出合法 JSON | ✅ | ✅ |
| 严格符合 Schema | ❌ | ✅ |
| 安全拒绝可被程序检测 | ❌ | ✅ |
| 支持模型 | gpt-3.5-turbo 及更新 | gpt-4o-2024-08-06 及更新 |
有了 Structured Outputs,你不再需要:
- ❌ 在 prompt 里反复强调输出格式
- ❌ 写各种 JSON 修复/容错代码
- ❌ 处理字段缺失或类型错误
- ❌ 担心枚举值越界
模型会保证输出的 JSON 100% 符合你给的 Schema。
总结
| 阶段 | 工具调用 | JSON 输出 | 可靠性 |
|---|---|---|---|
| 早期(~2023) | Prompt 描述 + 文本解析 | Prompt 约束 + 后处理修复 | 😰 经常出幺蛾子 |
| JSON Mode | Prompt 描述 + 文本解析 | json_object 保证合法 JSON |
😐 格式合法,Schema 不保证 |
| 现在 | 原生 tools + strict mode |
json_schema + Structured Outputs |
😎 开箱即用,严格可靠 |
回顾这段进化史,最大的感受是:大模型应用开发正在从”玄学”走向”工程化”。
以前我们不得不花大量精力在 prompt tuning 和 defensive parsing 上,和模型斗智斗勇;而现在,OpenAI 把这些能力做成了可靠的 API 原语,让我们可以把注意力放回业务逻辑本身。
如果你正在构建 LLM 应用,强烈建议直接上 原生工具调用 + Structured Outputs,别再回到 prompt 手工解析的黑暗时代了。