Java HttpClient 调用 Gemini 图像理解与金额字段校验

一张发票拍歪、带阴影、字段布局不统一,传统 OCR 往往要为每种模板写规则。视觉语言模型能同时看文字与版面,却仍可能把"含税金额"和"价税合计"看混。可行做法不是让模型直接报销,而是让它给出候选字段,Java 再检查金额格式、上限和必填项。读完你能搭出一个不会把模型文本直接当财务事实的最小入口。

Gemini 官方图像理解文档说明:小图片可作为 inline data 发送,模型能进行图像描述、分类与视觉问答。这里用 REST 而非 Java SDK,原因是 Java 17 自带 HttpClient,示例无需引入不确定版本的第三方包;正式项目可按官方迁移文档改用 Google GenAI SDK。

flowchart LR I[票据 JPG/PNG] --> B[Base64 编码] B --> G[Gemini 多模态请求] G --> J[提取 JSON 文本] J --> C{金额/发票号校验} C -->|通过| R[进入待复核报销单] C -->|失败| M[标记异常并人工查看原图]

先准备环境

需要 Java 17+ 与一张已脱敏的 receipt.jpg。从环境变量读取密钥,绝不把密钥写进类文件。模型名 gemini-3.8-flash 与请求路径来自官方生成内容与图像理解文档;不同账号可用模型会变化,应在发布前核对。

bash 复制代码
export GEMINI_API_KEY='你的密钥'
javac ReceiptCheck.java && java ReceiptCheck receipt.jpg

完整代码

为保持最小可复制性,提示要求单行 JSON;extract 是教学级的窄解析器,生产环境请使用 Jackson 等成熟 JSON 库并校验完整响应结构。HTTP 连接设为 10 秒、请求设为 25 秒。

java 复制代码
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.time.Duration;
import java.util.Base64;
import java.util.regex.*;

public class ReceiptCheck {
  static String value(String json, String name) {
    Matcher m = Pattern.compile("\\\"" + name + "\\\"\\s*:\\s*\\\"?([^,}\\\"]+)").matcher(json);
    if (!m.find()) throw new IllegalArgumentException("缺少字段: " + name);
    return m.group(1).trim();
  }
  static boolean valid(String invoiceNo, String amount) {
    if (!invoiceNo.matches("[A-Za-z0-9-]{6,32}")) return false;
    try {
      double money = Double.parseDouble(amount);
      return money >= 0 && money <= 50000;
    } catch (NumberFormatException e) { return false; }
  }
  public static void main(String[] args) throws Exception {
    if (args.length != 1) throw new IllegalArgumentException("用法: java ReceiptCheck receipt.jpg");
    String key = System.getenv("GEMINI_API_KEY");
    if (key == null || key.isBlank()) throw new IllegalStateException("缺少 GEMINI_API_KEY");
    String image = Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of(args[0])));
    String prompt = "读取票据。只返回 JSON: {\"invoice_no\":\"\",\"total_amount\":\"0.00\"};"
        + "看不清则用 UNKNOWN,不能猜测。";
    String safePrompt = prompt.replace("\\", "\\\\").replace("\"", "\\\"");
    String body = "{\"contents\":[{\"parts\":[{\"text\":\"" + safePrompt + "\"},"
        + "{\"inline_data\":{\"mime_type\":\"image/jpeg\",\"data\":\"" + image + "\"}}]}]}";
    HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
    HttpRequest request = HttpRequest.newBuilder(URI.create("https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent?key=" + key))
        .timeout(Duration.ofSeconds(25)).header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(body)).build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    if (response.statusCode() != 200) throw new IllegalStateException("API 状态: " + response.statusCode());
    String text = value(response.body(), "text");
    String no = value(text.replace("\\\\\"", "\\\""), "invoice_no");
    String amount = value(text.replace("\\\\\"", "\\\""), "total_amount");
    System.out.println(valid(no, amount) ? "PASS " + no + " " + amount : "REVIEW 原图与字段不一致");
  }
}

原理与边界

内联图片会被 Base64 编码后成为请求的一部分;模型输出只是候选,valid 把发票号限定为 6--32 位、金额限定为 0--50000。上限不是通用财务规则,而是教学阈值,应改成企业政策。成功的预期输出类似 PASS A1234567 128.50;格式异常或无法读取时应进入 REVIEW。本次已用 javac ReceiptCheck.java 完成语法编译,未提供密钥或票据,没有调用线上 API。

三个常见错误

  1. 把原始票据上传到调试日志:票据可能含税号和地址;保存哈希、字段结果和最短必要审计信息即可。
  2. 用 double 结算 :本例仅做范围门禁;真实金额应使用 BigDecimal 并按币种规则舍入。
  3. 只看 HTTP 200:200 不等于识别成功,仍要检查候选、字段与业务一致性。

适用场景与下一步

适合低风险的预填单、票据归档和人工审核加速;不适合无人工复核的付款、税务申报与凭证替代。工程化可增加:图片大小限制、病毒扫描、同一发票号去重、原图访问权限、双模型抽检与人工纠正回流。五分钟练习:把 mime_type 改为按文件扩展名选择,并为 PNG 写一个拒绝未知类型的测试。

你的票据流程里,哪一类错误必须由程序而不是模型拦住?

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


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

相关推荐
HUIBUR科技2 小时前
多系统整合不必搭建中台:AI中枢带来轻量化数字化集成
大数据·运维·人工智能
知几蜗牛2 小时前
GPT-6 Intelligent UI:从文本响应到可验证交互的产品迁移
人工智能
天空鸟_时光不老2 小时前
09-RAG问答系统落地:从默认分割器的坑到Milvus召回调优
java·人工智能·spring boot·spring·spring cloud·maven·mybatis
知几蜗牛2 小时前
Java Agent 工具调用的 allowlist、参数校验与调用预算
人工智能
GlobalInfo2 小时前
2026年AIoT芯片及平台市场报告正式发布:市场规模、十五五趋势与产业链全景一键获取
大数据·人工智能·ai·半导体
番茄不是西红柿kk2 小时前
免费的Hark,对上了最近热门的Muse
人工智能·aigc
是Dream呀2 小时前
Dropout 是暂退法还是丢弃法?我用 TextIn xParse 做了一个术语对账台
人工智能·agent·textin·ai数据层基础设施
中年阿甘2 小时前
对统计学在机器学习领城基础地位的理性认识
人工智能