把合同、报价单或工单交给模型,要求输出固定字段,已经可以明显减少手工整理。但数据库最容易接受的错误,恰恰是字段齐全、类型正确、内容却不真实的记录。本文的核心判断是:结构化输出应当生成候选数据,正式入库需要由独立校验和业务规则放行。以下方法围绕文档抽取,不讨论模型直接执行付款或审批;示例、阈值与统计数字均为构造,没有运行实验。
第一层:响应完成,才进入数据解析
先检查请求是否正常结束、是否拒绝处理、是否被长度限制截断,再解析结果。HTTP 请求成功不等于抽取任务成功。Anthropic 的结构化输出文档就明确说明,拒绝响应可能返回 HTTP 200,且优先于 schema 约束;文档也列出输出达到 token 上限时的例外。[1] 这些响应字段属于具体接口,应按实际平台实现分支。
应用应保存响应状态、任务编号、原文件版本和原始返回,随后决定进入校验、有限重试或人工处理。部分流式内容看起来已经像完整对象,也不应提前写进正式业务表;等待完整结束可以避免把半条记录当成最终结果。
如果原始返回包含企业敏感内容,保留范围和访问权应按用途确定,不是为了排错无限保存。运行日志可以只记录定位所需的编号和错误码,完整材料留在受控区域;取证与最小化保留需要同时设计。
第二层:明确结构,同时保留未知状态
JSON Schema 是描述数据结构与约束的规范。其官方文档指出,声明 properties 不自动要求字段存在,额外属性默认也可被允许;字段缺失与值为 null 并不相同。[2] 因此,需要分别决定字段是否必填、能否为空、类型和取值范围,以及额外字段如何处理。
对于采购报价,建议把文档编号、供应方、币种、金额、单位、税费状态和证据位置写成明确契约。金额值、原文单位和标准化结果分别保留。供应方名称存在不等于已匹配企业主数据,模型不得自行创造正式供应商编号;匹配结果应由受控规则或人员确认。
“未注明运费”不能默认为零运费,“无法识别税率”不能填入常见税率。可以使用有定义的状态,例如已提取、原文未提供、原文冲突、识别失败,并允许相应值为空。这样下游能够区分没有信息和信息确实为零;具体状态集合应由业务确定,而不是无限增加自由文本标签。
注意模型端约束只支持该服务实际实现的 schema 子集,服务端验证器也有版本和配置。固定使用的方言与校验库版本,保留一组边界样例;不要把同名 strict 参数在不同产品中的含义混为一谈。
第三层:用确定性规则检查业务一致性
类型通过以后,再检查金额单位、日期关系、重复项目和明细合计。若原文中的单价与数量均明确,可以由程序计算行金额,再与原文小计对照;折扣、税费和舍入规则不明确时,应标记待确认,而不是强行凑平。算术一致证明字段之间相容,不证明字段来自正确文档。
校验器也可能隐式转换数据。Pydantic 的文档说明,默认模式可尝试类型转换,严格模式会收紧转换,但对 JSON 中日期等类型仍可能采用不同规则。[3] 所以启用严格模式后仍要测试真实输入路径,不能只在 Python 对象上测试,再推断 JSON 接口行为相同。
跨字段关系可使用模型级验证器等机制实现,Pydantic 官方文档提供了字段级与模型级自定义校验能力。[4] 工程上建议把“原文抽取”“经规则标准化”“经业务核验”分开记录,标准化过程保存规则版本,不能覆盖原始值后只留下一个看似整洁的数字。
对于浮点舍入敏感的金额,选择适合业务精度的十进制或最小货币单位表示,并明确币种与舍入规则。误差容忍应来自约定的计算方式,不应为了让更多样例通过而随意放宽。跨币种比较还需要汇率来源及时间,不能由语言模型补一个看起来合理的汇率。
第四层:证据支持字段,不能只验证字段之间
候选记录应携带文件标识、文件版本、页码或稳定定位、原文片段,以及抽取方式。先验证定位与片段确实存在,再判断片段是否支持该字段含义。例如“预算上限十万元”可以真实存在,但不能作为“本次报价十万元”的证据。
扫描件的文字识别结果本身也可能错。关键数字应能回到原始图像核对,尤其是小数点、负号、单位和跨页表格。仅让第二个模型阅读同一份错误 OCR 文本,不会自然形成独立证据;若需要复核,应提供能揭露原错误的信息源或交给人员查看原件。
证据定位可以由系统从已有切片或标注中选择,不应完全依赖模型编造页码。模型返回的引文仍要验证,文档更新后还需防止旧位置指向新内容。对于无法自动确认语义的条款,把检查结果停留在候选或待审状态,是正确的系统行为。
构造示例:金额正确也可能还不能入库
假设一份报价单写明“设备价 12 万元,含税;运费另议”,没有提供税率。任务要求整理成采购系统的候选记录。金额标准化为人民币 120000 元可以由明确的单位换算得到;税率应记为未提供,运费应记为待议,不能分别猜成 13% 和 0。这里所有内容均为构造示例。
若模型输出金额 12、币种人民币、税率 13%、运费 0,JSON 可以完全合法,类型也可能全部满足约束。业务层会发现单位信息缺失或换算未完成,证据层则会发现税率没有来源、运费状态与原文冲突。增加更多必填数字字段而不提供未知状态,反而可能鼓励系统把缺项包装成完整记录。
如果采购系统的正式单据要求运费已经确定,整条候选应进入待审区,并返回明确缺项。若系统支持分阶段保存,可以保存设备报价部分及未决状态,但不能把它呈现为完整采购总额。处理方式取决于目标表的业务含义,不能只由数据库是否允许空值决定。
再考虑另一页同时出现旧版本报价。仅按金额大小或靠近页尾选择最终报价都缺少依据;需要明确版本、生效条件和业务确认。无法判定时标记冲突并引用两处材料,比生成一个确定金额更可追溯。
修复错误时,限制模型可以改变什么
校验失败后,可以向模型返回具体错误和对应原文,要求重新抽取受影响字段。不要笼统要求“修到通过”为止,也不要允许它为了平衡总额而修改已经核实的明细。每次修复都应重新检查相关字段和依赖关系,保留前后差异及修复次数。
将错误分为可重试的解析或格式问题、可按明确规则修正的单位与格式问题,以及必须补材料或人工确认的事实问题。比如缺少原文税率,再请求十次也不会获得真实税率。应用需要明确的重试上限和转人工出口;上限由成本、时效与业务后果决定,本文不设通用数字。
正式写入应由独立服务执行,校验过程本身不直接改变业务账。用任务编号和文档版本识别重复导入,避免重试生成重复记录。入库前再次核对目标权限和待写数据版本;通过格式与业务检查不等于获得写入授权。
用能解释失败的指标验收,而不是只报解析率
至少分别报告完整响应率、结构通过率、字段证据正确率和整条记录合格率。完整响应率以全部有效提交任务为分母;结构通过率可以以完整响应为分母,但必须明确这个条件,并同时给出相对全部任务的比例。整条记录合格应要求约定关键字段全部通过,而不能平均掉严重字段错误。
字段证据正确率应通过有参考标注的样本或人工核验计算,说明是按字段计数还是按记录计数。对缺失字段,正确标记未知同样可以算正确处理;如果任务要求完整单据,则该记录仍可能不能自动入库。因此还要单列自动放行比例、人工复核量以及错误自动放行情况。
构造算例:100 份输入中,95 份形成完整响应,90 份通过结构检查,最终只有 78 份通过全部入库要求。相对所有任务,三个比例分别是 95%、90% 和 78%;结构检查在完整响应中的条件通过率约为 94.7%。不能只展示最后这个较高数字,称其为整体正确率。这些仅为算术示例。
测试集应包含清晰文档、缺项、零值、不同金额单位、冲突版本、跨页表格、模糊扫描和截断响应。分别统计不同材料类型的错误,并检查待审队列是否能在业务时限内处理。若自动放行减少但复核积压上升,仍需调整范围或投入审核资源。
这套方法的交付物是结构契约、独立规则、证据定位、异常状态和可复测样例。它增加了实施工作,却能让错误停在明确的关口。只有字段格式、业务含义与原文支持共同成立,结构化输出才具备进入业务数据链路的基础。
