【SAA】SpringAI Alibaba学习笔记(三):ChatModel对话记忆存储和持久化
一、对话记忆是什么?
本质上用户使用AI模型的本质是与AI模型进行对话,而“大模型对话记忆”,指的是LLM在与用户进行交互式的对话中,能够追踪、理解并利用先前对话上下文的能力。这种能力可以使得大模型不仅能够响应即时的输入请求,还能基于之前交流内容能够在对话中记住先前的对话内容,并根据这些信息进行后续的响应。这种记忆机制让模型能够在对话中持续跟踪和理解用户的意图和上下文,从而实现更自然和连贯的对话。
如果不实现对话记忆持久化,如果电脑一重启,就什么都没有了。
二、记忆类型
这里我们只是分成两种类型:内存存储和持久化存储。
- 内存存储:被对话记忆存储到内存中,通常用于开发和测试简单的内存存储。
- 持久化存储:跟之前开发一样,把数据存储到数据库中,类似将结构化数据存储到结构化数据库Mysql。而大模型对话记忆可以存储到RedisStack、PostgreSQL、MongoDB等数据库。
三、实现持久化存储
我们这里使用的是RedisStack来进行对话持久化。
1. 导入依赖
<!--spring-ai-alibaba memory-redis--><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-memory-redis</artifactId></dependency><!--jedis--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>
2. 配置application.properties
server.port=8008# 设置响应的字符编码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=truespring.application.name=SAA-08Persistent# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}# ==========redis config ===============
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.data.redis.connect-timeout=3
spring.data.redis.timeout=23. 配置RedisMemoryConfig类
我们需要在RedisMemoryConfig类中把RedisMemoryRepository给配置好。
@Configuration
public class RedisMemoryConfig
{@Value("${spring.data.redis.host}") //从application.properties中读取信息private String host;@Value("${spring.data.redis.port}")private int port;@Beanpublic RedisChatMemoryRepository redisChatMemoryRepository(){return RedisChatMemoryRepository.builder().host(host) //设置主机名.port(port) //设置端口.build();}
}4. 配置大语言模型配置类
MessageWindowChatMemory:消息窗口聊天记忆。它维护一个消息窗口,可以指定最大存储对话记忆的大小,当消息超过最大值,就会删除比较旧的信息,同时保留系统信息。默认的存储的大小为20条信息。但是这种方式使用的是内存存储,也就是说只要关机后,信息就都没了。
因此,我们可以实现自己的ChatMemoryRepository。
@Bean(name = "deepseekChatClient")public ChatClient deepSeekChatCilent(@Qualifier(value = "deepseek") ChatModel deepseek, RedisChatMemoryRepository redisChatMemoryRepository) {MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder().chatMemoryRepository(redisChatMemoryRepository).maxMessages(10).build();return ChatClient.builder(deepseek).defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build()).defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build()).build();}Advisors(顾问)
这是ChatClient的一个关键特性,采用拦截器链的设计模式,允许通过注入检索数据和
ChatMemory(对话历史)来修改传入的Prompt。
MessageChatMemoryAdvisor使用提供的实现来管理对话内存。在每次交互中,它都会从内存中检索对话历史记录,并将其作为信息集合包含在提示中。
5. Controller实现
@RestController
public class ChatMemory4RedisController
{@Resource(name = "qwenChatClient")private ChatClient qwenChatClient;@GetMapping("/chatmemory/chat")public String chat(String msg, String userId){return qwenChatClient.prompt(msg).advisors(new Consumer<ChatClient.AdvisorSpec>(){@Overridepublic void accept(ChatClient.AdvisorSpec advisorSpec){advisorSpec.param(CONVERSATION_ID, userId); //根据对话ID从内存中检索对话历史记录。}}).call().content();
}
可以做一下优化,使用Lambda表达式进行实现:
@GetMapping("/chatmemory/chat")public String chat(String msg, String userId) {return deepseekChatClient.prompt(msg).advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID,userId)).call().content();}