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

资讯详情

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

Java后端AI Agent实战:Spring AI + Langchain4j构建RAG智能航空助手

Java后端AI Agent实战:Spring AI + Langchain4j构建RAG智能航空助手 先从开发者的视角说一个真实感受这两年 AI Agent 的战场几乎被 Python 生态霸屏了。很多 Java 后端团队想做智能客服、知识库问答、自动化办公 Agent翻遍资料发现要么是 LangChain Python要么是 FastAPI 写着 demo真到了 Spring Boot 项目里落地时还得自己啃各个框架的 Java 客户端。最近我在设计一个“智能航空 Agent”项目时把 Java 生态里主流的 AI 开发方案系统性梳理了一遍。Spring AI 2.0 作为 Spring 官方出品的 AI 开发框架给了 Java 开发者一套非常舒适的大模型接入体验而 Langchain4j 则更像 Java 界的 LangChain把 RAG、Tools、Agent 这些核心能力优雅地封装成了类型安全的 API。这篇文章会以“企业级 Agent 智能航空项目”为主线从概念拆解到完整代码实现带你走一遍 Java 后端接入大模型、构建 RAG 知识库、定义 Tool 工具、最后组装成 Agent 的全流程。文章偏实战包含可复制的代码和配置如果你正在研究 SpringAI、Langchain4j、RAG、Agent 这几块内容这篇应该能帮你省不少时间。1. 背景Java 开发者做 AI 应用为什么绕不开这几个关键词1.1 从“调用大模型”到“搭建 AI 应用”很多 Java 后端同学第一次接触 AI 开发是从写一个 HTTP 请求调用大模型接口开始的。那段代码很简单把用户的消息拼进请求体发给模型 API拿到返回结果后展示给用户。但真实的企业级 AI 项目远没有这么简单。举个例子用户问“明天从北京飞深圳帮我查一下早上的航班顺便看看有没有特价票”一个成熟系统需要做这些事理解用户意图识别出“北京”“深圳”“明天”“早上”这几个关键信息调用航班查询接口拿到真实航班数据查询特价票政策和退改签规则把航班列表和注意事项整理成一段自然语言回复。这些能力单靠一个大模型是做不到的。大模型只负责“理解与生成”它不知道你的航班数据存在哪也不会自动去查数据库。于是我们需要一套工程框架把模型能力、知识库、业务工具、任务编排组合起来这就是 Spring AI 和 Langchain4j 存在的意义。1.2 Java 生态为什么需要自己的 AI 框架Python 生态有 LangChain、LlamaIndex 这些成熟框架但 Java 后端有自己的技术约束我们需要强类型、Spring 容器管理、事务边界、安全校验、日志链路。如果让 Java 团队直接用 Python 生态会带来双语言维护成本。Spring AI 是 Spring 官方推出的 AI 集成框架目标很简单让开发者用定义 Bean 的方式接入大模型、向量数据库、Embedding 模型让 AI 能力像 Spring Data 操作数据库一样自然。Langchain4j 则是社区驱动的 Java AI 框架设计思路与 LangChain 对齐但在 Java 语言基础上做了大量类型安全和 API 简化优化。两者可以结合使用也可以独立选型。1.3 智能航空 Agent 的典型场景本文选择“航空”作为业务场景是因为它非常适合展示 AI 应用的几种核心能力航班查询需要 Tools 工具调用Agent 要能识别参数并触发真实查询航空公司政策文档退改签、行李额度、会员权益适合用 RAG 知识库管理用户问题往往混合了“实时数据查询”和“静态知识问答”是测试 Agent 编排能力的好场景。接下来我们先把概念理清楚。2. 核心概念Spring AI、Langchain4j、RAG、Agent、Tools 到底是什么2.1 Spring AI 2.0官方 AI 抽象层Spring AI 2.0 是 Spring 官方逐渐成熟的一个 AI 开发模块。它提供了一套统一 API屏蔽了不同大模型厂商之间的差异。简单理解你可以通过配置切换 OpenAI、通义千问、DeepSeek、Ollama 等模型而业务代码不需要大改。在 2.0 版本中核心概念包括ChatModel负责对话补全是最基础的模型接口EmbeddingModel负责把文本转换成向量ChatClient一种流式链式 API方便构建提示词、调用工具、管理上下文Memory负责多轮对话的上下文管理。Advisor拦截器可以在调用前后追加逻辑常用于 RAG、日志、限流。Spring AI 的角色是“底座”帮我们把模型接入这件事做得很干净。但如果你想快速实现 Agent 的工具调用、RAG 查询、任务自动编排你会发现这层抽象还比较薄需要自己写不少组装逻辑。2.2 Langchain4jJava 界的 AI 编排框架Langchain4j 的设计目标就是补上 Java 生态缺失的那层编排能力。它对标 LangChain但用纯 Java 实现。它提供的核心能力包括ChatLanguageModel 与 EmbeddingModel 的抽象AiServices可以像定义接口一样定义 AgentTool 注解把任意 Java 方法暴露给大模型调用ContentRetriever、ContentRetriever用于 RAG 检索Memory 接口管理多轮对话支持 OpenAI、DashScope、通义千问、Ollama、DeepSeek 等模型。在实战项目中我通常用 Spring AI 管理模型连接和 Web 层集成用 Langchain4j 实现 Agent 编排和 RAG 链路。两者并不冲突。2.3 RAG给大模型装上一个企业知识库RAGRetrieval-Augmented Generation检索增强生成是一种“先检索、再生成”的技术架构。它的核心思路是提前把企业文档分块、向量化存入向量数据库用户提问时系统用同一个 Embedding 模型将问题向量化从向量库中检索最相关的文档片段把检索结果作为上下文连同用户问题一起交给大模型生成回答。这样大模型不依赖训练数据里的旧知识也能回答最新的企业政策、产品文档和领域知识而且回答可以追溯到具体来源。在航空场景里退改签规则、行李限额、会员权益这些内容适合做 RAG。因为它们更新频繁、量很大不可能全部塞进提示词。2.4 Agent让大模型学会“动手做事”Agent智能体可以被理解为一个“有大脑、会调用工具”的对话系统。它比普通聊天机器人多了一个关键能力自动规划任务、调用外部工具、观察结果、决定下一步。例如用户问“帮我订一张明天北京到上海的机票”Agent 会经历这样的内部流程理解用户意图提取明天、北京、上海三个关键实体调用 searchFlights 工具拿到航班列表根据返回结果判断是否需要调用 booking 工具生成最终回复。这种“模型决策 工具执行 结果反馈”的循环就是 Agent 的核心机制。2.5 Tools大模型与外部世界的桥梁Tools 是 Agent 的“手和脚”。在 Langchain4j 中你只需要在某个 Bean 的方法上标注 Tool 注解并写清楚方法的作用和参数说明大模型就能在合适的时候调用它。Tool(根据起飞城市、到达城市和日期查询航班列表) public String searchFlights(String origin, String destination, String date) { // 业务逻辑 }关键点在于模型本身不执行这个方法它只负责“决定要不要调用”和“传什么参数”。真正执行逻辑的还是我们的 Java 代码。因此Tool 方法的返回值最好是结构清晰的文本或 JSON方便模型继续推理。3. 项目准备智能航空 Agent 需求分析与架构设计3.1 项目需求假设我们需要为一个航空公司开发一个智能客服 Agent它要能处理以下类型的问题问题类型示例依赖能力航班查询“明天北京到广州的航班有哪些”Tool 调用航班状态“CZ3101 航班现在准点吗”Tool 调用政策问答“退票需要手续费吗”RAG 检索会员权益“金卡会员能免费升舱吗”RAG 检索订票操作“帮我订明天 CA1831 航班的机票”Tool 调用3.2 技术选型JDK 17Spring Boot 3.2Spring AI 2.0Langchain4j通义千问 DashScopeQwen Chat Qwen EmbeddingMilvus 向量数据库Maven 构建REST API 对外提供服务模型可以选择国内模型厂商提供的 OpenAI 兼容接口配置思路完全一致。3.3 整体架构用户请求 → REST Controller → Agent 服务 ↓ Langchain4j AiServices / | \ Tools工具 RAG检索 ChatModel ↓ ↓ ↓ 航班服务接口 Milvus向量库 Qwen Chat ↑ 文档导入/分块/向量化我在项目里没有把 Agent 的编排逻辑写在 Controller 中而是用一个独立的 AgentService 封装方便未来扩展为消息队列消费、定时任务、WebSocket 等多种入口。4. 环境准备与依赖配置4.1 基础环境组件版本说明JDK17 及以上Maven3.8Spring Boot3.2.x 或 3.3.xMilvus2.3本地可用 Docker 运行 standalone 模式模型服务通义千问 DashScope 或任何 OpenAI 兼容 APIMilvus 本地启动可以用 Dockerdocker run -d --name milvus \ -p 19530:19530 \ -p 9091:9091 \ milvusdb/milvus:2.3.11注意如果没有 Milvus 环境可以在开发阶段改用 Langchain4j 的 in-memory 向量存储方便快速联调。4.2 Maven 依赖创建一个 Spring Boot 工程在 pom.xml 中引入核心依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI 基础能力 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-dashscope/artifactId /dependency !-- Langchain4j 核心 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId /dependency !-- Langchain4j DashScope 模型适配 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-dashscope/artifactId /dependency !-- Langchain4j Milvus 向量存储 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-milvus/artifactId /dependency !-- 文档解析 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-document-parser-apache-pdf/artifactId /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-document-parser-tika/artifactId /dependencySpring AI 的 BOM 管理方式dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement注意版本需要根据你实际拉取到的稳定版本调整。如果依赖冲突优先看 Spring Boot 和 Spring AI BOM 的兼容版本。4.3 application.yml 配置在 src/main/resources/application.yml 中配置模型和 Milvusspring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus langchain4j: dashscope: api-key: ${DASHSCOPE_API_KEY} chat-model: model-name: qwen-plus embedding-model: model-name: text-embedding-v3Milvus 配置则以 Bean 方式在代码中完成因为 Langchain4j 的 Milvus 模块提供的是 Builder API而非 Spring Boot 自动配置。4.4 项目结构src/main/java/com/example/airline/ ├── AirlineAgentApplication.java ├── agent/ │ ├── FlightAgent.java │ └── FlightAgentService.java ├── config/ │ ├── Langchain4jConfig.java │ └── MilvusConfig.java ├── controller/ │ └── ChatController.java ├── tools/ │ └── FlightTools.java ├── rag/ │ ├── KnowledgeBaseInitializer.java ├── service/ │ └── FlightService.java └── model/ ├── Flight.java └── BookingRequest.java5. 核心代码实现从 Tool 到 RAG 再到 Agent5.1 航班数据模型我们先定义航班数据这只用一个 Java record 即可package com.example.airline.model; import java.math.BigDecimal; public record Flight( String flightNo, String origin, String destination, String date, String departureTime, String arrivalTime, BigDecimal price, String status ) {}然后写一个 FlightService模拟航班查询真实项目中这里会替换为 RPC 或数据库查询package com.example.airline.service; import com.example.airline.model.Flight; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.List; import java.util.stream.Collectors; Service public class FlightService { public ListFlight searchFlights(String origin, String destination, String date) { // 这里做本地模拟真实项目改为调用航班查询接口 return List.of( new Flight(CA1831, origin, destination, date, 08:00, 10:30, new BigDecimal(1280), 准点), new Flight(CZ3101, origin, destination, date, 09:15, 11:50, new BigDecimal(1560), 延误), new Flight(MU5123, origin, destination, date, 10:20, 13:00, new BigDecimal(980), 准点) ); } public String getFlightStatus(String flightNo) { // 模拟状态 if (CZ3101.equals(flightNo)) { return 航班 flightNo 当前状态延误预计延误 40 分钟; } return 航班 flightNo 当前状态准点; } }5.2 编写 Tools 工具类这一层负责把外部能力暴露给大模型。package com.example.airline.tools; import com.example.airline.model.Flight; import com.example.airline.service.FlightService; import dev.langchain4j.agent.tool.Tool; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.List; Slf4j Component public class FlightTools { private final FlightService flightService; public FlightTools(FlightService flightService) { this.flightService flightService; } Tool(根据出发城市、到达城市和日期查询航班列表返回航班号、起降时间和价格) public String searchFlights(String origin, String destination, String date) { log.info(调用工具 searchFlights: {} - {}, {}, origin, destination, date); ListFlight flights flightService.searchFlights(origin, destination, date); if (flights.isEmpty()) { return 没有找到符合条件的航班; } StringBuilder sb new StringBuilder(查询到以下航班\n); for (Flight f : flights) { sb.append(String.format( %s %s - %s %s 起飞 %s 到达 %s 价格 %s 状态 %s%n, f.flightNo(), f.origin(), f.destination(), f.date(), f.departureTime(), f.arrivalTime(), f.price(), f.status())); } return sb.toString(); } Tool(根据航班号查询航班实时状态) public String getFlightStatus(String flightNo) { log.info(调用工具 getFlightStatus: {}, flightNo); return flightService.getFlightStatus(flightNo); } }这里有一个开发要点Tool 注解的作用是对模型描述“这个工具是干什么的”。描述写得越清晰模型就越知道何时应该调用。参数名也很有讲究必须使用有业务含义的名称比如 origin、destination而不是 a 和 b。5.3 配置 Chat Model 与 Embedding Model在 Langchain4jConfig 中我们使用 DashScope 的 OpenAI 兼容模式接入 qwen 模型package com.example.airline.config; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.dashscope.QwenChatModel; import dev.langchain4j.model.dashscope.QwenEmbeddingModel; import dev.langchain4j.model.embedding.EmbeddingModel; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class Langchain4jConfig { Value(${langchain4j.dashscope.api-key}) private String apiKey; Bean public ChatLanguageModel chatLanguageModel() { return QwenChatModel.builder() .apiKey(apiKey) .modelName(qwen-plus) .build(); } Bean public EmbeddingModel embeddingModel() { return QwenEmbeddingModel.builder() .apiKey(apiKey) .modelName(text-embedding-v3) .build(); } }如果你使用的是 OpenAI 兼容接口可以换成 OpenAiChatModel配置 baseUrl。这里需要特别注意模型 name 不要拼错不同模型厂商支持的模型名差异很大。5.4 配置 Milvus 作为向量存储Milvus 配置的核心是构建一个 EmbeddingStorepackage com.example.airline.config; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.milvus.MilvusEmbeddingStore; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class MilvusConfig { Bean public EmbeddingStore milvusEmbeddingStore() { return MilvusEmbeddingStore.builder() .host(localhost) .port(19530) .collectionName(airline_knowledge) .dimension(1024) .build(); } }dimension 必须与 Embedding 模型的输出向量维度一致。text-embedding-v3 的默认向量维度是 1024如果你的模型输出维度不同需要相应调整。这是初学者最容易踩的坑向量维度不匹配导致写入失败或检索不到。5.5 构建 RAG 知识库RAG 部分要做三件事准备知识文档将文档分块计算向量并存入 Milvus。我们先用一个知识库初始化的 Bean在应用启动时加载文档。为了便于演示我在 resources/knowledge 下放几份 txt 文件内容为航空公司政策// src/main/resources/knowledge/refund-policy.txt 国内航班退票政策起飞前24小时以上申请退票收取5%手续费起飞前2小时至24小时收取10%手续费起飞前2小时以内收取20%手续费。特殊折扣舱位可能收取更高费用以购票时展示规则为准。初始化逻辑package com.example.airline.rag; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.DocumentSplitter; import dev.langchain4j.data.document.loader.FileSystemDocumentLoader; import dev.langchain4j.data.document.splitter.DocumentSplitters; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStore; import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.List; Slf4j Component public class KnowledgeBaseInitializer { private final EmbeddingStore embeddingStore; private final EmbeddingModel embeddingModel; Value(classpath:knowledge/*.txt) private Resource[] resourceArray; public KnowledgeBaseInitializer(EmbeddingStore embeddingStore, EmbeddingModel embeddingModel) { this.embeddingStore embeddingStore; this.embeddingModel embeddingModel; } PostConstruct public void init() throws IOException { for (Resource resource : resourceArray) { // 解析文档 Document document FileSystemDocumentLoader.loadDocument( resource.getFile().toPath()); // 分块每块300字符重叠50字符 DocumentSplitter splitter DocumentSplitters.recursive(300, 50); ListTextSegment segments splitter.split(document); log.info(加载文档 {}共 {} 个片段, resource.getFilename(), segments.size()); // 生成向量并存储 embeddingStore.addAll( embeddingModel.embedAll(segments).content(), segments ); } } }这里有两个点需要说明。第一DocumentSplitter 的 windowSize 和 overlap 直接影响检索质量建议中文场景设置为 200 到 400 之间。第二生产环境中不应该在每次启动都做全量入库可以用版本号或写入时间做增量控制。5.6 创建 Agent 服务现在到了核心环节使用 Langchain4j 的 AiServices 把模型、工具、RAG 组合成一个 Agent。先定义一个接口package com.example.airline.agent; import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.spring.AiService; AiService public interface FlightAgent { SystemMessage( 你是一个航空公司的智能客服助手。 你可以使用工具查询航班、查看航班状态。 如果用户询问退改签、行李、会员等政策问题你可以基于知识库内容回答。 回答要简洁、专业、友好。如果信息不足明确告诉用户需要补充什么。 ) String chat(MemoryId String sessionId, UserMessage String userMessage); }然后创建一个服务类把 FlightTools 和 RAG 检索器注入package com.example.airline.agent; import com.example.airline.tools.FlightTools; import dev.langchain4j.memory.chat.TokenWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.rag.content.retriever.ContentRetriever; import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.service.AiServices; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; Service public class FlightAgentService { private final ChatLanguageModel chatLanguageModel; private final FlightTools flightTools; private ContentRetriever contentRetriever; private FlightAgent flightAgent; public FlightAgentService(ChatLanguageModel chatLanguageModel, FlightTools flightTools, EmbeddingStoreContentRetriever retriever) { this.chatLanguageModel chatLanguageModel; this.flightTools flightTools; this.contentRetriever retriever; } PostConstruct public void init() { this.flightAgent AiServices.builder(FlightAgent.class) .chatLanguageModel(chatLanguageModel) .tools(flightTools) .contentRetriever(contentRetriever) .chatMemory(MessageWindowChatMemory.withMaxMessages(20)) .build(); } public String chat(String sessionId, String userMessage) { return flightAgent.chat(sessionId, userMessage); } }这里用到了几个关键 APIAiServices.builderLangchain4j 的核心入口tools注册工具 Bean模型决策后自动调用contentRetriever把 RAG 检索器挂载到 Agent 上chatMemory保存会话上下文让 Agent 记得聊过什么。EmbeddingStoreContentRetriever 的构建方式package com.example.airline.config; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.rag.content.retriever.ContentRetriever; import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.store.embedding.EmbeddingStore; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class RetrieverConfig { Bean public ContentRetriever contentRetriever(EmbeddingStore embeddingStore, EmbeddingModel embeddingModel) { return EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(3) .minScore(0.6) .build(); } }maxResults 控制检索返回的片段数量minScore 是相似度阈值这两个参数需要根据业务调试。5.7 暴露 REST 接口最后写一个 Controller接收用户请求package com.example.airline.controller; import com.example.airline.agent.FlightAgentService; import org.springframework.web.bind.annotation.*; import java.util.Map; RestController RequestMapping(/api/chat) public class ChatController { private final FlightAgentService flightAgentService; public ChatController(FlightAgentService flightAgentService) { this.flightAgentService flightAgentService; } PostMapping public MapString, String chat(RequestBody ChatRequest request) { String answer flightAgentService.chat(request.sessionId(), request.message()); return Map.of(answer, answer); } public record ChatRequest(String sessionId, String message) {} }这样一个完整的 Agent 就搭好了。6. 运行验证试试 Agent 的真实表现6.1 启动项目确保 Milvus 已启动配置好 DASHSCOPE_API_KEY然后运行mvn spring-boot:run启动日志中可以看到知识库加载的片段数量INFO KnowledgeBaseInitializer : 加载文档 refund-policy.txt共 8 个片段 INFO KnowledgeBaseInitializer : 加载文档 baggage-policy.txt共 6 个片段6.2 测试航班查询工具发送请求curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {sessionId: u-1001, message: 帮我查明天北京到上海的航班}预期回答类似为您查到明天北京到上海的航班 - CA1831 08:00 起飞10:30 到达价格 1280 元状态准点 - CZ3101 09:15 起飞11:50 到达价格 1560 元状态延误 - MU5123 10:20 起飞13:00 到达价格 980 元状态准点这说明 Agent 正确识别了三个参数触发了 searchFlights 工具。6.3 测试航班状态查询curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {sessionId: u-1001, message: CZ3101 现在准点吗}预期回答CZ3101 航班当前状态为延误预计延误 40 分钟。建议您关注航班动态提前规划出行时间。6.4 测试 RAG 政策问答curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {sessionId: u-1001, message: 起飞前 10 小时退票手续费收多少}预期回答根据退票政策起飞前 24 小时以上申请退票收取 5% 手续费。您的情况符合这一规则预计手续费为票价的 5%。具体以购票平台实际展示为准。如果 RAG 检索正常回答会引用知识库中的内容而不会凭空编造。6.5 测试多轮对话和上下文记忆curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {sessionId: u-1001, message: 那刚才查到的便宜航班是哪一班}由于我们的 chatMemory 已经保存了 sessionId 对应的会话信息Agent 能回忆起上一步查询结果并回答是 MU5123。7. 常见问题与排查思路这一节整理我在实际开发过程中遇到的高频问题帮你少踩一些坑。7.1 常见问题汇总问题现象常见原因解决思路模型返回为空content 为 null模型名称错误、参数配置缺失、API 网关返回异常检查模型名、API Key增加日志打印完整请求响应降低 maxTokensAgent 执行超时provider 未响应工具调用次数过多、模型推理时间过长、网络波动调大超时时间减少最大工具调用轮数增加重试机制RAG 检索不到内容向量维度不匹配、collection 不存在、minScore 过高、文档未加载核对维度配置检查 Milvus collection调低 minScore确认加载日志工具调用参数传错Tool 描述不清晰、参数名无业务含义重写工具描述参数改成语义化命名多测几种问题表达Java OOM: insufficient memory本地向量库加载过大、JVM 堆内存不足增大 -Xmx减少测试文档量必要时使用独立 Milvus 服务Lombok 编译告警Java 版本与 Lombok 版本不兼容升级 Lombok 到 1.18.30或改用 Java record 代替Spring AI 连接 DeepSeek 不输出 contentDeepSeek 兼容接口需要显式配置 baseUrl、model、api-key在配置中指定 deepseek-chat 模型及兼容 baseUrl不要用默认 OpenAI 配置7.2 问题排查清单如果你遇到了“模型不输出”的情况按这个顺序排查确认 API Key 是否有权限是否欠费或额度用完确认模型名称是否正确特别是 DashScope 和 OpenAI 模型名差异很大在代码中打印请求体和响应体观察是否存在异常字段检查是否设置了 maxTokens 为 0 或过小检查网络代理是否拦截了请求试试从模型厂商的调试工具直接调用相同参数排除框架问题。7.3 Agent 工具调用失败的排查思路工具调用链路较长问题可能出在多个环节。建议给工具方法加上详细日志观察 Agent 是否调用了工具、传了什么参数、返回了什么结果。一个很实用的技巧在Tool方法入口打印参数出口打印结果摘要这样能快速定位是“模型没调用工具”还是“工具执行出错”。8. 最佳实践与工程建议8.1 提示词设计要明确Agent 的 System Message 应该明确告诉模型你能做什么、不能做什么、什么时候使用工具、什么时候检索知识库。模糊的提示词会让模型做出错误决策。建议把“工具使用边界”写进 System Message。比如只有用户询问航班信息或航班状态时才调用查询工具。 如果不确定优先询问用户补充信息。8.2 工具方法返回结构化数据工具返回值最好是纯文本或 JSON而不是对象。因为模型无法直接理解 Java 对象的内存结构。建议在工具方法内部统一转换为字符串再返回给模型。这样既降低模型解析难度也方便调试。8.3 RAG 质量比模型更重要很多项目 RAG 效果不好不是模型的问题而是知识库处理不到位。注意几点文档分块大小要适中中文 200 到 400 字比较合适分块重叠能避免上下文被切断使用元数据过滤让 Agent 只在相关政策范围内检索定期检查向量库中是否有过期文档。8.4 向量数据库的维度与集合管理向量维度是写入和检索的前提。修改 Embedding 模型后必须重建向量集合。生产环境建议按业务域拆分集合比如 policy_knowledge、operation_manual而不是所有文档放在一个集合里。8.5 超时、重试与熔断Agent 调用链路中有模型 API 和工具调用任何一个环节慢都可能拖垮服务。建议给模型调用设置合理的超时时间对工具调用做异常兜底。生产环境可以引入 Resilience4j 实现重试和熔断。8.6 多轮会话内存管理会话内存不能无限增长。使用 TokenWindowChatMemory 或 MessageWindowChatMemory 时要设置合适的窗口大小避免上下文超长导致费用暴涨。同时对于不同会话用 MemoryId 区分避免串话。8.7 日志与可观测性AI 应用的日志比传统业务更关键因为模型的输出不可控。建议在关键节点打印用户原始输入模型最终结果工具调用记录RAG 检索到的片段和相似度分数。这能帮助你回溯每次回答是否合理。8.8 安全与合规企业级 Agent 面临越狱攻击和提示词注入风险。建议对用户输入做敏感词过滤在 System Message 中限制模型不回答无关问题工具调用范围做白名单控制涉及用户隐私的会话数据要加密存储对 Agent 的行为日志保留审计记录。9. 总结与下一步学习建议本文从一个具体的智能航空 Agent 项目出发完整走了一遍 Spring AI 2.0 和 Langchain4j 的集成开发流程。核心内容包括理解 Spring AI 和 Langchain4j 的定位差别掌握 Spring Boot Langchain4j DashScope Milvus 的工程搭建方法用 Tool 暴露航班查询、状态查询能力用 RAG 构建航空公司政策知识库通过 AiServices 组装出具备工具调用、知识检索、多轮记忆的完整 Agent。如果你顺利跑通了上面的项目下一步可以从几个方向继续深入把 Tool 调用扩展到真实的预订、改签、支付等核心业务流程引入更复杂的 Agent 编排比如多步骤任务 Planner-Executor 模式为 RAG 增加混合检索与重排序提升知识库召回准确率将 Spring AI 的流式输出接入前端提升交互体验用 VectorStore 元数据过滤做多租户知识隔离。AI 应用开发最重要的是把“模型能力”和“业务能力”正确连接起来。本文里的 FlightTools 只是开端你可以根据自己的业务场景把无限多的 Java 服务暴露给大模型使用。动手把这些代码跑起来比看十遍教程都管用。如果这篇文章对你有帮助欢迎收藏备用。有疑问也可以在评论区留言我会根据大家反馈继续整理 Spring AI 与 Langchain4j 的进阶专题。
返回列表