9.0
重磅
AI SCORE
技术实践2026-08-15 18:18
结构化输出是契约而非请求
dev.to · AI#LLM#结构化输出#工程实践
Editor brief · 编辑速览
通过 JSON Schema 验证器强制约束 LLM 输出,拒绝不合规结果并反馈错误,让模型重新尝试;用枚举替代自由文本可将拒绝率从 4.1% 降至 0.2%。
当模型调用进入我们的流水线时,我们第一个标准化的东西是边界。每个输出给机器使用的调用都返回符合 schema 的 JSON,而 schema 由一个普通的验证器强制执行。不是在 prompt 里好言好语地请求,而是拒绝输出,让模型面对验证器的错误重新尝试。
async function extract(input, schema, tries = 3) {
let feedback = '';
for (let i = 0; i < tries; i++) {
const raw = await llm({
system: RULES + feedback,
user: input,
response_format: schema, // constrained decoding where the API supports it
temperature: 0,
});
const errors = validate(schema, raw); // plain JSON Schema, same lib as our forms
if (errors.length === 0) return raw;
feedback = ' Previous output failed validation: ' + errors.join('; ');
}
return quarantine(input); // a review queue. never a crash, never a guess.
}
三个细节承载了大部分重量:
枚举优于自由文本。将字符串字段替换为封闭枚举,带来了最大的可靠性提升。一个提取流水线在将其分类字段枚举化后,拒绝率从 4.1% 降到了 0.2%。一个必须从七个值中选择的模型,不可能凭空造出第八个。
将验证器错误原样反馈回去。轻飘飘一句"再试一次"只会重蹈覆辙。而精确的错误信息 "items[2].price must be integer",几乎每次都能在下次尝试时修复问题。
Temperature 0 是必要条件,而非充分条件。贪婪解码消除的是随机性,而不是错误。验证器才是那个告诉你真相的角色。
经历三次尝试仍然失败的项目才是最有价值的那个。它进入人工审核队列,由人标注后,标注过的案例加入回归套件。我们的 schema 就是这样被两年来的自身拒绝所硬化的。这也指向了真正的教训:schema 是 prompt 的一半。我们大部分的 prompt 工程时间都花在删减字段、收紧类型和关闭枚举上——因为你从输出中移除的每一个自由度,都是一个再也不可能发生的幻觉。