找回密码
 立即注册
首页 业界区 业界 Clawdbot 是如何实现永久记忆的?

Clawdbot 是如何实现永久记忆的?

要燥 前天 21:15
你是否曾经和AI助手聊了一整晚,第二天打开对话却发现它完全忘了你们讨论过的关键细节?或者当你在多个项目之间切换时,AI总是在问"你指的是哪个API",让你不厌其烦地重复背景信息。这种"金鱼式记忆"是当下大多数云端AI产品的通病——它们要么只能记住有限的上下文,要么把所有数据都存储在厂商的服务器上。
但如果有一个AI助手,它能像一位真正的私人助理那样,永远记住你的偏好、你的项目细节、甚至你三个月前提过的小习惯?更妙的是,这些记忆完全存储在你自己的电脑上,由你全权掌控。
这就是Clawdbot正在做的事情。作为一款开源的个人AI助手,Clawdbot在GitHub上已经获得了超过125,000个Star。与运行在云端的ChatGPT或Claude不同,Clawdbot直接运行在你的本地机器上,并且能够集成到你日常使用的聊天平台中(Discord、WhatsApp、Telegram等)。
它不仅是一个聊天机器人,更是一个能自主处理实际任务的助手:管理邮件、安排日历、处理航班值机、按计划运行后台任务。但最吸引我的是它的持久化记忆系统——它能实现24/7的全天候上下文保持,记住对话内容,并无限期地基于之前的交互进行累积。
如果你读过我之前关于ChatGPT记忆和Claude记忆的文章,就知道我对不同AI产品如何处理记忆这个问题非常着迷。Clawdbot采用了一种截然不同的方法:它不是基于云端、由公司控制的记忆,而是将一切保存在本地,让用户完全拥有自己的上下文和技能数据。
让我们一起深入了解它是如何工作的。
以下内容翻译自《How Clawdbot Remembers Everything》
上下文是如何构建的

在深入探讨记忆之前,我们先来理解模型在每次请求时能看到什么:
  1. [0] 系统提示词(静态指令 + 条件指令)
  2. [1] 项目上下文(引导文件:AGENTS.md、SOUL.md 等)
  3. [2] 对话历史(消息、工具调用、压缩摘要)
  4. [3] 当前消息
复制代码
系统提示词定义了Agent的能力和可用工具。与记忆相关的是"项目上下文",它包含了用户可编辑的Markdown文件,这些文件会被注入到每次请求中:
这些文件位于Agent的工作空间中,与记忆文件并存,使得整个Agent的配置变得透明且可编辑。
上下文 vs 记忆

理解上下文和记忆之间的区别,是理解Clawdbot的基础。
上下文是模型在单次请求中能看到的一切:
  1. 上下文 = 系统提示词 + 对话历史 + 工具结果 + 附件
复制代码
上下文的特性:

  • 临时的——只存在于本次请求期间
  • 有限的——受限于模型的上下文窗口(例如20万token)
  • 昂贵的——每个token都计入API成本和速度
记忆是存储在磁盘上的内容:
  1. 记忆 = MEMORY.md + memory/*.md + 会话转录文件
复制代码
记忆的特性:

  • 持久的——在重启、日复一日、月复一月后依然存在
  • 无限的——可以无限增长
  • 低成本的——存储不产生API费用
  • 可搜索的——建立索引以支持语义检索
记忆工具

Agent通过两个专用工具来访问记忆:
1. memory_search

用途:在所有文件中查找相关的记忆
  1. {
  2.   "name": "memory_search",
  3.   "description": "强制性回忆步骤:在回答关于之前工作、决策、日期、人员、偏好或待办事项的问题之前,对MEMORY.md和memory/*.md进行语义搜索",
  4.   "parameters": {
  5.     "query": "我们对API做了什么决定?",
  6.     "maxResults": 6,
  7.     "minScore": 0.35
  8.   }
  9. }
复制代码
返回结果
  1. {
  2.   "results": [
  3.     {
  4.       "path": "memory/2026-01-20.md",
  5.       "startLine": 45,
  6.       "endLine": 52,
  7.       "score": 0.87,
  8.       "snippet": "## API 讨论\n决定为了简单起见使用REST而不是GraphQL...",
  9.       "source": "memory"
  10.     }
  11.   ],
  12.   "provider": "openai",
  13.   "model": "text-embedding-3-small"
  14. }
复制代码
2. memory_get

用途:在找到内容后读取具体内容
  1. {
  2.   "name": "memory_get",
  3.   "description": "在使用memory_search后,从记忆文件中读取特定行",
  4.   "parameters": {
  5.     "path": "memory/2026-01-20.md",
  6.     "from": 45,
  7.     "lines": 15
  8.   }
  9. }
复制代码
返回结果
  1. {
  2.   "path": "memory/2026-01-20.md",
  3.   "text": "## API 讨论\n\n与团队讨论API架构。\n\n### 决策\n我们选择REST而非GraphQL,原因如下:\n1. 实现更简单\n2. 更好的缓存支持\n3. 团队更熟悉\n\n### 端点\n- GET /users\n- POST /auth/login\n- GET /projects/:id"
  4. }
复制代码
写入记忆

并没有专门的memory_write工具。Agent使用标准的写入和编辑工具来写入记忆——这些工具它本来就在用于处理任何文件。由于记忆就是普通的Markdown,你也可以手动编辑这些文件(它们会被自动重新索引)。
写入位置的决策是通过AGENTS.md中的提示来驱动的:
在预压缩刷新和会话结束时,也会自动进行写入(后续章节会介绍)。
记忆存储

Clawdbot的记忆系统建立在"记忆就是Agent工作空间中的纯Markdown"这一原则之上。
双层记忆系统

记忆位于Agent的工作空间中(默认:~/clawd/):
  1. ~/clawd/
  2. ├── MEMORY.md              - 第二层:长期策划的知识
  3. └── memory/
  4.     ├── 2026-01-26.md      - 第一层:今天的笔记
  5.     ├── 2026-01-25.md      - 昨天的笔记
  6.     ├── 2026-01-24.md      - ...以此类推
  7.     └── ...
复制代码
第一层:每日日志(memory/YYYY-MM-DD.md)
这些是仅追加的每日笔记,Agent会在一天中随时写入。当Agent想要记住某事,或被明确告知要记住某事时,就会写入这里。
  1. # 2026-01-26
  2. ## 10:30 AM - API 讨论
  3. 与用户讨论REST vs GraphQL。决策:为了简单使用REST。
  4. 关键端点:/users、/auth、/projects。
  5. ## 2:15 PM - 部署
  6. 将v2.3.0部署到生产环境。没有问题。
  7. ## 4:00 PM - 用户偏好
  8. 用户提到他们喜欢TypeScript胜过JavaScript。
复制代码
第二层:长期记忆(MEMORY.md)
这是经过策划的、持久的知识。当发生重大事件、想法、决策、观点和学到的教训时,Agent会写入这里。
  1. # 长期记忆
  2. ## 用户偏好
  3. - 喜欢TypeScript胜过JavaScript
  4. - 喜欢简洁的解释
  5. - 正在做"Acme Dashboard"项目
  6. ## 重要决策
  7. - 2026-01-15:选择PostgreSQL作为数据库
  8. - 2026-01-20:采用REST而非GraphQL
  9. - 2026-01-26:使用Tailwind CSS进行样式设计
  10. ## 关键联系人
  11. - Alice (alice@acme.com) - 设计负责人
  12. - Bob (bob@acme.com) - 后端工程师
复制代码
Agent如何知道要读取记忆

AGENTS.md文件(会自动加载)包含以下指令:
  1. ## 每次会话
  2. 在做其他事情之前:
  3. 1. 阅读 SOUL.md - 这是你是谁
  4. 2. 阅读 USER.md - 这是你在帮助谁
  5. 3. 阅读 memory/YYYY-MM-DD.md(今天和昨天)获取近期上下文
  6. 4. 如果是在主会话中(与你的主人直接聊天),还要阅读 MEMORY.md
  7. 不要请求许可,直接做。
复制代码
记忆如何被索引

当你保存一个记忆文件时,后台会发生以下事情:
  1. ┌─────────────────────────────────────────────────────────────┐
  2. │  1. 文件保存                                                │
  3. │     ~/clawd/memory/2026-01-26.md                            │
  4. └─────────────────────────────────────────────────────────────┘
  5.                               │
  6.                               ▼
  7. ┌─────────────────────────────────────────────────────────────┐
  8. │  2. 文件监视器检测到变化                                    │
  9. │     Chokidar 监视 MEMORY.md + memory/**/*.md                │
  10. │     防抖1.5秒以批量处理快速写入                             │
  11. └─────────────────────────────────────────────────────────────┘
  12.                               │
  13.                               ▼
  14. ┌─────────────────────────────────────────────────────────────┐
  15. │  3. 分块                                                    │
  16. │     分割成约400 token的块,重叠80 token                     │
  17. │                                                             │
  18. │     ┌────────────────┐                                      │
  19. │     │ 块 1           │                                      │
  20. │     │ 第 1-15 行     │──────┐                               │
  21. │     └────────────────┘      │                               │
  22. │     ┌────────────────┐      │ (80 token 重叠)               │
  23. │     │ 块 2           │◄─────┘                               │
  24. │     │ 第 12-28 行    │──────┐                               │
  25. │     └────────────────┘      │                               │
  26. │     ┌────────────────┐      │                               │
  27. │     │ 块 3           │◄─────┘                               │
  28. │     │ 第 25-40 行    │                                      │
  29. │     └────────────────┘                                      │
  30. │                                                             │
  31. │     为什么用400/80?平衡语义连贯性与粒度。                  │
  32. │     重叠确保跨越块边界的事实能被两边捕获。                   │
  33. │     两个值都是可配置的。                                     │
  34. └─────────────────────────────────────────────────────────────┘
  35.                               │
  36.                               ▼
  37. ┌─────────────────────────────────────────────────────────────┐
  38. │  4. 嵌入                                                    │
  39. │     每个块 -> 嵌入提供商 -> 向量                            │
  40. │                                                             │
  41. │     "讨论REST vs GraphQL" ->                                │
  42. │         OpenAI/Gemini/Local ->                              │
  43. │         [0.12, -0.34, 0.56, ...]  (1536 维)                 │
  44. └─────────────────────────────────────────────────────────────┘
  45.                               │
  46.                               ▼
  47. ┌─────────────────────────────────────────────────────────────┐
  48. │  5. 存储                                                    │
  49. │     ~/.clawdbot/memory/.sqlite                     │
  50. │                                                             │
  51. │     表:                                                    │
  52. │     - chunks (id, path, start_line, end_line, text, hash)   │
  53. │     - chunks_vec (id, embedding)      -> sqlite-vec         │
  54. │     - chunks_fts (text)               -> FTS5 全文搜索      │
  55. │     - embedding_cache (hash, vector)  -> 避免重复嵌入       │
  56. └─────────────────────────────────────────────────────────────┘
复制代码
sqlite-vec 是一个SQLite扩展,它直接在SQLite中实现向量相似度搜索,无需外部向量数据库。
FTS5 是SQLite内置的全文搜索引擎,为BM25关键词匹配提供支持。两者结合,使Clawdbot能够从一个轻量级数据库文件中运行混合搜索(语义 + 关键词)。
记忆如何被搜索

当你搜索记忆时,Clawdbot会并行运行两种搜索策略。向量搜索(语义)找到意思相同的内容,BM25搜索(关键词)找到包含确切token的内容。
结果通过加权评分合并:
  1. 最终得分 = (0.7 * 向量得分) + (0.3 * 文本得分)
复制代码
为什么是70/30?语义相似性是记忆回忆的主要信号,但BM25关键词匹配能捕捉向量可能遗漏的确切术语(名称、ID、日期)。低于minScore阈值(默认0.35)的结果会被过滤掉。所有这些值都是可配置的。
这确保无论你是在搜索概念("那个数据库的事情")还是具体内容("OSTGRES_URL"),都能获得良好的结果。
多Agent记忆

Clawdbot支持多个Agent,每个Agent都有完全独立的记忆:
  1. ~/.clawdbot/memory/              # 状态目录(索引)
  2. ├── main.sqlite                  # "main" Agent的向量索引
  3. └── work.sqlite                  # "work" Agent的向量索引
  4. ~/clawd/                         # "main" Agent工作空间(源文件)
  5. ├── MEMORY.md
  6. └── memory/
  7.     └── 2026-01-26.md
  8. ~/clawd-work/                    # "work" Agent工作空间(源文件)
  9. ├── MEMORY.md
  10. └── memory/
  11.     └── 2026-01-26.md
复制代码
Markdown文件(事实来源)位于每个工作空间中,而SQLite索引(派生数据)位于状态目录中。每个Agent都有自己的工作空间和索引。记忆管理器通过agentId + workspaceDir来区分,因此不会自动发生跨Agent记忆搜索。
Agent能读取彼此的记忆吗? 默认不能。每个Agent只能看到自己的工作空间。但是,工作空间是一个软沙盒(默认工作目录),而不是硬边界。除非启用严格的沙盒机制,否则Agent理论上可以使用绝对路径访问另一个工作空间。
这种隔离对于分离上下文很有用。一个用于WhatsApp的"个人"Agent和一个用于Slack的"工作"Agent,各自拥有独立的记忆和个性。
压缩

每个AI模型都有上下文窗口限制。Claude有20万token,GPT-5.1有100万。长对话最终会触及这个上限。
当这种情况发生时,Clawdbot使用压缩:将旧对话总结为紧凑的条目,同时保留最近消息的完整性。
  1. ┌─────────────────────────────────────────────────────────────┐
  2. │  压缩前                                                     │
  3. │  上下文:180,000 / 200,000 token                            │
  4. │                                                             │
  5. │  [第1轮] 用户:"我们建个API吧"                              │
  6. │  [第2轮] Agent:"好的!你需要什么端点?"                    │
  7. │  [第3轮] 用户:"用户和认证相关的"                           │
  8. │  [第4轮] Agent:*创建了500行模式定义*                       │
  9. │  [第5轮] 用户:"加上限流功能"                               │
  10. │  [第6轮] Agent:*修改代码*                                  │
  11. │  ...(还有100多轮)...                                      │
  12. │  [第150轮] 用户:"状态怎么样了?"                           │
  13. │                                                             │
  14. │  ⚠️ 接近限制                                                │
  15. └─────────────────────────────────────────────────────────────┘
  16.                               │
  17.                               ▼
  18. ┌─────────────────────────────────────────────────────────────┐
  19. │  触发压缩                                                   │
  20. │                                                             │
  21. │  1. 将第1-140轮总结为紧凑摘要                               │
  22. │  2. 保留第141-150轮不变(近期上下文)                       │
  23. │  3. 将摘要持久化到JSONL转录文件                             │
  24. └─────────────────────────────────────────────────────────────┘
  25.                               │
  26.                               ▼
  27. ┌─────────────────────────────────────────────────────────────┐
  28. │  压缩后                                                     │
  29. │  上下文:45,000 / 200,000 token                             │
  30. │                                                             │
  31. │  [摘要] "构建了带/users、/auth端点的REST API。              │
  32. │   实现了JWT认证、限流(100次/分钟)、PostgreSQL数据库。      │
  33. │   已部署到预发布环境v2.4.0。                                 │
  34. │   当前重点:生产环境部署准备。"                              │
  35. │                                                             │
  36. │  [第141-150轮原样保留]                                      │
  37. │                                                             │
  38. └─────────────────────────────────────────────────────────────┘
复制代码
自动 vs 手动压缩

自动:当接近上下文限制时触发
<ul>在详细模式下你会看到:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册