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] || ''
↩