1. 短视频电商 OCR 场景与飞桨引擎接入准备
短视频电商的评论区、商品主图、直播截图里藏着大量文字信息:用户晒单里的快递单号、商品图上的促销文案、直播间贴片里的价格标签。要把这些内容结构化,OCR 是绕不开的一环。Java 技术栈的团队通常会选 PaddleOCR,因为它的中文识别效果稳定,模型体积也适合放进业务服务里。
在 Java 里跑 PaddleOCR,主流有两条路:一条是 DJL 加飞桨引擎直接加载飞桨模型,另一条是 Paddle2ONNX 转成 ONNX 后用 ONNXRuntime 推理。这篇先聚焦第一条路的前置准备——把飞桨引擎的推理链路和统一 Key 的调用通道搭起来。很多同学一上来就写Criteria.builder(),结果卡在模型下载、引擎初始化、鉴权配置上,排查半天。我的做法是先把配置骨架和连通性验证跑通,再动推理代码。
这里会用到 TaoToken 的统一 Key 来管理模型调用通道。它的作用是把不同模型服务的鉴权收敛到一个 Key 上,配置一次就能在多个推理场景复用。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。下面从配置骨架开始,一步步把链路打通。
2. TaoToken 前置:统一 Key 与调用通道准备
在写 Java 推理代码之前,先把 TaoToken 这边的准备工作做完。核心是拿到统一 Key,并确认调用通道可用。这一步不做,后面config.toml和settings.json里的字段就没有来源。
先到控制台创建 API Key。打开 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,登录后进入 API Keys 页面,点新建,复制生成的 Key。这个 Key 就是后面配置里要填的凭证。注意 Key 只在创建时完整显示一次,先存到安全的地方。
如果你打算长期做编码类或 Agent 类任务,可以顺带看一下 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它适合需要持续调用模型的场景。单纯做 OCR 推理验证的话,先用按量 Key 就够了。
拿到 Key 之后,确认一下调用通道。API 基础地址是 https://taotoken.net/api ,不带任何查询参数。后面config.toml里的base_url和settings.json里的api_base都指向它。模型对话相关的调试可以走 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
注意:Key 不要硬编码进提交到 Git 的代码里。建议用环境变量注入,配置文件里写占位符,运行时替换。
3. 可复制配置:config.toml 与 settings.json 骨架
配置分两份:config.toml管服务级参数,settings.json管客户端调用参数。两份都放在src/main/resources下,打包后能直接读到。
先看config.toml。这份配置定义了 TaoToken 通道、超时、重试和日志级别。字段名保持和常见 TOML 习惯一致,方便你后续扩展。
# config.toml [taotoken] base_url = "https://taotoken.net/api" api_key = "${TAOTOKEN_API_KEY}" timeout_ms = 30000 max_retries = 3 [ocr] engine = "PaddlePaddle" det_model = "det_db" cls_model = "cls" rec_model = "rec_crnn" model_cache_dir = "./.djl.ai/cache" [log] level = "INFO"api_key用${TAOTOKEN_API_KEY}占位,运行时从环境变量读。model_cache_dir指定模型缓存目录,避免每次启动都重新下载。engine固定为PaddlePaddle,对应 DJL 的optEngine("PaddlePaddle")。
再看settings.json。这份配置给客户端 SDK 或自建 HTTP 客户端用,字段和config.toml有重叠但职责不同:config.toml是服务启动时加载,settings.json是每次请求时读取。
{ "taotoken": { "api_base": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "connect_timeout": 10, "read_timeout": 30 }, "ocr": { "det_model_url": "https://resources.djl.ai/test-models/paddleOCR/mobile/det_db.zip", "cls_model_url": "https://resources.djl.ai/test-models/paddleOCR/mobile/cls.zip", "rec_model_url": "https://resources.djl.ai/test-models/paddleOCR/mobile/rec_crnn.zip", "rotate_threshold": 0.8 }, "runtime": { "threads": 4, "use_gpu": false } }api_key_env指向环境变量名,代码里用System.getenv()读。rotate_threshold是角度检测的阈值,后面推理时会用到。threads控制 DJL 的线程数,短视频电商场景下并发不会太高,4 个线程够用。
两份配置的字段对照如下:
| 配置项 | config.toml | settings.json | 说明 |
|---|---|---|---|
| 基础地址 | base_url | api_base | 都指向 https://taotoken.net/api |
| 凭证 | api_key | api_key_env | 前者直接填,后者填环境变量名 |
| 超时 | timeout_ms | read_timeout | 单位不同,注意换算 |
| 模型地址 | 无 | det/cls/rec_model_url | 仅 settings.json 管模型 |
| 缓存目录 | model_cache_dir | 无 | 仅 config.toml 管缓存 |
提示:两份配置不要混用同一个 Key 字段。
config.toml的api_key是给服务端读的,settings.json的api_key_env是给客户端读的,职责分开排查起来更快。
4. 验证请求:最小连通性验证动作
配置写好后,先别急着写完整 OCR 推理。用一个最小验证动作确认 TaoToken 通道和飞桨引擎都能初始化。这个动作分两步:先验证 TaoToken 通道,再验证 DJL 飞桨引擎加载。
第一步,验证 TaoToken 通道。写一个简单的 Java 方法,用HttpClient发一个请求到https://taotoken.net/api,带上 Key,看返回状态码。
import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; public class TaoTokenCheck { public static void main(String[] args) throws Exception { String apiKey = System.getenv("TAOTOKEN_API_KEY"); if (apiKey == null || apiKey.isEmpty()) { System.out.println("TAOTOKEN_API_KEY 未设置"); return; } HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://taotoken.net/api")) .timeout(Duration.ofSeconds(30)) .header("Authorization", "Bearer " + apiKey) .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("status=" + response.statusCode()); System.out.println("body=" + response.body()); } }跑一下,如果返回 200 或 401,说明通道通了。401 说明 Key 有问题,检查环境变量。如果连接超时,检查网络和connectTimeout设置。
第二步,验证 DJL 飞桨引擎。写一个最小加载动作,只加载检测模型,不跑推理。
import ai.djl.modality.cv.Image; import ai.djl.modality.cv.output.DetectedObjects; import ai.djl.paddlepaddle.zoo.cv.objectdetection.PpWordDetectionTranslator; import ai.djl.repository.zoo.Criteria; import ai.djl.repository.zoo.ZooModel; import java.util.concurrent.ConcurrentHashMap; public class PaddleEngineCheck { public static void main(String[] args) throws Exception { Criteria<Image, DetectedObjects> criteria = Criteria.builder() .optEngine("PaddlePaddle") .setTypes(Image.class, DetectedObjects.class) .optModelUrls("https://resources.djl.ai/test-models/paddleOCR/mobile/det_db.zip") .optTranslator(new PpWordDetectionTranslator(new ConcurrentHashMap<String, String>())) .build(); try (ZooModel<Image, DetectedObjects> model = criteria.loadModel()) { System.out.println("飞桨引擎加载成功"); System.out.println("模型输入: " + model.describeInput()); System.out.println("模型输出: " + model.describeOutput()); } } }第一次跑会下载paddle_inference.dll、openblas.dll、onnxruntime.dll等原生库,日志里能看到下载进度。下载完成后会解压到缓存目录。如果卡在下载,检查model_cache_dir是否有写权限。
两步都通过后,把两个验证合并成一个ConnectivityCheck类,作为项目启动时的自检。这样每次改配置后跑一次,能快速定位是通道问题还是引擎问题。
5. 本篇常见错排查
配置和验证过程中,有几个错误出现频率很高。我按现象、原因、解决三段式列出来,方便你对照。
错误一:TAOTOKEN_API_KEY读不到,返回 null。现象是TaoTokenCheck打印「未设置」。原因是环境变量没配,或者 IDE 运行配置里没加。解决:在终端export TAOTOKEN_API_KEY=你的Key,或者在 IDEA 的 Run Configuration 里加 Environment variables。Windows 用set TAOTOKEN_API_KEY=你的Key。
错误二:UnsatisfiedLinkError: no paddle_inference in java.library.path。现象是加载模型时抛链接错误。原因是 DJL 的原生库没下载成功,或者缓存目录被清理了。解决:删掉model_cache_dir重新跑,让 DJL 重新下载。如果公司网络限制下载,手动把paddle_inference.dll放到java.library.path包含的目录。
错误三:Criteria构建时optEngine("PaddlePaddle")报引擎不存在。现象是No engine found for PaddlePaddle。原因是paddlepaddle-model-zoo依赖没加,或者版本和pytorch-engine不匹配。解决:确认pom.xml里两个依赖版本一致,都是0.25.0。飞桨引擎无 NDArray,需要借用 PyTorch 的 NDArray,所以pytorch-engine必须加。
错误四:模型下载超时,日志停在Downloading ...。现象是启动卡住。原因是模型地址在国外,下载慢。解决:把optModelUrls换成国内镜像,或者提前下载 zip 包,用optModelPath(Paths.get("本地路径"))加载。settings.json里的det_model_url也同步改。
错误五:config.toml里的${TAOTOKEN_API_KEY}没被替换。现象是请求返回 401。原因是代码里没做占位符替换,直接把${...}当成了 Key。解决:读config.toml后,用正则把${VAR}替换成System.getenv("VAR")。或者干脆不在 TOML 里写占位符,运行时用代码覆盖。
错误六:settings.json的api_base带了尾部斜杠。现象是请求路径变成https://taotoken.net/api//v1/...。原因是拼接时没处理。解决:api_base统一不带尾部斜杠,拼接时用api_base + "/v1/..."。config.toml的base_url同理。
注意:排查时先看日志级别。
config.toml里level = "INFO"能看到下载和加载过程,调成DEBUG能看到请求头。但DEBUG会打印 Key,排查完记得调回去。
6. 接入文档与后续推理准备
配置骨架和连通性验证跑通后,下一步就是写完整的 OCR 推理代码。推理部分涉及区域检测、角度检测、文字识别三个模型的串联,以及getSubImage、extendRect、rotateImg这些工具方法。这部分内容放在下一篇展开。
在写推理代码之前,建议先把接入文档过一遍。TaoToken 的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有通道参数和错误码说明。API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,Key 轮换和权限控制都在这里。
如果你在验证模型效果,可以走模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,快速对比不同模型的输出。长期做编码类任务的话,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 有更合适的调用方案。
最后提醒一个实操细节:settings.json里的rotate_threshold默认 0.8,短视频电商的截图里文字方向往往比较正,这个阈值可以调到 0.9,减少误旋转。等推理代码写完,拿一张商品主图跑一遍,看检测框和识别结果是否符合预期。配置骨架这一步做扎实,后面调参就轻松很多。