登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

多模态模型读取发票图片时如何保留字段证据

来源:17golang原创

时间:2026-09-12 16:12:00 446浏览 收藏

多模态模型能从发票图片中读出发票号码、金额和日期,但如果系统只保存一段模型文本,财务人员很难回答“这个值在原图哪里”“为什么相信它”。更稳妥的做法是把每个字段设计成一个证据对象:值、原图归一化坐标、证据描述、置信说明和复核状态一起保存。

官方地址:https://platform.openai.com/docs/

要点速览
  • 用 JSON Schema 固定每个字段的证据结构,避免模型只返回自由文本。
  • 坐标保存为 0 到 1 的比例值,并与原图宽高、哈希绑定。
  • 置信度只代表抽取判断的把握,缺证据或低置信字段仍要人工复核。

一、先定义字段证据模型

字段证据的核心不是给结果再加一个 confidence,而是让值和它的来源区域成为一条不可拆开的记录。下面的结构以发票号码、价税合计为例;box 使用左上角和右下角的归一化坐标,范围固定在 0 到 1。

# 用同一种结构保存字段值、原图位置和复核状态
FIELD_SCHEMA = {
    "type": "object",
    "properties": {
        "value": {"type": "string"},
        "box": {
            "type": "object",
            "properties": {
                "x1": {"type": "number", "minimum": 0, "maximum": 1},
                "y1": {"type": "number", "minimum": 0, "maximum": 1},
                "x2": {"type": "number", "minimum": 0, "maximum": 1},
                "y2": {"type": "number", "minimum": 0, "maximum": 1}
            },
            "required": ["x1", "y1", "x2", "y2"],
            "additionalProperties": False
        },
        "evidence": {"type": "string"},
        "confidence_note": {"type": "string"},
        "needs_review": {"type": "boolean"}
    },
    "required": ["value", "box", "evidence", "confidence_note", "needs_review"],
    "additionalProperties": False
}

这里的 evidence 应该是“字段出现在发票右上区域的黑色印刷文字”这类可读说明,而不是再次复制整张发票。confidence_note 也要描述不确定性来源,例如“数字 8 的边缘略模糊”,不要把模型置信度写成税务真实性结论。

发票字段证据对象的静态数据结构框图
图1:字段证据对象把 value、box、evidence、confidence_note 和 needs_review 绑定在同一个数据结构中。

二、把发票图片和抽取规则一起提交

图像输入和抽取规则要在一次请求中表达清楚。官方 Responses API 支持把图片作为 input_image 传入;实际项目还应在提示中声明:看不清时返回空值并标记复核,不要猜测号码。

import base64
import json
from openai import OpenAI

# 图片只作为本次请求输入;密钥应从服务端环境变量读取
client = OpenAI()
with open("invoice.jpg", "rb") as image_file:
    image_data = base64.b64encode(image_file.read()).decode("ascii")

prompt = """
# 只抽取发票号码和价税合计,并为每个字段返回原图归一化坐标。
# 无法确认的字段 value 返回空字符串,needs_review 必须为 true;不要猜测。
"""
response = client.responses.create(
    model="gpt-5",
    input=[{
        "role": "user",
        "content": [
            {"type": "input_text", "text": prompt},
            {"type": "input_image", "image_url": f"data:image/jpeg;base64,{image_data}"}
        ]
    }],
    text={
        "format": {
            "type": "json_schema",
            "name": "invoice_evidence",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "invoice_number": FIELD_SCHEMA,
                    "total_amount": FIELD_SCHEMA
                },
                "required": ["invoice_number", "total_amount"],
                "additionalProperties": False
            }
        }
    }
)
result = json.loads(response.output_text)  # 结构固定后再交给业务层处理

Schema 的价值是让缺失字段也有稳定形状,便于后续审计和数据库入库。不要把图片压缩、裁剪后的坐标和原图坐标混在一起;如果为了识别做了裁剪,要同时记录裁剪框。

发票图片、抽取规则与结构化响应的静态模块关系框图
图2:发票图片、抽取提示、JSON Schema 与结构化响应之间的静态模块关系示意。

三、把坐标和原图绑定到审计记录

归一化坐标的计算很简单:原图宽度为 W、高度为 H 时,像素点 (px, py) 保存为 (px / W, py / H)。入库时建议额外保存原图 SHA-256、宽高和处理时间,这样同一张发票被重新识别时仍能对比证据。

import hashlib
from pathlib import Path

# 生成可复核的原图指纹,并把比例坐标恢复为像素框
image_path = Path("invoice.jpg")
image_hash = hashlib.sha256(image_path.read_bytes()).hexdigest()

def to_pixels(box, width, height):
    # 乘回原图尺寸,方便前端在原图上画定位框
    return {
        "x1": round(box["x1"] * width),
        "y1": round(box["y1"] * height),
        "x2": round(box["x2"] * width),
        "y2": round(box["y2"] * height),
    }

audit_record = {
    "image_sha256": image_hash,
    "image_width": 2480,
    "image_height": 3508,
    "fields": result,
}

如果模型返回的框越界、x1 > x2 或没有覆盖字段文字,不要静默修正后当成可信结果;应保留原始响应并把该字段设为待复核。坐标只回答“模型指向哪里”,不能代替发票验真、税率核验或业务规则。

四、用证据完整度决定是否人工复核

自动入账前至少检查四件事:字段值非空、坐标在范围内、证据说明非空、needs_review 为 false。金额还要和业务侧的数值解析、币种及合计规则独立核对。只要一项不满足,就把整张发票送入人工队列,并保留模型原始响应。

检查项通过条件不通过处理
字段值非空且符合字段格式标记缺失字段
图像定位四个坐标在 0 到 1 之间保留原始 JSON,人工查看原图
证据说明能说明文字所在区域或可见特征禁止自动入账
复核标记needs_review=false进入人工复核队列

最后要强调:证据链提升的是可追溯性,不是模型的绝对正确率。发票识别涉及财务和合规场景时,仍应结合原图、业务规则和人工抽查;不要因为 JSON 格式正确,就把抽取结果当作已经验证的事实。

相关问题

为什么不直接保存 OCR 文本?

纯文本缺少字段与原图的对应关系,后续很难复核。把文本、坐标和说明放在同一字段对象里,前端才能点击字段回看原图。

置信度低于多少才需要人工复核?

不要只设一个脱离业务的通用阈值。先按字段风险、金额范围、图像清晰度和历史误差制定规则,并把阈值命中原因写入复核记录。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>