使用 Pydantic 和 OpenAI 获取可靠结构化输出的指南
- 作者

- 姓名
- Nino
- 职业
- Senior Tech Editor
多年来,在大型语言模型 (LLM) 之上构建应用程序的开发人员一直面临着一个持续且令人头疼的问题:输出的可靠性。你要求 LLM 返回一个 JSON 对象,它可能会给你一个看起来像 JSON 的字符串,但其中可能包含多余的逗号、解释性的前言,或者是一个在你的模式 (Schema) 中根本不存在的键。我们尝试过正则表达式,尝试过在 try-except 块中使用 json.loads(),也尝试过复杂的提示工程 (Prompt Engineering)。
随着 OpenAI 推出原生的结构化输出 (Structured Outputs) 功能,并与 Pydantic 实现了无缝集成,那种不稳定的时代已经结束了。通过使用 n1n.ai,开发人员可以在各种模型版本中访问这些强大的功能,并获得优化的延迟和高速吞吐量。在本指南中,我们将深入探讨如何停止手动解析 JSON,并开始利用 Pydantic 信任模型的输出。
核心痛点:文本的“随机性”
传统的 LLM 交互是“文本输入,文本输出”。当你需要将 LLM 集成到软件流水线中时——例如,从医疗报告中提取数据或生成配置文件——你需要的是结构化数据。
在结构化输出功能出现之前,你可能会使用这样的提示词: “请返回一个包含 'name' 和 'age' 键的 JSON 对象。不要包含任何其他文字。”
即使有这样的指令,像 GPT-4o 这样的模型偶尔也会失败,特别是在高负载或模式复杂的情况下。这迫使开发人员编写大量的“防御性代码”来清理输出。然而,通过使用像 n1n.ai 这样的专业 API 聚合器,你可以确保调用的是支持最新 response_format 协议的最稳定端点。
为什么 Pydantic 是最佳解决方案?
Pydantic 是 Python 中最广泛使用的数据验证库。它允许你使用标准的 Python 类型提示 (Type Hints) 来定义数据的形状。当与 OpenAI 的 API 配合使用时,Pydantic 充当了 LLM 的概率世界与软件工程的确定性世界之间的桥梁。
其核心优势包括:
- 类型安全:确保标记为
int的字段确实是整数。 - 自动验证:利用 Pydantic 的验证器检查值是否在特定范围内(例如,年龄 > 0)。
- 自动生成文档:Pydantic 模型本身可以生成 LLM 用来约束其输出的 JSON Schema。
实战指南:分步实现
在开始之前,请确保你已经安装了最新版本的 openai 和 pydantic 库。我们建议使用 n1n.ai 来管理你的 API 密钥,并跨模型(如 GPT-4o 或 Claude 3.5 Sonnet)监控使用情况。
1. 定义你的数据模型
首先,我们定义一个 Pydantic 模型。假设我们正在构建一个从电影评论中提取信息的工具。
from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum
# 定义枚举类型,限制情感分类
class Sentiment(str, Enum):
positive = "正面"
negative = "负面"
neutral = "中性"
# 定义主提取模型
class MovieExtraction(BaseModel):
title: str = Field(description="电影名称")
rating: int = Field(description="评分(0-10分)", ge=0, le=10)
sentiment: Sentiment
actors: List[str] = Field(default_factory=list, description="演员列表")
summary: Optional[str] = Field(None, description="电影简短摘要")
2. 调用 API 并解析
OpenAI 的 SDK 现在包含一个 beta.chat.completions.parse 方法,它负责将 Pydantic 模型序列化为 JSON Schema,并将响应自动反序列化回 Pydantic 对象。
from openai import OpenAI
# 使用 n1n.ai 提供的 API 基础地址和密钥
client = OpenAI(api_key="YOUR_N1N_API_KEY", base_url="https://api.n1n.ai/v1")
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "请从提供的文本中提取电影详细信息。"},
{"role": "user", "content": "昨晚看了《盗梦空间》,太震撼了!10/10 分。莱昂纳多·迪卡普里奥的演技绝了。"},
],
response_format=MovieExtraction, # 直接传入 Pydantic 类
)
# 获取解析后的数据对象
movie_data = completion.choices[0].message.parsed
print(f"电影名称: {movie_data.title}")
print(f"评分: {movie_data.rating}")
print(f"情感: {movie_data.sentiment.value}")
高级模式:处理嵌套对象与复杂逻辑
这种方法最强大的功能之一是处理嵌套数据结构的能力。如果你正在构建一个 RAG(检索增强生成)系统,你可能希望模型在返回答案的同时返回引用列表。
class Citation(BaseModel):
source_id: str
quote: str
class AnswerWithCitations(BaseModel):
answer: str
citations: List[Citation]
通过将 AnswerWithCitations 传递给 response_format,LLM 的底层推理引擎会在数学上受到约束,仅产生满足此特定 JSON 结构的 Token。这不仅仅是一个提示,它是采样过程中的一种硬性约束。
方案对比:手动解析 vs 结构化输出
| 特性 | 手动解析 | 函数调用 (Function Calling) | 结构化输出 (Pydantic) |
|---|---|---|---|
| 可靠性 | 低 (容易因格式错误崩溃) | 中 | 高 (模式强制约束) |
| 开发复杂度 | 高 (需要大量正则表达式) | 中 | 低 (原生 Python 类型) |
| 类型安全 | 无 | 有限 | 完全支持 Python 类型提示 |
| 性能表现 | 不稳定 | 良好 | 优秀 |
专家建议与最佳实践
- 利用 Field 描述:
Field(description="...")参数不仅是为了代码文档化。它会被作为 Schema 的一部分发送给 LLM,充当该特定字段的“微提示词”。利用它来引导模型理解细微差别。 - 处理拒绝响应 (Refusals):有时模型可能会因为安全策略拒绝回答。在访问
parsed数据之前,务必检查completion.choices[0].message.refusal。 - 严格模式 (Strict Mode):OpenAI 的结构化输出在后台要求
strict=True。这意味着输出中必须包含所有字段,且不允许出现额外的属性。这就是为什么 Pydantic 如此有用——它在代码层面强制执行了这种严格性。 - 性能优化:虽然结构化输出确保了准确性,但由于模型需要处理 Schema,首字延迟 (TTFT) 可能会略有增加。使用像 n1n.ai 这样的高性能提供商,可以通过在全球范围内调度最快的可用节点来缓解这一问题。
总结
Pydantic 与 OpenAI 的结合代表了 LLM 开发体验的一次巨大飞跃。它将验证的负担从应用逻辑转移到了模型本身的推理引擎中。这带来了更简洁的代码、更少的运行时错误以及更可预测的用户体验。
无论你是在构建复杂的 AI Agent 还是简单的信息提取流水线,结构化输出模式都是新的行业标准。要使用最可靠的 LLM 基础设施开始尝试这些功能,请访问 n1n.ai。
在 n1n.ai 获取免费 API 密钥。