结构化提取

金额可以是负数吗:把业务规则和类型规则分开

把所有金额字段写成 `minimum: 0`,表面上很严谨,却会把正常的退款记录挡在门外。金额有没有提取成数字,与这笔业务允不允许负数,是两个问题。应该先确认单据的含义,再写范围规则。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 把所有金额字段写成 minimum: 0 ,表面上很严谨,却会把正常的退款记录挡在门外。金额有没有提取成数字,与这笔业务允不允许负数,是两个问题。应该先确认单据的含义,再写范围规则。 先定义负号代表什么 本文合成协议约定:普通收费非负,退款以负金额表示。真实系统也可能使用“正金额加退款类型”的记账方式。两种方式都能成立,但不能在同一个接口里靠读者猜。这里的零点边界是示例协议,不是财务行业的统一标准。 让类型决定约束 JSON Schema 的 minimum 包含边界, exclusiveMinimum 不包含边界。若业务要求严格为正,不能用 minimum: 0 。先列出允许零值的场景,再选关键字,避免把免费项目、冲销或预估值误判为错误。 from jsonschema import Draft202012Validator schema = { "type": "object", "required": ["kind", "amount"], "additionalProperties": False, "properties": {"kind": {"enum": ["charge", "refund"]}, "amount": {"type": "number"}}, "allOf": [ {"if": {"properties": {"kind": {"const": "charge"}}}, "then": {"properties": {"amount": {"minimum": 0}}}}, {"if": {"properties": {"kind": {"const": "refund"}}}, "then": {"properties": {"amount": {"exclusiveMaximum": 0}}}} ] } v = Draft202012Validator(schema) valid = lambda x: not list(v.iter_errors(x)) assert valid({"kind": "charge", "amount": 0}) assert valid({"kind": "refund", "amount": -12.5}) assert not valid({"kind": "refund", "amount": 12.5}) assert not valid({"kind": "charge", "amount": True}) assert not valid({"amount": 12.5}) 示例依赖 jsonschema ,按 Draft 2020-12 校验。它没有做币种换算,也没有验证分项与合计关系。货币计算需要明确精度和舍入策略,不能因为数值通过 Schema 就改用浮点数随意累加。 报错需要指出业务上下文 观察 这里的处理 还需核对 退款为正数 拒绝 是符号丢失还是来源采用另一协议 收费为零 接受结构 是否属于免费项目 金额为字符串 拒绝类型 千位符、币种与小数规则 类型缺失 拒绝 不按金额符号猜单据类型 别用自动取绝对值掩盖错误 OCR 漏掉短横线、翻译移动负号和业务采用相反符号约定,可能表现为同一个异常。直接调用 abs() 会消除最重要的诊断线索。应保存原文片段、页号和解析前字符串,再决定重提取还是人工复核。 交付时一起检查金额与类型 用 OmniDoc(产品入口) 处理含金额的文档时,可以把这张规则表作为译后验收的一部分:不只看数字有没有丢,还要看退款、贷项等语境有没有改变。工具帮助完成翻译,金额协议仍应由业务方明确。 参考: JSON Schema 数值约束 。 - 1 -
更新于 2026-10-08