尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java生态AI应用开发:SpringAI与Langchain4j构建RAG与Agent实战

Java生态AI应用开发:SpringAI与Langchain4j构建RAG与Agent实战 2026年开始重新看Java生态的AI应用开发我最大的体感是Java开发者已经不能再拿“AI应用是Python专属”当借口。最近我在梳理 SpringAI 2.0、Langchain4j、RAG、Agent 这些技术栈时发现很多团队的痛点并不在模型能力上而在“Java服务怎么低摩擦地把模型接进业务流程”。如果你所在的团队已经跑着大量 Spring Boot 服务却为了一个知识库问答系统另起一套 Python 技术栈这种割裂感一定不陌生。SpringAI 和 Langchain4j 就是来补这座桥的。这篇文章不打算复述任何教程而是把一个模拟航空客服智能体作为贯穿案例拆解 Java SpringAI 2.0 Langchain4j RAG Agent 这条链路里哪些事情值得做哪些地方最容易踩坑。1. 先用Java生态的视角重新看一遍AI应用开发1.1 为什么Java在AI应用层一直有种“慢半拍”的观感过去几年AI 领域的很多Demo都是Python写的因为模型训练、科研代码、Notebook 生态在Python里最成熟。但落到企业级业务系统时情况不一样银行、航空、制造、政务这些领域核心业务系统大量是 Java尤其是 Spring Boot。AI 只是这些系统中的一个小模块它要被事务、权限、审计、日志、灰度发布这些约束包裹着。Java 生态真正的优势从来不是模型训练而是服务治理。Spring Boot 里有成熟的路由、拦截、配置中心、链路追踪、消息队列集成方案。如果 AI 能力能作为 Spring Bean 被注入到业务代码里那么它就不再是一个“外挂”而是一个普通的外部服务依赖。这也是我判断 SpringAI 2.0 和 Langchain4j 更有价值的原因它们不是来抢Python饭碗的而是让Java团队用熟悉的工程方式来接入大模型。1.2 SpringAI 和 Langchain4j 不是重复方案而是两条路线很多人在刚开始看材料时会困惑到底选 SpringAI 还是 Langchain4j它们看起来都在做“Java调大模型”这件事但侧重点不完全一样。SpringAI 是 Spring 官方体系里的 AI 抽象层思路更接近“把模型接入变成一种 Spring 风格的配置”。如果你已经在用 Spring Boot它的集成体验更顺很多配置可以走 starter 和环境变量。Langchain4j 则更像 LangChain 思路的 Java 实现早期在工具调用、Agent 链路的抽象上更灵活社区里也有不少从 Python LangChain 迁移过来的用户。下面这张表是我个人理解的综合对比具体以当前官方文档为准维度SpringAILangchain4j定位Spring 官方 AI 应用接入框架受 LangChain 启发的 Java AI 框架模型接入统一模型抽象Spring 风格配置ChatLanguageModel 等接口模型来源多工具调用支持函数回调、Tool 等方式Tool AiServices 装配Spring Boot 集成原生贴合也有 spring-boot-starter适合人群深度使用 Spring 生态的团队习惯 LangChain API、需要快速迁移的团队在实际项目里两支框架都在快速演进选型时不要只看名气要看你团队对“哪一种抽象方式更容易接受”。如果原本就是 Spring 开发团队我建议先顺Spring 的思路跑通一个最小例子如果想复用 LangChain 里的记忆、Chain 概念Langchain4j 的手感会更接近。1.3 它们真正解决的是低摩擦接入而不是模型能力很多新手容易把 SpringAI 或 Langchain4j 当成“模型本身”实际上它们只是封装了模型调用协议。模型还是那个模型但接入方式从“写HTTP客户端、拼Json”变成了“声明一个接口、注入一个Bean”。这里有个关键变化过去Java项目接大模型最常见方案是写一个 RestTemplate 请求然后解析返回。单次调用没问题但一旦涉及工具调用、知识库检索、多轮对话、Agent 循环自己手写很容易乱。框架要做的是把这些流程标准化让开发者只需要关心业务逻辑。建议先不要纠结“哪个框架更强”先跑通一个最小调用再去对比两者的工具调用和RAG体验。2. 拆解一个智能航空项目的“零件”Tools、RAG、Agent各自负责什么2.1 用一个航空客服智能体作为贯穿案例假设我们要做一个航空客服智能体用户可能会问三类问题“CA1234航班现在到哪了”“退改签政策是什么”“我行李丢了应该怎么办”第一类问题需要查询航班系统拿到实时数据第二类问题需要读取航空公司的内部业务手册第三类问题可能需要同时读手册、查行李状态、甚至创建一个工单。这里面就出现了三个核心能力Tools工具调用、RAG检索增强生成、Agent智能编排。它们缺一不可也只有三种能力组合起来这个智能体才像一个能干活的人而不是一个只会聊天的对话模型。2.2 Tools让模型可以查实时数据的“手”大模型本身不知道某个航班此刻的状态因为它的训练数据不可能实时更新。Tools 就是给模型外接一个查询能力模型在回答用户问题时如果发现需要实时信息会生成一个调用请求由Java后端执行这个方法再把返回结果交回给模型去组织语言。在航空场景里常见的 Tools 包括航班状态查询行李状态查询退改签规则实时查询值机柜台查询投诉工单创建工具的价值不是“把API包装一下”而是让模型知道“什么时候该调用哪个工具”。这需要工具命名、描述、参数定义都足够清晰否则模型很难判断。2.3 RAG让模型可以读内部文档的“记忆”RAG 解决的是“知识新鲜度”和“专业文档动态更新”的问题。航空业务手册可能几百页且会持续更新。模型不能把这些内容都塞进上下文里也不该依赖训练语料里的旧知识。RAG 的一般链路是把文档切分、向量化、存入向量库用户提问时先在知识库里检索相关内容再带着检索结果让大模型生成答案。这样回答的“素材来源”可控而且可以做到引用溯源。在航空场景RAG 尤其重要因为退改签政策、行李规定、危机处理流程一旦说错代价很高。单纯靠模型“记忆”回复不可靠必须有文档依据。2.4 Agent把“手”和“记忆”串起来的“调度器”Agent 是这几年最难被准确定义的概念。在我的理解里它的核心是“决策”面对用户问题决定是先查工具还是先检索知识库还是两者都做还是直接回答。技术上Agent 循环大致是接收用户问题判断是否需要调用工具若需要则调用工具并拿到结果判断结果是否足够回答用户问题若不够继续调用下一个工具或检索知识库最终生成回答如果只做一次工具调用那还不能叫 Agent更像“带功能插件的对话”。Agent 的价值在于它可以多次迭代直到拿到足够信息后再回复。2.5 三者协作的一个典型流程当用户问“我行李丢了怎么办”时完整链路的理想形态是Agent 判断这个问题需要航空手册中的“行李异常处理流程”先去 RAG 知识库里检索相关章节同时调用“行李状态查询”工具尝试获取用户行李的最近追踪记录将检索结果和工具结果合并给模型模型组织一段有依据、有步骤的回答并给出人工服务入口。这个流程里Tools 提供实时数据RAG 提供静态规则Agent 负责决定谁先谁后。Java 开发者要做的就是用 Spring AI 或 Langchain4j 把这些流程串起来而不是让用户在Prompt里自己拼。3. 从零跑通一个最小Agent不要一开始就做知识库3.1 最小闭环只接模型加一个航班查询工具我见过太多团队一上来就准备向量库、切片、调参结果两三天后才想起来还没验证“模型能不能正确调用一个简单工具”。更稳妥的做法是先跑通最小闭环一个模型、一个航班查询工具、一个问答入口。这个闭环一旦跑通你就理解了链路中最重要的“工具调用”机制模型怎么识别意图、怎么把用户问题里的参数抽出来、怎么生成方法调用、结果怎么回填给大模型。3.2 环境准备常规情况下你至少需要JDK 17 以上一个 Spring Boot 项目或者直接使用 Langchain4j / SpringAI 的 starter 依赖一个模型API服务支持 OpenAI 兼容格式或对应厂商格式基础的内存配置不要太低由于这些框架版本迭代很快我下面只写通用案例不写死版本号。具体依赖坐标和类名一定要以你当前引入版本的官方文档为准。// 常见依赖坐标示例具体版本号请查官方文档 // implementation dev.langchain4j:langchain4j-spring-boot-starter // implementation dev.langchain4j:langchain4j-open-ai3.3 定义一个航班查询工具在 Langchain4j 风格的写法里Tools 通常是一个普通类方法上用Tool注解描述这个工具的作用。下面是一个结构化示例import dev.langchain4j.agent.tool.Tool; public class FlightTools { Tool(查询指定航班号在当前日期的实时状态返回航班状态、起飞时间、到达时间) public String getFlightStatus(String flightNumber, String date) { // 这里调用内部航班系统通常是连接数据库或外部API // 这里用模拟数据代替真实项目需要替换 if (CA1234.equalsIgnoreCase(flightNumber)) { return 航班号: CA1234, 日期: date , 状态: 准时, 计划起飞: 08:30, 预计到达: 11:40; } return 未找到航班信息; } }这里要注意Tool注解里的描述不是给用户看的是给大模型看的。描述写得越清楚模型越容易在正确的时候调用这个工具。3.4 把工具接入Agent在 Langchain4j 里可以用AiServices把一个接口和工具组合起来public interface FlightAssistant { String chat(String userMessage); } FlightAssistant assistant AiServices.builder(FlightAssistant.class) .chatLanguageModel(chatModel) .tools(new FlightTools()) .build(); String answer assistant.chat(CA1234航班现在什么状态); System.out.println(answer);这段代码看起来简单但它背后完成了好几件事接收用户问题、判断需要调用工具、解析参数、调用getFlightStatus、把返回值交给模型、生成最终回答。如果把这段代码放到 Spring Boot 里就可以把chatModel和FlightTools配成 Bean然后通过接口注入到 Controller 或 Service 中。3.5 验证时要看什么而不是只看能不能回复第一次跑通时很多人只看最终输出是否像话这是远远不够的。你需要打开日志重点确认模型是否生成了工具调用指令工具方法是否被正确调用参数是否被正确抽取比如用户说“CA1234现在”模型有没有自动补当天日期工具返回后模型是否基于返回结果组织回答而不是自说自话如果发现模型没有调用工具直接凭“印象”回答了航班状态那问题往往出在工具描述不够清晰或者模型服务不支持 function calling 配置。建议先别急着做知识库把你最核心的一到两个工具调通理解Agent的循环机制再往后走。4. RAG才是航空场景里最难啃的部分文档解析、索引和检索4.1 航空知识库的典型麻烦航空业务手册不是一篇干净博客。它们通常是数百页的 PDF里面既有章节目录又有表格、流程图、政策版本更新说明甚至还有扫描件。这种情况下RAG 的难点往往不在“向量化”这一步而在“文档加载和解析”。如果文档解析不到位后面无论怎么调检索都没有意义。常见问题包括PDF 里表格被拆碎语义丢失页眉页脚混进正文“旧版政策”和“新版政策”同时存在需要定位版本“行李”和“手提行李”是两个概念必须靠分块和检索区分4.2 全流程要从加载、清洗、分块到检索闭环一个可用于航空知识库的 RAG 流程至少包含以下环节文档加载读取 PDF、Word、Markdown 等格式解析清洗去掉页眉页脚、处理表格、保留章节上下文切分按章节、段落而不是固定字符切必要时增加重叠向量化用 Embedding 模型把文本变成向量存储写入向量数据库检索用户提问后做向量检索重排在多召回结果里挑出最相关的生成把检索结果交给大模型要求它依据材料回答在 Java 生态里文档解析、切分、向量化、向量存储都有对应抽象。比如 Langchain4j 提供了Document、DocumentSplitter、EmbeddingStore等概念SpringAI 也有对应的文档读取器。这里给一个 Java 环境下常见的处理思路示意// 示意代码具体实现和类名以框架版本为准 Document document loadDocument(flight-policy.pdf); ListTextSegment segments DocumentSplitter.recursive(500, 50).split(document); ListEmbedding embeddings embeddingModel.embedAll(segments).content(); embeddingStore.addAll(segments, embeddings);向量数据库可以用 Milvus、Redis 或关系型数据库的向量插件。如果你已经有 MilvusLangchain4j 和 SpringAI 都有集成方式。关键点在于不要为了用向量库而用向量库先想清楚数据量、检索频率和部署成本。4.3 检索质量差先别急着调Prompt一个很常见的错误是RAG 结果答非所问于是反复调 Prompt结果还是没用。实际上大部分检索问题出在“召回”环节分块太大把不相关内容塞进同一块分块太小一段话被切断语义不完整Embedding 模型没有和语义匹配topK 参数太小漏掉关键文档没有做版本过滤旧政策和新政策混淆纯向量检索表现一般缺少关键词混合检索排查时建议先打印出实际召回的文档片段人工判断这些片段是否真的能回答用户问题。如果召回内容本身就不靠谱Prompt 写得再花哨也没用。4.4 结合外部Embedding模型和重排的通用策略在航空场景里我建议把检索链路做两层第一层用 Embedding 做语义召回第二层用重排模型或规则把最相关的结果排到最前面。必要时再叠加关键词匹配确保专有名词不被漏掉。例如用户问“手提行李尺寸限制”如果只做语义召回可能召回到“行李运输限制”的泛化内容。如果加入关键词和章节检索就能更精准命中“手提行李”章节。注意RAG 不是“向量数据库 Prompt”就行。它是一条数据处理管线必须用真实业务文档反复验证。5. Tools设计得好不好直接决定Agent像不像“真人”5.1 工具调用失败很多不是模型的问题当 Agent 在航空客服场景里表现不聪明时我首先怀疑的不是模型而是工具定义。模型能不能正确选择工具主要看三点工具是谁方法名或工具名是否直观什么时候用描述里有没有写清楚触发条件怎么用参数名和参数说明是否明确举例来说Tool(查询航班状态当用户询问航班是否准点、起飞时间、到达时间、航班动态时使用) public String queryFlightStatus( ToolParam(航班号例如 CA1234) String flightNumber, ToolParam(日期格式 yyyy-MM-dd默认当天) String date ) { ... }这段描述对模型就友好得多触发条件清晰参数含义清楚。很多模型之所以不调用工具是因为描述里只写了“get status”这种模糊定义。5.2 工具返回值也要给模型“能看懂的结构”工具返回的不应该是给人看的UI文案而应该是结构化数据。模型会把这段文本拼进上下文如果返回是一大段带样式的 HTML既浪费 token又干扰理解。比较差的返回当前航班状态为准时计划起飞时间为08:30预计到达时间为11:40登机口为23号。更好的返回{ flightNumber: CA1234, status: ON_TIME, scheduledDeparture: 2026-02-01 08:30, estimatedArrival: 2026-02-01 11:40, gate: 23 }当然实际上你给模型的返回不一定是 JSON 字符串但至少要保证信息边界清楚、无歧义。如果是对象可以在Tool方法里序列化为字符串。模型需要的是“能读的信息”不是“好看的信息”。5.3 超时、空结果和异常必须显式处理工具调用不是总能成功。航班查询接口可能超时行李状态可能查不到权限校验可能失败。这些都需要在 Tool 方法里显式处理不能把异常直接抛给 Agent。如果航班状态接口超时工具可以返回“查询超时请稍后重试”而不是让整个 Agent 循环卡死。如果你看到类似agent execution provider did not respond in time的报错优先检查你的工具调用耗时、网络超时配置、Agent 的执行超时时间而不是怀疑模型变笨了。5.4 一个工具设计检查清单我通常用下面这个清单检查一个 Tool 是否合格工具名是否直白避免“searchFlightInfo”和“getFlightDetails”这种容易混淆的名字描述是否说清了“什么时候该用”参数是否带示例模型才能正确抽取返回值是否结构化且不含多余格式是否处理了空结果、异常、权限不足是否做了幂等同一个参数重试不会产生副作用是否有超时时间避免Agent被拖死6. 从Demo到生产航空智能体落地还要补哪些工程能力6.1 不是能回答问题就够了很多团队跑通一个智能体之后会误以为剩下的工作只是上线。但在航空这类对准确性、安全性要求高的行业能回答问题和能生产使用之间隔着一整层工程能力。举个简单例子同样一个航班状态查询接口Demo 里可以直接调用生产环境必须有权限校验、限流、日志审计和降级方案。同样一个 RAG 知识库Demo 里可以用简单的 CSV 文件生产环境必须考虑文档版本、数据更新、引用溯源。6.2 需要补的工程清单下面是我建议的最低生产化清单日志记录用户问题、模型输出、工具调用输入输出、检索到的文档ID审计在大盘上能追踪每一次AI回答的依据和工具动作权限普通用户只能查自己的订单内部客服才能查全部航班数据限流模型API和工具接口都要有配额控制防止异常流量缓存高频问题、热点航班查询结果可以缓存减少模型调用成本幂等对于“创建工单”这类有副作用的工具必须防止重复提交可观测性用链路追踪把“用户问题 - RAG检索 - 工具调用 - 模型生成”串起来灰度发布先放内部客服使用再逐步扩大范围6.3 成本控制不能把所有流量都直接打到模型API一个航空客服智能体如果每个问题都走“模型 RAG 多轮工具调用”成本会很高。常见的降本策略包括先做意图分类简单FAQ直接走规则或知识库命中对高频查询结果做缓存限制 Agent 最大循环次数避免模型反复调用工具使用小的模型做意图分类用大模型做最终答案生成Embedding 向量缓存避免同一文档反复向量化6.4 准确性保障答案必须有出处不能只靠自觉在航空场景RAG 的优势就在于“答案可以有出处”。生产环境应该把模型回答和引用的知识库段落一起返回。前端可以展示“来源: 值机行李规定.pdf 第23页”后台也能据此定位问题。如果模型发现检索到的内容互相矛盾比如新旧版政策冲突应该让它主动说“存在多种说法需要人工确认”而不是硬挑一个答案。这一点可以在 Prompt 或后置校验里做。6.5 JVM内存问题不能只靠调大 -Xmx跑 Agent 和 RAG 应用时经常遇到java: OutOfMemoryError: insufficient memory或类似问题。很多人第一反应是调大堆内存但根因往往不是堆太小向量数据库客户端把大量数据加载到内存文档解析时一次性读取超大 PDFLangChain4j 或 SpringAI 在处理流式响应时有对象堆积容器内存限制小于 JVM 堆配置工具并发线程过多排查时先看是堆内存溢出、元空间溢出还是容器内存被杀。再结合 dump 文件确认是不是向量索引或文档对象占用了大量内存。不要一上来就-Xmx8g那可能只是掩盖问题。建议在生产前先做一次压力测试模拟并发用户同时提问观察内存、CPU、外部API超时情况。7. 遇到问题怎么排查按输入、模型、工具、流程逐层收敛7.1 先看现象再定位是哪一个环节智能体应用的问题通常复杂因为失败可能发生在模型层、工具层、RAG层也可能发生在 Agent 循环里。我习惯用“三层收敛法”来排查看输入用户问题是否清楚系统提示词是否有冲突上下文是否被截断看中间动作模型有没有正确生成工具调用工具返回了什么检索召回了什么看输出模型是依据中间结果生成答案还是自己发挥了大部分问题都能在中间动作里找到原因。7.2 输入层最容易犯的错模型API返回空content的时候不要急着怀疑模型。先检查请求参数是否传了streamfalse却收到了流式空响应是否设置了过高的temperature导致输出不稳定是否把系统提示词设置成了“你是一个什么都不懂的机器人”是否用户问题本身就包含太多噪声如果日志里能看到原始请求和响应这一步会省很多时间。很多兼容 OpenAI 协议的服务在异常时返回的content为空但reasoning_content或错误信息里有提示。7.3 工具层重点看参数和描述如果模型没有触发工具调用先检查工具描述是否清晰如果触发了但报错看参数是否传对了。工具层常见问题还包括方法只有一个字符串参数模型不知道怎么填充参数类型和模型生成类型不匹配工具返回超长文本导致上下文溢出工具内部抛了异常模型拿到的是异常信息排查方式就是把工具调用前后的输入输出完整打日志。7.4 RAG层先人工看召回结果RAG 答非所问时按这个顺序排查把用户问题单独拿出来看向量检索结果的前5条判断召回的片段是否真的能回答问题如果结果不对看是分块问题、TopK问题还是 Embedding 模型问题如果召回了但没有引用出处看 Prompt 里是否要求了引用如果新旧政策冲突看是否加了版本过滤字段很多 RAG 问题不是模型回答能力差而是根本没召回正确的知识点。7.5 Agent流程层防止循环和超时Agent 经常出现的两类流程问题死循环模型反复调用同一个工具拿不到决定性信息超时工具调用或模型响应太慢导致 Agent 执行 Provider 超时针对死循环设置最大工具调用次数针对超时区分是网络原因、工具耗时长还是模型 API 响应慢。如果报错提示agent execution provider did not respond in time优先检查执行超时配置再排查外部依赖。7.6 一张排查表放到团队文档里现象优先检查再进一步模型不调用工具工具描述、工具名、触发场景模型是否开启 function calling工具报错参数抽取是否正确工具方法内部异常日志返回空 contentAPI原始响应是否流式响应、是否超时RAG 答非所问召回的前几段片段分块、TopK、版本过滤Agent 死循环最大循环次数工具返回信息是否足够判定内存溢出JVM 堆 / 容器限制向量数据、文档对象、线程数执行超时Agent 超时配置外部API响应时间、网络写在最后先跑通最小闭环再谈AI航空梦如果只记住一句话我希望是Java 生态做 AI 应用已经不是“能不能做”的问题而是“怎么做成一个可靠业务系统”的问题。SpringAI 和 Langchain4j 提供了接入模型的脚手架但真正的复杂度在实时工具、知识库检索、Agent 编排和工程化落地里。航空客服智能体这个案例其实放大看就是无数企业级 AI 应用的缩影模型负责语言理解和生成Tools 负责触达业务系统RAG 负责引入企业私有知识Agent 负责判断先后顺序。谁先谁后有千百种可能但落地方法论是通的先跑通一个最小闭环再逐步把工具、知识库、可观测性加进去。下一次再有人问“Java 能不能做 Agent”你可以先让他跑一个航班查询工具试试。跑通之后他会发现真正的困难不在模型而在“把一个能力稳定地放进业务流程”。这个过程需要耐心但方向已经很清楚了。
返回列表