Python Responses API视觉输入与本地金额校验最小实现

把发票图片交给视觉模型,确实能省掉手输;但"模型说是 128.00 元"不等于系统该付款。更稳妥的做法是:模型只产生候选字段,本地程序以 Decimal 重算含税金额、检查币种和上限,再把通过或拒绝的理由交给界面。你将得到一个可以复制的最小链路,也能把同样的门禁迁移到合同、收据和质检单。

OpenAI 的 Images and vision 文档说明 Responses API 可接收图像输入。本文用 REST 而非 SDK,目的是让请求、超时和错误分支对初学者可见。模型 ID 请以目标账号的 Models 页面为准;代码中的 gpt-6 是示例占位,调用前替换为你账户可用模型。

flowchart LR A[授权票据图片] --> B[base64视觉请求] B --> C[模型返回JSON候选] C --> D[Decimal本地复算] D -->|一致且未超限| E[进入人工确认] D -->|异常| F[拒绝并显示原因]

环境准备与完整代码

Python 3.10+,安装 pip install requests,设置 export OPENAI_API_KEY='...',再运行 python receipt_gate.py receipt.jpg。示例未在本次任务中实际调用线上 API;已在本机完成语法检查。

python 复制代码
import base64, json, os, sys
from decimal import Decimal, InvalidOperation
import requests

API = "https://api.openai.com/v1/responses"
def gate(data: dict) -> tuple[bool, str]:
    try:
        subtotal = Decimal(str(data["subtotal"]))
        tax = Decimal(str(data["tax"]))
        total = Decimal(str(data["total"]))
    except (KeyError, InvalidOperation):
        return False, "字段缺失或金额不是数字"
    if data.get("currency") != "CNY": return False, "仅接受CNY票据"
    if total > Decimal("2000"): return False, "超过自动处理上限"
    if abs(subtotal + tax - total) > Decimal("0.01"):
        return False, "小计、税额与总额不一致"
    return True, "允许进入人工确认"

def main(path: str) -> None:
    key = os.environ.get("OPENAI_API_KEY")
    if not key: raise SystemExit("请设置OPENAI_API_KEY")
    image = base64.b64encode(open(path, "rb").read()).decode()
    prompt = "识别票据,严格只返回JSON:subtotal,tax,total,currency"
    body = {"model":"gpt-6", "input":[{"role":"user","content":[
        {"type":"input_text","text":prompt},
        {"type":"input_image","image_url":"data:image/jpeg;base64,"+image}]}]}
    try:
        r = requests.post(API, headers={"Authorization":f"Bearer {key}","Content-Type":"application/json"}, json=body, timeout=30)
        r.raise_for_status()
        text = r.json()["output"][0]["content"][0]["text"]
        ok, reason = gate(json.loads(text)); print({"approved": ok, "reason": reason})
    except (requests.RequestException, KeyError, json.JSONDecodeError) as e:
        print({"approved": False, "reason": f"调用或解析失败: {e}"})

if __name__ == "__main__":
    if len(sys.argv) != 2: raise SystemExit("用法: python receipt_gate.py receipt.jpg")
    main(sys.argv[1])

关键点有三个:不要用浮点数算钱;不要允许模型决定"批准";不要把原始票据和密钥写进日志。成功时预期输出 {'approved': True, 'reason': '允许进入人工确认'},仍不是自动付款。

常见失败:一是图片太大或格式不被账户支持,先压缩并核对视觉文档;二是模型返回 Markdown 围栏而非 JSON,应在生产中启用结构化输出或做更严格解析;三是网络超时,代码已设 30 秒,生产环境还应重试并加幂等键;四是发票有多税率,不能沿用单一小计公式。

它适合低风险预录入和人工前置核验,不适合绕过财务审批、税务判断或未获授权的证件处理。下一步可把字段置信度、供应商白名单和审计 ID 写入数据库。5分钟练习:把 2000 改成你的团队阈值,并给 gate 增加"日期不得晚于今天"的规则。

把它接进真实页面

页面至少应该同时展示原图缩略图、模型原始候选、本地复算值和拒绝原因。不要只用一枚绿色"已通过"徽标:使用者需要能看出是"识别到 128.00""规则算出 128.00",还是"金额相差 0.02"。若模型无法读取图片,保留上传记录并让用户补图;若规则不通过,允许修正字段但要求说明原因。对发票号、身份证号等敏感字段,日志只保留哈希或局部掩码,原图按组织保留期自动删除。批量场景可先排队、限制单用户并发,并把同一文件的 SHA-256 作为幂等键,避免重试造成重复报销草稿。

官方文档:Images and vision。你的业务里,哪一个AI提取字段必须由本地规则二次确认?

关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。


本文首发于 java4u.cn,转载请注明出处。

相关推荐
知几蜗牛5 小时前
Python实现声明级RAG证据覆盖率门禁
人工智能
天空鸟_时光不老5 小时前
01-我不转Python把AI塞进Java里
java·人工智能·spring boot·后端·spring·spring cloud·架构
武子康5 小时前
Cosmos Curator 只跑一条视频,为什么还会加载一串模型?
人工智能·深度学习·agent
云智慧AIOps社区5 小时前
云智慧智能巡检机器人矩阵亮相:三款产品场景与能力拆解
人工智能·机器人·具身智能·智能巡检机器人·企业级智能巡检机器人·双轮足机器人·四轮足机器人
桃西西呀5 小时前
Spring AI Alibaba 之四:拆开 ReactAgent 的图,看 ReAct 循环怎么拼出来
人工智能·spring·llm
hsfxuebao5 小时前
Hermes Agent能力篇:会话、工具、MCP、记忆
人工智能·后端
Rocky Ding*5 小时前
Janus-Pro深度解析:视觉编码解耦之后,统一多模态模型如何通过训练与数据扩展能力
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·janus-pro
康实训6 小时前
养老实训室布局设计与器械工具配置清单
大数据·人工智能
weixin_416660076 小时前
AI 的 Markdown 复杂表格转 Word:单元格换行、合并与长文本怎么处理
人工智能·word·deepseek·鲸鱼ai助手