SpringAI 2.0与Langchain4j构建Java智能航空Agent实战

发布时间:2026/10/24 22:54:45

SpringAI 2.0与Langchain4j构建Java智能航空Agent实战 之前帮团队落地一个 Java 后端智能问答项目时最头疼的不是模型接口接入而是把 RAG、Tools 调用、Agent 编排这些概念真正落到 Spring Boot 工程里。网上资料大多以 Python 为主Java 生态里的完整案例少之又少尤其是一套能直接跑起来的企业级 Agent 项目翻遍社区也找不到几个。本文基于 SpringAI 2.0 与 Langchain4j 整理出一套智能航空项目全流程教程覆盖 Tools、RAG、Agent 三大核心模块附带完整代码与高频问题排查方案适合有 Java 基础、正在做 AI 应用落地的开发者参考。1. 智能航空项目背景与核心概念1.1 为什么 Java 项目也要做 Agent过去一年大模型应用爆发Python 生态的 LangChain、LlamaIndex 等框架确实丰富但企业级后端系统里 Java 仍然占据主导地位。很多航空、金融、政务类项目不能简单地用 Python 重写也不能在 Java 服务里直接拉起一个 Python 进程去跑 AI 链路最合理的方案是在 Java 技术栈内实现大模型接入、知识库检索、工具调用和智能体编排。SpringAI 2.0 和 Langchain4j 的出现让这件事变得可行。SpringAI 是 Spring 官方推出的 AI 框架负责统一对接大模型、向量数据库、Embedding 模型等底层能力Langchain4j 则是社区中非常成熟的 Java LLM 编排框架提供了类似 LangChain 的链式调用、AiServices、RAG、Tools 等高级能力。把这两个框架结合起来可以在 Spring Boot 工程中构建真正的企业级 Agent用户问一句“帮我查一下明天北京到上海的航班”Agent 能自动调用航班查询工具返回结构化结果同时结合航空政策知识库给出退改签建议。整个过程是 Java 代码在大模型帮助下自主完成任务而不是简单地一问一答。1.2 四个关键词必须分清SpringAI、Langchain4j、RAG、Agent先理清概念后面写代码才不容易混乱。Langchain4j 是一个 Java 版本的 LLM 应用框架它的设计目标是把大模型接入、提示词模板、输出解析、记忆管理、RAG 检索、工具调用等功能封装成可组合的组件。如果你用过 Python LangChainLangchain4j 的很多概念是类似的比如 ChatLanguageModel、EmbeddingModel、ContentRetriever、Tool 等。SpringAI 是 Spring 官方推出的 AI 支持库它更强调与 Spring Boot 生态的整合提供了类似 Spring Data 的抽象层。SpringAI 2.0 版本开始支持更完整的 ChatModel、EmbeddingModel、VectorStore 抽象并且内置了 OpenAI、Ollama、Qwen、DeepSeek 等多种模型的适配。对于已经使用 Spring Boot 的团队来说SpringAI 的学习成本更低但它在 Agent 编排方面相比 Langchain4j 轻量一些。实际项目中两者可以协同使用SpringAI 负责模型与向量库的标准接入Langchain4j 负责 Agent 编排和工具调用。RAGRetrieval-Augmented Generation是指检索增强生成。大模型的知识截止时间有限也无法知道企业内部数据。RAG 的思路是先把文档切分成片段经过 Embedding 模型转换成向量存入向量数据库用户提问时先从向量库中检索相似的文档片段再把这些片段和用户问题一起交给大模型让模型基于检索到的内容生成答案。这样回答就有依据而不是凭空编造。Agent 可以理解为一种能够自主调用工具、规划任务的智能体。Agent 不满足于“问答”它可以判断当前问题需要调用哪个工具比如查航班、查政策、查天气它也可以根据用户意图拆分任务然后循环执行“思考—调用—观察结果—再思考”的过程直到完成任务。在 Langchain4j 中AiServices 提供了便捷的 Agent 构建方式。1.3 航空场景为什么适合用 Agent RAG Tools航空业务天然适合演示这一整套技术栈因为它的核心痛点很典型。第一政策文档多。航空公司有大量旅客须知、行李政策、退改签规则、会员权益文档这些内容更新频繁用户咨询量大。这类知识非常适合做 RAG 知识库问答。第二实时数据查询需求强。航班时刻、余票、价格、延误状态都是实时数据大模型不能凭空生成必须通过工具调用查询接口。Tools 机制正好解决这个问题。第三用户问题往往涉及多步操作。用户说“帮我订明天早上从北京出发去上海的最早航班顺便问一下能带多少行李”。这句话既需要查航班又需要查行李政策还可能需要判断是否适合下单。Agent 可以拆分这些意图分别调用不同工具最后汇总回复。所以本文的智能航空项目会包含三个核心能力航班查询工具通过 Tools 机制让大模型调用 Java 方法查询航班。航空政策知识库问答通过 RAG 流程让大模型基于文档回答行李、退改签等问题。Agent 编排通过 Langchain4j 把工具调用 知识库检索统一编排实现多轮对话和自主决策。2. 技术选型与环境准备2.1 环境依赖清单在开始写代码之前先确认环境。下面的版本是当前示例使用的实际项目请根据你的环境调整重点演示的是思路与完整链路。组件版本建议说明JDK17Spring Boot 3.x 要求 JDK 17 起Maven3.8构建工具Spring Boot3.3.x 或 3.4.x以 Spring AI 2.0 兼容版本为准Spring AI2.0.x官方 AI 框架Langchain4j1.0.x 或最新稳定版Java LLM 编排框架大模型 APIDeepSeek / Qwen / OpenAI 兼容可以使用 OpenAI 兼容协议接入Embedding 模型Qwen Embedding 或 OpenAI Embedding用于 RAG 向量化向量数据库Milvus / Redis / Elasticsearch生产推荐 Milvus版本说明Spring AI 与 Langchain4j 版本迭代很快不同版本之间的 API 可能有差异。本文代码以较新的稳定版本写法为例如果编译报错优先检查依赖版本并参考官方迁移文档。2.2 Maven 依赖配置如果使用 Spring Boot SpringAI核心依赖如下。这里保留一个相对通用的配置实际版本号请以 Maven 中央仓库和官方 BOM 为准。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.5/version relativePath/ /parent properties java.version17/java.version spring-ai.version2.0.0/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- SpringAI 核心 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency !-- 如果使用 DeepSeek 等 OpenAI 兼容接口同样走 openai starter -- !-- 向量数据库 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-milvus/artifactId /dependency !-- Langchain4j 核心 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version1.0.0/version /dependency !-- Langchain4j OpenAI 兼容模块 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version1.0.0/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这里有一个容易踩坑的点SpringAI 与 Langchain4j 都会引入部分相同语义的包比如 ChatModel、EmbeddingModel使用时要避免把两者的 API 混用。建议团队内明确一个原则模型接入以 SpringAI 为主Agent 编排与 RAG 链路以 Langchain4j 为主不要在同一个类里混用两套 API。2.3 配置文件示例配置文件里需要包含模型接入、向量库连接等关键信息。spring: application: name: airline-agent ai: openai: base-url: https://api.deepseek.com/v1 api-key: ${DEEPSEEK_API_KEY} chat: options: model: deepseek-chat temperature: 0.7 embedding: options: model: text-embedding-v2 milvus: client: host: localhost port: 19530 database: airline_db collection: policy_docs embedding-dimension: 1024 index-type: IVF_FLAT metric-type: COSINE langchain4j: open-ai: chat-model: base-url: ${spring.ai.openai.base-url} api-key: ${spring.ai.openai.api-key} model-name: ${spring.ai.openai.chat.options.model} temperature: 0.7配置项中比较关键的是embedding-dimension它必须与 Embedding 模型输出向量的维度一致。不同 Embedding 模型输出维度不同OpenAI 的 text-embedding-3-small 是 1536 维Qwen 的 text-embedding-v2 通常是 1024 维。如果向量维度不一致写入 Milvus 时会直接报错。3. Agent、Tools、RAG 核心原理拆解3.1 RAG 的完整处理链路RAG 不是单一功能而是一整条数据处理流水线。完整的 RAG 流程包含以下步骤第一步文档加载。把 PDF、Word、TXT、Markdown 等格式的文档读入系统。Java 中常见方案是使用 Tika 或 PdfBox 解析Langchain4j 也内置了多种 DocumentLoader。第二步文档切分。大模型上下文窗口有限不可能把整本手册塞进去需要把文档按固定长度或语义边界切成片段。常见切分参数是 chunkSize每个片段的大小和 chunkOverlap相邻片段重叠长度一般 chunkSize 取 500 到 1000 个 token 比较合适重叠 50 到 100 个 token 用于保持上下文衔接。第三步Embedding 向量化。把每个文本片段通过 Embedding 模型转成向量。Embedding 模型的作用是让语义相近的文本在向量空间中的距离更近。第四步向量存储。将文本片段和对应向量写入向量数据库。Milvus 是当前企业级项目常用的向量库支持百万级向量检索Redis 也可以作为轻量级向量存储方案适合数据量较小的场景。第五步检索召回。用户提问时先把用户问题转成向量然后在向量库中执行相似度检索返回最相关的 topK 个片段。第六步重排与增强。检索出来的片段不一定按相关性排序最优可以接入 Rerank 模型做二次排序。将排序后的片段拼入提示词形成增强后的上下文。第七步生成回答。把增强后的提示词交给大模型模型基于检索内容生成答案并在回答中注明信息来源。对于航空政策问答RAG 链路最关键的优化点在于文档切分和检索结果排序。如果切分不合理一段完整政策被拦腰截断检索召回就会出现信息缺失如果检索结果排序不准大模型可能会引用不相关内容。3.2 Tools 机制的本质Tools 机制的本质是让大模型在对话过程中调用你预先定义好的 Java 方法。大模型本身不直接执行代码而是根据用户问题和工具描述生成一个 JSON 格式的调用请求框架把这个请求转换成 Java 方法调用再把方法返回值交回给大模型让模型基于真实数据继续回答。Langchain4j 中实现一个 Tool 非常简单核心思路是把工具类注册成一个 Bean然后在工具类上使用Tool注解描述每个方法。Component public class FlightTools { Tool(根据出发城市、到达城市和日期查询航班列表) public ListFlightInfo searchFlights(String departureCity, String arrivalCity, String date) { // 调用真实的航班查询接口或查询数据库 return flightService.search(departureCity, arrivalCity, date); } }写工具时要注意描述准确。大模型是根据方法名和方法描述来判断何时调用这个工具的描述写得模糊会导致模型误调用。例如searchFlights方法如果只写“查询航班”模型可能不知道该传什么参数明确写出“根据出发城市、到达城市和日期查询航班列表”之后模型就能准确抽取参数。3.3 Agent 是如何编排任务和调用工具的Agent 的核心不是单次问答而是多轮推理。Langchain4j 中 Agent 底层采用 ReAct 模式Reason Act模型先根据用户问题推理下一步应该做什么决定调用哪个工具工具返回结果后模型继续观察结果并决定下一步动作直到模型认为已获得足够信息才生成最终回复。在一个智能航空 Agent 中典型执行流程如下用户提问“明天从北京去上海出差能带多少行李帮我查一下航班”时Agent 会先拆解用户意图需要查询航班也需要了解行李政策。Agent 先调用航班查询工具拿到北京到上海明天的航班列表再从 RAG 知识库中检索行李政策相关文档最后把航班信息与行李政策整合到回答中给出“推荐乘坐 MU5101经济舱可免费托运 20kg 行李”这类结果。Agent 与普通问答的最大区别在于它具备“主动性”。普通问答只能做单轮生成Agent 可以循环调用工具直到任务完成。3.4 Spring AI 2.0 与 Langchain4j 的定位差异在实际项目中经常有团队纠结到底选 SpringAI 还是 Langchain4j。这里给出一个比较实际的分工建议。SpringAI 2.0 的优势在于官方生态和 Spring Boot 原生整合尤其是对 OpenAI 协议、向量数据库、Advisor 等模块的抽象比较优雅。如果项目希望尽量减少第三方依赖优先选 SpringAI。Langchain4j 的优势在于 Agent 编排的完整度更高AiServices 可以直接声明一个接口让框架自动生成实现Tool 注解更成熟RAG 相关的 DocumentSplitter、EmbeddingStoreIngestor 等组件开箱即用。本文采用“SpringAI 做模型底座Langchain4j 做 Agent 编排”的组合方案这也是目前 Java AI 项目里比较流行的架构。4. 智能航空 Agent 项目实战4.1 项目需求与功能拆分为了让项目完整可落地我们设定一个智能航空助手需要支持以下功能用户询问某条航线的航班时刻、航班号、起降时间。用户询问行李托运政策、退改签规则。用户组合提问例如“明天从北京到深圳最早的一班航班是什么能带登机箱吗”。Agent 需要根据用户问题自动选择调用工具或知识库或两者结合。项目结构设计如下airline-agent/ ├── pom.xml ├── src/main/java/com/example/airline/ │ ├── AirlineAgentApplication.java │ ├── config/ │ │ ├── LLMConfig.java │ │ └── RAGConfig.java │ ├── controller/ │ │ └── AgentController.java │ ├── service/ │ │ ├── FlightService.java │ │ ├── PolicyRagService.java │ │ └── AirlineAgentService.java │ ├── tools/ │ │ └── FlightTools.java │ ├── model/ │ │ ├── FlightInfo.java │ │ └── AgentRequest.java │ └── repository/ │ └── FlightRepository.java4.2 航班数据模型与航班查询服务航班查询是 Agent 的工具能力之一。为了演示方便我们使用内存数据模拟航班查询服务实际项目中可以替换为数据库或第三方接口。// 文件路径src/main/java/com/example/airline/model/FlightInfo.java Data Builder NoArgsConstructor AllArgsConstructor public class FlightInfo { private String flightNo; private String airline; private String departureCity; private String arrivalCity; private String departureTime; private String arrivalTime; private String status; }// 文件路径src/main/java/com/example/airline/service/FlightService.java Service public class FlightService { private final ListFlightInfo flights new ArrayList(); public FlightService() { flights.add(FlightInfo.builder() .flightNo(MU5101) .airline(东方航空) .departureCity(北京) .arrivalCity(上海) .departureTime(08:00) .arrivalTime(10:15) .status(准点) .build()); flights.add(FlightInfo.builder() .flightNo(CA1831) .airline(中国国航) .departureCity(北京) .arrivalCity(上海) .departureTime(09:30) .arrivalTime(11:45) .status(准点) .build()); flights.add(FlightInfo.builder() .flightNo(CZ3151) .airline(南方航空) .departureCity(北京) .arrivalCity(深圳) .departureTime(07:30) .arrivalTime(11:00) .status(延误) .build()); } public ListFlightInfo searchFlights(String departureCity, String arrivalCity) { return flights.stream() .filter(f - f.getDepartureCity().contains(departureCity)) .filter(f - f.getArrivalCity().contains(arrivalCity)) .collect(Collectors.toList()); } public OptionalFlightInfo findEarliestFlight(String departureCity, String arrivalCity) { return searchFlights(departureCity, arrivalCity) .stream() .min(Comparator.comparing(FlightInfo::getDepartureTime)); } }FlightService 提供两个方法查询航班列表和查询最早航班。这两个方法会作为 Agent 的工具能力暴露给大模型。4.3 航班查询工具注册// 文件路径src/main/java/com/example/airline/tools/FlightTools.java Component RequiredArgsConstructor public class FlightTools { private final FlightService flightService; Tool(根据出发城市和到达城市查询可用航班列表) public ListFlightInfo queryFlights(String departureCity, String arrivalCity) { return flightService.searchFlights(departureCity, arrivalCity); } Tool(根据出发城市和到达城市查询最早的一班航班) public FlightInfo queryEarliestFlight(String departureCity, String arrivalCity) { return flightService.findEarliestFlight(departureCity, arrivalCity) .orElse(null); } }queryFlights和queryEarliestFlight分别对应航班查询和最早航班查询场景。通过Tool注解Langchain4j 会在构建 Agent 时自动扫描这两个方法并把方法描述注册到模型的工具列表中。4.4 RAG 知识库服务航空政策知识库需要先把文档写入向量库。这里以简化方式演示 RAG 服务的构建过程。// 文件路径src/main/java/com/example/airline/service/PolicyRagService.java Service RequiredArgsConstructor public class PolicyRagService { private final EmbeddingModel embeddingModel; private final VectorStore vectorStore; public void ingestPolicyDocument(String documentId, String content) { Document document Document.builder() .id(documentId) .text(content) .build(); // 切分文档 DocumentSplitter splitter DocumentSplitters.recursive(600, 100); ListTextSegment segments splitter.split(document); // 向量化并存储 embeddingModel.embed(segments); vectorStore.add(segments); } public ListTextSegment searchPolicy(String query, int topK) { return vectorStore.search(query, topK); } }这里解释一下几个核心组件的作用。Document是文档对象text是文档内容DocumentSplitter负责文档切分recursive(600, 100)表示每个片段约 600 字符重叠 100 字符EmbeddingModel将每个片段转成向量VectorStore把向量和原文写入 Milvus。实际航空项目中政策文档通常来自 PDF 或 Word可以通过 Tika 解析后传入。4.5 知识库文档初始化为了让项目启动后能够直接测试 RAG 效果需要在应用启动时初始化一部分政策文档。这里使用ApplicationRunner在服务启动时向向量库写入演示数据。// 文件路径src/main/java/com/example/airline/config/RAGDataInitializer.java Component RequiredArgsConstructor public class RAGDataInitializer implements ApplicationRunner { private final PolicyRagService policyRagService; Override public void run(ApplicationArguments args) { policyRagService.ingestPolicyDocument( policy_001, 国内航班经济舱旅客免费托运额度为20公斤公务舱为30公斤头等舱为40公斤。超出部分按每公斤经济舱全价票的1.5%计算。 ); policyRagService.ingestPolicyDocument( policy_002, 退票规则航班起飞前2小时以上申请退票收取票面价格10%退票手续费起飞前2小时以内申请退票收取票面价格30%退票手续费。 ); policyRagService.ingestPolicyDocument( policy_003, 登机行李尺寸不得超过20*40*55厘米重量不得超过7公斤。超出规定的行李必须办理托运。 ); } }在实际项目中这批文档应该由运维或运营团队定期更新而不是写死在代码里。这里只是演示链路。4.6 Agent 编排核心代码Agent 编排是整个项目最核心的部分。Langchain4j 的AiServices提供了一种声明式的 Agent 构建方式你定义一个接口框架自动生成实现。// 文件路径src/main/java/com/example/airline/service/AirlineAssistant.java public interface AirlineAssistant { SystemMessage( 你是一个专业的航空智能助手。你可以查询航班信息也可以通过知识库回答航空政策问题。 回答时请使用中文语言风格专业、简洁。如果用户询问的是实时航班信息必须调用工具获取不要编造航班数据。 如果用户询问的是行李政策、退改签规则等知识库内容请基于检索到的知识回答并注明该内容根据航空公司政策整理。 ) String chat(String userMessage); }// 文件路径src/main/java/com/example/airline/service/AirlineAgentService.java Service RequiredArgsConstructor public class AirlineAgentService { private final ChatLanguageModel chatLanguageModel; private final FlightTools flightTools; private final PolicyRagService policyRagService; public String chat(String message) { // 构建 Agent AirlineAssistant assistant AiServices.builder(AirlineAssistant.class) .chatLanguageModel(chatLanguageModel) .tools(flightTools) .retriever(policyRagService::searchPolicy) .build(); // 执行对话 return assistant.chat(message); } }这段代码有三个关键点第一AirlineAssistant接口上使用SystemMessage定义了系统提示词。系统提示词的作用是约束 Agent 的角色和回答规则。这里特别强调了“不要编造航班数据”实际项目中这句约束非常重要因为大模型没有实时航班数据不约束就会出现幻觉。第二AiServices.builder()是 Langchain4j 的核心 API。通过.tools(flightTools)注册工具通过.retriever()注册 RAG 检索器。这样 Agent 在回答时既可以使用工具也可以检索知识库。第三assistant.chat(message)是 Agent 的入口。整个 ReAct 循环发生在内部业务代码不需要关心模型到底调了几次工具。4.7 Controller 控制器最后写一个简单的 REST 接口用于测试。// 文件路径src/main/java/com/example/airline/controller/AgentController.java RestController RequestMapping(/agent) RequiredArgsConstructor public class AgentController { private final AirlineAgentService airlineAgentService; PostMapping(/chat) public ResponseEntityMapString, String chat(RequestBody AgentRequest request) { String answer airlineAgentService.chat(request.getMessage()); return ResponseEntity.ok(Map.of(answer, answer)); } }// 文件路径src/main/java/com/example/airline/model/AgentRequest.java Data public class AgentRequest { private String message; }4.8 运行与验证启动 Spring Boot 应用后使用 curl 测试curl -X POST http://localhost:8080/agent/chat \ -H Content-Type: application/json \ -d {message: 明天从北京去上海出差最早的一班航班是什么}预期返回结果中会包含 Agent 调用工具的痕迹最终回答类似为您查询到明天北京到上海最早的航班是 MU510108:00 起飞10:15 到达由东方航空执飞当前状态为准点。再测试 RAG 链路curl -X POST http://localhost:8080/agent/chat \ -H Content-Type: application/json \ -d {message: 经济舱可以免费托运多少公斤行李}预期回答会基于向量库中的 policy_001 文档生成根据航空公司政策国内航班经济舱旅客免费托运额度为 20 公斤。超出部分按每公斤经济舱全价票的 1.5% 计算。如果 Agent 回答正确说明 Tools 调用和 RAG 检索链路都已打通。5. 常见问题与排查思路5.1 SpringAI 连接 DeepSeek 不输出 content很多开发者在用 SpringAI 接入 DeepSeek 时遇到请求成功但 content 为空的问题。这个问题的常见原因有三个第一DeepSeek 采用了 OpenAI 兼容协议但部分参数不完全兼容。比如stream默认行为和max_tokens参数。遇到 content 为空时建议先关闭流式输出测试确认是非流式场景是否正常。第二模型名称配置错误。Spring AI 中配置的是model-nameDeepSeek 可用模型是deepseek-chat和deepseek-reasoner。如果配置了不存在的模型名接口虽然可能返回成功但 content 为空或报错。第三API Key 或 base-url 配置有误。检查base-url是否包含/v1路径。通常需要配置为https://api.deepseek.com/v1漏掉会导致 404 或空响应。排查建议先用 curl 直接调 DeepSeek API确认模型正常返回再检查 SpringAI 配置映射是否正确最后再排查代码中是否对输出做了错误处理。5.2 Langchain4j 向量维度不一致导致写入 Milvus 失败如果使用 Qwen Embedding 生成向量并存储到 Milvus出现维度不一致的报错通常是embedding-dimension配置错误。Qwen 的 text-embedding-v2 默认输出 1024 维OpenAI 的 text-embedding-3-small 输出 1536 维。Milvus 创建 Collection 时指定的维度必须与 Embedding 模型的实际维度一致。修改方式是在配置文件中调整embedding-dimension并注意如果 Collection 已经存在Milvus 不允许直接修改维度需要删除重建或新建 Collection。5.3 Agent 执行超时或 no responseLangchain4j Agent 调用工具后超时通常出现在工具方法执行时间过长或者模型推理次数过多。排查思路先确认工具方法内部是否有网络请求超时再确认 Agent 的 maxIterations 是否设置过小导致模型在几次工具调用后还没有生成最终回答最后检查模型服务是否本身过慢比如 DeepSeek 在高负载时推理时间较长可以适当增加 HTTP 客户端超时时间。示例配置调整spring: ai: openai: client: connect-timeout: 30s read-timeout: 60s5.4 Lombok 编译报错报错信息类似java: You arent using a compiler supported by lombok, so lombok will not work通常是因为 JDK 版本过高或 Lombok 版本过旧。解决方案是先升级 Lombok 到最新版本再确认 Maven 编译器插件版本与 JDK 匹配。Spring Boot 3.x 项目建议使用 Lombok 1.18.30 以上版本。如果项目中使用 Java 21需要特别注意 Lombok 对 JDK 21 的支持情况必要时升级到最新版。5.5 RAG 检索结果不准确RAG 回答质量差多数不是模型问题而是检索链路问题。可以从以下几个方面排查文档切分是否合理。切分过大导致上下文不聚焦切分过小导致信息不完整。Embedding 模型是否适合当前领域。通用 Embedding 模型对航空专业术语的理解能力有限可以考虑垂直领域微调模型。是否使用重排。只依赖向量相似度排序前几个结果可能不是最相关的建议接入 Rerank 模型。检索 topK 数量。topK 太小可能漏掉关键内容topK 太大可能引入噪声。一般建议取 3 到 5。5.6 常见问题速查表问题现象常见原因解决思路SpringAI 请求 DeepSeek 返回空 content流式/非流式配置不一致、模型名错误先 curl 验证模型再检查配置Milvus 写入报维度错误Embedding 模型维度与 Collection 维度不一致统一下游模型与配置维度Agent 调用工具后不返回工具方法耗时过长或 maxIterations 太小检查工具网络耗时调整迭代次数Lombok 编译失败JDK 与 Lombok 版本不匹配升级 LombokRAG 回答答非所问切分不合理、缺少重排优化切分参数、接入 Rerank内存溢出JVM 堆内存不足调整 JVM 参数并检查大文档加载逻辑内存溢出问题在 RAG 项目中比较常见。如果你在加载大 PDF 时遇到java.lang.OutOfMemoryError: Insufficient memory通常是文档解析时一次性把整个文件读入内存。建议分批读取文档并适当增加 JVM 堆内存例如java -Xms512m -Xmx2g -jar airline-agent.jar6. 最佳实践与工程建议6.1 配置隔离与环境管理AI 项目最容易出问题的是配置管理。API Key、模型名称、底座地址在不同环境通常不同建议区分本地、测试、生产环境配置避免把真实 API Key 提交到代码仓库。推荐使用环境变量或配置中心管理敏感配置spring: ai: openai: api-key: ${DEEPSEEK_API_KEY}实际生产环境建议使用 Apollo、Nacos 等配置中心统一管理模型参数支持动态调整 temperature、topK 等参数无需重启服务。6.2 工具方法的防御式编程Tools 暴露给大模型后模型可能传入不存在的城市名、非法日期等参数。工具方法必须对参数做校验和兜底不能因为一个脏参数导致整个 Agent 崩溃。Tool(根据出发城市和到达城市查询可用航班列表) public ListFlightInfo queryFlights(String departureCity, String arrivalCity) { if (departureCity null || arrivalCity null) { return Collections.emptyList(); } return flightService.searchFlights(departureCity.trim(), arrivalCity.trim()); }这里对空参数做了防御避免后续代码出现空指针。生产项目中还可以加参数长度限制、城市名校验等逻辑。6.3 日志与链路追踪Agent 项目比普通 CRUD 项目复杂得多一次用户请求背后可能有多轮模型调用和工具调用。如果没有日志追踪定位问题会非常困难。建议至少记录以下信息用户原始问题。模型每次推理的输入输出。工具方法名和入参出参。RAG 检索到的文档片段 ID。Agent 最终回答。Langchain4j 中可以通过监听器或拦截器实现日志记录。示例代码如下// 伪代码演示监听器思路 ChatModelListener listener new ChatModelListener() { Override public void onRequest(ChatModelRequestContext context) { log.info(Model request: {}, context.request()); } Override public void onResponse(ChatModelResponseContext context) { log.info(Model response: {}, context.response()); } };6.4 生产环境安全边界在真实企业项目中Agent 可能会调用查询订单、改签、退票等敏感操作工具。这时必须增加权限校验和人工确认环节。安全建议只读操作可以自动执行例如查航班、查政策。写操作必须经过二次确认例如退票、改签Agent 只能生成操作建议不能直接调用工单系统。工具方法内部要做身份校验从登录上下文获取用户信息不要相信模型传入的用户 ID。对模型输出做敏感信息过滤防止提示词注入导致的信息泄露。6.5 性能优化建议Agent 项目性能瓶颈通常不在模型本身而在工具调用和向量检索。工具调用层面建议给外部接口设置超时时间和缓存。同一航线的查询结果在几分钟内基本不变可以加本地缓存减少对外部系统的压力。向量检索层面Milvus 索引类型会影响查询速度和召回精度。数据量较小时使用 FLAT 暴力检索精度最高数据量达到百万级时切换 IVF_FLAT 或 HNSW提升查询性能。实际项目中可以在建索引时做性能压测选择最合适的索引类型。RAG 链路还可以增加多路召回机制既走向量检索也走全文检索如 Elasticsearch 的 BM25最后用 Rerank 模型融合排序。混合检索的效果通常优于单路向量检索这也是 Langchain4j 中支持的做法。7. 总结与下一步学习建议这篇教程从零到一构建了一个完整的智能航空 Agent 项目覆盖了 SpringAI 2.0 模型接入、Langchain4j 工具调用、RAG 知识库问答、Agent 自动编排四大核心模块。对照项目可以直观地看到Tools 让大模型具备了实时查询航班的能力RAG 让大模型能够回答企业内部政策文档中的问题Agent 则把两者统一编排起来实现复杂任务的自主完成。对于刚接触 Java AI 项目的开发者建议先动手把本文的项目跑通再逐步替换成自己的业务场景。替换时重点关注三个地方一是模型的接入方式和配置参数二是工具方法的设计和描述三是知识库文档的切分与检索策略。这三块质量决定了 Agent 在真实业务中的表现上限。下一步可以继续深入的方向有RAG 混合检索与重排优化、Agent 多工具复杂任务规划、SpringAI 官方 Advisor 机制、生产环境模型调用链路监控以及 Agent 结果评估体系建设。建议在实际项目中使用小流量灰度上线持续积累 Agent 回答失败案例再反向优化提示词、工具描述和检索链路。如果你在搭建过程中遇到问题欢迎把报错信息发在评论区一起讨论。
延伸阅读

更多相关文章

2026/10/24 4:25:32

AI写文献综述总编参考文献?我把这几款工具捋了一遍

每年开题季,我都会收到同一种求助:“文献综述写不出来,用 AI 吧,结果生成的参考文献一查全是假的,导师脸都绿了。” 这两年我前前后后用过二十多款 AI 学术工具,从通用大模型到智能体平台再到垂直学术产品&…

2026/10/23 10:22:19

金三银四前端社招面试指南:从基础考点到系统设计的实战策略

金三银四的社招面试,和前两年比真的变了不少。一个很直观的感受是:面试官不再满足于“你知道这个API怎么用”,而是会沿着你的回答一路追问到底层的设计取舍、边界条件和真实场景下的权衡。尤其是AI辅助开发工具普及之后,八股文的比…

2026/10/23 14:14:23

Android校招核心考点全解析:从Handler到Binder的进阶之路

1. 一场校招笔试背后的Android技术全景1.1 为什么“第三场”值得认真对待爱奇艺2018秋季校招Android工程师(第三场),这个标题对很多当年投过简历的同学来说,可能只是一封普通的笔试通知。但如果把“第三场”这三个字单独拎出来看&…

2026/10/24 22:52:13

电脑多开软件选哪个 电脑多开软件推荐

很多人在寻找电脑多开软件时,容易混淆本地应用分身和多设备远程管控两类产品。电脑多开软件如果只局限单台电脑内部分开程序,很难满足批量管理多终端的需求,无界趣连2.0以远程互联方式,实现多设备并行管控,适配批量巡检…

2026/10/24 22:52:12

手机被远程操控怎么办 怎么远程协助手机更安全

手机被远程操控怎么办,很多人担心远程工具会带来隐私泄露风险,不敢轻易使用手机互控功能。手机被远程操控怎么办,挑选正规远程软件,守住授权关卡,无界趣连2.0在保障远程操控能力的同时,搭建多层防护&#x…

2026/10/24 22:52:12

PRP与HSR:工业以太网毫秒级无缝冗余协议解析

简介:本资源为IEC 62439-3:2016第3版正式英文原版标准全文,面向工业自动化、电力系统通信、轨道交通信号及高可靠性网络设计领域的工程师与系统架构师,解决关键场景下网络单点故障导致服务中断的核心痛点。标准完整定义了Parallel Redundancy…

2026/10/24 22:52:12

分段弹道建模:四段拼接与连续性约束下的动态轨迹设计方法

简介:第八届MathorCup数学建模竞赛优秀论文C3289.pdf聚焦陆基导弹打击航母的数学建模与算法设计,面向数学建模竞赛备赛者、军事运筹方向学习者及相关领域师生。资源包内共1个PDF文件,大小511KB,内容完整,便于直接阅读与…

2026/10/24 22:52:12

S1数据转发原理与End Marker机制详解

简介:本资源是一份聚焦4G LTE网络S1接口数据转发机制的深度测试分析文档,面向通信工程专业学生、移动网络优化工程师及协议栈开发人员,旨在系统解析切换过程中用户面数据连续性保障的关键技术与验证方法。文档完整覆盖数据流向逻辑、TEID/Seq…

2026/10/24 22:47:11

阶梯碳价与电制氢协同优化:MATLAB/YALMIP可商用MILP建模

简介:本资源是一份面向能源系统优化研究者与工程师的MATLAB实战教程,聚焦阶梯型碳交易机制与电制氢技术融合下的综合能源系统24小时优化调度问题。内容涵盖系统建模(传统机组、风电、电解槽、储氢罐、燃料电池五单元耦合)、分段线…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑