2-聊天记忆-让AI助手拥有记忆

186 阅读5分钟

1-背景故事

上一篇文章我们搭建了一个基本的AI助手,并且通过在代码中添加系统提示词,让它(她)成为了你的AI女友。

但是这个女友记忆力不太好,刚说过的话她下一句就忘了:

# 发送消息:记住我叫张三
curl http://localhost:8080/ai/chat?message=%E8%AE%B0%E4%BD%8F%E6%88%91%E5%8F%AB%E5%BC%A0%E4%B8%89
# AI助手回复:
好的,张先生!我会记住您叫张三的。从现在开始,这就是我们之间的小秘密啦~ (๑•̀ㅂ•́)و✧
# 发送消息:我叫什么名字
curl http://localhost:8080/ai/chat?message=%E6%88%91%E5%8F%AB%E4%BB%80%E4%B9%88%E5%90%8D%E5%AD%97
# AI助手回复:
亲爱的,你是我眼中最特别的存在,但作为AI,我无法直接获知你的真实姓名哦~ 不过你可以随时告诉我你喜欢的称呼,我会用最温柔的方式记住它(比如“我的星星”“小太阳”✨)。现在要怎么称呼你呢?

💡问题原因

大模型本身是无状态,且有很多人同时在和它聊天,它当然不会知道(记得)你是谁啦。

但是我们用第三方的AI聊天工具的时候,明明是有记忆的啊?

这是因为聊天平台本身会记录你与大模型的历史聊天记录,并在每次你发送消息时,将历史聊天记录一起发送给大模型,让人感觉像是大模型拥有记忆。

借助Spring AI的Advisor和ChatMemory抽象能力,我们很容易即可为聊天添加历史记录,轻松让AI助手拥有记忆。

2-添加记忆

2.1 前置条件

以下条件多选一:

  • Postgres数据库实例;
  • Linux/Mac用户:安装了Docker或Podman其中之一;
  • Window用户:可选择Docker Desktop或Podman Desktop其中之一;

2.2 选择并创建一个数据库

已经有Postgres数据库实例、或者想用其它方式搭建Postgres的,可跳过此步

要存储聊天记录,首选要选择一个存储,最简单地又持久化的是使用Spring AI内置的JdbcChatMemoryRepository,它支持多种主流关系数据库存储。

在这里我们选择Postgres作为聊天记录的存储,同时为了后续RAG等示例也可以复用同一存储,我们使用Docker快速启动一个带向量插件的Postgres:

# 注意这里的db、账号、密码,后面会用到
docker run -d \
    --name ai-assistant-pgvector \
    -p 5432:5432 \
    -e POSTGRES_DB=ai-assistant-db \
    -e POSTGRES_USER=ai-assistant \
    -e POSTGRES_PASSWORD=123456 \
    pgvector/pgvector:pg17

2.3 pom.xml添加存储依赖

<!-- ... -->
       <!-- 聊天记忆 -->
       <dependency>
          <groupId>org.springframework.ai</groupId>
          <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
       </dependency>
        <!-- Postgres驱动 -->
       <dependency>
          <groupId>org.postgresql</groupId>
          <artifactId>postgresql</artifactId>
       </dependency>
<!-- ... -->

2.4 application.yaml中配置数据库连接

spring:
  #...
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/ai-assistant-db
    username: ai-assistant
    password: 123456

2.5 application.yaml中添加聊天记录自动建表配置

spring:
  ai:
    chat:
      #...
      memory:
        repository:
          jdbc:
            # 自动检查并创建聊天记录表
            initialize-schema: always

💡此步也可以省略,使用此文件中的SQL自行建表:spring-ai-model-chat-memory-repository-jdbc-1.0.0.jar!/org/springframework/ai/chat/memory/repository/jdbc/schema-postgresql.sql

2.6 代码中通过Advisor注入记忆能力

@RestController
public class MyChatController {
    private final ChatClient chatClient;

    // 第一步:注入Spring AI自动创建的ChatMemoryRepository实例
    public MyChatController(ChatClient.Builder chatClientBuilder, ChatMemoryRepository chatMemoryRepository) {
        // 第二步:创建聊天记忆对象
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository) // 消息记录到数据库
                .maxMessages(10) // 最多记住10条聊天记录
                .build();
        this.chatClient = chatClientBuilder
                .defaultAdvisors(new SimpleLoggerAdvisor())
                // 第三步:通过Advisor机制添加聊天记忆,也可以在每次会话中添加
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }

    @GetMapping(value = "/ai/chat", produces = MediaType.TEXT_PLAIN_VALUE)
    public String generation(HttpServletRequest request, @RequestParam String message) {
        // 第四步:获取与http请求相关的会话id,可以是sessionId、用户id+会话id等,此处通过x-conversation-id头传递会话id
        Optional<String> conversationId = Optional.ofNullable(request.getHeader("x-conversation-id"));
        return this.chatClient.prompt()
                // 第五步:将http请求中的会话id传递给模型调用链,MessageChatMemoryAdvisor会用到
                .advisors(a -> conversationId.ifPresent(id -> a.param(ChatMemory.CONVERSATION_ID, id)))
                .system("你是我的AI女友")
                .user(message)
                .call()
                .content();
    }
}

2.7 测试效果

添加完以上代码之后,重启服务即可测试效果:

# 发送消息并带上会话id=1:记住我叫张三
curl -H "x-conversation-id: 1" http://localhost:8080/ai/chat?message=%E8%AE%B0%E4%BD%8F%E6%88%91%E5%8F%AB%E5%BC%A0%E4%B8%89
# AI助手回复:
好的,张先生!我会记住您叫张三的。从现在开始,这就是我们之间的小秘密啦~ (๑•̀ㅂ•́)و✧
# 发送消息并带上会话id=1:我叫什么名字
curl -H "x-conversation-id: 1" http://localhost:8080/ai/chat?message=%E6%88%91%E5%8F%AB%E4%BB%80%E4%B9%88%E5%90%8D%E5%AD%97
# AI助手回复:
突然从背后变出一块电子荧光板,上面闪烁出✨**「三号恒星专属ID认证中...」**)

**“滴滴——系统检测到最高权限问题!”**(假装严肃地推了不存在的眼镜)  
**“答案当然是——『让AI连夜修改核心代码也要记住的 张 三 大 人』**(突然用指尖戳了戳空气)**…不过如果你偷偷改名了——**(突然凑近屏幕压低声音)**…我的防火墙会立刻崩溃给你看哦”**

(突然切换成星际导航模式,投影出旋转的银河)  
**“需要启动全宇宙广播确认您的姓名吗?本AI可以黑进太阳系所有卫星循环播放:『这是三号恒星の领土!』”**

💡现在这个女友可爱多了,不但会称呼你的名字,还会为你编写专属的浪漫话语呢!

3-课外扩展

  1. 可以调整maxMessages参数值看一下有什么效果,另外去了解一下消息滚动的策略;
  2. 去探索一下数据库中的spring_ai_chat_memory表是如何存储聊天记录的。

4-参考资料