LLM 提取失败了怎么办?别只盯着 Retry
把验证失败当成一个简单的 Exception 抛掉,是很多大模型工程实践里的误区。在 LLM 提取流水线里,格式错误的发票、OCR 识别崩了的布局、或者违反 Schema 的响应,本质上都是系统“尚未建立信任”的数据。如果你的处理逻辑只有“重试”或“丢弃”,结果要么是浪费钱重复失败,要么是悄悄丢失数据。
高置信度的数据直接走通,可恢复的传输故障走有限次数的重试,而模糊或无效的数据必须进入 DLQ 等待人工审查。
下一篇
上下文窗口:为什么大模型会一本正经地胡说八道? →
真正能上生产环境的方案是建立死信队列(Dead-Letter Queue, DLQ)。
验证逻辑应该是路由的起点
不管是用 Constrained Decoding 还是后验验证,总会有数据掉队。一个健壮的验证边界不应该只返回 true 或 false,而应该输出一个可执行的路由决策。
一个合格的验证结果得包含:
- 使用的 Schema 和模型版本
- 哪个字段失败了,具体原因是什么
- 是格式损坏、内容缺失还是语义错误
- 提取时的置信度信号
- 判定这次失败是否可以通过重试解决
高置信度的数据直接走通,可恢复的传输故障走有限次数的重试,而模糊或无效的数据必须进入 DLQ 等待人工审查。
死信记录需要的是“证据链”
很多人的 DLQ 只是简单地把原始输入扔进去,这在排查时简直是灾难,因为你得在海量日志里去拼凑当时发生了什么。
我建议死信记录应该采用“信封”模式,包含以下元数据:
{
"record_id": "unique_id_123",
"idempotency_key": "key_abc",
"source_ref": "path/to/immutable/doc",
"payload": {
"raw_response": "...",
"extracted_data": "..."
},
"context": {
"model_version": "claude-3-5-sonnet",
"prompt_version": "v2.1",
"schema_version": "1.0.4"
},
"error": {
"code": "SEMANTIC_INVALID",
"detail": "Date cannot be in the future",
"confidence_score": 0.65
},
"trace_id": "trace_xyz_789",
"attempt_count": 3
}这样在几天后回溯时,工程师能一眼看出:系统看到了什么 → 输出了什么 → 被哪个规则拦截了 → 现在是否可以安全重放。别盲目重试,先给失败分类
不是所有失败都值得再次调用模型。我会把失败分为四类来处理:
- 瞬时基础设施故障: 超时、限流。这种用指数退避(Exponential Backoff)重试即可。
- 确定性契约失败: 同样的输入永远违反同样的 Schema。不改 Prompt 或模型,重试就是纯烧钱。
- 源数据模糊: 文档本身没信息。直接路由给人工审核,别让模型在那儿瞎猜。
- 版本漂移: 新文档格式导致原有的假设失效。这类数据需要隔离,用于更新 Schema 或微调 Prompt。