📰 来源:Towards Data Science | 📅 翻译日期:2026年6月19日
🔗 原文:查看原文
🤖 翻译:DeepSeek AI · 仅供参考
概述
在最近的几篇文章中,我们讨论了许多优化AI应用性能和成本的流行技术,如响应流式传输或提示缓存。今天,我想谈谈另一个对构建真实AI应用同样重要的话题——结构化、机器可读的输出。
到目前为止,我分享的大多数示例中,我们处理的都是AI模型的自由文本响应。用户提问,模型用自然语言回答,我们以某种方式向用户展示该响应。这相当简单直接。但是,当我们需要模型以特定格式(如JSON对象)返回数据,以便后续通过编程进行进一步处理时,该怎么办?如果我们需要模型从文本或图像中提取特定字段、填充数据库条目或基于其响应触发后续操作呢?在这些情况下,返回一长段文本就不太方便了。🤔
幸运的是,有多种解决方案。从LLM获取结构化、机器可读的输出主要有两种方法:JSON模式和函数调用(也称为工具使用)。这两者经常被混淆(这在意料之中,因为显然它们都处理结构化输出),但它们服务于截然不同的目的。此外,OpenAI还引入了函数调用的更严格变体,称为结构化输出,它进一步增强了模式强制,我们稍后会看到。在本文中,我们将仔细研究这三种方法,了解它们各自的工作原理,并弄清楚何时使用它们。
那么,让我们开始吧!
什么是JSON模式?
JSON模式是从LLM获取机器可读输出的更简单方法。它本质上是一个可以在API请求中设置的参数,用于指示模型始终返回有效的JSON对象。这就是它的全部内容!尽管如此,这种简单性是有代价的,因为无法保证JSON的结构或模式(记住,我们没有定义任何模式、字段名、类型等),只能保证它是有效的、可解析的JSON。
例如,使用OpenAI的Python API,我们可以通过向模型调用添加参数 response_format={"type": "json_object"} 来启用JSON模式。具体来说,它看起来像这样:
from openai import OpenAI
client = OpenAI(api_key="your_api_key")
response = client.chat.completions.create(
model="gpt-4o-mini",
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": "You are a helpful assistant. Always respond in JSON format."
},
{
"role": "user",
"content": "Extract the name, age, and city from this text: 'Maria is 32 years old and lives in Athens.'"
}
]
)
print(response.choices[0].message.content)响应将如下所示:
{
"name": "Maria",
"age": 32,
"city": "Athens"
}瞧!✨ 仅通过一个简单的参数更改,我们每次都能得到有效的JSON。无需字符串解析或奇怪的正则表达式技巧。
不过有一个问题。JSON模式确实保证输出是有效的JSON,但不保证特定的结构。如果我们多次运行同一个示例,每次可能会得到略有不同的字段名或结构。例如,一次运行可能返回 "name",另一次返回 "full_name"。如果我们试图以编程方式可靠地提取特定字段,这就是一个问题。
另一个问题是,除了设置 response_format={"type": "json_object"} 之外,最好在系统提示中始终明确指示模型以JSON格式响应。在上面的示例中,注意我们在系统提示中添加了“Always respond in JSON format”。没有这个,模型有时可能返回有效的JSON,但并非总是如此,因为其行为可能变得不可预测。
什么是函数调用?
函数调用(或工具使用)是从LLM获取结构化、机器可读输出的更高级方法。我们不是仅仅要求模型将其响应格式化为JSON,而是定义一个特定的模式。即,我们明确描述我们期望输出遵循的结构,这样模型更受约束,返回的数据将完全匹配该模式。换句话说,使用函数调用,我们预先定义期望的字段、字段类型、哪些是必需的、哪些不是等。
以下是使用函数调用的相同提取示例:
from openai import OpenAI
import json
client = OpenAI(api_key="your_api_key")
# 定义我们期望输出的模式
tools = [
{
"type": "function",
"function": {
"name": "extract_person_info",
"description": "Extract personal information from a text",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The full name of the person"
},
"age": {
"type": "integer",
"description": "The age of the person"
},
"city": {
"type": "string",
"description": "The city the person lives in"
}
},
"required": ["name", "age", "city"]
}
}
}
]
response = client.chat.completions.create(
model="gpt-4o-mini",
tools=tools,
tool_choice={"type": "function", "function": {"name": "extract_person_info"}},
messages=[
{
"role": "user",
"content": "Extract the name, age, and city from this text: 'Maria is 32 years old and lives in Athens.'"
}
]
)
# 解析结构化输出
tool_call = response.choices[0].message.tool_calls[0]
result = json.loads(tool_call.function.arguments)
print(result)输出将如下所示:
{
"name": "Maria",
"age": 32,
"city": "Athens"
}这个示例中使用函数调用的输出与使用JSON模式的输出完全相同。然而,关键区别在于,与JSON模式不同,使用函数调用时,输出将严格遵循我们定义的模式。字段名称、类型和必需性都是强制的。此外,函数调用还可以用于触发外部工具或API,而不仅仅是格式化输出。
什么是结构化输出?
OpenAI引入的结构化输出是函数调用的一个更严格版本。它确保输出不仅有效,而且精确匹配提供的JSON模式,包括对嵌套模式的支持。它通过在后处理步骤中验证输出与模式的匹配来实现这一点,如果输出不符合模式,则重试生成。
启用结构化输出通常通过设置 response_format 参数为 {"type": "json_schema", "json_schema": {...}} 或类似的方式。这在需要高度可靠模式一致性的场景中非常有用,例如自动化数据处理流水线。
何时使用哪种方法?
- JSON模式:当你需要一个简单的JSON输出,且对结构要求不严格时使用。例如,生成配置对象或简单的数据提取,其中字段名称可能变化。
- 函数调用:当你需要严格定义的模式,并可能调用外部函数或API时使用。适合分类、提取特定结构、或触发后续动作的场景。
- 结构化输出:当模式一致性至关重要,且你需要精确嵌套结构时使用。例如,生成遵循复杂模式的UI组件或数据迁移。
总结:JSON模式简单但结构不保证;函数调用提供严格模式且可集成工具;结构化输出则确保万无一失。
评论已关闭