spring ai alibaba第一章ChatClient讲解
spring ai alibaba
本文章将从源码开始分析spring ai alibaba,带你一步一步了解spring ai alibaba,适合新手来学习,下面是作者使用的版本
-spring-ai-alibaba版本1.0.0-M5.1
-springboot版本3.2.4
-jdk版本17
若要使用jdbc持久化需要以下版本
-mysql-connector-j版本8.1.0
-mybatis-plus版本3.5.7
一、初始化
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M5.1</version>
</dependency>
2、配置yml(注意这个是没有提示的)
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
注意初始化的ai是阿里云的qwen-plus,故我们需要去阿里云的百炼平台申请它的api-key,当然也可以修改其他平台的ai平台,这个后续会讲
源码如下:
在自动配置类中可以看出来,在需要导入的配置属性文件中反射出了七个类(DashScopeConnectionProperties、DashScopeChatProperties、DashScopeImageProperties、DashScopeSpeechSynthesisProperties、DashScopeAudioTranscriptionProperties、DashScopeEmbeddingProperties、DashScopeRerankProperties)

在DashScopeChatProperties配置属性中我们可以清楚的看见使用的是阿里云的qwen-plus

二、Chat Client
1、简介
首先我们讲解一下chat client
我们在阿里云官网中我们可以得到以下的定义:

说白话就是chatClient相当于封装了chat Model 使使用更为简单,但相对应的其的灵活性将大大降低。
其主要功能有:
包括一些基础功能,如:
- 定制和组装模型的输入(Prompt)
- 格式化解析模型的输出(Structured Output)
- 调整模型交互参数(ChatOptions)
还支持更多高级功能:
- 聊天记忆(Chat Memory)
- 工具/函数调用(Function Calling)
- RAG
2、创建Chat Client
使用 ChatClient.Builder 对象创建 ChatClient 实例,您可以自动注入由Spring Boot 自动配置创建的默认 ChatClient.Builder 实例,您也可以通过编程方式自行创建一个 ChatClient.Builder 实例并用它来得到 ChatClient 实例。
下面使用ChatClient.Builder 对象创建ChatClient 实例
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
}
3、创建一个简单的问答系统
创建好chatClient后我们就可以使用这个chatClient进行制作简单的问答系统
@RestController
@RequestMapping("/helloworld")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/chat")
public String chat(String input) {
//将用户输入的 input 构造成一个提示。
return this.chatClient.prompt()
.user(input) //将用户输入的 input 作为问题添加到对话上下文中;
.call() // 调用大模型进行推理处理。
.content(); // 获取模型返回的回答内容。最终将模型的回答结果作为字符串返回给前端。
// .entity(ActorFilms.class); // 可以使用该 entity 方法轻松地将 AI 模型的输出映射到 ActorFilms 类型
}
}
prompt和user区别:prompt通常由开发者或应用系统提供,用户看不到。用来设置对话的基础规则、语境和行为模式,定义 AI 的角色、语调和限制。user由最终用户(如你)输入。用来向模型提问或提供上下文信息,引导模型生成具体的回答。
当我们访问http:localhost:8080/helloworld/chat?input="讲个笑话?"就可以调用ai进行回复
以下是ai的回复:

3、进阶使用(此模块需要有一点源码知识)
但是我们发现这个对话系统只能回答我们当前问的问题,不会记忆以前的问题,那么我们怎么去设置呢?
出现这个问题我们就应该去查看源码看看如何去设置。
下面是我截出的设置的源码并对其附上了解释
// 设置对应的Advisor,我们可以在此设置对原功能增强功能,当然我们可以设计自己的Advisor
Builder defaultAdvisors(Advisor... advisor);
Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);
Builder defaultAdvisors(List<Advisor> advisors);
// 此方法通常用于设置默认的聊天选项,供构建器(Builder)模式使用,以便在创建对象时预设一些配置。
Builder defaultOptions(ChatOptions chatOptions);
// 该方法通常用于构建者模式中,设置默认用户信息
Builder defaultUser(String text);
Builder defaultUser(Resource text, Charset charset);
Builder defaultUser(Resource text);
Builder defaultUser(Consumer<PromptUserSpec> userSpecConsumer);
// 通常用于构建器模式中,允许链式调用设置默认系统信息。
Builder defaultSystem(String text);
Builder defaultSystem(Resource text, Charset charset);
Builder defaultSystem(Resource text);
Builder defaultSystem(Consumer<PromptSystemSpec> systemSpecConsumer);
(1)defaultAdvisors
此方法可以在此设置对原功能进行增强
那么系统自带的Advisor有哪些呢?通过继承关系可以看出来继承的Advisor有两类接口分别是正常的传输或流式传输。其下有其的实现方法。下面我调一些重点的来讲解一下!!

AbstractChatMemoryAdvisor // 实现记忆存储功能
BaseAdvisor // 用于在调用前后处理请求和响应。
DashScopeDocumentRetrievalAdvisor // 基于检索文档的问答增强功能,主要作用是在大模型调用前后插入文档检索与引用处理逻辑。
DocumentRetrievalAdvisor // 是一个增强处理器,用于在调用模型前根据用户输入检索文档,并将文档内容注入到提示词中。
QuestionAnswerAdvisor // 问答顾问组件,主要用于在调用模型前通过向量检索补充上下文信息,再将增强后的上下文用于生成回答。
RequestResponseAdvisor // 已弃用
RetrievalRerankAdvisor // 检索-重排序的拦截器,用于增强问答系统的上下文信息。
SafeGuardAdvisor // 用于在用户输入中检测敏感词,若存在敏感词,则返回预定义的失败响应,避免直接处理敏感内容
SimpleLoggerAdvisor // 是一个用于记录 ChatClient 的 request 和 response 数据 Advisor,这对于调试和监控您的 AI 交互非常有用
有人发现DashScopeDocumentRetrievalAdvisor和DocumentRetrievalAdvisor类似那么这两个有什么区别呢?
- 功能复杂度与用途
| 特性 | DocumentRetrievalAdvisor | DashScopeDocumentRetrievalAdvisor |
|---|---|---|
| 用途 | 基础文档检索增强器,适用于通用问答场景 | 支持高级引用格式的问答增强器,专为 DashScope 等大模型平台设计 |
| 提示词模板 | 提供简单提示模板,直接拼接检索内容到用户输入中 | 提供结构化提示模板,支持引用编号(如 [1])和推理步骤 |
| 响应后处理 | 仅将检索结果附加到元数据中 | 支持提取回复中的引用标记(如 [1]),并关联原始文档 |
- 对引用的支持
| 特性 | DocumentRetrievalAdvisor | DashScopeDocumentRetrievalAdvisor |
|---|---|---|
| 是否支持引用标注 | ❌ 不支持引用标注机制 | ✅ 支持引用标注(通过正则匹配 [1]) |
| 是否自动构建引用文档映射 | ❌ 仅保存原始文档列表 | ✅ 构建索引 ID 到文档的映射表(便于后续引用) |
| 是否启用引用过滤 | ❌ 不涉及引用判断逻辑 | ✅ 可配置 enableReference 控制是否返回引用文档 |
总结
- 如果你只需要基础的文档检索和上下文注入,选择 DocumentRetrievalAdvisor;
- 如果你需要支持引用标注、结构化提示词、引用文档映射与返回等功能,尤其是对接 DashScope 平台,应选择 DashScopeDocumentRetrievalAdvisor。
- 两个类都继承自 BaseAdvisor,共享基本的拦截逻辑,但在具体实现上针对不同需求进行了扩展和优化。
AbstractChatMemoryAdvisor讲解
这个接口实现有以下三种方式
MessageChatMemoryAdvisor:内存被检索并作为消息集合添加到提示中PromptChatMemoryAdvisor:检索内存并将其添加到提示的系统文本中。VectorStoreChatMemoryAdvisor:构造函数VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize)允许您指定要从中检索聊天历史记录的 VectorStore、唯一的对话 ID、要检索的聊天历史记录的大小(以令牌大小为单位)
我们使用MessageChatMemoryAdvisor来讲解:
根据此方法的构造函数可知若要实现此类需要ChatMemory参数,那么ChatMemory是什么呢?
public MessageChatMemoryAdvisor(ChatMemory chatMemory) {
super(chatMemory);
}
ChatMemory 接口表示聊天对话历史记录的存储,它提供向对话添加消息、从对话中检索消息以及清除对话历史记录的方法。
目前提供两种实现方式 InMemoryChatMemory(基于内存存储)、CassandraChatMemory(在Cassandra中带有过期时间的持久化存储。)(在1.0.0-M5.1中可以自动配置)、JdbcChatMemory(在JDBC中没有过期时间限制的持久化存储) 、Neo4jChatMemory(在Neo4j中没有过期时间限制的持久化存储),分别为聊天对话历史记录提供内存存储和 time-to-live 类型的持久存储。
CassandraChatMemory的改变


CassandraChatMemory的实现
由源码可知若要使用这个方法则必须导入cassandraChatMemory
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-cassandra-store</artifactId>
<version>1.0.0-M5</version>
</dependency>
public HelloworldController(ChatClient.Builder chatClientBuilder) {
this.dashScopeChatClient = chatClientBuilder
.defaultSystem(DEFAULT_PROMPT)
// 实现 Chat Memory 的 Advisor
// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。
.defaultAdvisors(
new MessageChatMemoryAdvisor(new CassandraChatMemory(CassandraChatMemoryConfig.builder().build()))
.build();
}
InMemoryChatMemory实现方法
public HelloworldController(ChatClient.Builder chatClientBuilder) {
this.dashScopeChatClient = chatClientBuilder
.defaultSystem(DEFAULT_PROMPT)
// 实现 Chat Memory 的 Advisor
// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。
.defaultAdvisors(
new MessageChatMemoryAdvisor(new InMemoryChatMemory())
)
.build();
}
JdbcChatMemory 的实现
如果要将对话持久化到数据库中,就可以使用JdbcChatMemory。但是spring-ai-starter-model-jdbc依赖模板版本很少,而且缺乏相关介绍,Maven官方仓库还搜不到依赖,所以不推荐使用。在Spring仓库能搜到,但是用的人太少了,并且会遇到版本兼容问题,故我们决定自己创建一个在数据库存储的功能。
导入坐标
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M5.1</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
导入数据结构
CREATE TABLE ai_chat_memory (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
conversation_id VARCHAR(255) NOT NULL comment '会话id',
type VARCHAR(20) NOT NULL comment '消息类型',
content TEXT NOT NULL comment '消息内容',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP comment '创建时间',
update_time TIMESTAMP default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
is_delete tinyint default 0 not null comment '是否删除',
INDEX idx_conv (conversation_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创建数据实体类
@Data
@TableName("ai_chat_memory")
public class AIPersistenceEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 会话id
*/
@TableField("conversation_id")
private String conversationId;
/**
* 消息类型
*/
@TableField("type")
private String type;
/**
* 消息内容
*/
@TableField("content")
private String content;
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
@TableField("update_time")
private Date updateTime;
/**
* 是否删除
*/
@TableLogic
@TableField("is_delete")
private Integer isDelete;
}
创建映射接口并继承基础映射
@Mapper
public interface AIPersistenceDao extends BaseMapper<AIPersistenceEntity> {
}
创建实体类
@Component
public class MyBatisPlusChatMemory implements ChatMemory {
@Autowired
private AIPersistenceDao aiPersistenceDao;
@Override
public void add(String conversationId, Message message) {
AIPersistenceEntity aiChatMemory = new AIPersistenceEntity();
aiChatMemory.setConversationId(conversationId);
aiChatMemory.setType(message.getMessageType().getValue());
aiChatMemory.setContent(message.getText());
aiPersistenceDao.insert(aiChatMemory);
}
@Override
public void add(String conversationId, List<Message> messages) {
List<AIPersistenceEntity> aiChatMemories = new ArrayList<>();
for (Message message : messages) {
AIPersistenceEntity aiChatMemory = new AIPersistenceEntity();
aiChatMemory.setConversationId(conversationId);
aiChatMemory.setType(message.getMessageType().getValue());
aiChatMemory.setContent(message.getText());
aiChatMemories.add(aiChatMemory);
}
aiPersistenceDao.insert(aiChatMemories);
}
@Override
public List<Message> get(String conversationId, int lastN) {
Page<AIPersistenceEntity> page = new Page<>(1, lastN);
QueryWrapper<AIPersistenceEntity> wrapper = new QueryWrapper<>();
wrapper.eq("conversation_id", conversationId)
.orderByDesc("create_time");
List<AIPersistenceEntity> aiChatMemories = aiPersistenceDao.selectList(wrapper);
Collections.reverse(aiChatMemories);
List<Message> messages = new ArrayList<>();
for (AIPersistenceEntity aiChatMemory : aiChatMemories) {
String type = aiChatMemory.getType();
switch (type) {
case "user" -> messages.add(new UserMessage(aiChatMemory.getContent()));
case "assistant" -> messages.add(new AssistantMessage(aiChatMemory.getContent()));
case "system" -> messages.add(new SystemMessage(aiChatMemory.getContent()));
default -> throw new IllegalArgumentException("Unknown message type: " + type);
}
}
return messages;
}
@Override
public void clear(String conversationId) {
QueryWrapper<AIPersistenceEntity> wrapper = new QueryWrapper<>();
wrapper.eq("conversation_id", conversationId);
aiPersistenceDao.delete(wrapper);
}
}
SimpleLoggerAdvisor讲解
SimpleLoggerAdvisor 是一个用于记录 ChatClient 的 request 和 response 数据 Advisor,这对于调试和监控您的 AI 交互非常有用。
要启用日志记录,请在创建 ChatClient 时将 SimpleLoggerAdvisor 添加到 Advisor 链中。建议将其添加到链的末尾:
ChatResponse response = ChatClient.create(chatModel).prompt()
.advisors(new SimpleLoggerAdvisor())
.user("Tell me a joke?")
.call()
.chatResponse();
org.springframework.ai.chat.client.advisor=DEBUG
将其添加到您的 application.properties 或 application.yaml 文件中。
您可以使用以下构造函数自定义如何使用 SimpleLoggerAdvisor 记录来自 AdvisedRequest 和 ChatResponse 的数据:
SimpleLoggerAdvisor(
Function<AdvisedRequest, String> requestToString,
Function<ChatResponse, String> responseToString
)
例如:
javaCopySimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> "Custom request: " + request.userText,
response -> "Custom response: " + response.getResult()
);
更多推荐
所有评论(0)