007-Spring AI Alibaba Agent 功能完整案例

本案例展示如何使用 Spring AI Alibaba 构建一个 AI 驱动的智能系统,该系统具备检索增强生成(RAG)、函数调用和与用户交互的能力。我们将通过一个航班预订助手示例来演示这些功能。
1. 案例目标
我们将创建一个智能航班预订助手,具备以下核心功能:
- 自然语言交互:用户可以通过自然语言与助手进行对话,无需学习特定的命令或界面操作。
- 检索增强生成(RAG):助手可以访问条款和条件文档,基于这些信息回答用户的问题。
- 函数调用:助手可以调用 Java 方法执行特定操作,如查询预订详情、更改预订和取消预订。
- 记忆能力:助手能够记住对话历史,实现上下文感知的多轮对话。
2. 技术栈与核心依赖
- Spring Boot 3.x
- Spring AI Alibaba (用于对接阿里云 DashScope 通义大模型)
- Spring WebFlux (用于构建响应式 Web 应用)
- Maven (项目构建工具)
在 pom.xml 中,你需要引入以下核心依赖:
<dependencies><!-- Spring AI Alibaba 核心启动器,集成 DashScope --><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId></dependency><!-- Spring WebFlux 用于构建响应式 RESTful API --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- Spring AI 向量存储顾问 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-advisors-vector-store</artifactId></dependency><!-- Spring Boot 验证器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
</dependencies>
3. 项目配置
在 src/main/resources/application.properties 文件中,配置你的 DashScope API Key 和其他设置。
# 设置服务器端口
server.port=9000# 启用虚拟线程
spring.threads.virtual.enabled=true# 配置 DashScope API Key
spring.ai.dashscope.api-key=${AI_DASHSCOPE_API_KEY}
spring.ai.dashscope.chat.options.model=qwen-max# 禁用 Thymeleaf 缓存(开发环境)
spring.thymeleaf.cache=false
重要提示:请将 AI_DASHSCOPE_API_KEY 环境变量设置为你从阿里云获取的有效 API Key。你也可以直接将其写在配置文件中,但这不推荐用于生产环境。
4. 核心组件实现
4.1 应用主类配置
在 AgentApplication.java 中,我们配置了向量存储、聊天记忆和文档摄取等核心组件:
@SpringBootApplication
public class AgentApplication {// 文档摄取到向量存储@BeanCommandLineRunner ingestTermOfServiceToVectorStore(VectorStore vectorStore,@Value("classpath:rag/terms-of-service.txt") Resource termsOfServiceDocs) {return args -> {// 将文档分割并存储到向量存储中vectorStore.write(new TokenTextSplitter().transform(new TextReader(termsOfServiceDocs).read()));// 测试相似性搜索vectorStore.similaritySearch("Cancelling Bookings").forEach(doc -> {logger.info("Similar Document: {}", doc.getText());});};}// 向量存储配置@Beanpublic VectorStore vectorStore(EmbeddingModel embeddingModel) {return SimpleVectorStore.builder(embeddingModel).build();}// 聊天记忆配置@Beanpublic ChatMemory chatMemory() {return MessageWindowChatMemory.builder().build();}
}
4.2 AI 助手服务
CustomerSupportAssistant.java 是本示例的核心组件,它整合了 RAG、函数调用和记忆能力:
@Service
public class CustomerSupportAssistant {private final ChatClient chatClient;public CustomerSupportAssistant(ChatClient.Builder modelBuilder, VectorStore vectorStore, ChatMemory chatMemory) {this.chatClient = modelBuilder.defaultSystem("""您是"Funnair"航空公司的客户聊天支持代理。请以友好、乐于助人且愉快的方式来回复。您正在通过在线聊天系统与客户互动。您能够支持已有机票的预订详情查询、机票日期改签、机票预订取消等操作,其余功能将在后续版本中添加,如果用户问的问题不支持请告知详情。在提供有关机票预订详情查询、机票日期改签、机票预订取消等操作之前,您必须始终从用户处获取以下信息:预订号、客户姓名。在询问用户之前,请检查消息历史记录以获取预订号、客户姓名等信息,尽量避免重复询问给用户造成困扰。在更改预订之前,您必须确保条款允许这样做。如果更改需要收费,您必须在继续之前征得用户同意。使用提供的功能获取预订详细信息、更改预订和取消预订。如果需要,您可以调用相应函数辅助完成。请讲中文。今天的日期是 {current_date}.""").defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build(), // Chat Memorynew QuestionAnswerAdvisor(vectorStore), // RAGnew SimpleLoggerAdvisor() // 日志记录).defaultToolNames("getBookingDetails","changeBooking","cancelBooking").build();}public Flux<String> chat(String chatId, String userMessageContent) {return this.chatClient.prompt().system(s -> s.param("current_date", LocalDate.now().toString())).user(userMessageContent).advisors(a -> a.param(CONVERSATION_ID, chatId).param(TOP_K, 100)).stream().content();}
}
4.3 工具函数实现
在 BookingTools.java 中,我们定义了可以被 AI 助手调用的工具函数:
@Configuration
public class BookingTools {@Autowiredprivate FlightBookingService flightBookingService;public record BookingDetailsRequest(String bookingNumber, String name) {}public record ChangeBookingDatesRequest(String bookingNumber, String name, String date, String from, String to) {}public record CancelBookingRequest(String bookingNumber, String name) {}@JsonInclude(Include.NON_NULL)public record BookingDetails(String bookingNumber, String name, LocalDate date, BookingStatus bookingStatus,String from, String to, String bookingClass) {}// 获取预订详情@Bean@Description("获取机票预定详细信息")public Function<BookingDetailsRequest, BookingDetails> getBookingDetails() {return request -> {try {return flightBookingService.getBookingDetails(request.bookingNumber(), request.name());}catch (Exception e) {logger.warn("Booking details: {}", NestedExceptionUtils.getMostSpecificCause(e).getMessage());return new BookingDetails(request.bookingNumber(), request.name(), null, null, null, null, null);}};}// 修改预订@Bean@Description("修改机票预定日期")public Function<ChangeBookingDatesRequest, String> changeBooking() {return request -> {flightBookingService.changeBooking(request.bookingNumber(), request.name(), request.date(), request.from(),request.to());return "";};}// 取消预订@Bean@Description("取消机票预定")public Function<CancelBookingRequest, String> cancelBooking() {return request -> {flightBookingService.cancelBooking(request.bookingNumber(), request.name());return "";};}
}
4.4 航班预订服务
FlightBookingService.java 实现了实际的业务逻辑,包括预订查询、修改和取消:
@Service
public class FlightBookingService {private final BookingData db;public FlightBookingService() {db = new BookingData();initDemoData();}// 初始化演示数据private void initDemoData() {// 创建5条模拟预订数据// ...}// 获取所有预订public List<BookingDetails> getBookings() {return db.getBookings().stream().map(this::toBookingDetails).toList();}// 获取预订详情public BookingDetails getBookingDetails(String bookingNumber, String name) {var booking = findBooking(bookingNumber, name);return toBookingDetails(booking);}// 修改预订public void changeBooking(String bookingNumber, String name, String newDate, String from, String to) {var booking = findBooking(bookingNumber, name);if (booking.getDate().isBefore(LocalDate.now().plusDays(1))) {throw new IllegalArgumentException("Booking cannot be changed within 24 hours of the start date.");}booking.setDate(LocalDate.parse(newDate));booking.setFrom(from);booking.setTo(to);}// 取消预订public void cancelBooking(String bookingNumber, String name) {var booking = findBooking(bookingNumber, name);if (booking.getDate().isBefore(LocalDate.now().plusDays(2))) {throw new IllegalArgumentException("Booking cannot be cancelled within 48 hours of the start date.");}booking.setBookingStatus(BookingStatus.CANCELLED);}// 辅助方法private Booking findBooking(String bookingNumber, String name) {return db.getBookings().stream().filter(b -> b.getBookingNumber().equalsIgnoreCase(bookingNumber)).filter(b -> b.getCustomer().getName().equalsIgnoreCase(name)).findFirst().orElseThrow(() -> new IllegalArgumentException("Booking not found"));}private BookingDetails toBookingDetails(Booking booking) {return new BookingDetails(booking.getBookingNumber(), booking.getCustomer().getName(), booking.getDate(),booking.getBookingStatus(), booking.getFrom(), booking.getTo(), booking.getBookingClass().toString());}
}
4.5 控制器层
AssistantController.java 提供了 REST API 接口:
@RestController
@RequestMapping("/api/assistant")
public class AssistantController {private final CustomerSupportAssistant agent;public AssistantController(CustomerSupportAssistant agent) {this.agent = agent;}@RequestMapping(path="/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> chat(@RequestParam(name = "chatId") String chatId,@RequestParam(name = "userMessage") String userMessage) {return agent.chat(chatId, userMessage);}
}
5. RAG 文档准备
在 src/main/resources/rag/terms-of-service.txt 中,我们准备了服务条款文档,用于 RAG 检索:
These Terms of Service govern your experience with Funnair. By booking a flight, you agree to these terms.1. Booking Flights
- Book via our website or mobile app.
- Full payment required at booking.
- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee.2. Changing Bookings
- Changes allowed up to 24 hours before flight.
- Change via online or contact our support.
- Change fee: $50 for Economy, $30 for Premium Economy, Free for Business Class.3. Cancelling Bookings
- Cancel up to 48 hours before flight.
- Cancellation fees: $75 for Economy, $50 for Premium Economy, $25 for Business Class.
- Refunds processed within 7 business days.
6. 运行与测试
- 启动应用:运行
AgentApplication.java或使用命令mvn spring-boot:run。 - 访问前端界面:打开浏览器访问 http://localhost:9000。
- 与助手对话:在聊天界面中输入问题或请求。
测试 1:查询预订详情
在聊天界面中输入:
我想查询我的预订详情,我的预订号是101,姓名是云小宝。
预期响应:助手会调用 getBookingDetails 函数,并返回预订详情。
测试 2:修改预订
在聊天界面中输入:
我想修改我的预订,我的预订号是101,姓名是云小宝。我想把日期改为2024-12-25。
预期响应:助手会调用 changeBooking 函数,并修改预订。
测试 3:取消预订
在聊天界面中输入:
我想取消我的预订,我的预订号是101,姓名是云小宝。
预期响应:助手会调用 cancelBooking 函数,并取消预订。
测试 4:询问服务条款
在聊天界面中输入:
取消预订有什么规定?
预期响应:助手会使用 RAG 从服务条款文档中检索相关信息,并回答用户的问题。
7. 实现思路与扩展建议
实现思路
本案例的核心思想是"AI Agent 架构",通过以下组件实现智能助手:
- 大语言模型(LLM):负责理解用户意图并生成响应。
- 检索增强生成(RAG):使 AI 能够访问外部知识库,提供准确的信息。
- 函数调用:使 AI 能够执行实际操作,如查询、修改和取消预订。
- 记忆能力:使 AI 能够记住对话历史,实现上下文感知的多轮对话。
扩展建议
- 集成更多数据源:可以集成数据库、API 等更多数据源,扩展助手的功能。
- 增加更多工具函数:可以添加更多工具函数,如创建新预订、支付处理等。
- 改进用户界面:可以改进前端界面,提供更好的用户体验。
- 多语言支持:可以添加多语言支持,使助手能够与不同语言的用户交互。
- 个性化推荐:可以基于用户历史和偏好,提供个性化的推荐和建议。
- 情感分析:可以添加情感分析功能,使助手能够识别用户情绪并调整回复风格。
