AI工具教程

用视觉模型提取票据字段:从图片整理到结果校验

AI智能摘要

票据图片看似清晰,字段提取仍可能漏项、日期格式混乱,甚至混淆含税与未税金额;单靠更强的识别模型,并不能消除这些风险。文章从保留完整原图、预先定义字段与状态,到约束模型输出、用程序校验结构和格式,再接入人工复核,梳理一条可追溯的整理流程。怎样让识别结果便于核对,却不被误当成可靠凭证?

— 此摘要由AI分析文章内容生成,仅供参考。

票据图片看起来很清楚,提取出来的字段却可能出现漏项、日期格式不一致,甚至把含税金额当成未税金额。解决办法不是只换一个“识别更准”的模型,而是先定义好数据结构,再把缺失处理、格式校验和人工复核串成一条流程。视觉语言模型可以协助读取和整理信息,但输出不能直接当作财务凭证或无误结果。

票据图片经提取、校验与人工复核后整理为结构化数据的流程示意

先准备图片:让模型看见完整信息

拍摄或扫描时,尽量让票据平整、主体完整,避免阴影、反光和明显倾斜。不要为了突出某个字段而把边缘裁掉:商户名称、日期、金额和票据编号有时分布在不同位置,过度裁切会丢失上下文。多页文件应按页拆分或明确页序,并保留原始图片,方便回查。

如果图片质量不佳,先处理旋转、透视和裁切,再交给模型;处理后的图片仍应与原件对应。不要把模型“看不清”的字段通过猜测补上。对同一批文件,尽量统一图片命名和编号,并用独立记录关联图片与识别结果。

先定义字段,再调用模型

不要只说“识别这张发票”。先决定下游实际需要什么字段,以及每个字段采用什么格式。例如,日期统一为 YYYY-MM-DD,金额以字符串保存,避免不同软件对小数的处理差异;币种单独记录,不要默认所有金额都是人民币。

建议至少为每个字段保留三类信息:识别值、原图依据、识别状态。状态用于区分“已读出”“看不清或有歧义”“票据上没有该字段”。这样,空值就不会被误当成模型漏答,也不会让系统把猜测当成事实。

纯文本
{
  "merchant": {
    "value": "示例商户",
    "evidence": "票据顶部的商户名称",
    "status": "read"
  },
  "invoice_date": {
    "value": "2026-03-08",
    "evidence": "开票日期:2026年3月8日",
    "status": "read"
  },
  "total_amount": {
    "value": "128.50",
    "evidence": "价税合计:128.50元",
    "status": "read"
  },
  "currency": {
    "value": "CNY",
    "evidence": "金额旁标注人民币元",
    "status": "read"
  },
  "invoice_number": {
    "value": null,
    "evidence": null,
    "status": "missing"
  }
}

字段名和状态值应在整批数据中保持一致。收据、发票和普通表单的字段并不完全相同,可按业务需要设计不同的结构,但不要让模型随意新增字段或改变字段含义。

提示词要约束“如何回答”

可以先用以下模板测试,再根据票据类型调整:

纯文本
请从图片中提取 merchant、invoice_date、total_amount、currency、invoice_number。
只返回符合约定字段结构的 JSON,不要添加 Markdown 或其他说明。

规则:
- 只填写图片中能直接读到的信息,不推测、不补全。
- 日期统一为 YYYY-MM-DD;金额使用字符串,不带千位分隔符。
- 每个字段都返回 value、evidence、status。
- status 只能是 read、uncertain、missing。
- 图片没有该字段时,value 和 evidence 返回 null,status 为 missing。
- 内容模糊或存在多个可能值时,不要猜,status 返回 uncertain,
  并在 evidence 中说明看到的原始片段。

这里的 evidence 应是图片上的文字或简短位置说明,而不是模型对内容的解释。若所用模型支持返回文字区域或坐标,可一并保存用于复核;不支持时,也应保留原图和文件编号。模型自报的置信度不等于准确率,不宜单独用它决定是否入账。

用程序检查结构和格式

模型返回的 JSON 即使语法正确,也可能把日期写成“3月8日”、把金额写成带货币符号的文本,或漏掉必需字段。接收结果后,先做确定性的格式检查,再进入人工复核。下面的示例只检查字段结构、日期和金额格式,不会判断票据内容真假:

纯文本
import re
from datetime import date
from decimal import Decimal, InvalidOperation

FIELDS = {
    "merchant": "text",
    "invoice_date": "date",
    "total_amount": "amount",
    "currency": "text",
    "invoice_number": "text",
}
STATUSES = {"read", "uncertain", "missing"}

def validate(record):
    errors = []

    if set(record) != set(FIELDS):
        errors.append("字段集合与约定不一致")

    for name, kind in FIELDS.items():
        item = record.get(name)
        if not isinstance(item, dict):
            errors.append(f"{name}: 必须是对象")
            continue

        if set(item) != {"value", "evidence", "status"}:
            errors.append(f"{name}: value、evidence、status 结构不完整")
            continue

        value, evidence, status = (
            item["value"], item["evidence"], item["status"]
        )

        if status not in STATUSES:
            errors.append(f"{name}: status 不合法")
            continue

        if status == "missing" and value is not None:
            errors.append(f"{name}: missing 状态的 value 应为 null")
        if status == "read" and value is None:
            errors.append(f"{name}: read 状态不能没有 value")

        if evidence is not None and not isinstance(evidence, str):
            errors.append(f"{name}: evidence 必须是字符串或 null")

        if value is None:
            continue
        if not isinstance(value, str):
            errors.append(f"{name}: value 应为字符串或 null")
            continue

        if kind == "date":
            try:
                date.fromisoformat(value)
            except ValueError:
                errors.append(f"{name}: 日期应为 YYYY-MM-DD")
        elif kind == "amount":
            try:
                Decimal(value)
            except InvalidOperation:
                errors.append(f"{name}: 金额不是有效数字")

    return errors

实际使用时,可把校验失败的记录放入待复核队列,而不是直接丢弃。币种代码、必填字段、金额小数位等规则,应根据你接收的票据种类和后续系统要求设定;不要在不确认业务规则时强行补默认值。

人工抽查:优先看不确定和高风险字段

刚开始处理一批新格式票据时,先逐条复核,确认字段定义和提示词符合实际需要。流程稳定后,仍应优先检查状态为 uncertain 的字段、图片模糊的记录、金额较大的记录,以及模型提取结果与业务规则不一致的记录。即使状态为 read,也可定期抽查,尤其是金额、日期和票据编号。

金额之间的关系可以作为复核线索,但不应一律当作硬性判错规则:折扣、舍入、税额展示方式和票据类型都可能影响合计。发现不一致时,回到原图确认,并记录是图片质量、字段定义、模型读取还是校验规则导致的问题。

敏感资料先确认数据流向

上传前先确认图片是否含有个人信息、账号信息或其他不应外传的内容,并查看所用服务对数据传输、保存、访问权限和模型训练的说明。只提取任务所需字段;如果无需长期保存原图,应制定明确的保留和删除规则。使用本地模型也不代表数据自动安全,还需检查运行环境、日志、备份和文件权限。

上线前,用一小批具有代表性的图片比较模型输出与人工结果,重点统计漏字段、格式错误和无法判断的比例。只有在结构稳定、异常能被拦截、复核责任明确后,才适合把它接入后续表格或自动化流程。

热门话题

52okp 是一名关注人工智能、开源软件与效率工具的技术内容创作者,长期实践 Stable Diffusion、ComfyUI、AI 智能体、MCP、Codex 和各类开源项目。通过实际安装、配置与测试,整理可复现的操作教程、问题排查方法和工具使用经验。

登录用户才能发表评论! 登录账户

取消回复

评论列表 (2条):

加载更多评论 Loading...

延伸阅读:

让大模型稳定返回 JSON:结构约束、校验与失败重试

大模型看似听话地返回 JSON,却常夹杂解释文字,甚至把数字写成字符串,直接 json.loads 就报错;更棘手的是失...

52okp
2026-09-29

用 LibreOffice Calc 正确导入 CSV:设置编码和分隔符并保留编号

CSV 导入看似简单,编码或分隔符选错会导致中文乱码、列错位;邮编和编号若被当作数字,前导零也可能消失。文章介绍如何先查...

52okp
2026-10-02

用 Python 清洗 Excel 表格中的空值和重复行:保留原文件并生成校验报告

收集表里的空行、重复记录和首尾空格看似好处理,规则含糊却可能误删有效数据。本文提供一套 Python 脚本,按工作表清洗...

52okp
2026-09-25

用视觉语言模型自动整理图片并生成结构化标签

人工逐张整理图片既慢又容易出错,同一类物品常被写成不同名称,素材库标签越来越难检索。视觉语言模型能自动生成描述、候选标签...

52okp
2026-09-28

整理扫描件前先做什么:用 OCR 结果辅助文件命名和人工复核

整理扫描合同、票据时,逐份打开录入耗时,直接照搬 OCR 结果命名又可能把日期、金额或编号认错,甚至让敏感原件进入不合适...

52okp
2026-09-27
    返回顶部