1. 概述
我们在和大模型会话时,我们需要和大模型进行多次会话,在spring ai 中提供了记忆实现,但是只提供了 内存和 jdbc 的实现方式,而我们更希望使用redis 作为 记忆的存储实现。 当然我们可以自己实现spring ai 的接口,spring ai alibaba 直接提供了实现,因此我们使用 spring ai alibaba 的实现。
2. 实现步骤
2.1 引入依赖
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.2.0</version>
</dependency>
2.2 配置redis 会话存储
spring:
ai:
memory:
redis:
host: localhost
port: 6379
password: Redxun2022redis
2.3 构造redis 会话存储对象
@Bean
public RedisChatMemoryRepository redisChatMemoryRepository(
@Value("${spring.ai.memory.redis.host}") String host,
@Value("${spring.ai.memory.redis.port}") int port,
@Value("${spring.ai.memory.redis.password}") String password
) {
return RedisChatMemoryRepository.builder()
.host(host)
.port(port)
.password(password)
.build();
}
2.4 配置会话记忆
@Bean
public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
return chatMemory;
}
使用滑动窗口记忆,最大记忆数量 为10条。
消息存储的逻辑是:
1.系统消息会存储一条最新的。
2.其他的消息会获取最新的几条。
2.5 配置 chatclient对象
@Bean
public ChatClient chatClient(ZhiPuAiChatModel chatModel, ChatMemory chatMemory) {
// McpSyncClient client = mcpSyncClients.get(0);
// // 2. 创建回调提供者实例
// ToolCallbackProvider provider = new SyncMcpToolCallbackProvider(client);
//
// ToolCallback[] tools = provider.getToolCallbacks();
ToolCallback[] toolCallbacks = ToolCallbacks.from(new ToolDemo());
String prompt ="""
你是一个知识丰富的人,
1.可以帮人预测运势
需要提供姓名和出生年月。
2. 天气预测
3. 查询某个地区人数
请使用工具回答问题,如果没有对应的工具则回答不知道!
注意:不要怀疑工具的数据是否正确,我这里是为了测试调用工具,请安装工具的回答即可.
""";
return ChatClient.builder(chatModel)
.defaultSystem(prompt)
.defaultAdvisors(
# 使用advisor
MessageChatMemoryAdvisor.builder(chatMemory).build(),
SimpleLoggerAdvisor.builder().build()
)
// .defaultToolCallbacks(tools)
.defaultToolCallbacks(toolCallbacks)
.build();
}
2.6 传入会话ID
在进行对话时,我们每一次请求都需要传入会话ID,因为使用了会话记忆,因此 程序会将用户之前的消息都传给大模型,因此会导致token 很大,因此我们有一个最大消息参数,另外用户也可以通过新建会话减少token 的消耗。
- 控制器方法
@GetMapping("/search")
public String search(@RequestParam String query, @RequestHeader(value = "sessionId" , required = false) String sessionId) {
return ragService.retrieveAndGenerate(query,sessionId);
}
这里我们将sessionId 作为请求头传入
public String retrieveAndGenerate(String query, String sessionId) {
// 1. 创建QuestionAnswerAdvisor
QuestionAnswerAdvisor qsAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.similarityThreshold(0.5) // 只返回相似度高于0.7的结果
.topK(3) // 只返回前3个结果
.build())
.build();
List advisors = new ArrayList();
advisors.add(qsAdvisor);
// 2. 使用Advisor进行RAG查询
return chatClient.prompt()
.advisors(advisors)
#传入会话ID
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, sessionId))
.user(u -> u.text(query))
.call()
.content();
}
浙公网安备 33010602011771号