
大家好我是你们的Java AI开发引路人。最近在整理Spring AI相关技术体系时发现一个非常明显的趋势越来越多的Java后端开发开始关注AI应用开发但市面上的资料大多以Python为主Java方向的教程要么太散要么太浅能把 Langchain4j、DeepSeek、RAG、Agent 这些核心概念串成一条完整学习路径的内容非常少。这篇文章就把这套体系完整拆解一遍。全文没有只讲概念的“空谈”也没有只贴代码的“甩锅式教程”而是从工程落地的角度把 Java 生态下做 AI 应用开发的基础原理、环境搭建、代码实现、踩坑经验一次讲清楚。无论你是刚接触 AI 应用开发的 Java 新手还是已经用 Spring Boot 写了好几年业务代码、想往 AI 方向转型的后端工程师这篇教程都值得你从头到尾读完。1. 背景与核心概念Java 做 AI 应用开发为什么值得学1.1 AI 应用开发不只有 Python 一条路过去几年提起 AI 开发大家第一反应都是 Python。Python 在数据科学和深度学习领域确实有不可替代的优势但如果你仔细观察企业级应用的真实场景会发现一个很现实的问题大多数公司的核心业务系统都是 Java 写的。这就产生了一个需求AI 能力要和现有 Java 系统集成而不是再造一套 Python 服务。如果 AI 能力独立在 Python 服务里Java 服务通过 HTTP 调用会面临两个问题一是开发成本高需要维护两套技术栈二是链路长排查问题困难。Spring AI 和 Langchain4j 正是为了解决这个问题而出现的。1.2 Spring AI 与 Langchain4j 的关系Spring AI 是 Spring 官方推出的 AI 应用开发框架它的目标是把 AI 能力整合进 Spring 生态让开发者可以用统一的 API 对接不同的大模型如 OpenAI、DeepSeek、通义千问等。Langchain4j 则是参考 Python 生态中 LangChain 的设计思路在 Java 中实现了一套类似的能力包括模型对话、Prompt 模板、文档加载、向量检索、工具调用和 Agent 编排。两者的定位有很多重叠但侧重点不同框架优势适合场景Spring AI官方生态与 Spring Boot 集成度高被 Spring 生态带动发展速度快已经在使用 Spring Boot希望快速接入 AI 能力Langchain4jAPI 设计更接近 LangChain功能组件更丰富RAG 和 Agent 相关组件成熟度高需要深度定制 RAG、Agent 逻辑或已经熟悉 LangChain 概念实际项目中两个框架可以互为补充。本文的实战部分以 Langchain4j 为主因为它在 RAG 和 Agent 层面提供的组件更完整而且和 Spring Boot 的集成方式非常自然。1.3 DeepSeek 在整条链路中的角色DeepSeek 提供了兼容 OpenAI 格式的 API 接口可以作为底层的大模型服务。你可以理解成Langchain4j 负责“怎么编排”DeepSeek 负责“怎么生成”。我们通过统一的接口定义把对话请求发给 DeepSeek拿到模型生成结果后再交给业务系统处理。DeepSeek 的模型能力在中文场景下表现很好而且 API 调用成本相对较低非常适合个人学习和企业项目试水。这也是为什么这套技术栈越来越火。1.4 Tools、RAG、Agent 分别解决什么问题这三个概念是 AI 应用开发的核心简单概括Tools工具调用让大模型主动调用你写好的 Java 方法。比如用户问“帮我查一下订单状态”大模型不是自己编一个答案而是调用你提供的queryOrder()方法去查真实数据。RAG检索增强生成把外部知识库文档切块、向量化、存到向量数据库用户提问时先检索最相关的文档片段再把片段和问题一起交给大模型生成答案。解决大模型不知道私有知识、容易胡编乱造的问题。Agent智能体把大模型、工具、记忆、规划能力组合起来让 AI 能自主完成多步任务。比如“帮我分析这份报表并发送给领导”Agent 会自己决定先读取数据、再生成总结、最后调用邮件工具。这三者的关系是递进的Tools 是能力基础RAG 是知识增强Agent 是最终的综合形态。本文会按顺序逐个实战。2. 环境准备与项目初始化2.1 开发环境与版本说明在做任何实战之前先把环境准备好。因为 Spring AI 和 Langchain4j 版本迭代速度较快不同版本之间的 API 差异较大所以这里需要先说明版本策略。本文示例使用的环境如下JDK17 或 21推荐 17Spring Boot 3.x 要求 JDK 17 起步 构建工具Maven 3.8 或 Gradle 7.x 框架Spring Boot 3.3.x Langchain4j1.x版本号以 Maven 中央仓库最新稳定版为准 DeepSeek API使用 DeepSeek 官方 APIOpenAI 兼容模式 IDEIntelliJ IDEA 2024.x 或以上需要特别提醒Langchain4j 的版本更新非常频繁一些 API 细节可能在你看到本文时已经变了。建议优先查看官方文档确认最新版本本文的核心思路和代码结构具有参考价值具体包名和类名以实际版本为准。2.2 初始化一个 Spring Boot 项目推荐直接使用 Spring Initializr 创建项目选择 JDK 17、Spring Boot 3.3.x然后添加 Web 依赖。项目的基本结构如下spring-ai-demo/ ├── pom.xml ├── src/main/java/com/example/aidemo/ │ ├── AidemoApplication.java │ ├── config/ │ │ └── LlmConfig.java │ ├── controller/ │ │ └── ChatController.java │ ├── service/ │ │ └── ChatService.java │ ├── tool/ │ │ └── OrderTool.java │ └── rag/ │ └── RagService.java └── src/main/resources/ └── application.yml2.3 引入 Langchain4j 依赖项目创建完成后在pom.xml中引入 Langchain4j 的核心依赖以及对 Spring Boot 的集成依赖。以 Maven 为例parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.5/version relativePath/ /parent properties java.version17/java.version langchain4j.version1.0.0-beta1/langchain4j.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-boot-starter/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-easy-rag/artifactId version${langchain4j.version}/version /dependency /dependencies说明一下三个依赖的作用langchain4j-spring-boot-starterLangchain4j 对 Spring Boot 的自动配置支持可以让你通过配置文件直接定义模型。langchain4j-open-aiLangchain4j 对 OpenAI 协议的实现DeepSeek 的 API 兼容 OpenAI 格式所以可以直接用这个依赖。langchain4j-easy-rag简易 RAG 功能包可以快速从文件系统加载文档并完成向量化流程。3. 核心能力一接入 DeepSeek 大模型3.1 在 application.yml 中配置模型依赖引入后先在application.yml中配置 DeepSeek 的 API Key 和模型地址。langchain4j: open-ai: chat-model: base-url: https://api.deepseek.com api-key: ${DEEPSEEK_API_KEY} model-name: deepseek-chat temperature: 0.7 log-requests: true log-responses: true这里有几个配置项需要重点解释base-urlDeepSeek API 的接口地址。因为使用 OpenAI 兼容模式所以配置为https://api.deepseek.com。api-key在 DeepSeek 开放平台申请的密钥。建议通过环境变量注入不要硬编码在代码里。model-name这里写的是deepseek-chat对应 DeepSeek-V3 系列模型。如果后续有新的模型版本按官方文档调整。temperature控制模型生成的随机性。0 到 1 之间值越小越保守、越适合需要确定性的场景值越大越有创造性。一般业务场景 0.7 左右比较合适。log-requests和log-responses开启请求和响应日志方便调试生产环境建议关闭。3.2 编写最简单的对话接口配置完成后写一个最简单的 Controller 验证链路是否通畅。// 文件路径src/main/java/com/example/aidemo/controller/ChatController.java package com.example.aidemo.controller; import dev.langchain4j.model.chat.ChatLanguageModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController public class ChatController { private final ChatLanguageModel chatLanguageModel; public ChatController(ChatLanguageModel chatLanguageModel) { this.chatLanguageModel chatLanguageModel; } GetMapping(/chat) public String chat(RequestParam(value message, defaultValue 你好请介绍一下你自己) String message) { return chatLanguageModel.generate(message); } }这里ChatLanguageModel是 Langchain4j 的核心接口它由 Spring Boot Starter 根据application.yml配置自动创建并注入到容器中。启动项目后在浏览器或命令行访问curl http://localhost:8080/chat?message用一句话介绍你自己预期返回DeepSeek 模型生成的一段自我介绍文本。3.3 流式输出像 ChatGPT 一样逐字输出上面的示例是一次性返回完整结果体验不够好。实际产品中更常用的是流式输出也就是像 ChatGPT 那样一个字一个字往外蹦。Langchain4j 中通过StreamingChatLanguageModel接口实现流式输出。// 文件路径src/main/java/com/example/aidemo/controller/StreamChatController.java package com.example.aidemo.controller; import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.model.chat.response.ChatResponse; import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; RestController public class StreamChatController { private final StreamingChatLanguageModel streamingChatLanguageModel; public StreamChatController(StreamingChatLanguageModel streamingChatLanguageModel) { this.streamingChatLanguageModel streamingChatLanguageModel; } GetMapping(value /stream-chat, produces MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter streamChat(RequestParam(message) String message) { SseEmitter emitter new SseEmitter(0L); streamingChatLanguageModel.chat(message, new StreamingChatResponseHandler() { Override public void onPartialResponse(String token) { try { emitter.send(token); } catch (IOException e) { emitter.completeWithError(e); } } Override public void onCompleteResponse(ChatResponse completeResponse) { emitter.complete(); } Override public void onError(Throwable error) { emitter.completeWithError(error); } }); return emitter; } }这里使用了 Spring MVC 的SseEmitter实现 Server-Sent Events 推送。前端的EventSource可以直接订阅这个接口实现打字机效果。3.4 让 AI 记住上下文ChatMemory默认情况下每次调用模型都是无状态的也就是说 AI 不记得你在上一轮说过什么。要实现多轮对话需要引入 ChatMemory对话记忆。Langchain4j 提供了MessageWindowChatMemory可以控制记住最近多少轮对话。// 文件路径src/main/java/com/example/aidemo/service/ChatWithMemoryService.java package com.example.aidemo.service; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.SystemMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import org.springframework.stereotype.Service; import java.util.List; Service public class ChatWithMemoryService { private final ChatLanguageModel chatLanguageModel; private final MessageWindowChatMemory chatMemory; public ChatWithMemoryService(ChatLanguageModel chatLanguageModel) { this.chatLanguageModel chatLanguageModel; // 维护最近 20 条消息的历史记录 this.chatMemory MessageWindowChatMemory.withMaxMessages(20); // 可选的系统提示词定义 AI 的角色 this.chatMemory.add(SystemMessage.from(你是一个智能助手回答要简洁准确。)); } public String chat(String userMessage) { chatMemory.add(UserMessage.from(userMessage)); AiMessage aiMessage chatLanguageModel.generate(chatMemory.messages()); chatMemory.add(aiMessage); return aiMessage.text(); } }这里的关键逻辑是每次调用模型前把历史消息取出来一起发给模型模型返回后再把 AI 的回答存回记忆里。这样 AI 就拥有了连续对话的能力。4. 核心能力二Tools 工具调用4.1 为什么需要工具调用大模型有一个天然缺陷它的知识截止于训练数据而且没有任何“行动能力”。比如用户问“帮我查一下订单号 10086 的物流状态”模型无法访问你的数据库只能基于猜测回答。工具调用Function Calling / Tool Calling就是为了解决这个问题。当你把工具的定义告诉模型后模型会在需要时返回一个“调用请求”你的应用代码收到请求后执行真实的函数再把结果返回给模型最终由模型生成最终答案。4.2 用 Tool 注解定义一个订单查询工具Langchain4j 提供了一种非常简洁的方式在 Spring Bean 的方法上加上Tool注解框架会自动把方法注册为可被模型调用的工具。// 文件路径src/main/java/com/example/aidemo/tool/OrderTool.java package com.example.aidemo.tool; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; Component public class OrderTool { // 模拟订单数据库 private static final MapString, MapString, String ORDER_DB new ConcurrentHashMap(); static { ORDER_DB.put(10086, Map.of( status, 已发货, logistics, 顺丰速运 SF1234567890, estimatedArrival, 2026-01-10 )); ORDER_DB.put(10010, Map.of( status, 待支付, logistics, 无, estimatedArrival, 无 )); } Tool(根据订单号查询订单状态、物流公司和预计送达时间) public String queryOrder(P(订单号例如 10086) String orderId) { MapString, String order ORDER_DB.get(orderId); if (order null) { return 未找到订单号 orderId 对应的订单信息; } return 订单状态 order.get(status) 物流公司 order.get(logistics) 预计送达 order.get(estimatedArrival); } }这段代码定义了一个订单查询工具。Tool注解里的描述用于告诉模型这个工具是做什么的P注解描述参数含义。模型会根据用户的提问自动决定是否调用这个工具以及传入什么参数。4.3 让 AI 自动决定调用哪个工具定义好工具后在 Service 中把这个工具交给 AI。// 文件路径src/main/java/com/example/aidemo/service/ToolService.java package com.example.aidemo.service; import dev.langchain4j.agent.tool.ToolSpecification; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.service.AiServices; import org.springframework.stereotype.Service; Service public class ToolService { public interface OrderAssistant { String chat(String userMessage); } private final OrderAssistant assistant; public ToolService(ChatLanguageModel chatLanguageModel, OrderTool orderTool) { this.assistant AiServices.builder(OrderAssistant.class) .chatLanguageModel(chatLanguageModel) .tools(orderTool) .build(); } public String chat(String userMessage) { return assistant.chat(userMessage); } }AiServices是 Langchain4j 的核心入口它可以把接口定义转换为一个 AI 服务对象自动完成工具方法的绑定和调用。这里将OrderTool实例传入模型就可以在合适的场景下调用它。添加一个 ControllerGetMapping(/tool-chat) public String toolChat(RequestParam(message) String message) { return toolService.chat(message); }测试一下效果。访问curl http://localhost:8080/tool-chat?message帮我查一下订单号10086的物流信息模型会经历以下过程分析用户意图判断需要查询订单。调用queryOrder(10086)方法拿到订单数据。根据工具返回的数据整理成自然语言回答。最终输出的效果类似订单号 10086 已发货物流公司为顺丰速运单号 SF1234567890预计 2026-01-10 送达。这就是工具调用的完整闭环也是后面 Agent 的基础能力。5. 核心能力三RAG 知识库实战5.1 RAG 解决什么问题在实际业务中很多问题无法只靠大模型“凭空回答”。比如你们公司的内部规章制度、产品使用手册、维修指南这些内容不在模型的训练数据里模型不知道硬答就会“一本正经地胡说八道”。RAGRetrieval-Augmented Generation检索增强生成的思路是不指望模型记住所有知识而是在用户提问时先到知识库检索相关内容把最相关的内容和问题一起给模型模型基于这些资料组织答案。这样做有两个明显优势答案基于真实资料极大程度减少幻觉。知识更新成本低不用重新训练模型修改文档即可。5.2 RAG 完整流程拆解一个标准的 RAG 链路包含四个关键步骤文档加载与解析 → 文本切块 → 向量化 Embedding → 存储到向量数据库 → 用户提问时检索 → 重排序 → 交给大模型生成每一步都有讲究文档加载与解析支持 PDF、Word、Markdown、TXT 等格式Langchain4j 中通过不同的DocumentParser实现。文本切块Chunking这是非常影响效果的一步。切块太大检索到的内容不够精准切块太小语义不完整。常见策略一是按固定大小切块如每 500 个 token 一段每段重叠 50 个 token二是按文档结构切块如按标题、段落切三是按语义切块根据句子语义边界切。实际项目中需要根据文档类型测试不同策略。对于企业规章制度类文档按章节和段落切效果通常更好。向量化Embedding把文本转换为向量。这一步需要一个 Embedding 模型Langchain4j 支持接入多种模型常见的有 OpenAI Embedding、Qwen Embedding也可以本地部署 BGE 等模型。向量存储存到向量数据库。Milvus、Qdrant、Chroma、pgvector 都可以。选择时需要考虑数据量、并发量、部署成本等因素。检索与重排用户提问后先把问题向量化到向量库中检索相似度最高的几个片段。如果字段中同时存了向量和关键词索引还可以做混合检索Hybrid Search把向量相似度和 BM25 关键词匹配结果融合。为了让最终结果更准确可以在检索后加一道重排Rerank环节让一个专门的重排模型对候选文档打分排序。引用溯源生产系统中RAG 输出必须支持引用溯源也就是答案后面要能标注“这段话来自哪个文档第几页”。实现方式是在切块时保留文档 ID、标题、页码等元信息答案生成后把使用的文档片段元信息一并返回给前端展示。5.3 用 Langchain4j Qwen Embedding Milvus 实现 RAG这里我们演示一个可运行的最小链路。整套链路基于“deepseek 生成答案 Qwen Embedding 做向量化 Milvus 做向量存储”。先引入相应依赖dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-milvus/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-embeddings/artifactId version${langchain4j.version}/version /dependency然后编写一个 RAG 服务// 文件路径src/main/java/com/example/aidemo/rag/RagService.java package com.example.aidemo.rag; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.loader.FileSystemDocumentLoader; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.service.AiServices; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import org.springframework.stereotype.Service; import java.nio.file.Path; import java.util.List; Service public class RagService { public interface RAGAssistant { String chat(String userMessage); } private final RAGAssistant assistant; public RagService( ChatLanguageModel chatLanguageModel, EmbeddingModel embeddingModel, EmbeddingStoreTextSegment embeddingStore ) { // 1. 加载文档 ListDocument documents FileSystemDocumentLoader.loadDocuments( Path.of(src/main/resources/docs) ); // 2. 创建文档摄取器切块 - 向量化 - 存储 EmbeddingStoreIngestor ingestor EmbeddingStoreIngestor.builder() .documentSplitter(new DocumentSplitter()) .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); ingestor.ingest(documents); // 3. 创建检索器 EmbeddingStoreContentRetriever retriever EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.6) .build(); // 4. 组装 RAG Assistant this.assistant AiServices.builder(RAGAssistant.class) .chatLanguageModel(chatLanguageModel) .contentRetriever(retriever) .chatMemory(MessageWindowChatMemory.withMaxMessages(20)) .build(); } public String chat(String userMessage) { return assistant.chat(userMessage); } }这段代码就是 RAG 最小闭环。原理上EmbeddingStoreIngestor负责把文档切块后向量化并存入向量库EmbeddingStoreContentRetriever负责在用户提问时检索相关片段。实际项目中推荐把文档摄取和检索分成两个阶段。启动时如果文档不变不必重复摄取文档发生变化时通过增量更新的方式替换对应向量数据。这样做既能提升启动速度也方便维护。5.4 RAG 效果调优的常见方向很多朋友第一次跑通 RAG 后会发现效果不理想问题往往出在三个地方一是切块策略不合理。如果检索到的片段与问题不相关优先调整切块大小和重叠窗口。对于问答类场景可以先尝试 300-500 字的分块大小对于长文档分类场景可以尝试按章节切块。二是 Embedding 模型选得不合适。通用 Embedding 对专业领域文本效果可能不好可以尝试领域微调过的 Embedding 模型或直接用效果更强的商用 Embedding API。三是缺少重排环节。向量检索的一级结果只是“候选集”加入 Rerank 模型对候选集重新排序可以明显提升最终答案质量。6. 核心能力四Agent 智能体开发6.1 Agent 到底“智能”在哪里Agent智能体的概念现在非常火但很多人对它的理解停留在“能自动执行任务”这个层面这个理解太粗糙了。普通程序是“固定流程”你写什么逻辑程序就执行什么逻辑。普通 Prompt 对话是“固定输出”模型根据你的提示词生成文本生成完了就结束。Agent 则不完全一样。一个通用 Agent 应该具备以下能力任务理解拆解用户意图制定执行计划。工具使用调用外部工具获取信息或执行操作。记忆能力记住前文对话、任务上下文和中间结果。自主决策执行完一个步骤后自行判断下一步做什么。自我修正执行出错时能读取错误信息调整方案再次尝试。在 Java 生态中Langchain4j 的AiServices框架已经把这些能力集成到了一起。6.2 用 AiServices 构建一个带记忆力、带工具的 Agent下面演示一个综合 Agent它能记住对话内容也能调用工具完成真实业务操作。这里可以在前面工具的基础上增加一个“发送邮件通知”的工具。// 文件路径src/main/java/com/example/aidemo/tool/EmailTool.java package com.example.aidemo.tool; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; Component public class EmailTool { Tool(给指定收件人发送邮件) public String sendEmail( P(收件人邮箱地址) String to, P(邮件主题) String subject, P(邮件正文) String content ) { // 模拟发送邮件 System.out.println(模拟发送邮件给 to 主题 subject 正文 content); return 邮件已发送成功; } }然后创建一个 Agent 服务把多个工具组合到一起// 文件路径src/main/java/com/example/aidemo/agent/CustomerServiceAgent.java package com.example.aidemo.agent; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.UserMessage; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import com.example.aidemo.tool.EmailTool; import com.example.aidemo.tool.OrderTool; import org.springframework.stereotype.Service; Service public class CustomerServiceAgent { public interface CustomerServiceAssistant { String chat(MemoryId String memoryId, UserMessage String userMessage); } private final CustomerServiceAssistant assistant; public CustomerServiceAgent(ChatLanguageModel chatLanguageModel, OrderTool orderTool, EmailTool emailTool) { // 使用 AiServices 构建带工具和记忆的 Agent this.assistant AiServices.builder(CustomerServiceAssistant.class) .chatLanguageModel(chatLanguageModel) .tools(orderTool, emailTool) .chatMemoryProvider(memoryId - MessageWindowChatMemory.withMaxMessages(30)) .build(); } public String chat(String memoryId, String userMessage) { return assistant.chat(memoryId, userMessage); } }这里用到MemoryId注解可以实现不同用户不同会话的对话记忆隔离。也就是说用户 A 的对话记忆不会串到用户 B 那里。测试时可以通过下面两个连续请求观察 Agent 的表现# 第一轮查询订单 curl http://localhost:8080/agent/chat?memoryIduser001message查一下订单10086到哪了 # 第二轮根据第一轮结果发邮件 curl http://localhost:8080/agent/chat?memoryIduser001message把订单物流信息发到 testexample.comAgent 的执行流程是第一轮识别用户意图调用OrderTool.queryOrder(10086)返回物流信息并将结果存入记忆。第二轮从记忆中读取订单物流信息调用EmailTool.sendEmail(...)发送邮件。最后返回“邮件已发送成功”给用户。这个过程的重点是 Agent 自主实现了“查询 → 信息提取 → 发送邮件”的多步任务。6.3 Agentic RAG让 Agent 自己决定要不要检索普通的 RAG 是“每次都检索”。Agentic RAG 则更智能让 Agent 自己判断什么时候需要检索知识库什么时候只需直接回答。这个模式在 Langchain4j 中可以通过把检索器封装成工具来实现。Tool(从公司知识库中检索信息当用户询问公司政策、产品说明、维修指南时使用) public String searchKnowledgeBase(P(用户的问题) String query) { // 调用 ContentRetriever 检索知识库 ListContent contents contentRetriever.retrieve(query); return contents.stream() .map(Content::text) .collect(Collectors.joining(\n\n)); }通过这种设计模型在遇到专业知识类问题时才会触发检索日常闲聊则直接回答。优点是节省查询成本减少无效检索带来的噪声干扰。6.4 Harness 与 Agent 的关系最近搜索热词里经常出现 DeepSeek Harness、Hermes Agent 之类的词汇。这里顺便澄清一下Harness套件/工作台本质上是帮助你构建和管理 Agent 的辅助工具而不是 Agent 本身。Agent 是“智能体”负责自主决策和执行任务Harness 是“承载 Agent 的开发工具链”负责配置模型、管理工具、监控日志。在 Java 生态中Langchain4j 就是你的 Harness 的一部分而整个 Spring Boot 应用就是你的 Agent 运行环境。7. 高频问题与排查思路7.1 SpringAI 连接 DeepSeek 不输出 content在配置 DeepSeek 接入 Spring AI 或 Langchain4j 时最容易遇到的问题就是请求发出去了但模型没有返回 content或者直接报错。表格展示常见现象和排查方向问题现象常见原因解决思路调用报 401 或 403API Key 错误或未配置检查环境变量是否正确确认 DeepSeek 控制台中的 Key 是否有效报 404 Not Foundbase-url配置错误或模型名错误确认base-url是https://api.deepseek.commodel-name是deepseek-chat或deepseek-reasoner响应为空 content使用了 reasoning 模型但解析方式不对部分模型如deepseek-reasoner返回的是推理内容需要检查响应结构中的content字段是否为空改为解析reasoning_content或更换为对话模型请求超时推理模型响应慢或网络不稳设置合理的超时时间流式输出优先增加重试机制中文回答质量差temperature 设置不合适尝试降低 temperature 到 0.3-0.5或调整 System Prompt排查优先级先看 HTTP 状态码再看请求体格式最后看响应体结构。开启log-requests: true和log-responses: true能最快找到问题。特别注意DeepSeek 官方同时提供了对话模型deepseek-chat和推理模型deepseek-reasoner。推理模型的响应结构和对话模型不同如果你使用的是推理模型content字段可能为空推理过程在reasoning_content字段里。这是“不输出 content”最常见的原因。7.2 Langchain4j 依赖冲突与版本不兼容Langchain4j 版本更新很快而且不同的模块可能依赖了不同版本的底层库容易出现冲突。处理策略统一版本所有langchain4j-*依赖使用同一个版本号不要混用。查看依赖树使用mvn dependency:tree排查冲突。版本升级谨慎Langchain4j 在 0.35 到 1.x 的升级中 API 有较大调整升级前务必查看官方 migration guide。Spring Boot 版本匹配确认 Spring Boot 3.x 版本和 Langchain4j 官方要求匹配。7.3 RAG 检索不到相关内容或检索结果不相关这个问题的排查顺序建议如下检查文档有没有被正确切块和向量化 → 查看向量数据库中是否真的存入了数据 → 调整切块大小 → 降低minScore阈值 → 更换 Embedding 模型 → 增加重排环节。最容易被忽略的其实是切块策略。切块太大检索精度差切块太小语义不完整。7.4 工具调用时模型没有按预期执行有时候模型该调用工具却不调用或者调用了错误的工具。常见处理和优化方式工具描述写得更清晰。Tool注解中的描述直接影响模型对工具功能的理解。描述越明确选择越准确。参数描述写细。P注解中写清楚参数含义、格式、示例值。比如日期参数写明“格式为 yyyy-MM-dd”。减少工具数量。工具太多会让模型难以抉择尽量只暴露当前场景需要的工具。观察日志。开启调用日志看模型实际选择了哪个工具、传入了什么参数。7.5 Agent 执行超时或没有响应如果使用 Agent 时遇到类似 “the agent execution provider did not respond in time” 的超时错误需要从以下三个维度排查一是大模型响应太慢。推理模型生成时间长建议调大 HTTP 客户端超时时间二是工具方法执行时间长。如果 Agent 调用的工具是慢操作比如远程 HTTP 调用、数据库慢查询建议优化工具方法本身或增加异步能力三是链路复杂导致累积时间超时。多轮工具调用会叠加响应时间可以考虑限制 Agent 最大执行步数。8. 最佳实践与工程建议8.1 配置管理API Key 等敏感信息一定通过环境变量或配置中心管理不要硬编码在代码里。建议使用application.yml中占位符引用环境变量langchain4j: open-ai: chat-model: api-key: ${DEEPSEEK_API_KEY}同时把application.yml加入.gitignore或使用独立的配置模板文件防止敏感信息泄漏到代码仓库。8.2 日志与可观测性AI 应用排查问题难度比传统应用大因为链路中多了一个不可完全可控的大模型。因此日志记录非常重要。建议至少记录以下信息每次请求的用户原始提问。模型最终返回的回答。工具调用名称、参数和返回值注意脱敏。RAG 检索到的文档片段 ID 和来源。每次调用的耗时和 token 消耗。生产环境推荐使用 OpenTelemetry 等链路追踪工具把从用户请求到模型调用的全链路可视化。8.3 安全边界AI 应用面临的安全问题需要重点关注。Prompt 注入用户可能在输入中恶意篡改提示词诱导模型执行非预期操作。应对措施对用户输入做长度限制和敏感词过滤系统提示词加边界说明告知模型哪些指令不可执行对工具方法做权限校验特别是涉及数据库写入、资金操作、邮件发送等敏感动作。工具权限最小化不要让 AI 能调用所有工具。每个工具按用户角色授权AI 不能执行超出用户权限的操作。比如普通用户不能通过 AI 调“删除订单”工具。输出内容合规对模型输出内容做敏感信息过滤和合规审核特别对外输出场景。数据隐私不要将敏感数据身份证、手机号、家庭住址直接发送给外部大模型。如果必须使用先做脱敏处理或选择私有化部署的模型服务。8.4 性能与成本优化大模型 API 调用成本不低性能优化和成本控制是两个永恒主题。缓存对于高频且答案相对固定的问题可以按“问题哈希”做结果缓存。模型分级简单任务用便宜的小模型复杂任务才调用强模型。不要所有请求都上大模型。Prompt 精简Prompt 越长token 消耗越高。移除不必要的系统提示词和冗余的历史记录。RAG 前置过滤如果知识库非常大可以先做粗粒度分类过滤再进入向量检索降低延迟。批量处理需要处理大量文档时考虑离线批量摄取避免在用户请求链路上做重计算。8.5 生产环境部署注意事项应用内存要预留充足。向量检索和文档解析都是内存密集型操作。配置合理的连接池和超时时间。外部模型调用如果超时要设置熔断降级。采用“降级策略”模型调用失败时可以返回兜底文案或走传统检索逻辑不要让用户看到白屏。多环境隔离。开发环境、测试环境、生产环境使用不同的 API Key 和模型配置。9. 总结与后续学习路线这篇文章从 Java 开发者的视角完整走了一遍 Spring AI 2.0 / Langchain4j 生态的入门和实战环境搭建、DeepSeek 接入、ChatModel 对话、流式输出、Tools 工具调用、RAG 知识库构建、Agent 智能体编排以及高频问题和工程化建议。现在你至少应该能够做到独立创建一个基于 Spring Boot Langchain4j 的 AI 应用。接入 DeepSeek 并实现普通对话和流式对话。通过Tool注解让大模型调用自己的业务方法。理解 RAG 全流程并能搭建一个最小知识库问答系统。理解 Agent 的构建思路能用AiServices组合工具和记忆。如果你想继续深入按下面顺序学习深入 Langchain4j 的AiServices源码理解动态代理和工具发现的实现机制。学习向量数据库的原理了解 HNSW、IVF 等索引结构以及混合检索的数学原理。探索当前主流的 RAG 增强方案包括 Agentic RAG、GraphRAG、多路召回与重排。研究模型微调和提示词工程提升模型在垂直场景下的表现。学习如何把 AI 能力真正落地到生产包括灰度发布、A/B 测试、监控告警和成本治理。Java 开发者的 AI 技术栈正在快速成型Spring AI 和 Langchain4j 这两大框架已经让 Java 后端在 AI 应用开发中焕发出新的活力。这个方向的技术迭代非常快建议保持“边学边用、以项目驱动”的节奏不要停留在刷文档的阶段。入门最好的时机是现在。花一个周末把这篇文章里的代码亲手敲一遍你会比 90% 只看不练的人收获更多。如果遇到问题欢迎随时留言交流。# 最后送上一句实践建议 # 把 Demo 跑通只是起点 # 能把 AI 能力接入真实业务才是真正入门的标志。