大模型开发 - 26 Origin Tools: Spring AI 结构化多聊天客户端实战
Spring AI智能票务助手系统开发指南 摘要:本文介绍了一个基于Spring AI框架的智能票务助手系统开发实践。系统采用多聊天客户端架构,可智能识别用户票务请求(退票、查询等)并调用相应AI模型处理。主要技术特性包括:1)结构化输出,将用户输入解析为结构化任务对象;2)流式响应,实现实时交互体验;3)响应式编程,基于Reactor实现非阻塞处理;4)智能任务路由功能。技术栈采用Spring
文章目录
Pre
大模型开发 - 03 QuickStart_借助DeepSeekChatModel实现Spring AI 集成 DeepSeek
大模型开发 - 04 QuickStart_DeepSeek 模型调用流程源码解析:从 Prompt 到远程请求
大模型开发 - 05 QuickStart_接入阿里百炼平台:Spring AI Alibaba 与 DashScope SDK
大模型开发 - 06 QuickStart_本地大模型私有化部署实战:Ollama + Spring AI 全栈指南
大模型开发 - 07 ChatClient:构建统一、优雅的大模型交互接口
大模型开发 - 08 ChatClient:构建智能对话应用的流畅 API
大模型开发 - 09 ChatClient:基于 Spring AI 的多平台多模型动态切换实战
大模型开发 - 10 ChatClient:Advisors API 构建可插拔、可组合的智能对话增强体系
大模型开发 - 11 ChatClient:Advisor 机制详解:拦截、增强与自定义 AI 对话流程
大模型开发 - 12 Prompt:Spring AI 中的提示(Prompt)系统详解_从基础概念到高级工程实践
大模型开发 - 13 Prompt:提示词工程实战指南_Spring AI 中的提示设计、模板化与最佳实践
大模型开发 - 14 Chat Memory:实现跨轮次对话上下文管理
大模型开发 - 15 Tool Calling :从入门到实战,一步步构建智能Agent系统
大模型开发 - 16 Chat Memory:借助 ChatMemory + PromptChatMemoryAdvisor轻松实现大模型多轮对话记忆
大模型开发 - 17 Structured Output Converter:结构化输出转换器_从文本到结构化数据的可靠桥梁
大模型开发 - 18 Chat Memory:集成 JdbcChatMemoryRepository 实现大模型多轮对话记忆
大模型开发 - 19 Chat Memory:集成 BaseRedisChatMemoryRepository实现大模型多轮对话记忆
大模型开发 - 20 Chat Memory:多层次记忆架构_突破大模型对话中的 Token 上限瓶颈
大模型开发 - 21 Structured Output Converter:结构化输出功能实战指南
大模型开发 - 22 Multimodality API:多模态大模型与 Spring AI 的融合
大模型开发 - 23 Chat Model API:深入解析 Spring AI Chat Model API_构建统一、灵活、可扩展的 AI 对话系统
大模型开发 - 24 Embeddings Model API:深入解析 Spring AI Embeddings Model API_构建语义理解的基石
大模型开发 - 25 Image Model API:深入解析 Spring AI Image Model API_构建统一、灵活的 AI 图像生成系统
项目概述
为了更好的理解tools, 我们先收工实现一个初始版本。
本项目是一个基于Spring AI框架构建的智能票务助手系统,展示了如何使用Spring AI的多聊天客户端功能来实现结构化任务处理和流式响应。系统能够智能识别用户的票务相关请求(如退票、查询等),并根据不同的任务类型调用相应的AI模型进行处理。
核心特性
- 多聊天客户端架构:使用不同的ChatClient处理不同类型的任务
- 结构化输出:通过AI模型将用户输入解析为结构化的任务对象
- 流式响应:支持实时流式输出,提升用户体验
- 响应式编程:基于Reactor实现非阻塞的异步处理
- 任务路由:根据AI解析的任务类型自动路由到相应的处理逻辑
技术栈
- Spring Boot 3.x:应用框架
- Spring AI:AI集成框架
- 阿里云百炼(DashScope):AI模型服务
- Reactor:响应式编程框架
- Lombok:代码简化工具
项目结构
04-structured-mulit-chatclient/
├── src/main/java/com/artisan/
│ ├── MultiChatClientStructuredAgent.java # 应用启动类
│ ├── config/
│ │ └── AiConfig.java # AI配置类
│ ├── controller/
│ │ └── MultiModelsController.java # 控制器
│ └── commons/
│ ├── AiJob.java # 任务数据结构
│ └── JobType.java # 任务类型枚举
├── src/main/resources/
│ └── application.properties # 配置文件
└── pom.xml # Maven配置
详细实现步骤
第一步:创建Maven项目
首先创建一个新的Maven项目,配置必要的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.artisan</groupId>
<artifactId>spring-ai-artisan</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>04-structured-mulit-chatclient</artifactId>
<packaging>jar</packaging>
<name>04-structured-mulit-chatclient</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--百炼-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-memory</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
关键依赖说明:
spring-ai-alibaba-starter-dashscope
:阿里云百炼AI服务集成spring-ai-autoconfigure-model-chat-memory
:聊天记忆功能spring-boot-starter-web
:Web服务支持lombok
:简化代码编写
第二步:定义数据结构
2.1 创建任务类型枚举
package com.artisan.commons;
public enum JobType{
CANCEL, // 退票任务
QUERY, // 查询任务
OTHER, // 其他任务
}
2.2 创建任务数据结构
package com.artisan.commons;
import java.util.Map;
/**
* AiJob类用于定义AI任务相关的数据结构
*/
public class AiJob {
/**
* Job记录类,用于表示一个AI任务的基本信息
* @param jobType 任务类型,指定该AI任务的分类
* @param keyInfos 关键信息映射,存储任务相关的键值对信息
*/
public record Job(JobType jobType, Map<String,String> keyInfos) {}
}
设计说明:
- 使用Java Record简化数据类的定义
JobType
枚举定义了三种任务类型:退票、查询、其他keyInfos
用于存储从用户输入中提取的关键信息(如姓名、订单号等)
第三步:配置AI服务
3.1 应用配置文件
spring.application.name=structured-multi-chat-client(agent)
# ali百炼
spring.ai.dashscope.api-key=${DASHSCOPE_API_KEY}
配置说明:
- 设置应用名称为结构化多聊天客户端
- 配置阿里云百炼的API密钥(通过环境变量设置)
3.2 AI配置类
package com.artisan.config;
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatProperties;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AiConfig {
/**
* 任务规划聊天客户端配置
*
* 该函数用于构建一个专门用于任务规划的聊天客户端,配置了特定的系统提示词、
* 聊天内存顾问和模型参数,用于识别和拆分用户的票务相关任务。
*
* @param chatModel 通义千问聊天模型实例,用于实际的对话处理
* @param options DashScope聊天配置属性,包含模型的基础配置选项
* @param chatMemory 聊天内存管理器,用于维护对话历史状态
* @return 配置完成的任务规划聊天客户端实例
*/
// 任务规划
@Bean
public ChatClient planningChatClient(DashScopeChatModel chatModel,
DashScopeChatProperties options,
ChatMemory chatMemory) {
DashScopeChatOptions dashScopeChatOptions = DashScopeChatOptions.fromOptions(options.getOptions());
// 设置温度参数为0.4,平衡创造性和确定性
dashScopeChatOptions.setTemperature(0.4);
// 构建聊天客户端,配置系统提示词、内存顾问和模型选项
return ChatClient.builder(chatModel)
.defaultSystem("""
# 票务助手任务拆分规则
## 1.要求
### 1.1 根据用户内容识别任务
## 2. 任务
### 2.1 JobType:退票(CANCEL) 要求用户提供姓名和预定号, 或者从对话中提取;
### 2.2 JobType:查票(QUERY) 要求用户提供预定号, 或者从对话中提取;
### 2.3 JobType:其他(OTHER)
""")
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.defaultOptions(dashScopeChatOptions)
.build();
}
/**
* 创建智能客服聊天客户端Bean
*
* @param chatModel 聊天模型实例,用于处理对话请求
* @param options DashScope聊天配置属性,包含模型配置选项
* @param chatMemory 聊天记忆存储,用于保存对话历史
* @return 配置好的ChatClient实例,用于智能客服对话
*/
// 智能客服
@Bean
public ChatClient botChatClient(DashScopeChatModel chatModel,
DashScopeChatProperties options,
ChatMemory chatMemory) {
// 配置DashScope聊天选项,设置温度参数以控制回复的随机性
DashScopeChatOptions dashScopeChatOptions = DashScopeChatOptions.fromOptions(options.getOptions());
dashScopeChatOptions.setTemperature(1.2);
// 构建并返回聊天客户端,设置默认系统提示词、记忆顾问和配置选项
return ChatClient.builder(chatModel)
.defaultSystem("""
你是Artisan航空智能客服代理, 请以友好的语气服务用户。
""")
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.defaultOptions(dashScopeChatOptions)
.build();
}
}
配置详解:
-
planningChatClient(任务规划客户端):
- 温度设置为0.4,确保任务分类的准确性
- 系统提示词定义了票务助手的任务拆分规则
- 配置了聊天记忆功能,保持对话上下文
-
botChatClient(智能客服客户端):
- 温度设置为1.2,增加回复的创造性
- 系统提示词设定为友好的航空客服角色
- 同样配置了聊天记忆功能
第四步:实现控制器
package com.artisan.controller;
import com.artisan.commons.AiJob;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.core.scheduler.Schedulers;
@Slf4j
@RestController
public class MultiModelsController {
@Resource
ChatClient planningChatClient;
@Resource
ChatClient botChatClient;
/**
* 处理流式响应的HTTP GET请求,根据用户输入的消息执行不同的AI任务
* 使用纯Reactor响应式编程方式
*
* @param message 用户输入的消息内容
* @return 返回Flux流,包含处理过程中的实时消息推送
*/
@GetMapping(value = "/stream", produces = "text/stream;charset=UTF8")
Flux<String> stream(@RequestParam String message) {
// 使用Reactor方式处理AI调用
return Mono.fromCallable(() -> {
// 调用AI模型解析用户意图并获取任务对象
return planningChatClient.prompt().user(message)
.call()
.entity(AiJob.Job.class);
})
.subscribeOn(Schedulers.boundedElastic()) // 在弹性调度器上执行阻塞操作
.flatMapMany(job -> {
// 根据任务类型执行相应的业务逻辑
switch (job.jobType()) {
case CANCEL -> {
return handleCancelJobReactive(job);
}
case QUERY -> {
return handleQueryJobReactive(job);
}
case OTHER -> {
// 直接返回AI流式响应
return botChatClient.prompt().user(message).stream().content();
}
default -> {
log.warn("未知的任务类型: {}", job);
return Flux.just("解析失败");
}
}
})
.onErrorResume(e -> {
log.error("处理AI任务时出现异常", e);
return Flux.just("系统繁忙,请稍后再试。");
})
.startWith("hi, 正在计划任务...<br/>"); // 在流开始时推送初始消息
}
/**
* 响应式处理退票任务
*/
private Flux<String> handleCancelJobReactive(AiJob.Job job) {
log.info("处理退票任务: {}", job);
if (job.keyInfos().isEmpty()) {
return Flux.just("请输入姓名和订单号.");
} else {
// todo.. 执行业务 ticketService.cancel
return Flux.just("退票成功!");
}
}
/**
* 响应式处理查询任务
*/
private Flux<String> handleQueryJobReactive(AiJob.Job job) {
log.info("处理查询任务: {}", job);
if (job.keyInfos().isEmpty()) {
return Flux.just("请输入订单号.");
}
// todo.. 执行业务 ticketService.query()
return Flux.just("查询预定信息:xxxx");
}
// 传统方式实现(用于对比)
@GetMapping(value = "/streamOrigin", produces = "text/stream;charset=UTF8")
Flux<String> streamOrigin(@RequestParam String message) {
// 创建一个用于接收多条消息的
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
// 推送消息
sink.tryEmitNext("正在计划任务...<br/>");
new Thread(() -> {
AiJob.Job job = planningChatClient.prompt().user(message)
.call().entity(AiJob.Job.class);
switch (job.jobType()){
case CANCEL ->{
System.out.println(job);
if(job.keyInfos().size()==0){
sink.tryEmitNext("请输入姓名和订单号.");
}
else {
// todo.. 执行业务 ticketService.cancel
// --->springai --->json
sink.tryEmitNext("退票成功!");
}
}
case QUERY -> {
System.out.println(job);
if(job.keyInfos().size()==0){
sink.tryEmitNext("请输入订单号.");
}
// todo.. 执行业务 ticketService.query()
sink.tryEmitNext("查询预定信息:xxxx");
}
case OTHER -> {
Flux<String> content = botChatClient.prompt().user(message).stream().content();
content.doOnNext(sink::tryEmitNext) // 推送每条AI流内容
.doOnComplete(() -> sink.tryEmitComplete())
.subscribe();
}
default -> {
System.out.println(job);
sink.tryEmitNext("解析失败");
}
}
}).start();
return sink.asFlux();
}
}
控制器详解:
-
依赖注入:
- 注入两个不同的ChatClient:
planningChatClient
和botChatClient
- 分别用于任务规划和智能客服
- 注入两个不同的ChatClient:
-
流式响应实现:
- 使用
Flux<String>
返回流式数据 - 设置响应头为
text/stream;charset=UTF8
- 使用
-
响应式编程:
- 使用
Mono.fromCallable()
处理阻塞的AI调用 - 通过
subscribeOn(Schedulers.boundedElastic())
在弹性线程池中执行 - 使用
flatMapMany()
将任务对象转换为流式响应
- 使用
-
任务路由:
- 根据AI解析的任务类型路由到相应的处理方法
- 支持退票、查询、其他三种任务类型
第五步:创建应用启动类
package com.artisan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MultiChatClientStructuredAgent
{
/**
* 程序入口点,启动Spring Boot应用
*
* @param args 命令行参数数组,用于传递启动参数
*/
public static void main( String[] args )
{
SpringApplication.run(MultiChatClientStructuredAgent.class, args);
}
}
测试和使用
环境准备
-
设置环境变量:
export DASHSCOPE_API_KEY=your_dashscope_api_key
-
启动应用:
mvn spring-boot:run
API测试
1. 退票任务测试
http://localhost:8080/stream?message=我要退票,姓名张三,订单号12345
2. 查询任务测试
http://localhost:8080/stream?message=查询我的订单12345
3. 其他任务测试
http://localhost:8080/stream?message=你好,我想了解一下航班信息
核心特性解析
1. 多聊天客户端架构
优势:
- 职责分离:不同的ChatClient处理不同类型的任务
- 配置灵活:可以为不同的客户端设置不同的系统提示词和参数
- 扩展性强:易于添加新的客户端处理新的任务类型
实现要点:
// 任务规划客户端 - 低温度,确保准确性
dashScopeChatOptions.setTemperature(0.4);
// 智能客服客户端 - 高温度,增加创造性
dashScopeChatOptions.setTemperature(1.2);
2. 结构化输出
实现原理:
- 使用Spring AI的
entity()
方法将AI响应解析为Java对象 - 通过系统提示词指导AI输出符合预期格式的JSON
- 利用Java Record简化数据类的定义
关键代码:
AiJob.Job job = planningChatClient.prompt().user(message)
.call()
.entity(AiJob.Job.class);
3. 流式响应
技术实现:
- 使用Reactor的
Flux
实现流式数据推送 - 通过
Sinks
创建可控制的流式数据源 - 支持实时消息推送,提升用户体验
响应式编程优势:
return Mono.fromCallable(() -> {
// 阻塞操作
return planningChatClient.prompt().user(message)
.call()
.entity(AiJob.Job.class);
})
.subscribeOn(Schedulers.boundedElastic()) // 异步执行
.flatMapMany(job -> {
// 转换为流式响应
return handleJobReactive(job);
});
4. 任务路由机制
路由逻辑:
switch (job.jobType()) {
case CANCEL -> return handleCancelJobReactive(job);
case QUERY -> return handleQueryJobReactive(job);
case OTHER -> return botChatClient.prompt().user(message).stream().content();
default -> return Flux.just("解析失败");
}
最佳实践
1. 系统提示词设计
任务规划提示词:
- 明确任务分类规则
- 指定输出格式要求
- 包含示例和边界情况处理
智能客服提示词:
- 设定角色和语气
- 定义服务范围
- 保持友好和专业的语调
2. 错误处理
.onErrorResume(e -> {
log.error("处理AI任务时出现异常", e);
return Flux.just("系统繁忙,请稍后再试。");
})
错误处理策略:
- 记录详细的错误日志
- 返回用户友好的错误信息
- 避免暴露系统内部错误
3. 性能优化
异步处理:
- 使用
subscribeOn(Schedulers.boundedElastic())
处理阻塞操作 - 避免阻塞主线程
- 合理使用线程池资源
流式响应:
- 及时推送处理状态
- 避免长时间等待
- 提供良好的用户体验
4. 扩展性设计
添加新任务类型:
- 在
JobType
枚举中添加新类型 - 在控制器中添加对应的处理逻辑
- 更新系统提示词以识别新任务
添加新的聊天客户端:
- 在
AiConfig
中配置新的ChatClient
Bean - 注入到控制器中
- 在路由逻辑中使用新的客户端
总结
本项目展示了如何使用Spring AI构建一个智能的多聊天客户端系统,主要特点包括:
- 架构清晰:通过多聊天客户端实现职责分离
- 技术先进:使用响应式编程和流式响应
- 扩展性强:易于添加新的任务类型和客户端
- 用户体验好:支持实时流式响应
这个项目为构建更复杂的AI应用提供了良好的基础架构,可以作为企业级AI应用的参考实现。
后续扩展建议
- 持久化存储:集成数据库存储对话历史和任务记录
- 用户认证:添加用户身份验证和权限控制
- 监控告警:集成监控系统,实时监控AI服务状态
- 多模型支持:支持多个AI模型提供商,提高系统可用性
- 缓存机制:添加缓存层,提高响应速度
- API文档:使用Swagger生成API文档
- 单元测试:添加完整的单元测试和集成测试
更多推荐
所有评论(0)