OpenClaw · Entry Model

记忆衰减、条目化与文件存储

这页专门回答一个容易绕晕的问题:OpenClaw 的记忆明明存在文件系统里, 为什么又说“每条记忆有鲜活度分数”?答案是:物理上它是文件,逻辑上它是条目(entry / chunk)

先说结论:文件是容器,条目才是检索单位

OpenClaw 不是把一整个 Markdown 文件当成一条记忆来搜索,而是把文件切成多个可检索片段。 所以“存储在文件系统”和“按条目计算衰减”并不冲突,它们是两层不同的表示方式。

📁 物理层:文件系统

磁盘上看到的是 Markdown 文件:

.openclaw/
├── memory/
│ ├── user.md # 长期用户偏好
│ ├── project.md # 项目长期上下文
│ ├── session/
│ │ ├── 2026-05-18.md # 当天会话 / 日记型记忆
│ │ └── 2026-05-17.md
│ └── .index/
│ ├── bm25 / fts 数据
│ └── 向量 embedding 数据

🧠 逻辑层:条目 / chunk

检索器真正操作的是一条一条的片段:

chunk_001 → user.md lines 8-10 “喜欢 TypeScript”
chunk_002 → user.md lines 11-13 “使用 Prettier”
chunk_003 → project.md lines 20-25 “当前正在重构支付模块”
chunk_004 → 2026-05-18.md lines 5-9 “今天决定改用 Fastify”
chunk_005 → 2026-05-18.md lines 10-14 “下午排查 JWT 过期 bug”
一句话: 一个文件里可以有很多条记忆;条目是“索引里的一行”,文件只是“这些条目的容器”。

长期记忆、短期记忆、情景记忆、每天的记忆,分别长什么样?

这些类型不一定对应四套完全独立的数据库;更常见的做法是: 按不同文件作用域来存,再按条目来检索

记忆类型
典型文件
内容形态
为什么像“一条一条”
衰减特点
长期记忆
user.md / project.md
稳定偏好、长期事实、项目决策
一个文件会拆成多个主题片段
年龄可能很大,但访问频繁,排名常被 boost
短期记忆
session/2026-05-18.md
当天对话、最近几轮工作上下文
按段落 / 小节切成多个 chunk
因为“新”,初始鲜活度高;不再使用就快速掉队
情景记忆
通常就在 session/day 文件里
某次报错、某次决策、某次实验结果
每个事件就是一条检索候选
非常依赖时间和语境,常被后续合并或归档
每日记忆
memory/YYYY-MM-DD.md 或 session/*.md
当日摘要、待续事项、沉淀笔记
“一天一个文件”,但文件内仍是多条条目
文件整体不衰减,参与检索的是其中每个条目
关键澄清: “长期 / 短期 / 情景 / 每日”更像是语义分类和作用范围, 不等于“四种完全不同的存储介质”。

文件里长什么样?为什么明明一个文件,却能拆成多条?

因为 Markdown 天然就有层级结构:标题、子标题、列表、段落。 OpenClaw 可以按这些边界,把一份文件拆成多个独立的检索片段。

📄 例子:磁盘上的真实文件
---
type: user_preference
created: 2026-05-01
updated: 2026-05-18
tags: [偏好, 语言, 工具]
---

# 用户偏好

## 编程语言
- 喜欢 TypeScript
- 不喜欢 any 类型逃逸

## 代码风格
- 使用 Prettier
- 喜欢小函数、低嵌套

## 沟通方式
- 先给结论,再解释
- 不喜欢废话
🧩 索引里会变成 3 条条目
entry_001
file_path: user.md
lines: 8-10
heading: 编程语言
content: 喜欢 TypeScript;不喜欢 any 类型逃逸

entry_002
file_path: user.md
lines: 12-14
heading: 代码风格
content: 使用 Prettier;喜欢小函数、低嵌套

entry_003
file_path: user.md
lines: 16-18
heading: 沟通方式
content: 先给结论,再解释;不喜欢废话
用户看到的是一份文件;检索系统看到的是 3 个可独立打分、独立排序、独立注入 prompt 的 entry。

“鲜活度分数”到底是存在哪?是写在文件里吗?

通常不是把每条记忆的最终鲜活度每天都回写到 Markdown 文件里。 更常见的是:文件里存元数据,检索时实时计算分数

📐 鲜活度不是静态文本,而是查询时算出来的
一个简化版公式:
freshness = relevance × e(-λ × ageDays) × accessBoost
其中:
1. relevance:这条 entry 和当前问题的相关度(BM25 + 向量)
2. ageDays:这条 entry 距创建或上次更新过了多少天
3. accessBoost:被命中过多少次,常用记忆会被加权
所以“每条记忆有鲜活度分数”更准确的说法是: 每个 entry 在每次检索时都会得到一个当前分数, 它不是永远写死在文件里的固定数字。
❌ 不是这样理解
1

不是“user.md 整个文件只有一个分数”

否则一个文件里只要有一小段相关,其它不相关内容也会被整块搬进 prompt。

2

不是“每天把所有文件都重写一遍”

那样代价太大,也不必要。时间衰减完全可以在查询时动态计算。

✅ 更接近真实情况
1

文件保留原始内容和基础元数据

比如 created、updated、tags、access_count、type。

2

索引层保存每个条目的位置和向量

file_path、line_start、line_end、chunk_id、embedding 等。

3

查询时为每个 entry 单独计算 freshness

最后只把高分条目注入 prompt。

条目到底存在哪?文件系统里没有“一行一个 entry”啊

对,entry 往往不是单独一个文件;它更像“索引数据库里的记录”。 文件系统存原文,索引层存切片信息。

📁 文件系统负责保存“原文”
user.md
project.md
session/2026-05-18.md
session/2026-05-17.md
...

这些文件负责:
- 保存人类可读内容
- 方便编辑和审查
- 作为长期持久化载体
🗃️ 索引层负责保存“条目映射”
entry_id: e_20260518_004
file_path: memory/session/2026-05-18.md
line_start: 10
line_end: 14
kind: episodic
created_at: 1716019200000
access_count: 2
bm25_terms: [jwt, 过期, bug]
embedding_ref: vec_8842
所以“一条一条”通常存在于 索引表 / FTS / 向量库 里,而不是肉眼直接看到的目录树里。

完整流程:从“今天的记忆文件”到“单条衰减排序”

下面这个流程把你前面问的几个概念串起来:每日记忆、情景记忆、衰减、条目化,其实是同一个过程的不同切面。

🔄 一次完整的生命周期
1

今天发生了一件事

比如今天下午修了 JWT 过期 bug,这段内容被写进 session/2026-05-18.md

2

索引器把新文件切成条目

“上午架构讨论”是一条,“下午 JWT bug 排查”又是一条。两条都来自同一个文件。

3

用户再次提问时逐条检索

系统会对这些 entry 做 BM25、向量相似度、时间衰减、访问加权,然后排序。

4

高分条目被注入 prompt

AI 不会把整份 2026-05-18.md 都看一遍,只会拿到最相关的那一两段。

5

久不用的情景条目自然衰减

如果这次 bug 以后再也没人提,它的排名会逐渐掉下去;文件仍然还在磁盘上。

所以最终答案是: OpenClaw 既是“文件系统记忆”,也是“条目化检索记忆”。
文件负责存,索引负责拆,检索负责算分,prompt 负责使用。

📁 文件层

  • 磁盘上看到的是 Markdown 文件
  • 长期 / 每日 / 会话内容都能落盘
  • 便于人读、人改、人审计

🧩 条目层

  • 一个文件会被切成多个 entry
  • 每个 entry 单独参与检索和排序
  • “一条一条”主要存在于索引层

📉 衰减层

  • 鲜活度通常是查询时动态计算
  • 新记忆天然更靠前
  • 常用旧记忆可被 access_count 拉回来