假设采购单写了一百件形式发票写了一百二十件。一个提取系统若把两份文件合并成“最新数据”维护者就失去了发现差异的机会。文档之间可能存在修改、拆单或错误应用不能仅按上传时间替业务决定谁正确。我会保留两份文档的独立身份再按明确的行关联逐字段比较。本文不判断合同效力只讨论数据提取与复核设计。先关联再比较商品名称不够稳定可能翻译不同、缩写不同甚至同名不同规格。更适合使用单据编号与行编号建立关联缺少稳定编号时进入人工映射而不是按相似名称直接自动覆盖。Structured Outputs能约束输出结构但不会保证两行指向同一项商品。Schema通过之后应用还需要核对身份与单位。图里差异进入复核不是把两份文件压成一份看不出来源的新表。复核界面应能一键回到两边的原始片段。比较函数不替人裁决const assert require(node:assert/strict); function compareLine(po, invoice) { if (po.lineId ! invoice.lineId) throw new Error(unmapped lines); const fields [quantity, unit, currency, unitPrice]; return fields.filter(key po[key] ! invoice[key]) .map(key ({ field: key, po: po[key], invoice: invoice[key] })); } const po { lineId: L1, quantity: 100, unit: piece, currency: USD, unitPrice: 5.00 }; const invoice { ...po, quantity: 120 }; assert.deepEqual(compareLine(po, invoice).map(x x.field), [quantity]); assert.equal(compareLine(po, po).length, 0); assert.throws(() compareLine(po, { ...invoice, lineId: L2 })); console.log(document-diff: 3 assertions passed);保存为document-diff.cjs运行node document-diff.cjs。输入是假设已经规范化的字符串“5”和“5.00”会被视为不同因此实际系统应先按各字段规则规范化再保留规范化前后的值。这个例子没有税额、折扣、运费或汇率公式。AI解释差异时保留证据模型可以帮助把“quantity不同”改写成易懂说明但不要让它发明原因。没有文档支持就只能说数量存在差异不能写成供应商已经同意增购。2026年10月19日的Clairic预览说明提取模型正在细分但专门模型也不能消除业务解释责任。本文未调用模型或读取真实采购文件仅验证差异列表与关联失败路径真正上线还需代表性文档集和人工复核记录。