十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Java AI应用开发实战:Spring AI与LangChain4j从入门到RAG落地

Java AI应用开发实战:Spring AI与LangChain4j从入门到RAG落地 Java 圈子最近有个现象挺有意思以前大家聊 AI 应用开发默认就是 Python 那一套LangChain、FastAPI、各种 notebook 跑起来。但这半年我身边不少做 Java 后端的兄弟开始转方向了原因很直接——公司里的核心业务系统是 Java 写的数据、权限、服务治理全在 Spring 生态里你让团队为了接个大模型能力再单独维护一套 Python 服务运维成本、联调成本、上线流程全得翻倍。于是 Spring AI 和 LangChain4j 这两个框架就被推到了台前。我从去年底开始陆续在几个项目里落地这两套东西踩的坑不算少今天就把从零上手到跑通完整链路的经验整理出来给同样想从 Java 侧切入 AI 应用的朋友一个可复现的参考。1. 为什么 Java 后端值得认真看这两个框架1.1 不是Java 也能做 AI而是Java 本来就该做 AI 应用层先把一个容易混淆的概念理清楚。模型训练、微调、推理引擎这些底层活儿确实 Python 生态更成熟这个短期内不会变。但绝大多数企业真正要落地的东西不是训练模型而是把已有的大模型能力接进业务系统——做智能客服、文档问答、工单分类、数据洞察、流程自动化。这些场景的本质是应用层集成考验的是工程能力依赖管理、事务、鉴权、可观测性、并发控制、灰度发布。这些恰恰是 Java 后端十几年积累下来的强项。你想想一个 RAG 问答服务上线后要面对的是什么是几千个并发请求、是权限校验、是调用链追踪、是失败重试和降级。用 Python 从零搭这套治理体系工作量比写业务逻辑本身还大。而 Spring AI 和 LangChain4j 做的事情就是把这套工程能力和大模型调用缝合起来让你用熟悉的Service、Bean、依赖注入那套思路去组织 AI 逻辑。所以我的判断是AI 应用层不是 Python 的专属地盘Java 团队做这块反而有天然优势前提是你选对框架、理解它的抽象边界。1.2 Spring AI 和 LangChain4j 的定位差异这两个框架经常被拿来对比但它们的出身和设计哲学其实不太一样选型前必须搞清楚。Spring AI是 Spring 官方团队主导的项目核心思路是把 AI 能力当成 Spring 生态里的一个普通组件。它的 API 设计高度贴合 Spring 的惯例ChatClient对标RestClientAdvisor对标拦截器配置走application.yml依赖注入天然支持。如果你团队本来就是 Spring Boot 重度用户上手成本极低几乎不需要改变思维习惯。LangChain4j则是社区驱动的项目设计上更贴近 Python 版 LangChain 的概念模型抽象层次更丰富ChatLanguageModel、EmbeddingModel、EmbeddingStore、AiServices、RetrievalAugmentor这些概念一应俱全。它在 RAG、工具调用、记忆管理这些高级场景上的封装更细灵活度更高但相应地你需要理解的概念也更多。我一般这么建议新项目、团队 Spring 背景深、需求偏标准的优先 Spring AI需求复杂、要做深度定制的 RAG 或 Agent 编排、想要更细粒度控制的看 LangChain4j。当然两者并不互斥我有个项目就是 Spring AI 做主链路、LangChain4j 做特定检索模块后面会讲怎么共存。1.3 环境准备里最容易被忽略的三件事零基础上手环境这关就能卡住不少人。我列几个实际踩过的点。第一JDK 版本。Spring AI 的较新版本对 JDK 有要求建议直接上 JDK 17 或 21别在 JDK 8 上折腾很多新特性用不了编译报错能让你怀疑人生。LangChain4j 相对宽松一些但同样推荐 17。第二Maven 依赖的版本对齐。这两个框架迭代都很快Spring AI 的版本号、LangChain4j 的版本号、以及它们各自依赖的底层 HTTP 客户端、JSON 库之间存在版本兼容矩阵。我见过最典型的问题就是引入了 Spring AI 的 starter又手动引了某个旧版本的 Jackson结果启动时序列化直接炸。原则是能用 BOM 统一管理的就用 BOM不要手动指定传递依赖的版本。第三模型服务的接入方式。国内项目大多对接的是国产大模型平台或者本地部署的模型服务这些平台的 API 协议和 OpenAI 的标准协议有差异。Spring AI 和 LangChain4j 都提供了多种模型适配模块你需要根据实际对接的平台选对应的 starter而不是无脑用 OpenAI 的那个。选错了模块接口对不上调半天以为是网络问题其实是协议不匹配。提示环境阶段先把一个最简单的发一句话、收一句话跑通别一上来就搞 RAG。链路越短出问题时定位越快。2. Spring AI 从依赖引入到第一个对话接口2.1 依赖怎么引starter 的选择逻辑Spring AI 的依赖组织方式是典型的 Spring Boot starter 风格。核心是spring-ai-bom做版本管理然后按需引入具体的模型 starter。dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement引入 BOM 之后具体模型依赖就不用写版本号了dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency这里有个关键判断你对接的平台是否兼容 OpenAI 协议。如果兼容用 openai starter 然后改base-url就行如果不兼容就得找对应平台的专用 starter或者自己实现模型接口。我见过有人对着一个不兼容 OpenAI 协议的国产平台硬套 openai starter改了半天 base-url 还是 404最后发现是请求体结构根本不一样。2.2 配置文件里的关键参数Spring AI 的配置集中在application.yml核心就几项spring: ai: openai: base-url: https://your-model-endpoint/v1 api-key: ${MODEL_API_KEY} chat: options: model: your-model-name temperature: 0.7 max-tokens: 2048几个参数的实际影响我展开说下。temperature控制输出随机性做客服问答这种要稳定答案的场景我一般压到 0.2 到 0.3做创意文案生成才往上调。max-tokens是单次响应的最大生成长度设太小会出现回答被截断的情况设太大又浪费额度和延迟需要根据业务回答长度实测调整。api-key千万别硬编码在配置文件里提交到仓库用环境变量或者配置中心注入。这个是最基础的安全习惯但我在代码 review 里见过太多次直接写死的。2.3 ChatClient 的三种调用姿势Spring AI 最核心的 API 是ChatClient。它有三种典型用法对应不同场景。第一种最简同步调用RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder.build(); } GetMapping(/chat) public String chat(RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }这种写法适合快速验证但生产环境直接用会有问题——没有超时控制、没有异常兜底、没有上下文管理。第二种带系统提示词和参数的调用String answer chatClient.prompt() .system(你是一个严谨的技术支持助手回答要简洁准确。) .user(userQuestion) .options(ChatOptions.builder() .temperature(0.3) .maxTokens(1024) .build()) .call() .content();系统提示词这块是很多人忽略的重点。同一个模型系统提示词写得好不好输出质量差距巨大。我一般会把角色定位、输出格式要求、禁止事项都写进 system 里比在 user 消息里反复强调有效得多。第三种流式输出GetMapping(value /chat/stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString chatStream(RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); }流式输出对用户体验的提升是质变的。大模型生成一段几百字的回答可能要好几秒同步等待用户会以为卡死了流式则是一个字一个字往外蹦感知延迟低很多。做对话类产品流式基本是标配不是可选项。2.4 结构化输出让模型返回能直接用的对象实际业务里你很少只需要一段纯文本更多是要结构化的数据。比如从用户描述里抽取订单号、问题类型、紧急程度。Spring AI 提供了结构化输出能力public record TicketInfo(String orderNo, String category, String urgency) {} TicketInfo info chatClient.prompt() .user(用户反馈我的订单 A12345 三天了还没发货很着急) .call() .entity(TicketInfo.class);框架会自动在提示词里加入格式约束并把模型返回的 JSON 反序列化成对象。这里有个实操经验模型返回的 JSON 偶尔会带 markdown 代码块标记或者多余的解释文字导致反序列化失败。稳妥的做法是在系统提示词里明确要求只返回 JSON不要任何其他文字同时在代码层做一次容错解析把可能的 json 标记剥掉再解析。3. LangChain4j 的抽象模型与 RAG 落地3.1 核心概念一次讲透LangChain4j 的概念比 Spring AI 多但理清了其实很顺。我用一个类比串起来把整个 AI 应用想象成一家餐厅。ChatLanguageModel是厨师负责根据要求做菜生成回答。EmbeddingModel是图书管理员负责把文字变成向量把书按内容归类上架。EmbeddingStore是书架存这些向量。Retriever是找书的人根据你的问题去书架上找最相关的几本。AiServices是餐厅经理把厨师、找书的人、记忆管理这些角色组织起来对外提供一个简单的点菜接口。理解了这个映射再看代码就不晕了。3.2 最小可运行的对话示例ChatLanguageModel model OpenAiChatModel.builder() .baseUrl(https://your-model-endpoint/v1) .apiKey(System.getenv(MODEL_API_KEY)) .modelName(your-model-name) .build(); String answer model.generate(用一句话解释什么是向量数据库);这是最裸的用法。但实际项目里我们更常用AiServices把模型包装成一个接口interface Assistant { String chat(String userMessage); } Assistant assistant AiServices.create(Assistant.class, model); String reply assistant.chat(帮我写一个 Java 的冒泡排序);这种声明式接口的写法非常优雅你定义接口框架生成实现业务代码里就像调用普通 Service 一样。这也是 LangChain4j 相比 Spring AI 在开发体验上的一个亮点。3.3 RAG 完整链路从文档到可问答RAG检索增强生成是当前企业落地最多的场景核心思路是把企业私有文档切块、向量化、存起来用户提问时先检索相关片段再把片段作为上下文喂给模型让模型基于这些真实资料回答而不是凭空编。完整链路分四步。第一步文档加载与切分Document document FileSystemDocumentLoader.loadDocument( Paths.get(/data/manual.pdf), new ApachePdfBoxDocumentParser()); DocumentSplitter splitter DocumentSplitters.recursive(500, 50); ListTextSegment segments splitter.split(document);这里的500是每块的目标字符数50是块之间的重叠字符数。切分参数是 RAG 效果的关键变量。切太大检索出来的片段包含太多无关信息干扰模型切太小语义被割裂检索到的片段不完整。我的经验是中文文档 300 到 500 字符一块比较合适重叠留 10% 左右保证跨块的句子不被切断。第二步向量化并存储EmbeddingModel embeddingModel OpenAiEmbeddingModel.builder() .baseUrl(https://your-embedding-endpoint/v1) .apiKey(System.getenv(EMBEDDING_API_KEY)) .build(); EmbeddingStoreTextSegment store new InMemoryEmbeddingStore(); EmbeddingStoreIngestor ingestor EmbeddingStoreIngestor.builder() .embeddingModel(embeddingModel) .embeddingStore(store) .build(); ingestor.ingest(segments);InMemoryEmbeddingStore只适合验证生产环境要换成持久化的向量库比如基于 PostgreSQL 的 pgvector、Milvus、Redis 等。选向量库的核心考量是数据量、是否需要和现有数据库复用、运维复杂度。中小规模、团队已经有 PostgreSQL 的pgvector 是最省事的选择。第三步组装检索增强的问答服务RetrieverTextSegment retriever EmbeddingStoreContentRetriever.builder() .embeddingStore(store) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.7) .build(); Assistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(model) .contentRetriever(retriever) .build();maxResults是每次检索返回的片段数minScore是相似度阈值。这两个参数直接决定回答质量。maxResults 设太大上下文塞满噪声设太小可能漏掉关键信息。我一般从 5 开始调配合 minScore 过滤掉低相关度的片段。第四步验证与调优。跑通之后一定要做的是拿一批真实问题去测看检索出来的片段是不是真的相关。我见过太多 RAG 项目模型回答得头头是道但检索出来的资料根本不对模型是在基于错误资料一本正经地编。RAG 的效果瓶颈八成在检索环节不在生成环节。3.4 工具调用让模型能操作你的系统LangChain4j 的工具调用Function Calling能力很实用。你可以把 Java 方法暴露给模型模型判断需要时自动调用class OrderTools { Tool(根据订单号查询订单状态) String queryOrderStatus(P(订单号) String orderNo) { return orderService.getStatus(orderNo); } } Assistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(model) .tools(new OrderTools()) .build();用户问我的订单 A12345 到哪了模型会自动识别需要调用queryOrderStatus传入订单号拿到结果后再组织成自然语言回答。这个能力是把 AI 从聊天变成干活的关键。但要注意工具方法的参数校验、权限控制、异常处理都得自己做别指望模型帮你兜底。模型传错参数是常有的事。4. 两个框架混用与生产环境的坑4.1 混用的边界怎么划前面提到我有个项目是两个框架共存的。具体怎么划边界我的做法是Spring AI 负责对话主链路和 Spring 生态集成LangChain4j 负责复杂的检索和 Agent 编排。原因是 Spring AI 的ChatClient和 Spring 的 Web、事务、安全体系结合得更自然做主链路省心而 LangChain4j 的Retriever、ContentInjector、AiServices在 RAG 细节控制上更灵活。两者通过共享同一个模型配置和向量库来打通互不干扰。混用要注意的是依赖冲突。两个框架可能依赖不同版本的底层库引入时用mvn dependency:tree检查一下有冲突就显式排除。我遇到过一次两个框架依赖的 HTTP 客户端版本不一致导致其中一个的流式响应解析异常排查了大半天。4.2 超时、重试与降级生产环境最容易被忽视的就是稳定性设计。大模型调用有几个特点延迟高、可能超时、可能限流、偶尔返回异常内容。这几点决定了你不能像调普通接口那样裸调。超时设置上同步调用我一般设 30 到 60 秒流式调用设更长。重试要谨慎不是所有失败都适合重试——限流失败适合退避重试参数错误重试多少次都没用。降级方案必须提前设计模型服务挂了是返回缓存答案、走规则引擎还是给用户一个友好提示这个决策要在架构阶段就定下来。Bean public RetryTemplate modelRetryTemplate() { return RetryTemplate.builder() .maxAttempts(3) .exponentialBackoff(1000, 2, 10000) .retryOn(TransientAiException.class) .build(); }只对瞬时异常重试业务异常直接抛出这个区分很重要。4.3 成本与并发控制大模型调用是按 token 计费的一个没控制好的 RAG 服务账单能吓死人。几个控制手段限制单次请求的上下文长度、对相同问题做结果缓存、对用户做频率限制。缓存这块特别值得做。很多业务场景下用户问的问题是高度重复的比如怎么退货发货要多久。这类问题完全可以把答案缓存起来命中缓存直接返回既省钱又快。缓存 key 用问题的向量做相似度匹配比字符串精确匹配命中率高得多。并发控制上模型服务通常有 QPS 限制超过就限流。用信号量或者线程池做一层本地限流比等被服务端限流再处理要主动。4.4 可观测性不埋点等于裸奔AI 应用的调试比普通应用难因为输出是不确定的。必须把每次调用的输入、输出、耗时、token 消耗、检索到的片段都记录下来。出问题时你能回放整个链路看到底是检索错了还是模型理解错了。Spring AI 和 LangChain4j 都提供了监听器或拦截器机制可以挂载日志和指标采集。我一般会记录请求 ID、用户问题、检索片段及相似度分数、模型原始输出、总耗时、token 数。这些数据积累起来还能反过来指导提示词优化和检索参数调优。5. 从跑通到用好几个实战心得5.1 提示词工程不是玄学是可迭代的工程很多人觉得提示词是调玄学其实它有方法论。我的做法是把提示词当成代码来管理版本化、可回滚、有测试用例。具体来说为每个核心场景准备一组标准测试问题每次改提示词后跑一遍对比输出质量。系统提示词里明确写清楚角色、任务、输出格式、约束条件、示例。给一两个 few-shot 示例效果往往比写一大段描述还好。还有个细节中文场景下提示词用中文写通常比英文效果好因为模型在中文语境下的指令遵循更自然。这个不是绝对的但对国产模型基本成立。5.2 模型选型要看场景不是越贵越好不同任务对模型能力的要求差异很大。简单的意图分类、信息抽取小模型完全够用又快又便宜复杂的推理、长文总结才需要上大模型。一个服务里混用多个模型是常态按任务难度路由到不同模型成本能降一大截。我一般会做一个简单的路由层先判断任务类型简单任务走小模型复杂任务走大模型。判断逻辑可以用规则也可以用一个小模型来做分类。5.3 别忽视数据安全与合规企业场景下数据往外发是敏感操作。几个必须做的敏感字段脱敏后再发给模型、私有数据优先走本地部署的模型、调用日志里的敏感信息要过滤。如果对接的是外部模型服务一定要确认数据使用条款别把客户隐私数据直接发出去。有条件的话核心业务数据走本地部署的模型非敏感场景才用外部服务。这个边界要在架构设计阶段就划清楚事后补救成本极高。5.4 版本升级要有策略这两个框架迭代都很快API 时有变动。不要盲目追新版本尤其是生产项目。我的做法是锁定一个稳定版本关注 changelog等新版本稳定一两个小版本后再评估升级。升级前在测试环境完整跑一遍回归重点验证模型调用、检索、结构化输出这几块这些是最容易受版本变动影响的。升级时特别注意配置项的变化有些参数在新版本里改了名字或者默认值不检查的话会出现代码没改但行为变了的诡异问题。6. 给不同基础读者的上手路径6.1 纯 Java 后端没碰过 AI你的优势是工程能力短板是对 AI 概念不熟。路径建议先用 Spring AI 跑通一个最简单的对话接口理解ChatClient的调用模型然后补一下向量、embedding、RAG 这几个基础概念不用深究数学原理理解它们解决什么问题就行接着做一个最小的 RAG demo用几篇文档跑通检索问答最后再考虑工具调用、多轮对话这些进阶能力。别一上来就啃论文先把工程链路跑通概念在实践中理解比看书快得多。6.2 有 Python AI 经验想转 Java 侧你懂概念缺的是 Java 生态的工程习惯。重点看Spring 的依赖注入和配置管理怎么组织 AI 组件、Java 的异步和流式处理怎么写、怎么用 Spring 的治理能力重试、熔断、监控包裹模型调用。LangChain4j 的概念模型和 Python 版 LangChain 很像你上手会很快主要精力放在工程集成上。6.3 团队技术选型的决策参考如果你是在为团队做选型我给一个简单的判断框架考量维度倾向 Spring AI倾向 LangChain4j团队 Spring 背景深一般需求复杂度标准对话、简单 RAG复杂 RAG、Agent 编排生态集成要求高要融入现有 Spring 体系中定制灵活度要求中高上手速度快中实际项目里两者混用也是很常见的方案不必非此即彼。6.4 学习资源怎么找官方文档永远是第一手资料Spring AI 和 LangChain4j 的官方文档都写得比较清楚示例代码可以直接跑。遇到问题优先看 GitHub 的 issue 区很多坑别人已经踩过。中文资料方面社区里有一些实战系列文章质量不错但要注意版本AI 框架迭代快半年前的文章可能 API 已经变了。我个人的习惯是以官方文档为准社区文章为辅遇到版本相关的疑问一定回到官方 changelog 确认。这个习惯帮我避开了不少因为照抄过时教程导致的坑。最后分享一个我自己的体会这两个框架的学习曲线前期陡、后期平。最开始那一两天概念多、配置杂容易烦躁但一旦跑通第一个完整链路后面的东西都是在这个基础上的组合和变体。所以别在环境阶段就放弃撑过最开始那段后面会顺很多。真正决定项目成败的从来不是框架本身而是你对业务场景的理解和对工程细节的把控——这两样恰恰是 Java 后端最擅长的。
返回列表