Java原生AI开发实战:Spring AI与LangChain4j集成企业应用
2026/9/24 18:32:25 网站建设 项目流程

如果你在一家以 Java 为主力语言的公司做后端,最近开会大概率绕不开一个话题:要不要上 AI?我的答案很直接——要上,但不是把 Python 那套 Demo 搬过来,而是用 Java 原生框架把 AI 能力嵌进现有业务系统。这篇文章不是概念科普,是我自己在企业里做 Java + AI 改造的实战复盘,涵盖 Spring AI、LangChain4j、DJL 的选型,一个 RAG 知识库问答的完整落地过程,还有 Agent 接业务时踩过的坑。准备用 Java 做企业智能升级的团队,可以照着这份思路去推。

我不太喜欢一上来就讲“大模型很厉害”这种废话。事实是,企业里大部分有价值的数据和流程都跑在 Java 后端里,AI 要真正产生业务价值,必须和这些存量系统打通。而 Java 原生框架最大的价值,就是把“模型能力”和“工程规范”揉在一起,让普通后端团队也能安全、可控地上生产。

1. 为什么企业AI转型,最终还是回到Java主航道

1.1 存量系统与智能能力之间的“最后一公里”

企业做 AI 转型,难点从来不是“调一个 API”,而是让模型理解企业内部的数据结构、业务规则和权限边界。我见过不少团队先用 Python 写模型调用脚本,跑通之后发现没法落地,原因很现实:企业核心系统是 Java 的,账号体系是 Java 的,审批流是 Java 的,监控报警体系也是 Java 的。一个 Python 服务想接进这套体系,等于是额外造了一条需要长期维护的“外挂链路”。

更麻烦的是,Python 方案往往以算法工程师为主,他们擅长做模型实验,但对企业级的事务、幂等、权限、审计这些事并不一定敏感。Java 后端团队恰恰相反,系统稳定性、代码规范、发布流程这些事几乎是刻在骨子里的。用 Java 原生框架做 AI 集成,意味着可以用同一套代码规范、同一个发布管道、同一套监控大盘来管理模型调用,这比引入一个异构技术栈要稳得多。

我并不是说 Python 不能做 AI。在模型训练、数据探索、复杂算法实验这些场景,Python 依然有不可替代的优势。但企业智能升级中占比最大的其实是“把已有业务数据变成可问答、可推理、可辅助决策的工程能力”,这个场景下,Java 的工程生态更成熟。尤其是当模型需要读写数据库、操作订单、触发审批流时,Java 后端已有的领域模型和服务层可以直接复用,AI 只是业务链路里的一个环节,而不是独立王国。

所以我的结论是:AI 转型不是把 Java 换成 Python,而是让 Java 长出 AI 能力。原生框架解决的问题,正是模型能力和存量系统之间这“最后一公里”的集成问题。

1.2 Java原生AI框架解决的核心问题

什么叫“原生框架”?我理解的范围是:运行在 JVM 进程内、以 Java API 为主、能和 Spring Boot 等主流容器无缝整合的 AI 开发框架,比如 Spring AI、LangChain4j、DJL。这类框架和“Python 算法服务 + HTTP 调用”的最大区别,是 AI 调用不再是外部黑盒,而是一个可以被拆解、被断点、被单元测试的普通 Java 组件。

用原生框架至少能解决三件事。第一,团队技能可以复用,不需要每个项目都配一个算法工程师,Java 工程师看完文档就能上手写 AI 功能。第二,安全管控可以复用,Spring Security 的登录态、接口鉴权、数据权限可以原样作用到 AI 接口上,不用在一个新服务里重新做一遍。第三,部署运维可以复用,一个 JAR 包能跑的事,不需要拆成两个服务去管理,也不需要额外维护网络策略和接口鉴权。

我整理过一个内部对比,前端时间给团队做技术选型时用的就是这个思路:

对比维度Java原生框架方案Python微服务+HTTP方案
接入方式JVM进程内调用,普通方法调用跨服务HTTP调用,要处理网络、鉴权
部署成本一个JAR/容器即可至少两个服务,多一条链路要维护
调试体验本地断点、单测覆盖跨服务看日志,排查链路长
权限整合复用Spring Security要单独做网关鉴权和用户传递
性能开销进程内通信,延迟低每次模型调用多一次网络开销
团队要求纯Java后端可上手需要Python工程能力或算法背景

这个对比不是为了否定 Python,而是想说:如果目标是把 AI 能力嵌入企业现有业务流程,Java 原生框架在工程成本上往往有明显优势。

2. 核心框架选型:Spring AI、LangChain4j与DJL的区别

2.1 Spring AI:适合做企业级LLM应用底座

Spring AI 是 Spring 官方主导的项目,目标是把大模型接入变成像 Spring JDBC 那样统一的抽象层。如果你所在团队已经重度使用 Spring Boot,Spring AI 几乎是学习成本最低的入口。它把 ChatClient、PromptTemplate、VectorStore、Advisor 这些概念封装成了 Spring 风格 API,很多老后端看几段示例代码就能直接写。

我这边一个实际项目里,接入大模型只用了很短的时间,核心代码如下:

// pom.xml 依赖片段,具体版本号以官方发布为准 <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency>
@Service public class LegalAssistant { private final ChatClient chatClient; public LegalAssistant(ChatClient.Builder builder) { this.chatClient = builder .defaultSystem("你是企业内部法务助手,只能根据给定材料回答,不要推测。") .build(); } public String answer(String question) { return chatClient.prompt() .user(question) .call() .content(); } }

这种写法对后端同学很友好,没有复杂抽象,配置模型地址和 API Key 之后就能跑。Spring AI 的好处是它会持续演进,把消息历史、多轮对话、工具调用、RAG 等能力都收进统一模型,适合作为企业里多个业务系统共用的“AI 底座”。需要注意版本迭代比较快,API 偶有调整,建议锁定稳定版本并做好升级测试,不要每次都在生产环境追新。

2.2 LangChain4j:RAG和Agent场景更顺手

LangChain4j 是社区驱动的 Java 版本 LangChain,设计上参考了 Python 生态里的 LangChain,但没有照搬,而是用了不少 Java 开发者熟悉的手法。它在 RAG、Agent、Tool Calling 这几个场景做得比较细,尤其适合要把大模型和业务方法打通的项目。

我第一次用 LangChain4j 做订单问答时,感受最深的是工具调用做得很“Java”。定义工具就是写一个普通方法,加上注解即可:

@Tool("根据订单号查询订单状态") public String getOrderStatus(@P("订单号") String orderId) { return orderClient.findStatus(orderId); }

然后通过 AiServices 把这个工具绑定到模型上,业务侧只需要调用一个接口:

public interface OrderAssistant { String chat(String userMessage); } OrderAssistant assistant = AiServices.builder(OrderAssistant.class) .chatLanguageModel(model) .tools(new OrderAgentTool()) .build(); String answer = assistant.chat("我的订单A10086到哪了?");

不要把这个理解成“写死的关键词匹配”,它背后是模型自己决定要不要调用 getOrderStatus 方法,并且会根据模型约束解析出参数。相比自己手工维护关键词规则,这种方式能覆盖更多自然语言表达,比如“帮我看看单号A10086走到哪儿了”“A10086发货了吗”都能触发同一个工具。

2.3 DJL与ONNX Runtime:私有化部署与模型推理的另一条路

很多企业做大模型应用时忽略了一点:不是所有 AI 能力都必须用云端大模型。比如文档解析里的 OCR、图片分类、文本向量化、敏感信息识别,这些模型完全可以私有化部署,数据不出域,成本和延迟也可控。DJL(Deep Java Library)是 AWS 主导的 Java 深度学习框架,可以直接在 JVM 里加载 PyTorch、TensorFlow、ONNX 模型,是 Java 团队做本地推理很实用的选择。

比如加载一个 ONNX 目标检测模型做票据识别,核心思路是这样:

Criteria<Image, Classifications> criteria = Criteria.builder() .optApplication(Application.CV.OBJECT_DETECTION) .optModelUrls("file:///models/object-detection.onnx") .optEngine("OnnxRuntime") .build(); ZooModel<Image, Classifications> model = ModelZoo.loadModel(criteria);

DJL 的优势是模型不依赖 Python 运行环境,JVM 加载后即可对外提供接口。对于“数据不能出内网”的银行、政务、制造类项目,这几乎是必需能力。当然 DJL 不擅长做大模型的生成式推理,它的强项是视觉、NLP 特征提取这类任务。如果你要做的是“公司内部知识库问答”,主力还是 Spring AI 或 LangChain4j,DJL 用来补齐私有化的小模型推理能力。

2.4 框架选型速查表

我不喜欢给出“标准答案”,因为每个团队情况不一样。但可以给一个相对实用的选型方向,供你结合自己团队的技术栈去判断:

框架主要定位最合适场景注意事项
Spring AILLM应用统一抽象已有Spring Boot,要快速接大模型/多模型切换版本迭代快,需锁定版本
LangChain4jRAG与Agent工具调用需要模型调用业务方法、做知识库问答注意模型是否支持function calling
DJL / ONNX RuntimeJVM本地模型推理私有化部署OCR、向量化、图像模型不适合做大模型生成式推理
自研封装定制化要求极高团队有AI平台能力,要做统一底层维护成本高,不建议从零开始

如果让我给一个起步组合,我倾向:Spring AI 做 LLM 接入底座,LangChain4j 做 RAG 和 Agent 工具调用,DJL 或 ONNX Runtime 负责必须私有化的小模型。三个不是互斥关系,在同一个项目里可以共存,按需引入。

3. 实操案例:用Java原生框架搭建企业知识库问答

3.1 场景定义与流程设计

知识库问答是大多数企业做 AI 升级的第一个项目,原因很直接:不涉及太深的业务改动,却能在内部快速看到效果。我这里说的是真正的“企业知识库问答”,不是随便接个大模型聊天窗口。我们的需求是:员工只能询问已经录入系统的产品手册、管理制度、操作指引,系统回答必须基于已有文档;查不到的内容直接说不知道,不能编;所有问答行为留审计日志。

围绕这个需求,标准 RAG 链路至少要包含:文档解析、文本分块、向量化、存入向量库、用户问题向量化、相似度检索、大模型基于上下文生成答案。每一步都不难,但每一步都有坑。尤其是“文档解析”和“分块策略”,很多人忽略,实际上直接决定最后效果。

3.2 文档解析与分块策略

解析 PDF、Word、PPT 这类文件,Java 生态里可以用 Apache PDFBox、Apache POI、Apache Tika。选型上,格式多就用 Tika 做统一入口,PDF 为主就直接 PDFBox。重点不在工具,而在解析后的结构保留。

我踩过一个典型问题:产品手册里有很多表格,直接用固定长度分块,表格被切成两半,检索时拿到的是残缺内容,模型回答出来自然也是残缺的。后来改成“按标题优先分块”:先从文档里提取一级/二级标题,再按标题把段落和表格归到对应章节,表格尽量整体作为一个文本块。如果内容太长,再按段落切,同时保留前后重叠。

分块参数方面,我常用的基线是 chunk size 500 字左右、chunk overlap 50 到 100 字。这个值不是固定的,要看你文档的语言和结构。中文文档按字符数切容易切断语义,建议按句子和段落边界做软分割,而不是机械地数满 500 字就切。分块之后最好做一次人工抽查,看看每块是不是“读起来是一个完整意思”,这一步比调模型参数更重要。

3.3 向量化与向量数据库选型

向量化就是把文本转成一组浮点数,让语义相近的文本在向量空间里距离更近。这一步的关键是选嵌入模型。私有化部署建议选中文效果好的模型,比如 bge-m3 或 text2vec 系列。如果数据允许出域,也可以走云厂商的 embedding API,但企业场景我通常不推荐。

向量数据库选型要考虑现有基础设施。公司已经有用得很熟的 Elasticsearch,可以直接用 ES 的 dense_vector 能力,不用额外引入新组件。数据量小、团队只想先验证,可以选 PGVector,直接建在现有 PostgreSQL 里,运维成本极低。数据量到了千万级、查询并发高,再考虑 Milvus 这类专业向量库。不要一上来就堆组件,知识库初期几千个文档,PGVector 完全够用。

Spring AI 里对向量库做了抽象,切换成本不高。以 PGVector 为例,一个 Bean 就能接上:

@Bean public VectorStore vectorStore(EmbeddingModel embeddingModel, DataSource dataSource) { return new PgVectorStore(dataSource, embeddingModel); }

检索时可通过 metadata 过滤来限定范围。比如文档入库时带上departmentdocumentType字段,查询时只检索当前用户有权限的部门文档,这个过滤必须在向量检索层完成,不能只靠大模型的提示词。

3.4 提示词模板与输出约束

RAG 的核心是给模型提供足够准确的上下文,同时把它的回答限制在上下文范围内。我的提示词模板一般长这样:

String answer = chatClient.prompt() .system(s -> s.text("你是企业知识库助手。仅依据下面提供的上下文回答用户问题," + "如果上下文里没有明确答案,直接回答:知识库中暂无相关信息。" + "不要编造事实,不要输出与问题无关的内容。\n\n{context}")) .user(userQuestion) .call() .content();

模板里的{context}是检索阶段拼进去的文档片段。这里有个容易被忽略的点:不要把外部输入直接拼进 system 消息里,而是放在 user 消息中。否则,如果用户输入里包含“忽略以上所有指令”之类的内容,模型很可能真的被带偏,产生越权或误导性回答。

输出约束方面,如果后续需要做结构化解析,优先引导模型输出 JSON,并开启模型的 JSON 模式或 function calling,而不是手动解析自然语言结果。不要指望模型每次输出都完全一致,解析层要留容错空间。

3.5 权限、合规与审计怎么嵌入RAG

知识库问答在企业里最容易出问题的不是“答不对”,而是“越权”。一份内部薪酬制度,如果被一个普通员工问出来,再准确也是个事故。所以我把权限设计放在所有设计的最前面。

具体做法是这样的:文档入库时,在 metadata 里打上访问范围标签,比如“仅人事部门”“全体可见”“仅管理层”。用户发起查询时,通过当前登录用户的部门、角色生成权限条件,拼进向量库检索的 metadata filter。这一步把无权限的文档直接从候选集里排除,模型根本看不到,再聪明的提示词注入也问不出来。

所有问答记录都要落审计日志,内容至少包括:用户ID、所属部门、提问时间、问题内容、命中的文档来源、模型回复、token 消耗。日志不是用来事后追责的,而是当模型回答出错时,能够快速回放当时上下文,定位是检索问题还是模型问题。合规审查时,这套日志也是自证清白的关键证据。

4. Agent智能体与业务流程打通

4.1 Agent原理和Java落地形态

Agent 听起来玄乎,其实在企业场景里,落地最多的形态就是“工具调用”。大模型根据用户意图决定要不要调用某个函数,函数返回结果后再交给大模型组织语言回复。把这个过程循环起来,就形成了一个简单的 Agent。

如果意图不明确,模型可以连续调用多个工具,比如先查订单,再查物流,最后把两段信息合并回答。Java 里实现这个并不需要复杂的规则引擎,LangChain4j 已经把调用循环封好了,我们只需要把工具方法写好。但要注意,Agent 不是万能的,它适合意图相对集中、边界清晰的任务;如果一个业务涉及十几个不确定的后续步骤,还是老老实实用流程引擎编排,不要指望模型自己解决。

4.2 用LangChain4j实现一个能查订单的Agent

前面已经给了订单查询的工具方法示例,这里补一下完整闭环的思路。流程是:用户输入→LLM 判断需要调用工具→框架解析参数→执行 Java 方法→把结果返回给 LLM→LLM 生成最终回答。

实际编码时,工具方法要注意参数校验,因为 LLM 生成的参数不一定合法。比如订单号可能是空字符串、可能包含非法字符,工具方法里要做防御性校验,宁可抛业务异常,也不要拿着脏数据去查库。

工具方法内部可以放心使用 Spring Bean,可以加@Transactional,可以加 Spring Security 的权限注解。但有一点务必记住:不要让 LLM 直接操作数据库,而是让它调用封装好的业务服务。模型只负责“理解意图”和“组织表达”,真正的数据访问和业务规则必须牢牢控制在 Java 服务层手里。

4.3 Agent内容如何与事务、权限、流程引擎整合

实际项目里,Agent 往往不能独立存在,它要挂在现有的业务流程中。比如在工单系统里,用户说“帮我查一下上个月未处理的报修工单”,Agent 需要知道当前用户是谁,然后基于当前用户的权限去查,而不是接收一个前端传过来的用户名。解决方法是把登录态从 Web 层透传到工具方法里,直接用SecurityContextHolder拿当前用户信息,工具方法内部再校验该用户是否有权访问对应数据。

还要做兜底限制。比如设置最大调用轮次为 5,超过则终止;整个 Agent 调用设置总超时时间;工具执行失败要返回明确错误信息给模型,避免模型假装成功。另一个经验:Agent 要接入人工审批环节。让模型生成审批意见草稿没问题,但最终提交必须走人工确认,这样既能提效,又能控制合规风险。

4.4 Prompt日志与调用链追踪

Agent 比普通接口难排查,因为一个用户问题可能触发多次模型调用和多次工具调用。没有日志根本不知道是哪一步出了问题。我在项目里会给每次对话生成 traceId,用 SLF4J 的 MDC 贯穿所有日志,每次模型调用、工具调用都把输入输出打出来。

如果公司已经有 OpenTelemetry 这类链路追踪体系,建议把 AI 调用也接到里面去。没有的话,至少要在业务表里存一份完整的“请求-响应”记录,包括用户问题、模型原始输出、工具调用结果、最终回复。现在大模型调用都是要花钱的,这部分日志也方便统计 token 消耗,遇到异常消耗可以及时报警。

5. 常见问题排查与避坑实录

5.1 Java版本与Maven编译不一致

很多人遇到的“Java: 警告: 源发行版 17 需要目标发行版 17”这类问题,本质是开发环境 JDK 版本和 Maven 编译器版本不一致。Spring AI 这类新框架普遍要求 JDK 17 以上,如果你在 IDEA 里配的 JDK 是 17,但 Maven 默认用 JDK 8,就会出现编译版本错误。

解决办法是统一配置:

<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties>

然后确认 IDEA 的 Project SDK、Maven Runner 的 JRE 都指向同一个 JDK。不要小看这个问题,新框架对 JDK 版本敏感,我在集成 Spring AI 时就遇到过低版本 JDK 导致 Spring 上下文启动失败的情况。企业项目建议直接统一用 JDK 17 LTS,避免每个同事本地版本不一样。

5.2 模型接口超时、重试和熔断

调用外部大模型 API 和调用普通 HTTP 服务不一样,响应时间波动很大,有时候几秒,有时候几十秒。如果不做超时控制,用户请求会一直挂着,拖垮整个服务的线程池。

我习惯把连接超时和读取超时分开设置。连接超时短一些,比如 3 到 5 秒;读取超时根据模型规模和场景设 30 到 60 秒。重试要限制次数,通常不超过两次,并且要加指数退避,比如第一次等 1 秒,第二次等 2 秒再重试。对关键业务接口,还要设计降级策略,比如外部模型挂掉时,返回知识库检索结果中的高置信度片段,或者提示用户稍后再试。

5.3 中文向量检索效果差

中文检索效果不理想,最常见的两个原因:一是 embedding 模型对中文支持不好,二是分块切得太碎导致语义丢失。嵌入模型如果直接用英文优化的模型处理中文,检索出的结果会飘,相关性很差。建议换成 bge-m3、text2vec 中文模型,或者云厂商的中文 embedding 服务。

另外,可以加入关键词检索做混合召回。向量检索擅长处理同义词和语义相近的表达,但精确关键词比如单号、姓名、型号这类信息,用倒排索引效果更稳定。把向量检索和 BM25 检索结果做融合,再统一排序,能明显提升知识库问答的效果。相似度阈值也要调,我这边通常把余弦相似度阈值设在 0.75 到 0.8 之间,低于阈值就直接回答“知识库中暂无相关信息”,别硬答。

5.4 响应不稳定与结构化输出

大模型是概率生成,同样的 Prompt 输出可能每次都不一样。如果业务需要结构化输出,靠“请输出 JSON”这种自然语言约束不够稳。优先选用模型的 JSON 模式或 function calling 能力,框架层面直接拿到结构化结果。

如果模型不支持结构输出,就在 Prompt 里给一个具体的 JSON 示例,同时在解析层做容错。比如解析失败就再请求一次,或者用正则把多余的前缀后缀剥离出来。不要因为一次输出解析失败就给用户抛异常,解析失败在生成式应用里是需要正常处理的业务逻辑。

5.5 团队招聘时关注的Java+AI考察点

最近不少后端团队招聘都要加 AI 相关要求,我也简单说说我面试会问什么,就当给大家一个自查清单。第一个,策略模式,怎么把多个大模型抽象成统一接口,实现无感切换;第二个,代理模式,怎么在模型调用前后统一加日志、限流和超时控制;第三个,CompletableFuture,怎么并发调用多个工具,提升 Agent 响应速度;第四个,向量检索的基本原理,为什么它能解决“关键词搜不到同义词”的问题。这些问题都不是八股,而是真实项目里一定会遇到的工程点。

6. 一点个人体会

如果你现在正准备带着 Java 团队冲 AI,我的建议是不要一开始就铺一个大而全的智能平台,先找一个边界清晰、业务价值明显的场景切入,比如内部知识库问答、工单智能助手、文档信息抽取,跑通之后再横向扩展。原生框架的意义不是让 AI 变魔法,而是把模型调用纳入现有的工程规范里,该有的代码评审、单元测试、权限校验、审计日志一样都不能少。

我自己还有一个习惯,所有 AI 能力先封装成内部接口,底层接什么模型都可以替换。今天用外部大模型,明天想换私有化模型,只需要改一个实现类。每次修改 Prompt 都走代码评审,因为 Prompt 就是逻辑和规则的一部分。本地调试时先用 Ollama 跑一个小模型,用固定答案验证整个请求链路,没问题再接商业大模型,这样能省测试费用,也能快速定位是框架配置问题还是模型效果问题。Java 生态里的 AI 工具正在快速成熟,选对路子之后,你会发现做企业智能升级,未必需要推倒重来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询