651 字
3 分钟
LLM 结构化输出
什么是结构化输出?
通常情况下,LLM 的输出是自然语言,但是有时候我们需要 LLM 输出特定的结构化数据,比如 JSON、XML 等。
让大语言模型(LLM)输出格式清晰、结构明确的数据,而不是一大段自然语言描述。
如何使用结构化输出?
1. Prompt 中指定输出格式
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: '你是一个专业的历史学家,你应该能够以简洁和准确的方式总结一个人的生活。',
},
{
role: 'user',
content: `请总结10个${name}生活中的重要事件。
以 JSON 格式输出,字段包括:
{
"name": "string", // name
"gender": "string", // gender
"type": "string", // type
"profile": "string", // profile 100 words
"timeline": [
{
"year": "string", // year
"title": "string", // title
"description": "string", // description 50 words
}
]
}
`,
},
],
});
// 期望输出
```json
{
"name": "string", // name
"gender": "string", // gender
"type": "string", // type
"profile": "string", // profile 100 words
"timeline": [
{
"year": "string", // year
"title": "string", // title
"description": "string", // description 50 words
}
]
}
2. 使用 zod 定义结构,配合 OpenAI 的 zodResponseFormat
函数
import OpenAI from 'openai';
import { zodResponseFormat } from 'openai/helpers/zod';
import { z } from 'zod';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const BiographySchema = z.object({
name: z.string(),
gender: z.string(),
type: z.string(),
profile: z.string(),
timeline: z.array(z.object({ year: z.string(), title: z.string(), description: z.string() })),
});
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: '你是一个专业的历史学家,你应该能够以简洁和准确的方式总结一个人的生活。',
},
{
role: 'user',
content: `请总结10个${name}生活中的重要事件。`,
},
],
response_format: zodResponseFormat(BiographySchema, 'biography'),
});
// 期望输出
{
"name": "string", // name
"gender": "string", // gender
"type": "string", // type
"profile": "string", // profile 100 words
"timeline": [
{
"year": "string", // year
"title": "string", // title
"description": "string", // description 50 words
}
]
}
其他
部分兼容 openai sdk 的服务不支持 zodResponseFormat
函数
deepseek-chat 模型不支持
zodResponseFormat
函数解决方法:
response_format: model.includes('deepseek-chat') ? { type: 'json_object' } : zodResponseFormat(BiographySchema, 'biography'),
deepseek-reasoner 模型不支持
json_object
解决方法:
response_format: model.includes('deepseek-reasoner') ? { type: 'text' } : zodResponseFormat(BiographySchema, 'biography'),
最终处理
const response_format = model.includes('deepseek') ?
{ type: model.includes('chat') ? 'json_object' : 'text', }
: zodResponseFormat(BiographySchema, 'biography'),
总结
方法 | 优点 | 缺点 |
---|---|---|
Prompt 中指定输出格式 | 简单易用,自然语言输入 | 1. 如果输出格式复杂,可能需要多次调整 2. 不同的模型可能对于输出格式的理解不同导致输出格式错误 3. 输出的文本一般被 ```jsonn ``` 包裹,需要自行匹配真正的 JSON 文本1 |
zod + zodResponseFormat | 结构清晰,易于维护,支持复杂类型,高可控 | 需要额外引入 zod |
Footnotes
匹配方法
text.match(/```json\s*([\s\S]*?)\s*```/)?.[1] || ''
↩