Explores

2025 Jul 30

SyncClipboard:替代微信输入法同步功能的最佳解决方案

介绍 SyncClipboard 这款跨平台剪贴板同步工具以及部署流程,作为微信输入法同步功能的最佳替代方案,实现多设备间内容无缝传输。

2024 Jun 12

自建Vaultwarden密码库:从部署到安全备份

记录了如何使用Docker和Nginx自建Vaultwarden密码库,并涵盖了安全加固与多云端自动备份的完整流程。

2024 May 8

域名管理:Cloudflare DNS托管与SSL证书配置

记录域名管理一些操作,涵盖了使用Cloudflare进行DNS解析与托管,对比并介绍了通过Cloudflare服务与acme.sh脚本两种方式申请和自动化管理SSL/TLS证书的完整步骤。

2024 Apr 12

新到手的Linux VPS, 我这样设置:从安全加固到Nginx反代部署

涵盖了从安全设置、Docker安装、Nginx反向代理及SSL证书配置,到Swap空间管理的完整步骤。

2024 Apr 10

VPS基础安全配置实践指南

记录了新购VPS后必须进行的一系列基础安全设置,旨在提升服务器的防御能力,防止常见攻击。

2024 Feb 29

图床搭建(Cloudflare R2 + WebP Cloud + PicList)

Cloudflare R2 + WebP Cloud + PicList 免费图床搭建全流程

2023 Oct 8

Obsdian的一些使用技巧

个人的Obsidian高效使用技巧的记录,助力提升笔记管理与工作流效率。

2023 Oct 6

Obsdian个人配置记录

Obsidian配置与同步备份全流程实录,涵盖iCloud与Git自动化方案及多种同步备选。

0001 Jan 1

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 → Memory List<Message> 50条 全量对象。内存里有 50 个 Message 对象
Memory → Advisor List<Message> 8条 精简切片。经过 Token 算法裁剪,只剩 8 个 Message 对象
发给 AI 的包 Prompt 1+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 始终能获得最相关的上下文
  • 避免超长请求导致的错误或额外费用