公众号文章修改
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -1,74 +1,79 @@
|
|||||||
# 5天血泪史:我的AI助理为什么总失忆?OpenClaw内存调试全记录
|
# 养龙虾5天血泪史:我的AI Agent为什么总失忆?OpenClaw 记忆调试全记录
|
||||||
|
|
||||||
|
![[IMG-20260331114331769.png]]
|
||||||
当你的AI助理像个金鱼一样只有7秒记忆,每次对话都要从头开始——这不是科幻,而是我花了5天时间解决的现实问题。从对话压缩到搜索失效,从系统臃肿到模型切换失忆,这是我用血泪换来的10条OpenClaw内存管理黄金法则。
|
当你的AI助理像个金鱼一样只有7秒记忆,每次对话都要从头开始——这不是科幻,而是我花了5天时间解决的现实问题。从对话压缩到搜索失效,从系统臃肿到模型切换失忆,这是我用血泪换来的10条OpenClaw内存管理黄金法则。
|
||||||
|
|
||||||
# 5天血泪史:我的AI助理为什么总失忆?OpenClaw内存调试全记录
|
# 5天血泪史:我的AI助理为什么总失忆?OpenClaw内存调试全记录
|
||||||
|
|
||||||
> 当你的AI助理像个金鱼一样只有7秒记忆,每次对话都要从头开始——这不是科幻,而是我花了5天时间解决的现实问题。
|
> 当你的AI助理像个金鱼一样只有7秒记忆,每次对话都要从头开始——这不是科幻,而是我花了5天时间解决的现实问题。
|
||||||
|
|
||||||
我叫Ramya,正在开发TweetSmash和LinkedMash两款社交书签工具。我的AI助理名叫Chiti,它运行在Telegram上,负责两个SaaS产品的客户支持、起草推文、管理发票,还要协调我和联合创始人的跨时区工作。
|
我叫比利哥,正在研究如何利用AI提高工作效率。我的OpenClaw AI助理名叫星辉,它运行在Telegram上,负责管理我的个人任务规划、日程安排、搜集资料、起草推文、文件整理。
|
||||||
|
|
||||||
它就像我的初级员工——直到它开始频繁失忆。
|
从开始"雇佣"它,它就像我的初级员工——直到它开始频繁失忆。
|
||||||
|
|
||||||
不是那种微妙的遗忘。我花一个小时配置每日定时任务,切换模型后,Chiti表现得像我们从未交谈过。我提到两天前的决定,它一脸茫然。让它继续任务,它却从头开始。
|
不是那种微妙的遗忘。我花一个小时配置每日定时任务,切换模型后,星辉表现得像我们从未交谈过。我提到两天前的讨论决定,它一脸茫然。让它继续任务,它却从头开始。
|
||||||
|
|
||||||
于是我停止了功能开发,花了5天时间专门修复内存问题。以下是我发现的一切、我搞砸的一切,以及真正有效的一切。
|
于是我停止了我手头的其他工作,花了5天时间专门修复OpenClaw的记忆问题。以下是我发现的一切、我搞砸的一切,以及真正有效的一切。
|
||||||
|
|
||||||
## Day 1:长对话后的集体失忆
|
## Day 1:长对话后的集体失忆
|
||||||
|
|
||||||
第一个问题描述简单,诊断痛苦。
|
第一个问题描述简单,诊断痛苦。
|
||||||
|
|
||||||
长对话后,Chiti开始丢失早期上下文。不是逐渐丢失,而是突然消失。20条消息前告诉它的事情,没了。会话开始时做的决定?从未发生。
|
长对话后,星辉开始丢失早期上下文。不是逐渐丢失,而是突然消失。20条消息前告诉它的事情,没了。会话开始时做的决定?从未发生。
|
||||||
|
|
||||||
**罪魁祸首是压缩机制。** 当对话填满上下文窗口时,OpenClaw会将旧消息压缩成摘要,为新消息腾出空间。摘要抓住了要点,但丢掉了细节——姓名、数字、具体决定,统统消失。
|
**罪魁祸首是压缩机制。** 当对话填满Context Window时,OpenClaw会将旧消息压缩成摘要,为新消息腾出空间。摘要抓住了要点,但丢掉了细节——姓名、数字、具体决定,统统消失。
|
||||||
|
|
||||||
> “这是设计使然。上下文窗口是有限的。但默认行为对一切一视同仁,这意味着你精心设计的第三条消息指令,和第七条消息的闲聊得到了相同待遇。”
|
> “这是设计使然。上下文窗口是有限的。但默认行为对一切一视同仁,这意味着你精心设计的第三条消息指令,和第七条消息的闲聊得到了相同待遇。”
|
||||||
|
|
||||||
**我的解决方案:**
|
**我的解决方案:**
|
||||||
|
|
||||||
启用压缩前的内存刷新。这告诉代理在压缩器运行前将重要上下文写入磁盘。
|
启用压缩前的内存刷新。这告诉代理在压缩器运行前将重要上下文写入磁盘。
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{ "compaction": { "memoryFlush": { "enabled": true, "softThresholdTokens": 4000 } } }
|
{
|
||||||
|
"compaction": {
|
||||||
|
"memoryFlush": {
|
||||||
|
"enabled": true,
|
||||||
|
"softThresholdTokens": 4000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
当会话接近上下文限制时,OpenClaw触发一个静默回合,提醒代理在压缩擦除前将持久事实保存到memory/YYYY-MM-DD.md。代理写入重要内容,压缩运行,即使上下文摘要丢失,重要内容仍保留在磁盘上。
|
当会话接近上下文限制时,OpenClaw触发一个静默回合,提醒Agent在压缩擦除前将持久事实保存到memory/YYYY-MM-DD.md。代理写入重要内容,压缩运行,即使上下文摘要丢失,重要内容仍保留在磁盘上。
|
||||||
|
不过这里要声明一下,4000这个数值要根据你使用的模型来考虑,如果你是使用Gemini/GTP-4.1/Claud等大模型通常上下文窗口可以达到32K/128K/200K tokens, 那么4000这个数值就太保守了,会导致频繁的summary, 上下文"碎片化",reasoning质量下降。
|
||||||
|
|
||||||
**关键洞察:**
|
**关键洞察:**
|
||||||
压缩不是敌人。压缩过程中丢失信息才是。修复方法是确保任何值得记住的内容在压缩器触及前写入文件。如果只在上下文窗口中,它是临时的。如果在磁盘上,它就能存活。
|
压缩不是敌人。压缩过程中丢失信息才是。修复方法是确保任何值得记住的内容在压缩器触及前写入文件。如果只在上下文窗口中,它是临时的。如果在磁盘上,它就能存活。
|
||||||
|
|
||||||
## Day 2:搜索返回垃圾结果
|
## Day 2:搜索返回垃圾结果
|
||||||
|
|
||||||
随着每日日志积累和MEMORY.md增长,我需要代理真正找到东西。内置的内存搜索返回不相关结果或错过明显匹配。
|
随着每日日志积累和MEMORY.md增长,我需要星辉开始真正找到东西。内置的内存搜索返回不相关结果或错过明显匹配。
|
||||||
|
|
||||||
**问题是搜索后端。** OpenClaw默认的基于SQLite的搜索使用向量嵌入(语义相似性)查找相关块。这对广泛查询有效,但在精确匹配上挣扎。我搜索特定客户名,却得到关于完全不同主题的结果,只是因为使用了相似语言。
|
**问题是搜索后端。** OpenClaw默认的基于SQLite的搜索使用向量嵌入(语义相似性)查找相关块。这对广泛查询有效,但在精确匹配上挣扎。我搜索特定内容,却得到关于完全不同主题的结果,只是因为使用了相似语言。
|
||||||
|
|
||||||
**我的解决方案:**
|
**我的解决方案:**
|
||||||
|
切换到QMD作为内存搜索后端。QMD结合了BM25(关键词匹配)、向量嵌入和重新排序器。所以当我搜索“n8n工作流执行结果”时,它找到包含这些确切词语的结果AND语义相关的结果,然后按相关性重新排序。
|
||||||
切换到QMD作为内存搜索后端。QMD结合了BM25(关键词匹配)、向量嵌入和重新排序器。所以当我搜索“Charles支付失败”时,它找到包含这些确切词语的结果AND语义相关的结果,然后按相关性重新排序。
|
|
||||||
|
|
||||||
**关键洞察:**
|
**关键洞察:**
|
||||||
纯语义搜索理论上听起来不错,但在专有名词、具体数字和确切短语上失败。混合搜索(关键词+向量+重新排序)对现实世界代理内存明显更好。如果你的代理找不到你知道在其文件中的内容,搜索后端可能是瓶颈,而不是文件本身。
|
纯语义搜索理论上听起来不错,但在专有名词、具体数字和确切短语上失败。混合搜索(关键词+向量+重新排序)对现实世界代理内存明显更好。如果你的代理找不到你知道在其文件中的内容,搜索后端可能是瓶颈,而不是文件本身。
|
||||||
|
|
||||||
## Day 3:代理找到了但不使用
|
## Day 3:Agent找到了但不使用
|
||||||
|
|
||||||
这是最令人沮丧的一天。我确认搜索有效,可以手动查询并获得正确结果。但在实际对话中,即使相关上下文明显存在于内存中,Chiti也不会检索。
|
这是最令人沮丧的一天。我确认搜索是有效,可以手动查询并获得正确结果。但在实际对话中,即使相关上下文明显存在于内存中,星辉也不会检索。
|
||||||
|
|
||||||
**问题是检索不是自动的。** 代理必须决定搜索。如果对话没有触发正确线索,它就不会查找。
|
**问题是检索不是自动的。** Agent必须决定搜索。如果对话没有触发正确线索,它就不会查找。
|
||||||
|
|
||||||
**我的解决方案:**
|
**我的解决方案:**
|
||||||
|
|
||||||
在启动序列中添加明确的检索指令。不是希望代理在需要时搜索,而是告诉它何时搜索:
|
在启动序列中添加明确的检索指令。不是希望代理在需要时搜索,而是告诉它何时搜索:
|
||||||
|
|
||||||
> 开始任何任务前:
|
> 开始任何任务前:
|
||||||
> - 搜索每日日志获取相关上下文
|
> - 搜索每日日志(memory/YYYY-MM-DD.md)获取相关上下文
|
||||||
> - 检查LEARNINGS.md获取此类任务的规则
|
> - 检查LEARNINGS.md获取此类任务的规则
|
||||||
> - 如果提到客户,搜索其历史记录
|
> - 如果提到客户,搜索其历史记录
|
||||||
|
|
||||||
我还建立了检索测试。我在每日日志中植入特定标记——类似“标记:2026-02-20 — 在声称代码已推送前始终检查git状态。”然后等待,开始新会话,问:“昨天的标记是什么?”如果代理找到它,检索有效。如果没有,某些地方出错了。
|
我还建立了检索测试。我在每日日志中植入特定标记——类似“标记:2026-02-20 — 在笔记更新后自动运行笔记同步到git”然后等待,开始新会话,测试问:“昨天的标记是什么?”如果Agent找到它,检索有效。如果没有,某些地方出错了。
|
||||||
|
|
||||||
**关键洞察:**
|
**关键洞察:**
|
||||||
“信息存在”和“代理使用信息”之间有区别。你需要两者。搜索基础设施处理第一部分。启动指令和检索习惯处理第二部分。分别测试两者。
|
“信息存在”和“Agent使用信息”之间有区别。你需要两者。搜索基础设施处理第一部分。启动指令和检索习惯处理第二部分。分别测试两者。
|
||||||
|
|
||||||
## Day 4:让它对压缩安全
|
## Day 4:让它对压缩安全
|
||||||
|
|
||||||
@@ -81,10 +86,16 @@
|
|||||||
配置上下文修剪与压缩协同工作:
|
配置上下文修剪与压缩协同工作:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{ "contextPruning": { "mode": "cache-ttl", "ttl": "6h", "keepLastAssistants": 3 } }
|
{
|
||||||
|
"contextPruning": {
|
||||||
|
"mode": "cache-ttl",
|
||||||
|
"ttl": "6h",
|
||||||
|
"keepLastAssistants": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这会在6小时后积极修剪旧上下文,同时保留最后3个助理响应。结合内存刷新,这意味着代理早期将重要内容写入磁盘,旧上下文在导致溢出前被清理。
|
这会在6小时后积极修剪旧上下文,同时保留最后3个Assistants响应。结合内存刷新,这意味着Agent早期将重要内容写入磁盘,旧上下文在导致溢出前被清理。
|
||||||
|
|
||||||
**关键洞察:**
|
**关键洞察:**
|
||||||
长会话是内存系统真正接受测试的地方。短对话很少触及压缩。是2小时的深度工作会话中你会丢失上下文且无法找出原因。在负载下测试你的内存系统,而不仅仅是在快速聊天中。
|
长会话是内存系统真正接受测试的地方。短对话很少触及压缩。是2小时的深度工作会话中你会丢失上下文且无法找出原因。在负载下测试你的内存系统,而不仅仅是在快速聊天中。
|
||||||
@@ -93,9 +104,9 @@
|
|||||||
|
|
||||||
这是所有事情都清晰的一天。我运行了/context detail并盯着数字。
|
这是所有事情都清晰的一天。我运行了/context detail并盯着数字。
|
||||||
|
|
||||||
我的代理在读取我的消息前加载了11,887个令牌的系统提示词。51个技能,其中20个我从未使用。MEMORY.md是200行公司维基,每个会话都加载。我有两个竞争的启动序列——一个在BOOT.md中(OpenClaw甚至不识别),一个埋在AGENTS.md的200行深处。
|
我的代理在读取我的消息前加载了20,9652个令牌的系统提示词。54个技能,其中20个我从未使用。我有两个竞争的启动序列——一个在BOOT.md中(OpenClaw甚至不识别),一个埋在AGENTS.md的200行深处。
|
||||||
|
|
||||||
最糟糕的是,每次切换模型,Chiti忘记一切。没有交接协议。没有当前上下文的写回。直接消失。
|
最糟糕的是,每次切换模型,星辉忘记一切。没有交接协议。没有当前上下文的写回。直接消失。
|
||||||
|
|
||||||
**根本原因:**
|
**根本原因:**
|
||||||
|
|
||||||
@@ -119,8 +130,8 @@ OpenClaw在每个新会话上自动读取这些文件:AGENTS.md、SOUL.md、TO
|
|||||||
|
|
||||||
**结果:**
|
**结果:**
|
||||||
|
|
||||||
- 系统提示词:11,887 → 8,529个令牌
|
- 系统提示词:20,9652 → 9,349个令牌
|
||||||
- 技能:51 → 32
|
- 技能:51 → 31
|
||||||
- 会话令牌:18,280 → 14,627
|
- 会话令牌:18,280 → 14,627
|
||||||
- 轻了28%。相同的代理。相同的模型。只是更少噪音。
|
- 轻了28%。相同的代理。相同的模型。只是更少噪音。
|
||||||
|
|
||||||
@@ -185,14 +196,13 @@ workspace/
|
|||||||
│ └── LEARNINGS.md (错误中的规则)
|
│ └── LEARNINGS.md (错误中的规则)
|
||||||
├── memory/ (每日日志:YYYY-MM-DD.md)
|
├── memory/ (每日日志:YYYY-MM-DD.md)
|
||||||
├── docs/ (参考文档移出MEMORY.md)
|
├── docs/ (参考文档移出MEMORY.md)
|
||||||
│ ├── tweetsmash-arch.md
|
|
||||||
│ ├── knowledge-transfer.md
|
│ ├── knowledge-transfer.md
|
||||||
│ ├── infrastructure.md
|
│ ├── infrastructure.md
|
||||||
│ └── group-chat-rules.md
|
│ └── group-chat-rules.md
|
||||||
└── skills/ (32个技能,从51个减少)
|
└── skills/ (32个技能,从51个减少)
|
||||||
```
|
```
|
||||||
|
|
||||||
系统提示词:8,529个令牌。会话令牌:14,627个,占200,000上下文窗口的7.3%。代理启动,读取所需内容,写入所学内容,在模型切换前交接上下文。
|
系统提示词:8,529个令牌。会话令牌:14,627个,占200,000上下文窗口的7.3%。Agent启动,读取所需内容,写入所学内容,在模型切换前交接上下文。
|
||||||
|
|
||||||
我花了5天时间到达这里。大部分是忘记“更多文件等于更好内存”的假设。不是这样。纪律才是。我的实验仍在继续。
|
我花了5天时间到达这里。大部分是忘记“更多文件等于更好内存”的假设。不是这样。纪律才是。我的实验仍在继续。
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s "https://api.unsplash.com/search/photos" \
|
curl -s "https://api.unsplash.com/search/photos" \
|
||||||
-H "Authorization: Client-ID 你的AccessKey" \
|
-H "Authorization: Client-ID bzq5vp2kcUqlKTtLL3dwECkha1-jinwttn5JlhwjTBw" \
|
||||||
-G \
|
-G \
|
||||||
--data-urlencode "query=AI automation" \
|
--data-urlencode "query=AI automation" \
|
||||||
--data-urlencode "per_page=3"
|
--data-urlencode "per_page=3"
|
||||||
@@ -34,7 +34,7 @@ curl -s "https://api.unsplash.com/search/photos" \
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s "https://api.unsplash.com/search/photos" \
|
curl -s "https://api.unsplash.com/search/photos" \
|
||||||
-H "Authorization: Client-ID 你的AccessKey" \
|
-H "Authorization: Client-ID bzq5vp2kcUqlKTtLL3dwECkha1-jinwttn5JlhwjTBw" \
|
||||||
-G \
|
-G \
|
||||||
--data-urlencode "query=AI automation" \
|
--data-urlencode "query=AI automation" \
|
||||||
--data-urlencode "per_page=3" \
|
--data-urlencode "per_page=3" \
|
||||||
|
|||||||
Reference in New Issue
Block a user