ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java原生AI开发实战:Spring AI与LangChain4j集成企业应用

Java原生AI开发实战:Spring AI与LangChain4j集成企业应用 如果你在一家以 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 BootSpring AI 几乎是学习成本最低的入口。它把 ChatClient、PromptTemplate、VectorStore、Advisor 这些概念封装成了 Spring 风格 API很多老后端看几段示例代码就能直接写。我这边一个实际项目里接入大模型只用了很短的时间核心代码如下// pom.xml 依赖片段具体版本号以官方发布为准 dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependencyService 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 LangChain4jRAG和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、图片分类、文本向量化、敏感信息识别这些模型完全可以私有化部署数据不出域成本和延迟也可控。DJLDeep Java Library是 AWS 主导的 Java 深度学习框架可以直接在 JVM 里加载 PyTorch、TensorFlow、ONNX 模型是 Java 团队做本地推理很实用的选择。比如加载一个 ONNX 目标检测模型做票据识别核心思路是这样CriteriaImage, Classifications criteria Criteria.builder() .optApplication(Application.CV.OBJECT_DETECTION) .optModelUrls(file:///models/object-detection.onnx) .optEngine(OnnxRuntime) .build(); ZooModelImage, Classifications model ModelZoo.loadModel(criteria);DJL 的优势是模型不依赖 Python 运行环境JVM 加载后即可对外提供接口。对于“数据不能出内网”的银行、政务、制造类项目这几乎是必需能力。当然 DJL 不擅长做大模型的生成式推理它的强项是视觉、NLP 特征提取这类任务。如果你要做的是“公司内部知识库问答”主力还是 Spring AI 或 LangChain4jDJL 用来补齐私有化的小模型推理能力。2.4 框架选型速查表我不喜欢给出“标准答案”因为每个团队情况不一样。但可以给一个相对实用的选型方向供你结合自己团队的技术栈去判断框架主要定位最合适场景注意事项Spring AILLM应用统一抽象已有Spring Boot要快速接大模型/多模型切换版本迭代快需锁定版本LangChain4jRAG与Agent工具调用需要模型调用业务方法、做知识库问答注意模型是否支持function callingDJL / 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 过滤来限定范围。比如文档入库时带上department和documentType字段查询时只检索当前用户有权限的部门文档这个过滤必须在向量检索层完成不能只靠大模型的提示词。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.source17/maven.compiler.source maven.compiler.target17/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 团队招聘时关注的JavaAI考察点最近不少后端团队招聘都要加 AI 相关要求我也简单说说我面试会问什么就当给大家一个自查清单。第一个策略模式怎么把多个大模型抽象成统一接口实现无感切换第二个代理模式怎么在模型调用前后统一加日志、限流和超时控制第三个CompletableFuture怎么并发调用多个工具提升 Agent 响应速度第四个向量检索的基本原理为什么它能解决“关键词搜不到同义词”的问题。这些问题都不是八股而是真实项目里一定会遇到的工程点。6. 一点个人体会如果你现在正准备带着 Java 团队冲 AI我的建议是不要一开始就铺一个大而全的智能平台先找一个边界清晰、业务价值明显的场景切入比如内部知识库问答、工单智能助手、文档信息抽取跑通之后再横向扩展。原生框架的意义不是让 AI 变魔法而是把模型调用纳入现有的工程规范里该有的代码评审、单元测试、权限校验、审计日志一样都不能少。我自己还有一个习惯所有 AI 能力先封装成内部接口底层接什么模型都可以替换。今天用外部大模型明天想换私有化模型只需要改一个实现类。每次修改 Prompt 都走代码评审因为 Prompt 就是逻辑和规则的一部分。本地调试时先用 Ollama 跑一个小模型用固定答案验证整个请求链路没问题再接商业大模型这样能省测试费用也能快速定位是框架配置问题还是模型效果问题。Java 生态里的 AI 工具正在快速成熟选对路子之后你会发现做企业智能升级未必需要推倒重来。
返回列表