使用 ONNX 将 AI 推理引入 Java:企业架构师实用指南
引言
虽然 Python 主导了机器学习生态,但大多数企业应用仍运行在 Java 上。这种脱节造成了部署瓶颈。用 PyTorch 或 Hugging Face 训练的模型在生产中往往需要 REST 封装、微服务或多语言变通方式才能运行。这些做法会增加延迟、提高复杂度,并削弱对系统的控制力。
对企业架构师而言,这个挑战并不陌生:如何在不破坏 Java 系统的简单性、可观测性和可靠性的前提下引入现代 AI?这个挑战延续了我们此前关于将 GPU 级性能带入企业 Java 的探索,其中保持 JVM 原生的效率与可控性被证明至关重要。
开放神经网络交换(ONNX) 标准给出了有力答案。在微软的支持下并被各大框架广泛采用,ONNX 能让包含命名实体识别(NER)、分类、情感分析等在内的 Transformer 推理原生地在 JVM 中运行。无需 Python 进程,无需容器蔓延。
本文面向希望将机器学习推理引入 Java 生产系统的架构师,提供设计层面的指南。我们将探讨分词器集成、GPU 加速、部署模式以及在受监管的 Java 环境中安全、可扩展地运行 AI 的生命周期策略。
为什么对架构师重要
企业系统日益需要 AI 来驱动客户体验、自动化工作流、并从非结构化数据中提取洞见。但在金融、医疗等受监管领域,生产环境更看重可审计性、资源控制与 JVM 原生工具链。
虽然 Python 在试验与训练阶段表现出色,但部署到 Java 系统时会带来架构摩擦。将模型包装为 Python 微服务会割裂可观测性、扩大攻击面,并引入运行时不一致。
ONNX 改变了这种局面。它为在 Python 中训练并在 Java 内运行的模型提供标准化的导出格式,原生支持 GPU 加速且不依赖外部运行时。关于在 JVM 内直接利用 GPU 加速的更多模式,请参见:将 GPU 级性能带入企业 Java。
对架构师而言,ONNX 解锁四项关键收益:
语言一致性:推理运行在 JVM 内,而不是旁路的进程。
部署简化:无需管理 Python 运行时或 REST 代理。
基础设施复用:沿用现有的基于 Java 的监控、追踪与安全控制。
可扩展性:在需要时启用 GPU 执行,无需重构核心逻辑。
通过消除训练与部署之间的运行时不匹配,ONNX 让我们可以像对待任何可复用的 Java 模块一样对待 AI 推理:模块化、可观测、并可经受生产环境的考验。
设计目标
在 Java 中设计 AI 推理不只是模型准确度问题,更是将机器学习嵌入企业系统的架构、运营与安全肌理中。良好的设计会设定系统级目标,确保 AI 的采用在各环境中可持续、可测试、且符合法规约束。
以下设计目标来自在 Java 中构建机器学习服务的高绩效企业团队的成功实践:
消除生产中的 PythonONNX 让团队可以将 Python 中训练的模型导出并在 Java 中原生运行,避免嵌入 Python 运行时、gRPC 桥接或容器化的 Python 推理服务,这些都会增加运维摩擦并复杂化安全部署。
支持可插拔的分词与推理分词器与模型应当模块化且可配置。分词器文件(如 tokenizer.json
)和模型文件(如 model.onnx
)应可按用例互换。通过分词器与模型的组合,可以适配 NER、分类、摘要等任务,而无需重写代码或违反整洁架构原则。
确保 CPU – GPU 的灵活性相同的推理逻辑应当既能在开发者笔记本(CPU)上运行,也能无改动地扩展到生产 GPU 集群。ONNX Runtime 原生支持通过 CPU 与 CUDA 执行提供者运行推理,使跨环境一致性既可行也具成本效益。
优化为可预测延迟与线程安全推理必须像任何企业级服务一样:确定性、线程安全、资源高效。干净的多线程、模型预加载与显式内存控制对于满足 SLA、增强可观测性并避免并发系统中的竞态条件至关重要。
设计为跨栈复用基于 ONNX 的推理模块应能够整洁地集成到 REST API、批处理管道、事件驱动处理器与嵌入式分析层。将预处理、模型执行与后处理解耦,是实现复用、可测试与长期维护合规性的关键。
这些目标帮助企业团队在不牺牲架构完整性、开发敏捷性或合规要求的情况下采用机器学习。
系统架构概览
将机器学习推理引入企业级 Java 系统不仅仅是模型集成的问题,更需要清晰的架构分层与模块化。一个健壮的基于 ONNX 的推理系统应被设计为一组松耦合组件,每个组件负责推理生命周期中的特定环节。
核心流程从接收来自 REST 端点、Kafka 流或基于文件的集成等多种来源的输入开始。原始输入会交由分词器组件处理,将其转换为 Transformer 模型所需的数值格式。分词器通过一个兼容 Hugging Face 的 tokenizer.json
文件进行配置,确保与训练时使用的词表与编码方式一致。
完成分词后,输入流入 ONNX 推理引擎。该组件调用 ONNX Runtime,在 CPU 或 GPU 后端执行模型推理。如果可用 GPU 资源,ONNX Runtime 可无缝委派给基于 CUDA 的执行提供者,而无需更改应用逻辑。推理引擎返回一组预测,通常是 logits(模型的原始、未经过 softmax 的输出分数)或类别 ID,随后由后处理模块进行解释。
后处理器将原始输出转化为有意义的领域实体,如标签、类别或抽取字段。最终结果会路由到下游消费者,无论是业务流程引擎、关系型数据库还是 HTTP 响应管道。
系统遵循整洁的架构流:适配器到分词器、分词器到推理引擎、推理引擎到后处理器、后处理器到消费者。每个模块都可以独立开发、测试与部署,使整个管道高度可复用与可维护。

图 1:Java 中可插拔的 ONNX 推理架构
通过将推理视为一条定义清晰的转换流水线,而非嵌入到单体服务内部的逻辑,架构师可以对性能、可观测性与部署进行精细化控制。这种模块化方法也支持模型的演进,能够在不破坏系统稳定性的情况下更新分词器或 ONNX 模型。
模型生命周期
在多数企业场景中,机器学习模型在 Java 生态之外进行训练,通常使用 Hugging Face Transformers 或 PyTorch 等 Python 框架。模型定稿后,会连同其分词器配置一起导出为 ONNX 格式,生成 model.onnx
文件与兼容的 tokenizer.json
文件。
对于基于 Java 的推理系统,这些工件相当于版本化输入,类似外部 JAR 或模式文件。架构师应将其视为受控的部署资产:经过验证、测试,并在各环境间有序发布,遵循与代码或数据库迁移同样的纪律。
可重复的模型生命周期包括导出模型与分词器、用代表性用例进行测试,并存储到内部的注册表或制品库。在运行时,推理引擎与分词器模块通过配置加载这些文件,实现安全更新而无需重启或完整重新部署应用。
通过将模型与分词器提升为一等部署组件,团队可以获得可追溯性与版本控制。在受监管环境中,这种提升对于可复现性、可解释性与回滚能力至关重要。
分词器架构
分词器是 Transformer 推理系统中最容易被忽视却至关重要的组件之一。人们的注意力往往集中在模型上,但分词器负责将人类可读文本转换为模型需要的输入 ID 与注意力掩码。只要这一转换过程存在任何不匹配,就会出现静默失败——预测在语法上看似合理,语义上却不正确。
在 Hugging Face 生态中,分词逻辑被序列化到 tokenizer.json
文件中。该工件编码了词表、分词策略(例如字节对编码或 WordPiece)、特殊 token 处理与配置设置。它必须使用训练时完全相同的分词器类与参数生成。即便是细微差异,如缺少 [CLS] token 或词表索引偏移,都会降低性能或破坏推理输出。
从架构上看,分词器应以独立、线程安全的 Java 模块存在,消费 tokenizer.json
文件并生成可用于推理的结构。它必须接受原始字符串并返回结构化输出,包含 token ID、注意力掩码以及(可选)偏移映射,以便下游解释。在 Java 服务中直接嵌入这层逻辑,而不是依赖 Python 微服务,可以降低延迟并避免脆弱的基础设施依赖。
在 Java 中构建分词层还能实现监控、单元测试,并完整集成到企业 CI/CD 流水线,同时便于在禁止 Python 运行时的安全或受监管环境中部署。在我们的架构中,分词器是一个模块化的运行时组件,能够动态加载 tokenizer.json
文件并在不同模型与团队之间复用。
推理引擎
一旦输入文本被转换为 token ID 与注意力掩码,推理引擎的核心任务就是将这些张量传入 ONNX 模型并返回有意义的输出。在 Java 中,这一过程由 ONNX Runtime 的 Java API 负责,提供成熟的绑定以加载模型、构造张量、执行推理与获取结果。
推理引擎的核心是 OrtSession
类,它是 ONNX 模型的编译、初始化后的表示,可在请求之间复用。该会话应在应用启动时初始化,并在各线程之间共享。每次请求都重建会话会引入不必要的延迟与内存压力。
准备输入涉及创建 NDArray
张量,如 input_ids
、attention_mask
,以及可选的 token_type_ids
,这些都是 Transformer 模型通常期望的标准输入。张量由 Java 原生数据结构构造,再传入 ONNX 会话。会话运行推理并生成输出,通常包括 logits、类别概率或结构化标签,具体取决于模型。
在 Java 中,推理调用通常如下:
ONNX Runtime 还支持执行提供者,用以决定推理在 CPU 还是 GPU 上运行。在启用了 CUDA 的系统上,推理可以以最小配置卸载到 GPU。如果 GPU 资源不可用,则优雅地回退到 CPU,使得跨环境行为保持一致。这种灵活性让单一 Java 代码库可以从开发者笔记本扩展到生产 GPU 集群,无需分支逻辑,与我们在“将 GPU 级性能带入企业 Java”一文中讨论的理念一脉相承。
从架构角度看,推理引擎必须保持无状态、线程安全与资源高效。它应提供干净的可观测性接口:日志、追踪与结构化错误处理。对于高吞吐场景,池化与微批处理有助于优化性能;在低延迟语境下,内存复用与会话调优对于保持可预测的推理成本至关重要。
将推理视为具有清晰契约与可边界化性能特征的模块化服务,架构师即可将 AI 逻辑完全与业务工作流解耦,实现独立演进与可靠扩展。
部署模式
设计推理引擎只是挑战的一半;如何在企业环境中部署同样重要。Java 系统涵盖从 REST API 到 ETL 管道与实时引擎的广泛场景,因此基于 ONNX 的推理必须在不重复逻辑或割裂配置的前提下进行适配。在多数情况下,分词器与推理引擎作为 Java 库直接嵌入,从而避免运行时依赖,并与日志、监控与安全框架整洁集成。在 Spring Boot 与 Quarkus 等框架中,推理就是另一个可注入的服务。
更大的团队常将这层逻辑外置为共享模块,负责分词器与模型加载、张量准备与 ONNX 会话执行。外置化促进复用、简化治理、并在各服务之间提供一致的 AI 行为。
在具备 GPU 的环境中,可以通过配置启用 ONNX Runtime 的 CUDA 提供者,无需更改代码。相同的 Java 应用既能在 CPU 集群上运行,也能在 GPU 集群上运行,使部署既可移植又资源感知。
模型制品既可随应用打包,也可从模型注册表或挂载卷动态加载。后者支持热替换、回滚与 A/B 测试,但需要谨慎的验证与版本管理。关键在于灵活性。一个可插拔、对环境敏感的部署模型,无论是嵌入式、共享式还是容器化,都能确保推理无缝融入现有的 CI/CD 与运行策略。
与框架层抽象的比较
Spring AI 等框架通过为 OpenAI、Azure 或 AWS Bedrock 等提供者提供客户端抽象,简化了调用外部大型语言模型的过程。这些框架在原型化对话界面与检索增强生成(RAG)管道方面很有价值,但其工作层次与基于 ONNX 的推理根本不同。前者将推理委托给远程服务,后者则在 JVM 内直接执行模型,使推理保持确定性、可审计且完全受企业控制。
这种区别具有实际影响。外部框架的输出不可重复,并依赖第三方提供者的可用性与不断演进的 API。相比之下,ONNX 推理使用版本化的工件,model.onnx
与 tokenizer.json
,在各环境中表现一致,从开发者笔记本到生产 GPU 集群都不例外。这种可重复性对合规与回归测试至关重要,因为模型行为的细微差异可能带来显著的下游影响。它也确保敏感数据不离开企业边界,这在金融与医疗等领域是基本要求。
也许更为重要的是,ONNX 保持供应商中立。作为被各训练框架广泛支持的开放标准,组织可以自由使用偏好的生态进行训练,并在 Java 中部署,而不必担心供应商锁定或 API 漂移。由此,ONNX 与 Spring AI 等框架是互补关系而非竞争关系。前者为合规关键的工作负载提供稳定的、进程内的基础;后者帮助开发者在应用边缘快速探索生成式用例。对架构师而言,能够清晰划定这条边界,才能确保 AI 采用既具创新性又在运营上可持续。
下一步
既然我们已经阐明如何通过原生分词与无状态推理层将 ONNX 模型集成到 Java 系统中,下一步的挑战是在生产环境中安全、可靠地扩展这套架构。
在下一篇文章中,我们将探讨:
Java 中的安全与审计:实现可追踪、可解释且符合法治的 AI 管道。
可扩展的推理模式:在 CPU/GPU 线程、异步作业队列与高吞吐管道间负载均衡,使用 Java 原生构造。
内存管理与可观测性:剖析推理内存占用、追踪慢路径,并使用 JVM 原生工具调优延迟。
超越 JNI 的演进:动手解读外部函数与内存 API(JEP 454)作为未来可靠推理管道对 JNI 的替代方案。
作者注:本文实现基于独立的技术研究,不代表任何特定组织的架构。
本文翻译自:https://www.infoq.com/articles/onnx-ai-inference-with-java/,作者:Syed Danish Ali
评论