Spring AI 对话记忆管理流程图 #

本文档展示了 Spring AI 对话记忆管理的完整流程,以用户发送消息 "帮我总结一下刚才的内容" (conversationId: "user-9527") 为例。

完整时序图 #

Image_2025-11-25_16-17-39_vavz5awg.vhe.png

sequenceDiagram
    autonumber
    participant User as 用户/前端
    participant Controller as Controller
    participant ChatClient as ChatClient
    participant Advisor as MessageChatMemoryAdvisor
    participant Memory as TokenWindowChatMemory
    participant Repo as RedisChatMemoryRepository
    participant Redis as Redis
    participant AI as AI模型

    rect rgb(240, 248, 255)
        Note over User,Redis: 第一阶段:读取与筛选 (Before Request)
        User->>Controller: 发送消息 (conversationId: user-9527)
        Controller->>ChatClient: chatClient.prompt().user(...).call()
        ChatClient->>Advisor: 拦截请求 (Before Request)
        Note over Advisor: 发现 conversationId=user-9527
        
        Advisor->>Memory: get(user-9527, ...)
        Note over Advisor,Memory: 请求历史记忆
        
        Memory->>Repo: 查询存储的消息
        Repo->>Redis: redisTemplate.opsForValue().get(chat:memory:user-9527)
        Note over Redis: 返回 JSON 字符串 (50条历史对话)
        Redis-->>Repo: String (JSON)
        Repo-->>Memory: 反序列化为 List Message (全量50条)
        
        Note over Memory: Token策略计算: 1.引入Tokenizer 2.从最后一条往前倒推 3.累加至2000Token限制 4.倒数第8条时满了
        Memory-->>Advisor: 返回精简切片 (8条历史+SystemMessage)
    end

    rect rgb(255, 250, 240)
        Note over Advisor,AI: 第二阶段:组装与发送 (Requesting)
        Note over Advisor: 组装最终Prompt: SystemPrompt + 8条历史 + 当前新问题
        Advisor->>AI: 发送完整请求包
    end

    rect rgb(240, 255, 240)
        Note over AI: 第三阶段:AI响应 (Response)
        Note over AI: AI理解上下文并思考
        AI-->>Advisor: 返回回复内容
    end

    rect rgb(255, 240, 245)
        Note over Advisor,Redis: 第四阶段:写入与更新 (After Request)
        Note over Advisor: 捕获本轮对话: 用户提问 + AI回复
        
        Advisor->>Memory: add(user-9527, [UserMsg, AssistantMsg])
        Note over Memory: 追加新消息到列表 (50条→52条)
        
        Memory->>Repo: update(...)
        Repo->>Redis: 序列化为JSON并覆盖写入, 刷新TTL
        Redis-->>Repo: 确认写入
        Repo-->>Memory: 更新成功
        Memory-->>Advisor: 存储完成
    end
    
    Advisor-->>ChatClient: 返回AI响应
    ChatClient-->>Controller: 返回结果
    Controller-->>User: 返回回复内容

数据形态变化表 #

环节数据形态数量示例备注
Redis 中String (JSON)50条全量存档。例如:[{"role":"user","content":"..."}, ...]
Repo → MemoryList<Message>50条全量对象。内存里有 50 个 Message 对象
Memory → AdvisorList<Message>8条精简切片。经过 Token 算法裁剪,只剩 8 个 Message 对象
发给 AI 的包Prompt1+8+1最终请求。包含 System + 8条历史 + 1条新消息
Redis (更新后)String (JSON)52条全量存档+2。变成了 52 条,等待下一次调用

核心组件职责 #

graph TB
    subgraph "存储层"
        Redis[(Redis 数据库)]
        Repo[RedisChatMemoryRepository]
    end
    
    subgraph "策略层"
        Memory[TokenWindowChatMemory]
        Tokenizer[Token 计算器]
    end
    
    subgraph "增强层"
        Advisor[MessageChatMemoryAdvisor]
    end
    
    subgraph "应用层"
        ChatClient[ChatClient]
        Controller[Controller]
    end
    
    Controller --> ChatClient
    ChatClient --> Advisor
    Advisor --> Memory
    Memory --> Tokenizer
    Memory --> Repo
    Repo --> Redis
    
    style Redis fill:#ffcccc
    style Repo fill:#ffe6cc
    style Memory fill:#fff4cc
    style Tokenizer fill:#fff4cc
    style Advisor fill:#e6f3ff
    style ChatClient fill:#f0f0f0
    style Controller fill:#f0f0f0

Token 窗口计算流程 #

flowchart TD
    Start([收到50条历史消息]) --> Init[初始化 Token 计数器 = 0]
    Init --> Loop{从最后一条往前遍历}
    Loop -->|取一条消息| Calc[计算该消息的 Token 数]
    Calc --> Add[累加到计数器]
    Add --> Check{累加后是否超过2000 Token?}
    Check -->|否| Loop
    Check -->|是| Stop[停止遍历]
    Stop --> Result[返回最近的 8 条消息]
    Loop -->|遍历完所有消息| Result
    Result --> End([发送给 Advisor])
    
    style Start fill:#e1f5e1
    style End fill:#e1f5e1
    style Check fill:#ffe6e6
    style Result fill:#e6f3ff

设计优势 #

1. 职责分离 #

  • Redis:只管存 JSON,不懂业务逻辑
  • Repository:只管序列化/反序列化,不懂 Token
  • Memory:只管 Token 计算,不管 IO
  • Advisor:只管请求增强,不管存储细节

2. 性能优化 #

  • Token 计算在内存中完成,速度极快
  • Redis 存储全量数据,但只传输必要的切片给 AI
  • 避免每次都重新计算整个对话历史

3. 可扩展性 #

  • 可以轻松替换存储层(Redis → MySQL → MongoDB)
  • 可以调整 Token 窗口大小
  • 可以实现不同的记忆策略(滑动窗口、摘要压缩等)

4. AI 友好 #

  • 无论 Redis 存了多少条消息,发给 AI 的永远不会超过 Token 限制
  • 保证 AI 始终能获得最相关的上下文
  • 避免超长请求导致的错误或额外费用