AIGS时代下的Java AI应用:框架选型与RAG落地实战

发布时间:2026/10/7 4:11:51
AIGS时代下的Java AI应用:框架选型与RAG落地实战 最近被问得最多的一句话是Java 还有没有补 AI 的必要AIGS 时代都摆到眼前了是不是大家都得转 Python我的回答一直很明确不用转。AIGSAI-Generated SoftwareAI 生成式软件工程并不等于“以后只写 Python”恰恰相反生产级 AI 应用的主要矛盾从来不在模型训练而在于模型服务化、知识库接入、Agent 编排和系统稳定性治理这些正是 Java 技术栈的主场。这篇文章不是一个框架的说明书而是把 Java 生态里能直接上生产的 AI 框架捋一遍从选型思路、核心组件对比到一个完整的 RAG 问答服务落地过程再到我实际踩过的依赖冲突、模型加载和内存问题。适合刚准备切入 AI 应用的 Java 工程师、正在做技术选型的架构师以及那些已经写了几年 Spring Boot、想搞清楚“Java 到底怎么接大模型”的人。1. 为什么 AIGS 时代 Java 依然有不可替代的生态位1.1 模型训练归 Python工程落地归 Java两边都不越界很多人一提到 AI 就默认等于 Python这是被模型训练阶段的生态误导了。训练、微调、做 paper 复现Python 确实无敌PyTorch、Transformer 这些库构成了完整的研究闭环。但到了企业里真正产生业务价值的是把模型能力嵌入现有系统智能客服、知识库问答、代码审查、自动化测试、报表解读这些都是“API 调用 业务逻辑 数据流”的组合。这一层恰恰不是 Python 的传统强项。Java 有 Spring Boot 的生态积累、有成熟的微服务治理体系、有 Concurrency 和容器化部署的最佳实践。说得直白点AI 模型最后都是以推理接口的形式对外提供能力Java 系统就是那个接口的消费者和调度者。我在实际项目中见过不少团队把推理逻辑直接写在 Python Web 服务里结果遇到高并发、鉴权和灰度发布时反而要额外补一堆工程能力。与其这样不如让 Python 专注模型Java 专注系统。1.2 Java 的稳定、并发和治理能力才是 AIGS 生产环境的地基AIGS 时代的应用有个显著特点调用链变长。一次用户请求可能先查向量库再拼提示词再调模型接口然后对输出做解析、校验、缓存中间还要考虑限流和降级。这条链路对运行时稳定性极其敏感。Java 在这一层的优势是累积了二十年的企业级能力线程池、连接池、熔断、重试、分布式追踪这些都不是新概念但恰好是 AI 应用最容易缺的东西。我见过一个很典型的案例一个基于大模型的文档解析服务凌晨高峰期连续超时排查下来发现调用模型 API 的 HttpClient 连接池没配好连接复用率极低每次请求都在新建连接。换成 Java 系的标准做法把连接池、超时、重试参数显式配置好问题立刻消失。这就是为什么我说 Java 在 AIGS 时代的定位不是“可以干活”而是“干得很稳”。模型可以换框架可以变但承载这些组件的底座终究需要一个扛得住生产压力的运行时。2. 能直接进 Maven 的 Java AI 框架盘点2.1 四个主力框架DJL、Spring AI、LangChain4j、ONNX Runtime先说 Deep Java LibraryDJL这是 AWS 主导的 Java 深度学习框架定位是“全栈”。它能加载 PyTorch、TensorFlow、ONNX 格式的模型直接跑推理也能做训练。如果你需要的是在 JVM 内直接加载一个模型文件不经过外部推理服务DJL 几乎是首选。它的 Model Zoo 里预置了一批经典视觉模型图片分类、目标检测这些场景开箱即用。Spring AI 是 Spring 官方团队推出的 AI 应用框架思路非常“Spring 系”统一抽象把 OpenAI、Ollama、Azure OpenAI、Google Vertex AI 都封装成一套 ChatClient 接口。它强调与 Spring Boot 生态的天然融合配置走 application.yml自动装配帮你搞定模型客户端工程化体验最好。如果你已经在一个 Spring Boot 项目里用 Spring AI 的摩擦最小。LangChain4j 是 LangChain 的 Java 移植版弥补了 Java 生态在链式调用、记忆管理、Agent 编排上的空白。它的 AiServices 接口可以把一个普通 interface 变成 AI 服务方法调用对应一次模型交互支持工具调用、RAG、对话记忆。我见过的很多 Java 团队做 Agent 都是靠它起步文档和示例一直在更新。ONNX Runtime 本身是跨平台推理引擎它的 Java API 很成熟适合把训练好的模型导出成 ONNX 格式后在 JVM 内执行。它的优势是性能稳定算子覆盖广在 CPU 上也能获得相对不错的推理速度。如果你的场景是模型已经确定、不需要频繁切换用 ONNX Runtime 做推理层是比较稳妥的选择。2.2 框架选型的四个判断维度选框架不能只看 GitHub Star 数我建议按四个维度权衡模型接入方式、RAG/Agent 支持深度、与现有工程体系融合度、社区更新频率。这四点直接决定了你三个月后是顺利上线还是重写一遍。框架模型接入方式RAG/Agent 能力与 Spring 融合度适合场景DJLJVM 内加载模型弱需自行组合一般模型推理、CV 任务Spring AI外部模型 API/本地推理强RAG 组件完善极高业务系统接入 LLMLangChain4j外部模型 API/本地推理强Agent 编排完善高复杂链式调用、多 AgentONNX RuntimeJVM 内加载 ONNX无一般固定模型高性能推理我个人的使用经验是如果主要做企业内的智能应用比如知识库问答、工单自动分类、报表解读用 Spring AI 起步需要复杂 Agent 时再引入 LangChain4j 做编排如果要在 JVM 里直接跑小模型DJL 和 ONNX Runtime 是更务实的选择。不要一上来就贪多框架越多类冲突和概念负担越重。3. 实战案例用 Spring AI Ollama PGVector 搭 RAG 问答服务3.1 环境准备这套组合为什么这么配理论知识讲再多不如跑通一个最小可用的例子。我选了一套对新手最友好的组合Spring Boot 3 Spring AI Ollama PGVector。Ollama 负责本地运行开源大模型和嵌入模型不需要申请外部 API Key也没有网络延迟问题PGVector 直接复用你熟悉的 PostgreSQL在上面加一个向量检索维度不用额外引入一套向量数据库。依赖坐标只需要几个核心项以 Spring AI 0.8.1 为例Maven 配置大致如下dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId version0.8.1/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pgvector-store-spring-boot-starter/artifactId version0.8.1/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-tika-document-reader/artifactId version0.8.1/version /dependency这里要提醒一句Spring AI 的版本迭代很快API 在 1.0 前后有过调整复制依赖时最好去 Maven Central 确认最新版本避免照着老教程写完编译不过。顺带把 PostgreSQL 的 JDBC 驱动也加上后面建表要用的。3.2 五步完成知识库构建切分、向量化、入库、检索、问答RAG 的核心逻辑不复杂先把你自己的文档切碎用嵌入模型转成向量存进数据库用户提问时也把问题转成向量去检索最相关的片段最后把片段作为上下文拼接进提示词交给大模型生成回答。我把这个过程拆成五个步骤一步步说。第一步准备知识文档。我用 Spring AI 的 TikaDocumentReader 读取 PDF、Word、TXT 等常见格式它对格式兼容性处理得比较省心。TikaDocumentReader reader new TikaDocumentReader(new FileSystemResource(/data/knowledge.pdf)); ListDocument documents reader.get();第二步切分文档。这一步很关键切太大检索不精准切太小丢失语境。我用 TokenTextSplitter默认按 500 个 token 切一块块与块之间有 100 个 token 重叠既能保证片段独立又不会把语义割裂得太碎。TokenTextSplitter splitter new TokenTextSplitter(500, 100); ListDocument chunks splitter.apply(documents);第三步向量化。把切好的片段交给 Ollama 的嵌入模型比如 nomic-embed-text转换为向量数组。这一步要注意嵌入模型必须和检索阶段保持一致否则向量空间不对齐检索结果完全没意义。第四步入库。写入 PGVector 前要建一张向量表字段包括内容、元数据、嵌入向量。Spring AI 的 VectorStore 提供了 add 方法会自动完成建表逻辑但生产环境建议手动管理表结构方便控制索引和分区。我习惯把 docId、来源文件、章节标题一并写进 metadata排查问题时特别有用。第五步问答链路。这一步用 ChatClient 完成代码很简洁RestController public class ChatController { private final ChatClient chatClient; private final VectorStore vectorStore; public ChatController(ChatClient.Builder builder, VectorStore vectorStore) { this.chatClient builder.build(); this.vectorStore vectorStore; } PostMapping(/chat) public String chat(RequestBody QuestionRequest request) { ListDocument similar vectorStore.similaritySearch( SearchRequest.query(request.question()).withTopK(5)); String context similar.stream() .map(Document::getContent) .collect(Collectors.joining(\n)); return chatClient.call(request.question() \n参考上下文\n context); } }看着是不是很简单但越简单的地方越容易出问题。topK 我设的是 5太少可能漏掉关键内容太多会让模型被无关片段干扰。这个参数最好根据你文档的切分粒度做几轮测试别拿来就用。另外我是直接拼了上下文更规范的做法是用 Spring AI 的 PromptTemplate 定义专门的提示词结构让模型明确区分“用户问题”和“参考资料”。3.3 检索质量和响应速度两次嵌入、分数阈值和流式输出RAG 的检索质量不是靠运气有几个细节值得花时间调。首先是“问题改写”用户的原始提问往往口语化直接嵌入去检索效果一般。我通常会让模型先做一次 query 改写把“那个上个月报销流程怎么弄”转成与数据库中片段风格更一致的表述代价是多一次模型调用但命中率明显提升。然后是相似度阈值。PGVector 默认返回 topK 个结果但相关度可能很低。Spring AI 的 SearchRequest 里可以设置相似度阈值低于阈值的片段直接丢弃宁可没有上下文也不要把无关内容硬塞给模型。我常用的阈值范围是 0.65 到 0.75具体看你用的嵌入模型输出分布。响应速度方面ChatClient 支持流式输出对用户体验改善很大。不过要注意流式接口意味着后端要换成 SSE 推送网关、负载均衡器的超时也要相应调大否则前端刚开始打字网关就掐断了连接。这个坑我在生产环境遇到过不止一次。4. AIGS 时代的工作流变革从 AI 生成代码到交给生产4.1 提示词模板和输出约束让模型输出结构可控AIGS 时代Java 工程师写业务代码的方式已经在变。现在很多团队用 AI 生成接口代码、生成单元测试、生成数据库脚本但把 AI 生成的内容直接合入主干是大忌。我的经验是提示词必须模板化输出必须结构化。模板化的意思是把提示词当成配置文件管理而不是散落在业务代码里。Spring AI 的 PromptTemplate 支持把变量注入提示词接口的字段说明、示例输出、禁止使用的事项都可以写进模板。结构化输出更关键让模型返回纯 JSON而不是自由文本否则下游解析一定崩。String prompt 你是一个订单审核助手。根据用户输入判断订单风险等级。 只返回 JSON格式为 {riskLevel: LOW|MEDIUM|HIGH, reason: 不超过20字} 用户输入{input} ;这里要注意模型返回的 JSON 并不保证 100% 合法偶尔会多一个感叹号偶尔会漏掉引号。我用 Jackson 解析时捕获异常解析失败就走重试或者降级返回这是必须做的兜底逻辑。4.2 多 AI 协作Java 里做 Agent 编排的核心思路单一模型调用解决不了复杂任务需要多个 AI 角色协作的场景越来越多比如一个 Agent 负责拆解问题另一个负责查数据库再一个负责汇总答案。LangChain4j 在这个方向给了 Java 开发者比较顺手的工具核心是 AiServices。做一个可用的 Agent本质上就是给模型一组工具方法模型根据用户目标决定调用哪个。Java 里你可以把工具定义为普通方法加上 Tool 注解模型就能在交互中自动触发public class OrderTools { Tool(根据订单号查询订单状态) public String queryOrderStatus(String orderId) { return orderMapper.findStatus(orderId); } } AiServicesAssistant services AiServices.builder(Assistant.class) .chatLanguageModel(chatModel) .tools(new OrderTools()) .build();多 Agent 协作的关键不是把 Agent 类写得多花哨而是明确每个 Agent 的职责边界和转交条件。我在项目里的做法是先定义一张 Agent 路由表写清楚什么问题交给哪个 Agent什么情况下需要升级到人工再让主 Agent 做调度。这一步理顺了后面的工具调用和数据流转才不会乱。4.3 内容安全、质量评估与可观测性AI 应用的三道闸门AIGS 时代把 AI 应用到真实业务里内容安全是第一道闸门。提示词注入要防敏感话题要控生成内容要过审核。我的做法是在模型输出和最终返回之间加一层内容过滤组件对输出做关键词检测、敏感信息识别和格式校验不合格的重试或者阻断。这些都是常规后端工程能力不需要依赖框架但必须作为 AI 链路的一部分提前设计进去。质量评估是第二道闸门。模型输出好不好不能靠感觉。我给每个对话请求记录模型名称、输入 token 数、输出 token 数、响应时长和用户反馈离线做统计。连续几天看下来哪个模型版本回复质量下降哪个提示词改动导致拒答率上升数据会告诉你答案。可观测性是第三道闸门。AI 调用比普通接口更复杂需要记录完整的调用链信息包括提示词原文、模型返回结果、检索到的知识片段。用 Micrometer 把指标暴露给 Prometheus用日志把每次调用细节落盘一旦出了线上问题能快速定位是模型层、检索层还是业务层出了状况。我把这套实践称为“把模型调用当接口治理”Java 系的标准姿势在这时发挥得淋漓尽致。5. 排查实录Java AI 项目最常见的 6 个坑5.1 依赖冲突与类路径问题Spring AI 和 LangChain4j 最容易打架同时引入多个 AI 框架时最常见的问题是依赖冲突。Spring AI 0.8.x 和 LangChain4j 都依赖 Jackson、SLF4J、Guava但版本要求不一致Maven 默认仲裁可能选到旧版本运行时报 NoSuchMethodError。我现在的习惯是先在 pom 里显式声明公共依赖的版本用 dependencyManagement 统一控制绝不给 Maven 随机发挥的空间。另一个隐蔽问题是 API 包名冲突。有些 Spring AI 的类名比如 Document、EmbeddingLangChain4j 里也有同名类。代码里同时 import 两个包编译期不报错运行期可能因为方法签名不同直接抛异常。规避方法就是在必要场景下二选一或者用全限定类名但长期看我还是建议分层隔离不要让一个类同时依赖两个框架的同类概念。5.2 模型加载慢、CPU 推理慢和内存溢出本地模型的性能瓶颈用 Ollama 跑本地模型最折磨人的是加载和推理速度。在 16G 内存的 Mac 上跑 7B 模型生成速度大概每秒 20 到 30 个 token勉强能接受部署到 2C4G 的云服务器上速度直接掉到每秒不到 10 个 token。这种体验上线就是事故。我的建议是模型选型要匹配机器资源7B 级别的模型至少需要 16G 内存1.5B 到 3B 的模型在 8G 内存上才比较流畅。如果业务并发高就不要想着单机本地推理了直接接 API 或者上 GPU 实例。还有一个容易忽略的参数是上下文长度Ollama 默认上下文可能只有 2048RAG 场景里塞一段参考文档就把上下文占满了输出会被截断。调整 num_ctx 参数到 8192 能缓解但内存占用也会跟着上涨。5.3 排查速查表遇到这些问题先查哪里我把实战中遇到的高频问题整理成一张速查表方便你定位问题时直接对照。现象可能原因排查与解决启动报 Bean 创建失败依赖坐标版本不兼容检查 Spring AI 与 Boot 版本对应关系检索结果明显不相关嵌入模型不一致或阈值不匹配确认入库和查询用同一个嵌入模型回答被截断上下文长度不足调大 num_ctx精简参考片段调用模型接口超时HttpClient 未配置显式配置连接池和超时时间内存持续增长向量列表未索引或模型驻留检查 PGVector 索引和模型加载策略输出解析异常模型返回非预期 JSON增加解析兜底和重试机制并发请求响应变慢线程池打满监控线程活跃数调整并发策略还有一点千万记住生产环境不要直接用 Ollama 的默认端口裸奔至少要加一层反向代理做鉴权否则同网段的其他服务都能自由调用你的模型接口。Java 程序里也要把模型地址配置到配置中心方便后续切换模型服务商不要让模型地址散落在代码里。最后分享一个我的习惯。每次做一个 Java AI 项目我都会在第一天就把日志规范、调用链追踪和限流熔断搭出来哪怕只是最小版本。因为 AI 应用的不确定性比普通接口高一个量级模型返回什么、耗时多久、上下文用了多少 token这些都是运行时才知道的。把可观测性前置后面调模型、调参数、定位线上问题都会轻松很多。AIGS 时代的技术栈会继续变但工程化的方法不会变它永远是稳中求进的。