Clawdbot 如何记住一切

图片


Clawdbot 是一个开源的个人 AI 助手(MIT 许可证),由 Peter Steinberger 创建。和运行在云端的 ChatGPT 或 Claude 不同,Clawdbot 跑在你自己的电脑上,并且能接入你已经在用的聊天平台,比如 钉钉、飞书 等。

Clawdbot 真正出圈的点,是它能自主完成现实世界里的任务:管理邮件、创建日历日程、帮你做航班值机、按计划运行后台任务……但最吸引我的,是它的持久化记忆系统:它能 24/7 保留上下文,记住你们聊过的内容,并且能在之后的互动里无限叠加、持续利用。

Clawdbot 的路线:它不把记忆放在云端、交给公司控制,而是把一切都放在本地——用户完全拥有自己的上下文与技能。

下面我们拆开看看它是怎么做到的。


上下文是怎么拼出来的

在深入记忆之前,先搞清楚:每次请求时,模型到底看到了什么:

[0] System Prompt(系统提示词:静态 + 条件指令)
[1] Project Context(项目上下文:启动文件,如 AGENTS.md、SOUL.md 等)
[2] Conversation History(对话历史:消息、工具调用、压缩摘要)
[3] Current Message(当前消息)

系统提示词决定了智能体的能力边界与可用工具。和记忆最相关的是 Project Context:它会把一组用户可编辑的 Markdown 文件注入到每一次请求里:

File
Purpose
AGENTS.md
智能体指令(包括记忆相关的规则)
SOUL.md
性格与语气风格
USER.md
关于用户的信息
TOOLS.md
外部工具使用指南

这些文件和记忆文件一起放在智能体的工作目录里,因此整个智能体的配置是透明、可见、可编辑的。


Context vs Memory

上下文是模型在单次请求里能看到的一切:

上下文 = 系统提示词 + 对话历史 + 工具结果 + 附件

上下文的特点是:

  • • 短暂:只为这一次请求存在
  • • 有限:受模型上下文窗口限制(比如 200K tokens)
  • • 昂贵:每个 token 都会影响 API 成本与速度

记忆则是写入磁盘的内容:

记忆 = MEMORY.md + memory/*.md + 会话转录(transcripts)

记忆的特点是:

  • • 持久:重启也不会丢,能跨天、跨月
  • • 无限:可以一直增长
  • • 便宜:存到本地不增加 API 成本
  • • 可搜索:会被索引,支持语义检索

记忆工具

智能体通过两个专门的工具来访问记忆:

memory_search

用途:在所有记忆文件里找相关内容

{
  "name": "memory_search",
  "description": "强制回忆步骤:在回答有关过去工作/决策/日期/人物/偏好/待办的问题前,先对 MEMORY.md + memory/*.md 做语义搜索",
  "parameters": {
    "query": "我们关于 API 做了什么决定?",
    "maxResults": 6,
    "minScore": 0.35
  }
}

返回:

{
  "results": [
    {
      "path": "memory/2026-01-20.md",
      "startLine": 45,
      "endLine": 52,
      "score": 0.87,
      "snippet": "## API 讨论\n为了简单起见,决定用 REST 而不是 GraphQL……",
      "source": "memory"
    }
  ],
  "provider": "openai",
  "model": "text-embedding-3-small"
}

memory_get

用途:在 memory_search 找到线索后,读取具体内容

{
  "name": "memory_get",
  "description": "在 memory_search 之后,从某个记忆文件里读取指定行数的内容",
  "parameters": {
    "path": "memory/2026-01-20.md",
    "from": 45,
    "lines": 15
  }
}

返回:

{
  "path": "memory/2026-01-20.md",
  "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"
}

写入记忆

Clawdbot 没有专门的 memory_write 工具。智能体写入记忆时,用的就是它平时写文件的标准 write 和 edit 工具。因为记忆本质就是 Markdown,你也可以手动改这些文件(系统会自动重新索引)。

至于该写到哪里,由 AGENTS.md 里的提示词规则来驱动:

触发条件
写到哪里
日常笔记、记住这个
memory/YYYY-MM-DD.md
长期稳定的事实、偏好、决策
MEMORY.md
经验教训
AGENTS.md
 或 TOOLS.md

此外,在压缩(compaction)前的 flush以及会话结束时,也会发生自动写入(后面会讲)。


记忆存在哪里

Clawdbot 的记忆系统建立在一个核心原则上:记忆就是智能体工作区里的一堆纯 Markdown。

Two-Layer Memory System

记忆文件放在智能体的工作区里(默认:~/clawd/):

~/clawd/
├── MEMORY.md              - 第 2 层:整理过的长期记忆
└── memory/
    ├── 2026-01-26.md      - 第 1 层:今天的日记
    ├── 2026-01-25.md      - 昨天的日记
    ├── 2026-01-24.md      - ...以此类推
    └── ...
Layer 1: Daily Logs (memory/YYYY-MM-DD.md)

这是只追加的日记式笔记。智能体会在一天中不断往里面写:当它觉得某件事值得记住,或者你明确让它记住时,它就写这里。

# 2026-01-26

## 10:30 AM - API 讨论
和用户讨论 REST vs GraphQL。决策:为了简单,选择 REST。
关键端点:/users、/auth、/projects。

## 2:15 PM - 部署
将 v2.3.0 部署到生产环境。没有问题。

## 4:00 PM - 用户偏好
用户提到自己更喜欢 TypeScript 而不是 JavaScript

Layer 2: Long-term Memory (MEMORY.md)

这是被整理过的长期知识库。只有当发生重要事件、关键决策、观点、经验教训时,智能体才会把内容写进这里。

# 长期记忆

## 用户偏好
- 更喜欢 TypeScript 而不是 JavaScript
- 喜欢简洁的解释
- 正在做一个名为 "Acme Dashboard" 的项目

## 重要决策
- 2026-01-15:数据库选用 PostgreSQL
- 2026-01-20:采用 REST 而不是 GraphQL
- 2026-01-26:使用 Tailwind CSS 做样式

## 关键联系人
- Alice (alice@acme.com) - 设计负责人
- Bob (bob@acme.com) - 后端工程师

How the Agent Knows to Read Memory

AGENTS.md(会被自动加载)里包含了这样的指令:

## 每次会话(Every Session)

在做任何事之前:
1. 读 SOUL.md - 这决定你是谁
2. 读 USER.md - 这决定你在帮助谁
3. 读 memory/YYYY-MM-DD.md(今天和昨天)获取最近上下文
4. 如果是 MAIN SESSION(直接和人类对话的主会话),也要读 MEMORY.md

不要征求许可,直接做。

翻成人话就是:每次会话开始,先把我是谁,我在帮谁,最近发生了啥(今天/昨天)读一遍;如果是主会话,再把长期记忆(MEMORY.md)也读上。


记忆如何被索引

当你保存了一条记忆文件,背后会发生这些事:

  1. 1. 文件保存(例如 ~/clawd/memory/2026-01-26.md
  2. 2. 文件监听器检测变化:用 Chokidar 监听 MEMORY.md + memory/**/*.md,并用 1.5 秒的 debounce 把频繁写入攒一攒再处理
  3. 3. 切块(Chunking):把文本切成大约 400 token 的块,并做 80 token 的重叠
  4. 4. 向量化(Embedding):每个 chunk 送给 embedding 提供方(OpenAI / Gemini / 本地)生成向量(示例里是 1536 维)
  5. 5. 存储:写入 ~/.clawdbot/memory/<agentId>.sqlite,其中包括 chunks 表、向量表、全文检索表、以及 embedding 缓存

文章里还提到:

sqlite-vec 是一个 SQLite 扩展,让你可以在 SQLite 里直接做向量相似度检索,不需要额外的向量数据库。
FTS5 是 SQLite 内置的全文检索引擎,支持 BM25 关键词匹配。两者结合后,Clawdbot 可以用同一个轻量数据库文件做混合检索(语义 + 关键词)。


How Memory is Searched

当你搜记忆时,Clawdbot 会并行跑两套检索:

  • • 向量检索(语义):找意思相近的内容
  • • BM25(关键词):找字面命中的 token

然后按权重融合:

最终得分 = 0.7 * 向量得分 + 0.3 * 文本得分

为什么是 70/30?因为语义相似度是主要信号,但 BM25 能兜住向量可能漏掉的精确项(人名、ID、日期等)。低于 minScore(默认 0.35)的结果会被过滤掉。这些值都可配置。

这让你既能用那个数据库的事儿这种模糊描述去搜,也能用 POSTGRES_URL这种精确关键词去搜。


多智能体记忆:彼此隔离

Clawdbot 支持多个智能体,并且它们的记忆是完全隔离的:

~/.clawdbot/memory/              # 状态目录(索引)
├── main.sqlite                  # main 智能体的向量索引
└── work.sqlite                  # work 智能体的向量索引

~/clawd/                         # main 智能体工作区(事实源文件)
├── MEMORY.md
└── memory/
    └── 2026-01-26.md

~/clawd-work/                    # work 智能体工作区(事实源文件)
├── MEMORY.md
└── memory/
    └── 2026-01-26.md

Markdown 文件(事实源)在各自 workspace 里;SQLite 索引(衍生数据)在 state 目录里。记忆管理器用 agentId + workspaceDir 做键,所以默认不会跨智能体检索记忆。

智能体能不能读到彼此的记忆?**默认不能。**每个智能体只看到自己的 workspace。只是要注意:workspace 更像软沙盒(默认工作目录),不是硬边界。除非你开启严格沙盒,否则智能体理论上可以用绝对路径去访问别的 workspace。

这种隔离非常适合做场景拆分:比如一个私人智能体在 WhatsApp 上陪你,另一个工作智能体在 Slack 上干活——各自有独立记忆与性格。


压缩:对话太长怎么办

每个模型都有上下文窗口上限:Claude 是 200K tokens,GPT-5.1 是 1M。对话变长后,总会撞墙。

这时 Clawdbot 会做 compaction(压缩/精简):把更早的对话总结成一段浓缩摘要,同时保留最近的消息原样。

┌─────────────────────────────────────────────────────────────┐
│  压缩前(Before Compaction)                                  │
│  上下文:180,000 / 200,000 tokens                             │
│                                                             │
│  [Turn 1] 用户:"我们来做个 API"                               │
│  [Turn 2] 智能体:"好呀!你需要哪些端点?"                     │
│  [Turn 3] 用户:"用户和鉴权"                                   │
│  [Turn 4] 智能体:*生成 500 行 schema*                          │
│  [Turn 5] 用户:"加上限流"                                     │
│  [Turn 6] 智能体:*改代码*                                     │
│  ...(又聊了 100 多轮)...                                    │
│  [Turn 150] 用户:"现在进度咋样?"                             │
│                                                             │
│  ⚠️ 快到上限了(APPROACHING LIMIT)                            │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  触发压缩(Compaction Triggered)                             │
│                                                             │
│  1. 将第 1-140 轮总结成一段精简摘要                            │
│  2. 保留第 141-150 轮原样不动(最近上下文)                    │
│  3. 把摘要写入 JSONL 会话转录(transcript)                    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  压缩后(After Compaction)                                   │
│  上下文:45,000 / 200,000 tokens                              │
│                                                             │
│  [摘要] "用 /users、/auth 端点搭了 REST API。                  │
│   做了 JWT 鉴权、限流(100 次/分钟)、                         │
│   用 PostgreSQL。部署到 staging v2.4.0。                      │
│   当前重点:准备上线生产。"                                   │
│                                                             │
│  [Turn 141-150 保留原样]                                      │
└─────────────────────────────────────────────────────────────┘

自动 vs 手动压缩

自动:接近上下文上限时触发

  • • verbose 模式下你会看到: Auto-compaction complete
  • • 原请求会在压缩后自动重试

手动:用 /compact 命令

/compact 只关注关键决策与未解决的问题

而且,compaction 不只是为了省 token的一次性优化:压缩摘要会写入磁盘,存到会话的 JSONL transcript 文件里,所以未来会话可以直接从压缩后的历史开始。


压缩前记忆冲刷

基于大语言模型的压缩是有损的:重要信息可能被总结抹掉甚至丢失。为了解决这个风险,Clawdbot 在压缩前会做一次 pre-compaction memory flush(压缩前记忆冲刷)

它的机制是:当上下文使用率跨过软阈值时,系统会插入一个静默的记忆冲刷轮次:

  • • System:提示智能体现在把耐久的记忆写进 memory/YYYY-MM-DD.md;如果没什么可写就回复 NO_REPLY
  • • Agent:回顾对话,把关键决策/事实写入记忆文件
  • • 用户:看不到这轮(因为 NO_REPLY

这样一来,真正 compaction 的时候,即使摘要有损,关键知识也已经落盘了。

┌─────────────────────────────────────────────────────────────┐
│  上下文接近上限(Context Approaching Limit)                   │
│                                                             │
│  ████████████████████████████░░░░░░░░  上下文已用 75%          │
│                              ↑                              │
│                    触发软阈值(Soft threshold crossed)       │
│        (contextWindow - reserve - softThreshold)           │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  静默记忆冲刷回合(Silent Memory Flush Turn)                  │
│                                                             │
│  System: "压缩前记忆冲刷。现在把需要长期保留的内容             │
│          写到 memory/YYYY-MM-DD.md。                          │
│          如果没什么可写,就回复 NO_REPLY。"                   │
│                                                             │
│  Agent:回顾对话,提取重要信息                               │
│        把关键决策/事实写入记忆文件                           │
│        -> NO_REPLY(用户什么都看不到)                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  安全地继续压缩(Compaction Proceeds Safely)                  │
│                                                             │
│  关键信息已写入磁盘                                           │
│  压缩不会把知识压没                                         │
└─────────────────────────────────────────────────────────────┘

这套 flush 也可在 clawdbot.yaml 或 clawdbot.json 里配置:

{
  agents: {
    defaults: {
      compaction: {
        reserveTokensFloor: 20000,
        memoryFlush: {
          enabled: true,
          softThresholdTokens: 4000,
          systemPrompt: "会话即将压缩。现在保存需要长期保留的记忆。",
          prompt: "把长期价值的内容写到 memory/YYYY-MM-DD.md;若无内容可写则回复 NO_REPLY。"
        }
      }
    }
  }
}

裁剪:工具输出太大怎么办

工具调用结果可能非常巨大:一次 exec 可能输出 50,000 字符的日志。裁剪会在不改写历史的前提下,把旧的工具输出削薄,以减少发送给模型的上下文体积。

它同样是有损的:旧输出被裁剪后就无法恢复。

原文用示意图展示了裁剪前后:旧工具输出被截断(soft trim)或直接清空为占位符(hard clear),但磁盘上的 JSONL 文件仍保留完整输出(不变)。

按缓存 TTL 裁剪

Anthropic 会缓存 prompt 的前缀最多 5 分钟,用于降低重复调用的延迟与成本:在 TTL 内复用缓存 token,成本大约能便宜 ~90%。但问题是:如果会话空闲超过 TTL,下一次请求就失去缓存,需要把整段上下文重新写入缓存,成本回到全价。

Cache-TTL pruning 的思路是:检测到缓存过期后,在下一次请求前把旧的工具输出裁剪掉,让需要重新缓存的 prompt 变短,从而降低成本。

示例配置:

{
  agent: {
    contextPruning: {
      mode: "cache-ttl",       // 只在缓存过期后才裁剪
      ttl: "600",              // 对齐你的 cacheControlTtl
      keepLastAssistants: 3,   // 保护最近几条助手消息(别裁掉)
      softTrim: {
        maxChars: 4000,
        headChars: 1500,
        tailChars: 1500
      },
      hardClear: {
        enabled: true,
        placeholder: "[旧工具结果内容已清空]"
      }
    }
  }
}

会话生命周期

会话不会永远持续。它会按可配置的规则重置,给记忆制造天然边界。默认行为是每天重置,但也支持其他模式:

模式
行为
daily
固定小时重置(默认:本地时间凌晨 4 点)
idle
空闲超过 N 分钟后重置
daily+idle
谁先到就按谁重置

Session Memory Hook

当你运行 /new 开启新会话时,有个 session memory hook 能自动把当前上下文保存下来:

  • • 从即将结束的会话里提取最近 15 条消息
  • • 用大语言模型生成一个描述性的 slug
  • • 保存到 ~/clawd/memory/2026-01-26-api-design.md 之类的文件
  • • 然后新会话开始:旧上下文就可以通过 memory_search 被检索到
/new
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  触发 SESSION-MEMORY HOOK                                     │
│                                                             │
│  1. 从即将结束的会话里提取最近 15 条消息                      │
│  2. 用大语言模型生成一个描述性的 slug                         │
│  3. 保存到 ~/clawd/memory/2026-01-26-api-design.md           │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  新会话开始                                                   │
│                                                             │
│  之前的上下文现在可以通过 memory_search 被检索到              │
└─────────────────────────────────────────────────────────────┘

总结

Clawdbot 的记忆系统之所以好用,是因为它坚持了几条关键原则:

1. Transparency Over Black Boxes(透明胜过黑盒)

记忆就是纯 Markdown:你能读、能改、能做版本控制。没有不透明的数据库格式或专有存储。

2. Search Over Injection(检索胜过硬塞)

不是把所有历史都塞进上下文,而是按需检索相关内容。上下文更聚焦,成本也更低。

3. Persistence Over Session(落盘胜过只活在会话里)

重要信息写进磁盘文件,而不是只留在对话历史里。只要已经保存,compaction 就毁不掉它。

4. Hybrid Over Pure(混合胜过单一)

纯向量检索会漏掉精确匹配;纯关键词检索会漏掉语义。混合检索两者都要。


参考

  • • Clawdbot Documentation - 官方文档:安装、配置与全部特性
  • • GitHub Repository - 源码、issue 与社区贡献
  • • How Clawdbot Remembers Everything