news 2026/8/31 20:33:11

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 项目,远没有这么简单。举个例子,用户问“明天从北京飞深圳,帮我查一下早上的航班,顺便看看有没有特价票”,一个成熟系统需要做这些事:

  1. 理解用户意图,识别出“北京”“深圳”“明天”“早上”这几个关键信息;
  2. 调用航班查询接口,拿到真实航班数据;
  3. 查询特价票政策和退改签规则;
  4. 把航班列表和注意事项整理成一段自然语言回复。

这些能力,单靠一个大模型是做不到的。大模型只负责“理解与生成”,它不知道你的航班数据存在哪,也不会自动去查数据库。于是,我们需要一套工程框架,把模型能力、知识库、业务工具、任务编排组合起来,这就是 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 Langchain4j:Java 界的 AI 编排框架

Langchain4j 的设计目标,就是补上 Java 生态缺失的那层编排能力。它对标 LangChain,但用纯 Java 实现。它提供的核心能力包括:

  • ChatLanguageModel 与 EmbeddingModel 的抽象;
  • AiServices,可以像定义接口一样定义 Agent;
  • @Tool 注解,把任意 Java 方法暴露给大模型调用;
  • ContentRetriever、ContentRetriever,用于 RAG 检索;
  • Memory 接口,管理多轮对话;
  • 支持 OpenAI、DashScope、通义千问、Ollama、DeepSeek 等模型。

在实战项目中,我通常用 Spring AI 管理模型连接和 Web 层集成,用 Langchain4j 实现 Agent 编排和 RAG 链路。两者并不冲突。

2.3 RAG:给大模型装上一个企业知识库

RAG(Retrieval-Augmented Generation,检索增强生成)是一种“先检索、再生成”的技术架构。它的核心思路是:

  1. 提前把企业文档分块、向量化,存入向量数据库;
  2. 用户提问时,系统用同一个 Embedding 模型将问题向量化;
  3. 从向量库中检索最相关的文档片段;
  4. 把检索结果作为上下文,连同用户问题一起交给大模型生成回答。

这样大模型不依赖训练数据里的旧知识,也能回答最新的企业政策、产品文档和领域知识,而且回答可以追溯到具体来源。

在航空场景里,退改签规则、行李限额、会员权益这些内容适合做 RAG。因为它们更新频繁、量很大,不可能全部塞进提示词。

2.4 Agent:让大模型学会“动手做事”

Agent(智能体)可以被理解为一个“有大脑、会调用工具”的对话系统。它比普通聊天机器人多了一个关键能力:自动规划任务、调用外部工具、观察结果、决定下一步。

例如用户问“帮我订一张明天北京到上海的机票”,Agent 会经历这样的内部流程:

  1. 理解用户意图,提取明天、北京、上海三个关键实体;
  2. 调用 searchFlights 工具,拿到航班列表;
  3. 根据返回结果判断是否需要调用 booking 工具;
  4. 生成最终回复。

这种“模型决策 + 工具执行 + 结果反馈”的循环,就是 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 17
  • Spring Boot 3.2+
  • Spring AI 2.0
  • Langchain4j
  • 通义千问 DashScope(Qwen Chat + Qwen Embedding)
  • Milvus 向量数据库
  • 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.8+
Spring Boot3.2.x 或 3.3.x
Milvus2.3+(本地可用 Docker 运行 standalone 模式)
模型服务通义千问 DashScope 或任何 OpenAI 兼容 API

Milvus 本地启动可以用 Docker:

docker 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> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI 基础能力 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-dashscope</artifactId> </dependency> <!-- Langchain4j 核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> </dependency> <!-- Langchain4j DashScope 模型适配 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-dashscope</artifactId> </dependency> <!-- Langchain4j Milvus 向量存储 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-milvus</artifactId> </dependency> <!-- 文档解析 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-document-parser-apache-pdf</artifactId> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-document-parser-tika</artifactId> </dependency>

Spring AI 的 BOM 管理方式:

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

注意版本需要根据你实际拉取到的稳定版本调整。如果依赖冲突,优先看 Spring Boot 和 Spring AI BOM 的兼容版本。

4.3 application.yml 配置

在 src/main/resources/application.yml 中配置模型和 Milvus:

spring: 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-v3

Milvus 配置则以 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.java

5. 核心代码实现:从 Tool 到 RAG 再到 Agent

5.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 List<Flight> 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); List<Flight> 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 配置的核心是构建一个 EmbeddingStore:

package 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 部分要做三件事:

  1. 准备知识文档;
  2. 将文档分块;
  3. 计算向量并存入 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); List<TextSegment> 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); } }

这里用到了几个关键 API:

  • AiServices.builder:Langchain4j 的核心入口;
  • 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 Map<String, 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;增加日志打印完整请求响应;降低 maxTokens
Agent 执行超时,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 问题排查清单

如果你遇到了“模型不输出”的情况,按这个顺序排查:

  1. 确认 API Key 是否有权限,是否欠费或额度用完;
  2. 确认模型名称是否正确,特别是 DashScope 和 OpenAI 模型名差异很大;
  3. 在代码中打印请求体和响应体,观察是否存在异常字段;
  4. 检查是否设置了 maxTokens 为 0 或过小;
  5. 检查网络代理是否拦截了请求;
  6. 试试从模型厂商的调试工具直接调用相同参数,排除框架问题。

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。

如果你顺利跑通了上面的项目,下一步可以从几个方向继续深入:

  1. 把 Tool 调用扩展到真实的预订、改签、支付等核心业务流程;
  2. 引入更复杂的 Agent 编排,比如多步骤任务 Planner-Executor 模式;
  3. 为 RAG 增加混合检索与重排序,提升知识库召回准确率;
  4. 将 Spring AI 的流式输出接入前端,提升交互体验;
  5. 用 VectorStore + 元数据过滤做多租户知识隔离。

AI 应用开发最重要的是把“模型能力”和“业务能力”正确连接起来。本文里的 FlightTools 只是开端,你可以根据自己的业务场景,把无限多的 Java 服务暴露给大模型使用。动手把这些代码跑起来,比看十遍教程都管用。

如果这篇文章对你有帮助,欢迎收藏备用。有疑问也可以在评论区留言,我会根据大家反馈继续整理 Spring AI 与 Langchain4j 的进阶专题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 20:31:27

怎么用AI把静态图动态化,做成能发布的条漫动效视频

很多创作者手头已有完整的条漫或插画分镜&#xff0c;却卡在“让它动起来”这一步&#xff1a;传统动画软件学习成本高&#xff0c;逐帧手绘又太耗时。其实现在借助AI工具完成“静态图→动态视频”的流程已经很成熟&#xff0c;只要掌握正确的工作流&#xff0c;零基础也能在几…

作者头像 李华
网站建设 2026/8/31 20:30:02

需求追踪几个问点

这个问题问到了需求工程的“灵魂”深处。如果说需求评审解决的是“写得好不好”,那需求追溯解决的就是“活得明不明白”——它建立的是需求在整个软件生命周期中的“血缘关系网”。 你问的这7个点,本质上是同一个问题的不同切面。我把它们整合成一套“追溯兵法”,帮你彻底吃…

作者头像 李华
网站建设 2026/8/31 20:28:33

基于MATLAB的GARCH与Realized GARCH模型实现与对比

简介&#xff1a;本资源是一套面向金融工程与量化分析学习者的Realized GARCH波动率建模MATLAB实现代码&#xff0c;适用于具备基础时间序列知识和MATLAB编程能力的高年级本科生、研究生及初级量化从业者&#xff0c;用于解决传统GARCH模型对日内波动信息利用不足的问题。压缩包…

作者头像 李华
网站建设 2026/8/31 20:28:18

MATLAB下NaveGo的GNSS/INS组合导航:从松组合到科里奥利项解析

简介&#xff1a;本资源是面向导航定位领域科研人员与高校师生的GNSS信号接收模拟工具包&#xff0c;聚焦GPS信号建模、多径效应分析及INS/GNSS组合导航算法验证。NaveGo-master由circusjwz与coriolis_master团队开发&#xff0c;提供从卫星信号生成、IMU误差建模到卡尔曼滤波融…

作者头像 李华
网站建设 2026/8/31 20:27:59

基于YOLOv8的自动售货机缺货检测:从数据标注到PyQt5部署全流程实战

简介&#xff1a;本资源是一套面向计算机、人工智能、自动化等专业在校学生的毕业设计级项目&#xff0c;聚焦校园自动售货机货道缺货智能检测场景&#xff0c;基于YOLOv8目标检测模型实现端到端的缺货识别与可视化分析。资源包共8个文件&#xff0c;含3个核心Python脚本&#…

作者头像 李华
网站建设 2026/8/31 20:25:46

Surface Pro 6 vs 8 跑 Matlab:启动与仿真性能实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华