ARTICLE DETAIL

资讯详情

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

Java AI应用开发实战:Spring AI 2.0带你打通RAG、Tools与Agent

Java AI应用开发实战:Spring AI 2.0带你打通RAG、Tools与Agent 先说一个很多 Java 后端同学共同的困惑看了大量 AI 应用开发教程结果打开全是 Python 代码LangChain、LlamaIndex 的示例很丰富但和自己手里的 Java/Spring 项目完全对不上。最近在系统学习这套 Java Spring AI 2.0 实战内容时发现真正难的不是调一个对话接口而是把模型调用、Tools、RAG、Agent 串成一条完整链路。这篇文章就把这条链路拆开整理成一篇可以照着配、照着跑的实战笔记适合有 Java 基础、想快速上手 AI 应用开发的同学。1. 为什么 Java 开发者要关注 Spring AI 2.0 与 Langchain4j1.1 AI 应用开发不是只有 Python 一条路先看一个现实情况语言模型本身通过 HTTP 接口提供服务任何语言都能调用。Python 生态的优势在于科研和快速原型但 Java 在后端领域有大量成熟基础设施比如 Spring Boot、微服务、配置中心、监控体系。企业做 AI 功能时往往希望复用已有的 Java 技术栈而不是维护一套 Python 服务。目前 Java 生态里最值得关注的两套方案Spring AISpring 官方推出的 AI 应用开发框架目标是把模型接入、向量检索、工具调用、Agent 编排都做成 Spring 风格的组件。Langchain4jJava 社区对 LangChain 编程模型的移植起步早、组件丰富官方也维护了针对 Milvus、Elasticsearch、Redis 等存储的适配。两者解决的问题高度重合但设计思路不同。很多人纠结“学哪个”我的建议是先理解核心链路再选一个顺手的主线框架。1.2 Spring AI 2.0 的定位Spring AI 提供了大量抽象组件核心包括ChatClient统一对话客户端支持同步、流式调用。ChatModel / EmbeddingModel封装不同厂商的大模型和向量模型。VectorStore统一向量数据库操作接口。Advisor类似 Spring MVC 的拦截器用于注入对话记忆、RAG 检索、提示词增强。Tool Calling把 Spring Bean 中的方法暴露给模型调用。Spring AI 2.0 是 2026 年视角下的一个相对稳定的大版本API 相比早期版本收敛了不少。需要提醒的是Spring AI 迭代非常快不同小版本的 starter 名称、advisor 类名可能存在差异本文示例核心思路通用如果遇到编译问题记得对照当前版本官方文档。1.3 Langchain4j 的定位Langchain4j 更像“Java 版 LangChain”。它的编程模型很直接最核心的是AiServices你只需要定义一个接口框架自动把接口方法变成支持工具调用、记忆和 RAG 的智能体。对比来看维度Spring AILangchain4j发起方Spring 官方Java 社区学习曲线依赖 Spring 体系熟悉 Spring 的更顺手贴近 LangChain 概念Python 转过来容易理解工具调用Tool 注解 ChatClientTool AiServicesRAG 支持Advisor VectorStore 抽象EmbeddingStore ContentRetrieverAgent 支持基于 ChatClient 编排AiServices 原生支持实际项目中选哪套可以看团队情况。本文重点以 Spring AI 2.0 为主线在 Agent 部分会单独展示 Langchain4j 的写法。2. 先把概念理顺LLM、RAG、Agent 与 Tools2.1 LLM 应用的三层架构一个完整的 AI 应用大致可以拆成三层模型层对话模型和 Embedding 模型。记忆与上下文层对话历史、检索知识、上下文窗口管理。能力层工具调用、RAG、Agent 编排。模型层是基础但业务效果主要取决于能力层怎么组合。RAG 让模型“知道不知道的事”Tools 让模型“能做不会做的事”Agent 让模型“自己决定先做什么后做什么”。2.2 RAG 是什么RAGRetrieval-Augmented Generation叫检索增强生成。可以这样理解大模型训练完成后知识是“封存”的你没办法直接让它知道公司内部文档里的内容。RAG 的做法是把知识放在外部存储里用户提问时先检索最相关的内容再把这些内容拼到 Prompt 里让模型回答。完整流程文档加载文档拆分ChunkingEmbedding 向量化存入向量库用户提问时问题向量化相似度检索 TopK 片段拼接上下文与问题模型生成回答RAG 解决的核心问题有两个一是私有知识问答二是降低模型幻觉。它能明确告诉模型“参考内容里没有答案”而不是凭空编造。2.3 Tools 是什么Tools 翻译过来是工具调用底层机制叫 Function Calling。大模型本身不会查数据库也不会调用业务接口但它能根据用户问题判断“需要调用哪个函数、传什么参数”。开发者只需要把函数注册给模型模型在合适的时候会自动触发调用。用户提问 - 模型判断需要调用工具 - 应用执行工具 - 结果返回模型 - 模型生成最终回答Tools 是把大模型和真实业务系统连接起来的关键机制。2.4 Agent 是什么Agent智能体没有统一标准定义。工程上可以把它理解为“模型 规划 工具 记忆 执行循环”。最简单的 Agent 工作方式接收用户任务。模型判断需要哪些工具。调用工具并获取结果。根据结果决定是否继续调用其他工具。最终生成答案。Agentic RAG 就是把 RAG 和 Agent 结合模型不只是做一次检索而是能根据问题决定检索几次、是否需要调用工具、是否需要追问用户。比如用户问“哪些订单今天会到”常规 RAG 回答不了但 Agent 可以先查订单系统再做知识库补充。3. 环境准备与项目初始化3.1 开发环境清单版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。推荐环境JDK 17 或 21Maven 3.8IntelliJ IDEA一个支持 OpenAI 协议的大模型服务比如 DeepSeek、通义千问、智谱也可以用本地 Ollama向量数据库 Milvus用 Docker 启动可选Ollama 本地模型注意Spring AI 2.0 要求 JDK 17 起步如果你还在用 JDK 8第一步是先升级项目基础环境。3.2 创建项目结构可以用 Spring Initializr 生成项目也可以手动创建。推荐结构如下ai-demo/ ├── pom.xml ├── src/main/java/com/example/aidemo/ │ ├── AiDemoApplication.java │ ├── config/ │ │ ├── LLMConfig.java │ │ └── VectorStoreConfig.java │ ├── controller/ │ │ ├── ChatController.java │ │ └── RagController.java │ ├── service/ │ │ ├── ChatService.java │ │ └── RagService.java │ ├── tool/ │ │ └── OrderTool.java │ └── agent/ │ └── CustomerAgent.java └── src/main/resources/ ├── application.yml └── docs/ └── 产品手册.txt3.3 pom.xml 依赖以 Maven 为例核心依赖大致如下。Spring AI 的 starter 命名不同版本有变化本文写法以较新的 2.x 风格为例实际使用时对照官网获取正确坐标。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.4.5/version relativePath/ /parent dependencies !-- Web 支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- OpenAI 协议模型 Starter -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency !-- Milvus 向量库 Starter -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-milvus/artifactId /dependency !-- 文档解析 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-tika-document-reader/artifactId /dependency /dependencies实际配置时需要添加 Spring AI BOM 来管理版本dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version2.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement3.4 application.yml 基础配置如果你的模型服务兼容 OpenAI 协议比如 DeepSeek可以这样配置spring: ai: openai: base-url: https://api.deepseek.com api-key: ${AI_API_KEY} chat: options: model: deepseek-chat temperature: 0.7如果使用通义千问 DashScopespring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus这里强调一个工程习惯API Key 不要硬编码在配置文件里必须使用环境变量或配置中心。后面进入生产环境时这一点直接关系到安全性。4. 第一轮对话Spring AI 2.0 基础接入4.1 注入 ChatClientSpring AI 2.0 中最核心的组件是ChatClient。它由ChatClient.Builder构建Spring Boot 会自动注册 Builder Bean。// 文件路径src/main/java/com/example/aidemo/service/ChatService.java Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient.Builder builder) { this.chatClient builder .defaultSystem(你是一个乐于助人的 AI 助手。) .build(); } public String chat(String userMessage) { return chatClient.prompt() .user(userMessage) .call() .content(); } }defaultSystem用于设置系统提示词相当于给模型设定身份和回答规则。4.2 暴露 HTTP 接口// 文件路径src/main/java/com/example/aidemo/controller/ChatController.java RestController RequestMapping(/api/chat) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } PostMapping public MapString, String chat(RequestBody MapString, String body) { String message body.get(message); String reply chatService.chat(message); return Map.of(reply, reply); } }启动项目后用 Postman 或 curl 测试curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 请用三句话介绍自己}预期返回 JSON 格式的回复内容。4.3 流式输出大模型生成回答需要几秒甚至更久如果让用户干等体验很差。流式输出可以让内容逐字显示。Spring AI 支持响应式流import reactor.core.publisher.Flux; public FluxString chatStream(String userMessage) { return chatClient.prompt() .user(userMessage) .stream() .content(); }Controller 对应改为PostMapping(value /stream, produces text/event-stream) public FluxString chatStream(RequestBody MapString, String body) { return chatService.chatStream(body.get(message)); }前端通过 SSE 就可以接收流式内容。4.4 多轮记忆默认情况下ChatClient是无状态的每一次call()都是一次独立请求。要实现多轮对话需要自己保存上下文或者使用 Spring AI 提供的记忆 Advisor。// 关键代码示例API 名称以当前 Spring AI 版本为准 chatClient.prompt() .user(我的名字是张三) .advisors(advisor - advisor .param(ChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID, user-001) .param(ChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE, 20)) .call() .content();这里CHAT_MEMORY_CONVERSATION_ID是会话 ID相同 ID 的请求共享上下文CHAT_MEMORY_RETRIEVE_SIZE控制携带最近多少条消息。多轮记忆的坑在于会话 ID 容易泄露、上下文越长 token 消耗越大生产环境需要控制。5. 给模型装上手Tools 函数调用实战5.1 Tools 在 Spring AI 中的实现方式Spring AI 对工具调用的封装非常简洁只需要在 Spring Bean 的方法上添加Tool注解框架会自动生成模型可识别的 JSON Schema并且在模型需要时执行对应方法。5.2 定义订单查询工具我们模拟一个真实业务场景用户问“订单 20260101 到哪了”模型需要调用订单查询工具。// 文件路径src/main/java/com/example/aidemo/tool/OrderTool.java Component public class OrderTool { Tool(name queryOrderStatus, description 根据订单号查询订单物流状态) public String queryOrderStatus(String orderId) { // 实际项目中这里可以调用订单服务接口或查询数据库 if (20260101.equals(orderId)) { return 订单 20260101 已发货当前位于杭州转运中心预计 2 天内送达。; } return 未查询到订单 orderId; } }注意description字段非常重要。模型不是通过方法名理解工具而是通过描述判断当前问题是否应该调用这个工具。5.3 启用工具调用在构建ChatClient时通过defaultTools注册工具// 文件路径src/main/java/com/example/aidemo/service/ToolChatService.java Service public class ToolChatService { private final ChatClient chatClient; public ToolChatService(ChatClient.Builder builder, OrderTool orderTool) { this.chatClient builder .defaultSystem(你是客服助手可以查询订单物流状态。) .defaultTools(orderTool) .build(); } public String chat(String userMessage) { return chatClient.prompt() .user(userMessage) .call() .content(); } }测试时请求“订单 20260101 现在到哪了”模型会先调用queryOrderStatus再基于返回结果生成回答。5.4 工具调用执行流程用文字描述整个执行链路应用启动时Spring AI 扫描Tool注解生成工具定义发给模型。模型根据用户问题判断“需要查询订单”返回一个函数调用请求包含方法名和参数。Spring AI 在 Spring 容器中找到对应 Bean 并执行方法。执行结果返回给模型。模型基于结果整理最终答案。这个“模型请求执行工具——工具结果返回模型”的循环是后续 Agent 编排的最小单元。5.5 工具调用常见误区使用 Tools 时几个容易踩的坑工具方法必须是 public并且所在类需要被 Spring 管理。参数尽量用 String 和基本类型复杂对象可能影响 JSON Schema 生成。工具描述要写清楚边界比如“只能查询 2025 年后的订单”否则模型容易误用。不要在工具方法里做耗时很长的同步操作容易触发模型调用超时。必须对工具做权限控制不能把高风险接口直接暴露给模型。6. RAG 实战知识库问答完整实现6.1 先看 RAG 完整流程RAG 的工程实现可以拆成两条链路离线索引链路和在线查询链路。离线索引链路加载文档 - 拆分片段 - 向量化 - 写入向量库在线查询链路用户提问 - 问题向量化 - 向量库检索 - 拼接 Prompt - 模型回答Spring AI 对这两条链路都有抽象下面逐步实现。6.2 文档加载与拆分假设我们在resources/docs下放了一个产品说明文档。先加载文档再做拆分。// 文件路径src/main/java/com/example/aidemo/config/DocIndexConfig.java Configuration public class DocIndexConfig { Bean public ApplicationRunner indexDocuments( VectorStore vectorStore, Value(classpath:docs/product-manual.txt) Resource resource) { return args - { // 1. 加载文档 TikaDocumentReader reader new TikaDocumentReader(resource); ListDocument documents reader.get(); // 2. 拆分文档 TokenTextSplitter splitter new TokenTextSplitter(); ListDocument chunks splitter.apply(documents); // 3. 写入向量库 vectorStore.add(chunks); System.out.println(已写入 chunks.size() 个文档片段); }; } }为什么需要拆分大模型上下文窗口有限整篇文档塞进去会浪费 token而且检索精度差。按语义或 token 数量拆成小片段后只需要把最相关的片段送给模型。TikaDocumentReader底层使用 Apache Tika可以解析 TXT、PDF、Word、Markdown 等常见格式。如果不用 Tika也可以根据场景选择PdfDocumentReader、TextReader等。6.3 Embedding 模型接入向量化的质量直接决定检索效果。这里有两种方案调用云端 Embedding API或使用本地模型。云端方式以通义千问 DashScope 为例spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} embedding: options: model: text-embedding-v3本地方式以 Ollama 为例spring: ai: ollama: base-url: http://localhost:11434 embedding: options: model: qwen2.5:7b本地方案的好处是数据不出内网适合私有化部署劣势是需要 GPU 资源且小模型的向量质量可能不如云端大模型。6.4 接入 Milvus 向量数据库Milvus 是目前主流的开源向量数据库。先用 Docker 启动服务具体启动方式建议参考 Milvus 官方文档通过 docker-compose 同时启动 etcd、minio、standalone 组件。Spring AI 配置spring: ai: vectorstore: milvus: client: host: localhost port: 19530 database-name: default collection-name: java_rag_demo index-type: HNSW metric-type: COSINE embedding-dimension: 1024重点强调embedding-dimension必须与 Embedding 模型的输出维度一致。切换到不同 Embedding 模型后如果不重新建集合会出现维度不匹配的报错。6.5 检索问答实现RAG 查询部分我用手动检索的方式实现更直观、也更好调试。// 文件路径src/main/java/com/example/aidemo/service/RagService.java Service public class RagService { private final ChatClient chatClient; private final VectorStore vectorStore; public RagService(ChatClient.Builder builder, VectorStore vectorStore) { this.chatClient builder.build(); this.vectorStore vectorStore; } public String ask(String question) { // 1. 相似度检索 ListDocument documents vectorStore.similaritySearch( SearchRequest.builder().query(question).topK(5).build()); // 2. 拼接参考上下文 String context documents.stream() .map(Document::getText) .reduce(, (a, b) - a \n----\n b); // 3. 构造 RAG Prompt String prompt 你是一个企业知识库问答助手。请根据下面的参考内容回答问题。 如果参考内容中没有答案请明确说明不要编造。 参考内容 %s 问题%s .formatted(context, question); // 4. 模型回答 return chatClient.prompt().user(prompt).call().content(); } }这里topK(5)表示检索最相似的 5 个片段。参数越大模型看到的上下文越多但 token 消耗也越高。6.6 验证 RAG 效果写一个简单的 ControllerRestController RequestMapping(/api/rag) public class RagController { private final RagService ragService; public RagController(RagService ragService) { this.ragService ragService; } PostMapping public MapString, String ask(RequestBody MapString, String body) { String question body.get(question); String answer ragService.ask(question); return Map.of(answer, answer); } }测试时如果文档里有“年假超过 5 天的申请需要部门总监审批”这句话那么询问“年假申请超过 5 天找谁审批”模型会基于检索片段回答而不是凭空发挥。6.7 进阶混合检索与重排基础 RAG 只做向量相似度检索实际项目里往往不够。热词里有“langchain4j milvus java混合检索跟重排”这正是生产级 RAG 的关键。混合检索是把“关键词检索 BM25”和“语义向量检索”结合解决向量检索对专有名词、商品编码不敏感的问题。Milvus 2.4 版本之后支持稀疏向量和稠密向量混合检索。在 Langchain4j 中接入 Milvus 的示例如下// 关键代码示例实际写法以 Langchain4j 官方文档为准 EmbeddingStoreTextSegment embeddingStore MilvusEmbeddingStore.builder() .uri(http://localhost:19530) .collectionName(java_rag_demo) .dimension(1024) .build();重排Rerank是在检索出 TopK 候选后用专门的排序模型对结果重新打分把最相关的片段排到前面。Langchain4j 提供ReRankingModel接口可以接入 Cohere、Jina 或本地的 BGE Reranker。混合检索 重排的完整链路用户提问 - 向量检索 Top20 关键词检索 Top20 - 结果合并去重 - Rerank Top5 - 送大模型生成这一步能明显提升答案质量但代价是检索链路变长、耗时增加需要结合业务场景权衡。7. Agent 实战从单工具到多步协作7.1 Agent 与 RAG 的本质区别RAG 是“查一次答一次”。Agent 是“根据任务规划循环调用工具逐步逼近答案”。用客服场景举例RAG用户问退换货政策系统检索知识库并回答。Agent用户问“帮我查一下订单 20260101 的物流顺便看看是否还在退换货期内”系统需要先查订单状态、再查退换货政策并把两者组合。Agentic RAG 的核心是让模型自己决定“先做什么、后做什么”。7.2 用 Langchain4j 的 AiServices 实现 AgentLangchain4j 的 Agent 开发体验很接近 LangChain。核心步骤是定义一个接口用AiServices绑定模型和工具。// 引入依赖后定义助手接口 public interface CustomerAssistant { String chat(String userMessage); }绑定模型和工具ChatLanguageModel model OpenAiChatModel.builder() .apiKey(System.getenv(AI_API_KEY)) .modelName(qwen-plus) .build(); CustomerAssistant assistant AiServices.builder(CustomerAssistant.class) .chatLanguageModel(model) .tools(new OrderTool()) .build(); String answer assistant.chat(订单 20260101 现在到哪了); System.out.println(answer);如果还需要记忆可以加入MessageWindowChatMemoryMessageWindowChatMemory memory MessageWindowChatMemory.builder() .maxMessages(10) .build(); CustomerAssistant assistant AiServices.builder(CustomerAssistant.class) .chatLanguageModel(model) .chatMemory(memory) .tools(new OrderTool()) .build();AiServices会自动完成工具调度模型决定调用工具框架执行结果再返回给模型。开发者不需要手动维护循环。7.3 用 Spring AI 编排多工具 AgentSpring AI 没有单独的“Agent”类实现方式是把系统提示词、Tools、记忆组合到ChatClient里。// 文件路径src/main/java/com/example/aidemo/agent/CustomerAgent.java Service public class CustomerAgent { private final ChatClient chatClient; public CustomerAgent( ChatClient.Builder builder, OrderTool orderTool, StockTool stockTool) { this.chatClient builder .defaultSystem(你是智能客服助手。你可以查询订单状态和商品库存。 回答要简洁信息不足时主动询问用户。) .defaultTools(orderTool, stockTool) .build(); } public String chat(String sessionId, String userMessage) { return chatClient.prompt() .user(userMessage) .advisors(advisor - advisor .param(ChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID, sessionId)) .call() .content(); } }这个例子虽然代码量不大但已经包含一个 Agent 的基本要素系统提示词规定角色和规则Tools 提供能力记忆保存会话上下文。7.4 Agent 的边界与风险控制Agent 越强大风险越大。如果模型可以调用数据库删除接口、转账接口、发送通知接口那么一次错误调用可能造成严重事故。工程上必须遵守以下原则高风险工具必须增加二次确认机制Agent 先生成“待确认操作”人工确认后再执行。工具方法内部要校验入参合法性不能盲目信任模型生成的参数。所有工具调用都要记录日志方便审计。不要给 Agent 暴露不必要的管理接口遵循最小权限原则。8. 高频问题排查下面整理开发过程中最容易遇到的问题包括热词里出现过的典型报错。问题现象常见原因解决思路启动报错找不到 ChatClient.Builder Bean没有引入模型 Starter或 Spring AI 自动配置未生效检查 pom.xml 依赖坐标确认 Starter 名称和版本匹配控制台输出 OutOfMemoryError: Insufficient memoryJVM 堆内存太小或本地模型推理占用过多内存增加 JVM 内存参数如 -Xmx4g本地模型改成远程 API 或换小量化模型模型不触发工具调用工具描述不清晰、参数类型复杂、模型版本不支持 Function Calling简化参数为 String 或基本类型完善 Tool 描述确认模型支持工具调用RAG 检索结果不准文本拆分粒度过大或过小、Embedding 模型维度不一致、向量库数据不匹配调整拆分策略重新向量化检查 embedding-dimension 配置Milvus 连接超时服务未启动、端口错误、防火墙
返回列表