记忆系统架构文档
记忆系统架构文档
1. 系统概览
记忆系统是一个多层文件持久化框架,以腾讯云 COS(对象存储)为唯一后端,无本地磁盘回退。
┌──────────────────────────────────────────────────────────────┐
│ 记忆系统 (Memory System) │
├──────────┬──────────┬──────────┬──────────┬──────────────────┤
│Agent │Auto │Session │Snapshot │Relevant Recall │
│Memory │Memory │Memory [OK] │ │ │
│持久记忆 │自动提取 │会话摘要 │可分发型 │智能回忆 │
│绑定Agent │后台任务 │长对话压缩 │记忆资产 │最多5个文件 │
├──────────┴──────────┴──────────┴──────────┴──────────────────┤
│ memdir (MEMORY.md 索引 + 提示词构建) │
├──────────────────────────────────────────────────────────────┤
│ MemoryTools (COS-backed 文件工具) │
│ save_file | read_file | list_files | delete_file │
│ replace_file_chunk │
├───────────────────────────┬──────────────────────────────────┤
│ COSStorage (COS) │ Redis (Session Memory 热数据) │
│ 记忆文件持久化 (所有层) │ 消息累积 + 提取状态 (多 Pod 共享) │
│ 摘要持久化 (Session) │ 分布式锁 (防并发提取) │
└───────────────────────────┴──────────────────────────────────┘
2. 功能模块
2.1 Agent Memory (agent_memory.py)
核心层,负责将持久记忆注入 Agent 运行时。
| 功能 | 说明 |
|---|---|
| 提示词注入 | load_agent_memory_prompt() 将记忆目录结构、MEMORY.md 索引、使用指令注入 system prompt |
| 工具注入 | inject_memory_tools() 将 COS-backed MemoryTools 挂载到 Agent |
| 目录管理 | ensure_memory_dir() 确保记忆目录在 COS 上存在(异步、不阻塞) |
| 作用域 | USER(跨项目用户级)/ PROJECT(项目级)/ LOCAL(本机级) |
指令结构(注入到 Agent system prompt 的 additional_context):
# Persistent Agent Memory
├── 记忆目录路径
├── ## Types of memory (user/feedback/project/reference)
├── ## What NOT to save (排除项)
├── ## How to save memories (save_file 用法 + frontmatter 格式)
├── ## When to save memories (何时主动保存)
├── Scope-specific guidance (USER/PROJECT/LOCAL 作用域指引)
├── ## When to access memories (何时读取 + read_file 指引)
└── ## MEMORY.md (当前索引内容)
2.2 Auto Memory (auto_memory.py)
后台自动提取层,从对话记录中异步提取持久记忆。
| 功能 | 说明 |
|---|---|
extract_memories() |
使用独立模型扫描对话,提取值得持久化的信息 |
extract_memories_background() |
fire-and-forget 封装,不阻塞主流程 |
| 提取类型 | user / feedback / project / reference |
| 解析格式 | TYPE|NAME|DESCRIPTION|CONTENT,--- 分隔多条目 |
当前状态:模块已定义,尚未集成到对话 Pipeline 中。
2.3 Session Memory (session_memory.py + session_manager.py)
长对话压缩层,防止上下文窗口溢出。支持多 Pod 部署。
| 文件 | 职责 |
|---|---|
session_memory.py |
纯逻辑:token 估算、阈值判断、自然断点检测 |
session_manager.py |
运行时管理器:消息累积、摘要提取、COS 持久化、Redis 共享状态 |
存储分层
热数据 (Redis) 温/冷数据 (COS) 本地 (内存)
┌─────────────────┐ ┌──────────────────────┐ ┌──────────────┐
│ messages (List) │ │ session-{id}.md │ │ 摘要 LRU 缓存 │
│ state (Hash) │ │ (摘要 Markdown 文件) │ │ (命中直接返回) │
│ lock (String) │ │ │ │ │
│ TTL 3600s │ │ 持久化,不自动过期 │ │ Pod 重启即丢 │
└─────────────────┘ └──────────────────────┘ └──────────────┘
多 Pod 共享 多 Pod 共享 单 Pod 可见
- 消息和提取状态 -> Redis(需跨 Pod 共享,控制累积一致性)
- 摘要 -> COS 持久化 + 内存 LRU(不再存 Redis,减少内存占用)
- 跨 Pod 摘要可见性:Pod A 提取 -> 写 COS -> Pod B 下次请求 -> 读 COS -> 缓存到内存
Redis Key 设计
| Key | 类型 | 内容 | 限制 |
|---|---|---|---|
wclaw:session_memory:{session_id}:messages |
List | JSON 消息对象 | LTRIM 最多 200 条 (~50KB) |
wclaw:session_memory:{session_id}:state |
Hash | initialized, last_uuid, token_count |
— |
wclaw:session_memory:{session_id}:lock |
String | 提取分布式锁 (SETNX) | TTL 60s |
全局 TTL 3600s,每次 collect_messages 续期。用户停止对话 1 小时后自动过期清理。
双模式运行
启动时自动 PING Redis 探测可达性:
| 模式 | 条件 | 消息/状态存储 | 多 Pod 安全 |
|---|---|---|---|
| Redis 模式 | REDIS_URL 配置 + 可达 |
Redis | [OK] |
| 内存模式 | Redis 未配置或不可达 | 进程内 dict | [X] (开发/测试用) |
阈值配置
| 参数 | 默认值 | 环境变量 |
|---|---|---|
| 初始化阈值 | 10,000 tokens | SESSION_MEMORY_MIN_TOKENS_TO_INIT |
| 更新阈值 | 5,000 tokens | SESSION_MEMORY_MIN_TOKENS_BETWEEN_UPDATE |
| 工具调用阈值 | 3 次 | SESSION_MEMORY_TOOL_CALLS_BETWEEN_UPDATE |
核心方法
| 方法 | 说明 |
|---|---|
collect_messages(session_id, messages) |
追加消息到 Redis List(去重 + LTRIM 截断) |
should_extract(session_id) |
检查阈值 + 自然断点 |
maybe_extract(session_id, model, force=False) |
获取分布式锁 -> LLM 提取摘要 -> 写 COS -> 重置消息缓冲 |
get_summary(session_id) |
内存缓存 -> COS 回退读取 |
clear_session(session_id) |
删除 Redis key + 内存缓存 |
stats(session_id) |
调试信息(消息数、tokens、后端模式等) |
内存控制策略
| 策略 | 说明 |
|---|---|
| 摘要不存 Redis | 摘要已在 COS 持久化,Redis 不再冗余存储;跨 Pod 通过 COS 读取 |
| LTRIM 截断 | collect_messages 时自动截断到 200 条,防止单会话消息无限增长 |
| 提取后重置 | 提取成功后消息缓冲重置为最后 5 条(保留上下文衔接) |
| TTL 自动过期 | 非活跃会话 1 小时后所有 Redis key 自动删除 |
| 分布式锁 | SETNX 60s TTL,防止多 Pod 同时提取同一会话 |
当前状态:[OK] 已集成,支持多 Pod Redis 共享。详见 5.4 Session Memory 集成。
2.4 Snapshot (snapshot.py)
可分发的记忆资产,让项目附带 Agent 的初始记忆结构。
| 功能 | 说明 |
|---|---|
check_agent_memory_snapshot() |
检查快照状态:none / initialize / prompt-update |
initialize_from_snapshot() |
从快照目录复制文件到 Agent 记忆目录 |
replace_from_snapshot() |
强制替换(删除本地 .md 后全量复制) |
create_snapshot() |
从当前记忆反向生成快照 |
mark_snapshot_synced() |
记录已同步,不覆盖本地记忆 |
状态转换:
snapshot不存在 ──-> none
记忆目录为空 ──-> initialize (复制快照作为初始状态)
有新版快照 ──-> prompt-update (通知用户同步)
已同步 ──-> none
2.5 Relevant Recall (find_relevant.py)
智能回忆,用轻量模型从记忆目录中选取最多 5 个最相关文件。
| 功能 | 说明 |
|---|---|
scan_memory_files() |
扫描所有 .md 文件的 frontmatter,提取 name/description/type |
format_memory_manifest() |
构建紧凑文件清单(仅文件名+描述,不含正文) |
find_relevant_memories() |
用轻量模型选择相关文件,或 keyword fallback |
| 最大数量 | 5 个文件 |
| 去重 | 过滤已在最近轮次中 surfaced 的文件 |
3. COS 存储路径映射
3.1 目录布局
<memory-base>/
├── users/<user_id>/agent-memory/<agent_type>/ <- Agent Memory (用户隔离)
│ ├── MEMORY.md <- 索引文件
│ ├── *.md <- 记忆文件
│ └── .snapshot-synced.json <- 快照同步状态
├── agent-memory/<agent_type>/ <- Agent Memory (共享)
├── users/<user_id>/projects/<cwd>/memory/ <- Auto Memory (用户)
├── projects/<cwd>/memory/ <- Auto Memory (共享)
└── sessions/ <- Session Memory
3.2 COS Key 生成
# local path -> COS key
/Users/mac/.harness/users/100/agent-memory/default/user_role.md
v 以 /.harness/ 为分界截断
memory/users/100/agent-memory/default/user_role.md
3.3 记忆文件格式
---
name: memory_name
description: 一行描述,用于判断相关性
type: user|feedback|project|reference
---
正文内容。Markdown 格式。
4. 核心流程
4.1 Agent 创建时记忆注入
流程图(原文包含 Mermaid 流程图,请在原文中查看)
4.2 主动保存记忆(save_file)
流程图(原文包含 Mermaid 流程图,请在原文中查看)
4.3 跨会话读取记忆
流程图(原文包含 Mermaid 流程图,请在原文中查看)
4.4 纠正/更新记忆
流程图(原文包含 Mermaid 流程图,请在原文中查看)
4.5 删除记忆文件
流程图(原文包含 Mermaid 流程图,请在原文中查看)
4.6 列出记忆文件
流程图(原文包含 Mermaid 流程图,请在原文中查看)
4.7 Session Memory 运行时流程(多 Pod Redis 模式)
流程图(原文包含 Mermaid 流程图,请在原文中查看)
5. 集成点
5.1 Agent 创建 (blaze_agent.py)
_build_agent()
├── 过滤 agno 内置工具 (FileTools, update_user_memory, update_profile)
├── load_agent_memory_prompt() -> additional_context
├── Agent.__init__(tools=filtered, additional_context=prompt)
└── inject_memory_tools() -> MemoryTools 追加到 agent.tools
5.2 请求入口 (routes.py)
sandbox_agent_runs
├── 提取 user_id (header wid 或 body)
├── 创建 session
└── get_blaze_executor(user_id, ...)
└── _build_agent(user_id) -> 触发记忆注入
5.3 学习模块 (learning_manager.py)
get_learning_machine()
├── user_profile=False <- 由 COS MemoryTools 接管
├── user_memory=False <- 由 COS MemoryTools 接管
└── learned_knowledge (可选)
5.4 Session Memory 集成
两个注入点:
请求入口 (routes.py)
├── get_session_memory_manager().get_summary(session_id) <- async
│ ├── 内存缓存命中 -> 直接返回
│ ├── 内存缓存未命中 -> COS 读取 -> 缓存到内存 -> 返回
│ └── 如果有摘要 -> 注入到 message 前面
│ 格式: "[会话历史摘要]\n{summary}\n\n---\n用户最新消息:{message}"
└── agent.run(injected_message)
Agent 创建 (blaze_agent.py)
└── post_hooks: _build_session_memory_post_hook()
└── 每轮对话结束后 (async):
├── await mgr.collect_messages(session_id, messages)
│ └── Redis: RPUSH + LTRIM + EXPIRE (或内存 dict)
└── if await mgr.should_extract(session_id):
└── asyncio.create_task(mgr.maybe_extract(session_id, model))
├── Redis SETNX lock (防并发)
├── LLM 提取摘要
├── COS write_memory (持久化)
├── 内存缓存摘要 (当前 Pod)
├── Redis: 重置消息缓冲为最后 5 条
└── Redis DEL lock
启动时自动探测:
SessionMemoryManager.__init__()
└── (lazy) _ensure_redis()
├── REDIS_URL 未配置 -> 内存模式 (单 Pod)
├── Redis PING 成功 -> Redis 模式 (多 Pod 安全)
└── Redis PING 失败 -> 内存模式 + 警告日志
调试端点 (routes.py):
| 端点 | 方法 | 说明 |
|---|---|---|
/wclaw/agents/session-memory/stats?session_id=xxx |
GET | 查询消息数、tokens、摘要状态、后端模式 (redis/memory) |
/wclaw/agents/session-memory/force-extract?session_id=xxx |
POST | 手动触发提取(绕过阈值检查) |
6. 关键设计决策
| 决策 | 原因 |
|---|---|
| COS 唯一后端,无本地回退 | 多实例部署时保证记忆一致;本地文件系统不可靠 |
| MEMORY.md 自动维护 | Agent 只需调 save_file,索引自动同步,避免手动两步操作 |
/.harness/ 作为 COS Key 分界 |
消除本地家目录路径污染,保持 COS Key 简洁 |
| 过滤 agno 内置记忆工具 | update_user_memory 走 PostgreSQL,与 COS 路径冲突 |
| 过滤 agno FileTools | 方法名重叠(save_file 等),且 FileTools 写本地磁盘 |
| 提示词注入而非 system_message | 追加到 additional_context,不影响 Agent 核心角色定义 |
| Session Memory 消息/状态走 Redis | 多 Pod 共享消息累积和提取状态,避免 Pod 间消息碎片化导致阈值永远达不到 |
| Session Memory 摘要走 COS 不存 Redis | 摘要已在 COS 持久化,Redis 冗余浪费内存;跨 Pod 通过 COS 读取,首次后内存缓存 |
| Redis 不可达自动降级为内存模式 | 开发/测试环境无 Redis 时也能正常运行,生产环境确保配置 REDIS_URL |
| 分布式锁防并发提取 | SETNX 60s TTL,防止多 Pod 同时检测到阈值触发、重复调用 LLM 提取 |
| LTRIM 200 条 + TTL 3600s | 控制单会话 Redis 内存上限 (~50KB);非活跃会话 1 小时自动过期 |
| 提取后消息重置为 5 条 | 既保留上下文衔接,又避免同一会话反复提取 |
夜雨聆风