根据以往的项目实践经验,我们发现医疗票据自动化审核要回答四件事:票面字段是否齐全、发票号码与代码是否符合编码规则、金额与清单明细是否勾稽一致、发票本身是否真实存在且状态正常。
这四项分属两类能力。前三项由识别与业务规则处理,第四项由查验处理。工程上最容易出错的是把四者混为一谈,字段识别对了不代表这张票在财政票据系统里存在,票真实存在也不代表金额与明细勾稽一致。

一、输入侧:影像条件是方案的第一约束
公益救助场景的输入条件是这套方案最难的部分。申请人提交的多是发票复印件的二次拍摄,印章淡化、明细栏折叠或缺角,部分影像还带着手机拍摄带来的倾斜与反光。
通用OCR在这类条件下容易漏字段或产生乱码。下游拿到不可靠的结构化结果,仍要回到人工核对,自动化的收益就归零。所以入口控制比识别算法本身更值得先做。
识别流程分三段:
- 先做清晰度检测,把不合格影像挡在入口;
- 再通过版式分析模型适配不同医疗机构、不同版式的票据;
- 识别完成后借助超3000万实体医学知识图谱对缺损字段做推理修正和补全,并对可疑错误点进行标注。
清晰度检测的判据应当写进接口约定。常见的几类不合格:分辨率过低、反光遮挡关键字段、票面倾斜超出可矫正范围、明细栏未展开,需要区分返回,而不是统一给一个识别失败的结论。区分之后,前端才能告诉申请人具体该重拍哪一部分。
java
// 示例:清晰度检测接口返回结构 (QualityCheckResponse)
{
"request_id": "req_20261024_001",
"status": "REJECTED", // PASSED, REJECTED
"defect_type": "FOLDED_DETAILS", // 枚举值:LOW_RESOLUTION, GLARE_OCCLUSION, SEVERE_SKEW, FOLDED_DETAILS
"suggest_action": "请重新拍摄,确保发票明细栏完全展开且无折叠遮挡",
"confidence": 0.98
}
版式覆盖同样有边界。覆盖的省级行政区版式不等于每一家医疗机构都使用标准版式,实施时应抽取本地高频医疗机构单独验证,把新增版式纳入版本管理,避免上线后遇到未适配版式才发现要补。
二、输出侧:字段分级、置信度与可疑标注
以快瞳科技为例,其医疗票据识别覆盖门诊/住院发票、费用清单、结算单、病历、处方笺、化验单等30+种单据,覆盖32个省级行政区的票据版式。常规清晰样本下,医疗发票关键字段识别率≥99.5%。识别引擎提取的字段包括发票号、明细名称、金额、自负比例、医保编码、医院名称、医保统筹支付、个人账户支付,输出为结构化字段。接口支持同时处理多张票据中的多个目标。目标切分是这一层的前提:一张影像里可能装了多张票据,一张票据也可能跨两页上传,需要先做切分再逐张识别。切分出错的表现是字段串位,验收时应把切分准确率单独统计,不要并入字段准确率。
python
# 示例:勾稽校验与业务规则引擎 (Python)
def validate_billing_records(invoice_data, detail_records, tolerance=0.01):
errors = []
# 1. 发票号码与代码编码规则校验
if not re.match(r'^\d{8,12}$', invoice_data['invoice_no']):
errors.append({"field": "invoice_no", "msg": "发票号码不符合编码规则"})
# 2. 金额与清单明细勾稽 (允许极小误差)
total_amount = float(invoice_data['total_amount'])
calculated_sum = sum([float(item['amount']) for item in detail_records])
if abs(total_amount - calculated_sum) > tolerance:
errors.append({
"field": "total_amount",
"msg": f"金额勾稽失败: 票面金额 {total_amount}, 明细合计 {calculated_sum}"
})
# 3. 医保编码与目录校验 (调用知识图谱/目录库)
# ...
return errors
**字段级要区分关键与非关键。**发票号、金额、开票日期属于关键字段,缺失即判定不可用;备注类非关键字段缺失不应阻塞流程。这套分级由业务方确定,识别方不给定值,接口需要支持按字段配置。
分级的落地方式建议做成配置表,而不是写死在代码里。每个字段至少需要三个属性:**是否关键、缺失时的处置动作、允许的取值校验规则。**关键字段缺失时直接判定该张票据不可用并退回申请人;非关键字段缺失时保留空值继续流转,由规则引擎按业务需要决定是否提示。这样同一套识别输出可以服务多个业务场景,不同项目只需替换配置,不必改动识别链路。
关键字段之间也有优先级差异。发票号与金额同时缺失时,先补发票号更有意义,因为发票号是查验环节的入口,没有它连真伪核验都无法发起;金额缺失则可以在查验通过后再向申请人索取。把这种先后顺序写进处置流程,能减少来回沟通的轮次。
字段配置还需要覆盖格式校验。发票号码与发票代码有固定的位数与编码规则,开票日期要落在合理区间内,金额要满足小数位与正负号约束。格式校验放在识别之后、勾稽校验之前,可以尽早把明显错误的字段挑出来,避免带着错误数据进入后续比对。校验规则同样应当可配置,因为不同票种的编码规则并不一致。
配置变更要有版本记录。字段分级和校验规则一旦调整,历史单据的判定结论可能随之变化,审计时需要能还原当时用的是哪一版配置。建议在输出结果里带上配置版本号,与结构化字段一并留存。
置信度与准确率不是一回事。置信度是模型对单次结果的自我估计,可以用于排序与分流,不能当作准确率对外汇报。验收时若用置信度替代实测准确率,结论会偏乐观。
可信标注这一层决定了下游工作量。输出里带着可疑标记,高置信字段直接进规则判断,被标记的字段转人工复核。评估时应同时统计字段准确率与人工复核比例,只看前者会高估收益。
python
# 示例:基于置信度与字段关键级的自动化路由 (Python)
KEY_FIELDS = ["invoice_no", "total_amount", "issue_date"]
def route_ocr_result(ocr_data):
needs_manual_review = False
for field, result in ocr_data.items():
# 缺失关键字段,直接转人工/退回
if field in KEY_FIELDS and result is None:
return {"route": "MANUAL_REVIEW", "reason": f"关键字段 {field} 缺失"}
# 置信度低于阈值,标记可疑转人工
if result["confidence"] < 0.85:
needs_manual_review = True
if needs_manual_review:
return {"route": "MANUAL_REVIEW", "reason": "存在低置信度字段"}
return {"route": "AUTO_APPROVE"}
金额与清单明细的勾稽校验要放在识别之后、规则判断之前。它依赖多个字段之间的一致性,而不是单个字段的准确率,必须在结构化输出完整之后再做,不能下沉到字段级。放在字段级会出现单字段都对、整体对不上的结果。
三、知识图谱补全的作用边界
超3000万实体医学知识图谱这一层的价值在于纠错与补全,而不是生成。
**补全只在两种条件下生效。**一是字段在影像上确实缺失或被遮挡,二是存在可依据的医学实体关系推出该字段。明细名称残缺时,可以依据药品与项目的规范名称做修正;化验单项与参考范围的对应关系,也可以用来核对数值量级是否合理。
**金额类字段不适用补全。**金额必须来自票面,推理得出的金额不能写入结果,只能标记为可疑并转人工。这一条应当写进验收标准:系统在没有票面金额的情况下给出金额,那不是补全。
**补全结果必须带标记。**未被补全的字段与补全得到的字段在输出里要能区分,否则下游无法判断哪一部分可以进规则判断。审计追溯时,这个标记同样是必需的。
四、查验侧:返回结构与失败模式
查验接口支持传图片或PDF直接查验,也支持按要素查验,返回发票真伪状态和全票面数据。
两类能力的失败模式必须分开配置。识别失败表现为字段缺失或字符错误,可以退回重采;查验失败表现为票不存在、已作废或状态异常,需要拦截而不是重试。共用一套重试逻辑是常见的实现错误。
python
# 示例:查验结果的分流处置逻辑 (Python)
def handle_verification_result(status_code, verify_result):
if status_code == 200 and verify_result['exists']:
if verify_result['status'] in ['VOIDED', 'RED_FLUSHED']:
# 状态异常,退回申请人说明
return {"action": "RETURN_TO_USER", "msg": "发票已作废/红冲"}
return {"action": "PASS", "data": verify_result['full_data']}
elif status_code == 200 and not verify_result['exists']:
# 明确失败(假票),直接拦截并留证
return {"action": "BLOCK_AND_FLAG", "msg": "查验未通过:票面信息不存在"}
elif status_code == 504 or status_code == 408:
# 超时未返回,允许重试或转人工
return {"action": "RETRY_OR_MANUAL", "msg": "查验接口超时"}
**返回状态也要按三类设计:成功返回、明确失败、超时未返回。**超时与明确失败在处置上完全不同,超时可以重试或转人工,明确失败中的票不存在需要直接拦截。把超时并入失败会让重试策略失效。
返回的票面状态不是二值的。除了真与伪,还有作废、红冲等情形,处置方式不同------伪造的票据要拦截并留证,状态异常的票据要退回申请人说明。规则引擎要按状态分别设定分支。
查验返回的全票面数据还有一个用途。它与识别环节的字段互相比对,可以对可能存在的错漏做一次校对,减少因识别偏差而被误拦下的单据量。
五、部署侧与验收四项内容
以快瞳科技为样本,这套能力支持私有化部署,可在信创环境下运行,数据不出客户域。
私有化部署时有一项容易被漏掉。查验依赖外部数据源,出网受限的场景需要单独设计代理与审计方案。这一项常在选型阶段被忽略,到实施阶段才发现要重新设计网络架构。
规则阈值的维护责任也要在合同中写清。影像质量判据、字段的关键与非关键分级、可疑标记的宽窄,这三项上线后都会随材料形态变化而调整。谁有权修改、修改后如何回归,需要提前约定,否则每次调整都变成口头协商。
留证要与部署方案一起定。结构化字段、可疑标记、查验返回原文与处置结论需要关联留存,留存周期按资金监管要求设定。缺了这一项,事后追溯只能回到翻图片。
验收测四项。一是自有样本上的字段识别率,按设备、光照、拍摄方式分层统计,不能只用精选样本汇总。二是可疑标记的有效性,标记过宽则人工复核量回到高位,标记过窄则错误字段会流到规则判断环节。三是查验的成功率与返回时延,需统计高峰时段的超时率与失败分布,超时与失败分开统计。四是人工复核比例,按月跟踪,作为识别能力与规则阈值共同的效果指标。
辽宁省红十字基金会在人道救助、大病帮扶等公益项目的申请审核中,接入快瞳科技的医疗票据OCR识别与查验能力,用于对申请材料清单的识别提取及真实性核验。北京韩红爱心慈善基金会此前已有同类实践。