LLM 输出结构化数据的可靠方法

本文分享从 LLM 获取可靠结构化数据的实战经验,涵盖输出约束、Schema 优先设计、验证与重试机制等核心技巧,帮助开发者避免常见解析陷阱。
从“能跑”到“可靠”:LLM 结构化输出的进阶指南
很多 LLM 教程在 JSON.parse(response.content) 这一步就戛然而止。这个看似简单的操作,在最初十几个测试用例里确实没问题,但一旦上线,问题就会接踵而至:模型可能编造一个不存在的日期、返回超出 schema 限制的数组项,或者给出一个字段缺失但语法完全合法的 JSON。
笔者在开发 Temploracraft(一个将简历文档转换为可编辑结构化数据的工具)时,对此深有体会。输入文档格式千奇百怪——双栏 PDF、伪表格、十几种日期写法——而输出必须严格符合预期,因为每个字段最终都会展示在用户面前。模型一旦弄错日期,用户几秒钟内就会察觉。
本文要讨论的,正是介于“模型返回文本”和“应用获得可信数据”之间的那层关键逻辑。
为什么仅靠 Prompt 不够
最初的想法很自然:在提示词里礼貌地要求“返回合法 JSON,不要包含其他内容”,再附上示例。开发阶段一切正常,演示也很顺利。但问题是,LLM 是逐 token 概率生成的,你的指令只是众多影响因素之一,它还要与模型训练数据、输入文档特征以及前文上下文竞争。大多数时候指令能赢,但偶尔会输。
以下是从生产环境提取管线中收集的真实失败案例,模型都明确收到了返回严格 JSON 的指令:
- 响应被包裹在 Markdown 代码块中,尽管明确告知不要这样做。
- 返回
"2019 - Present"字符串,而 schema 要求分开的startDate和endDate字段。 - 为文档明确标注为“至今”的职位,编造了一个
2024-12-31的结束日期。 - 返回字符串
"null"而非真正的null。 - 为最多允许五项的职位输出了七个要点。
- 因超出输出 token 限制而中途截断。
注意,这些失败并非同一性质。代码块和截断属于语法问题,通常可以在本地修复,无需重新调用模型;而日期合并、多余要点则是 schema 问题——JSON 解析没问题,但不符合预定义结构。
约束输出的三种机制
要解决上述问题,不能只靠提示词,而应该利用平台提供的结构化输出能力:
- JSON Schema 约束:许多主流 API(如 OpenAI、Anthropic)支持在请求中直接指定 JSON Schema,模型会尽力生成符合该结构的响应。
- 函数调用(Function Calling):将结构化输出定义为函数参数,模型会以参数形式返回数据。
- 工具使用(Tool Use):类似函数调用,但更强调模型“选择工具”的过程,适合多步骤任务。
这三种机制各有适用场景,但核心思想一致:把约束从“提示词的软性要求”变为“API 的硬性保证”。
先设计 Schema,再写 Prompt
一个常见误区是先写长篇提示词,再考虑数据结构。正确的顺序应该反过来:先用 Zod 或 JSON Schema 定义好严格的输出结构,再基于此设计提示词。
例如,在简历提取场景中,日期字段不应是自由字符串,而应定义为 startDate: string 和 endDate: string | null,并配合正则或格式验证。这样即使模型返回了“2019 - Present”,验证层也能立刻捕获错误,而不是让错误数据流入下游。
验证是两道工序
验证不是一次性的,而是两层:
- 语法验证:检查 JSON 是否能解析,字段类型是否正确。
- 语义验证:检查值是否合理,比如结束日期是否早于开始日期、数组长度是否在限制内。
用 Zod 这类库可以轻松实现两层校验。第一层 safeParse 捕获类型错误,第二层 refine 或 superRefine 处理业务规则。两层都通过,数据才能进入应用。
构建不烧钱的重试循环
重试是必需的,但必须聪明地设计:
- 区分失败类型:语法错误(如代码块包裹)可以本地修复后直接重试;schema 错误(如字段缺失)则需要带着错误信息重新调用模型。
- 限制重试次数:一般 2-3 次足够,超过即放弃并走人工处理流程。
- 利用错误信息:把 Zod 的校验错误反馈给模型,让它知道哪里不对,而不是盲目重试。
const result = schema.safeParse(raw);
if (!result.success) {
// 将错误信息拼入新的 prompt,重新调用
}
流式输出与不可重试的失败
流式输出可以提升用户体验,但要注意:结构化输出的流式解析比普通文本复杂,需要逐块拼接并延迟验证,直到收到完整对象。
另外,有些失败是重试无法解决的:模型对模糊输入(如扫描质量极差的 PDF)的解读偏差、源文档本身信息缺失、或者语义歧义(例如“2024”到底指入学年份还是毕业年份)。这类情况应设计为人工审核队列,而不是无休止地消耗 token。
成本与适用边界
结构化输出通常比普通生成消耗更多 token(因为需要遵守严格格式),且部分 API 对结构化模式有额外计费。因此,并非所有场景都需要这套机制:如果只是给用户看一段总结,普通文本生成就够了;但如果是关键业务数据,投资结构化输出是值得的。
总结
可靠的 LLM 结构化输出不是靠运气,而是靠系统设计:
- 用 API 原生约束机制替代提示词软要求。
- 先定义 Schema,再设计 Prompt。
- 双层验证(语法 + 语义)确保数据质量。
- 智能重试,区分可修复与不可修复的错误。
- 为不可重试的失败预留人工处理通道。
这套方法论不限于简历提取,凡是需要 LLM 输出稳定数据的场景——表单填充、数据迁移、内容分类——都适用。核心思想是:把不确定性隔离在模型层,让应用层只处理可信数据。