Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Model-driven context management (Active Context Pruning / ACP) for the DeepSeek
| [Porting feasibility analysis](dsh-porting-analysis.md) | Initial study: Pi ↔ DSH API mapping, the core difficulty (no in-memory message rewrite hook), three porting paths |
| [Porting verification report](dsh-porting-verification.md) | The verified evidence behind every claim, plus the **v0.1.1 long-session battle report** (6 bugs found and fixed in real use) |
| [Configurable prompts design](configurable-prompts-design.md) | Design review draft: per-stage prompt overrides (nudge / range table / system prompt / tool descriptions) via `config.prompts`, template + named placeholders, build-time validation |
| [Cross-session memory design](cross-session-memory-design.md) | Design draft (P1-4): model-driven long-term memory — `memory_commit` at nudge time → one-shot LLM extraction → human-readable markdown memory bank → resident index + `memory_recall` readback; kernel-first ownership split (§6A: algorithms upstream to acp-kernel, host keeps DSH integration); research archive in [memory-research/](memory-research/README.md) |

## 🗂 Source layout

Expand Down
264 changes: 264 additions & 0 deletions docs/cross-session-memory-design.md

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions docs/memory-research/01-autonomous-memory-papers.md

Large diffs are not rendered by default.

362 changes: 362 additions & 0 deletions docs/memory-research/02-autonomous-memory-implementations.md

Large diffs are not rendered by default.

359 changes: 359 additions & 0 deletions docs/memory-research/03-memory-readback-timing.md

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions docs/memory-research/04-claude-code-source-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Claude Code 记忆机制源码核实(Claude Code Memory — Source-Verified)

> **核实时间**:2026-08 | **来源**:[liuup/claude-code-analysis](https://github.com/liuup/claude-code-analysis)(Claude Code 反编译镜像,`/tmp/claude-code-analysis` 已随重启消失,本文件为提炼结论)
> **用途**:billion-context-dsh 跨会话记忆设计的生产实践背书(见 `docs/cross-session-memory-design.md` §5.2)

## 1. 记忆写入(extractMemories)

- **触发**:每次完整 query loop 结束(模型产出最终回复、无更多工具调用时),经 `handleStopHooks`(`stopHooks.ts`)。
- **实现**:`runForkedAgent`——主对话的完美 fork,共享父 prompt cache(零重算),fire-and-forget 不阻塞。
- **互斥**:主 agent 本 turn 已写过记忆则跳过提取。
- **提取 fork 权限**:只允许只读工具(FileRead/Grep/Glob/只读 Bash)+ FileEdit/FileWrite **仅限** auto-memory 目录;`Bash rm` 被禁。
- **存储路径**:`~/.claude/projects/<sanitized-git-root>/memory/`。
- **4 类 taxonomy**:`user`(偏好)/`feedback`(纠正确认)/`project`(上下文决策)/`reference`(外部指针)。
- **negative constraints(防膨胀)**:显式禁止保存 code patterns、git history、debugging plans、已在 CLAUDE.md 的内容、临时任务状态。
- **金句提示**:即使确认要保存,也追问"其中什么令人惊讶或不显然?"——非显然部分才值得存。
- **团队记忆**:HTTP 同步 + secret 扫描(写前与 push 时)+ `pushSuppressedReason` gate 防认证失败无限重试。

## 2. 记忆读取(双层)

### 2.1 MEMORY.md 常驻 system prompt

- `findRelevantMemories.ts:31` 注释原文:"Excludes MEMORY.md (already loaded in system prompt)"——MEMORY.md 已在 system prompt 中,选择器排除它。
- 索引格式 = 一句话/条:`formatMemoryManifest` → `- [type] filename (timestamp): description`。
- **上限**:`MAX_MEMORY_FILES = 200`(memoryScan.ts:21,**记忆文件数上限**,非 MEMORY.md 行数——注意与子代理报告"前 200 行"表述的区别,后者可能指 docs 层的注入截断)。

### 2.2 relevant_memories 异步 prefetch(默认关闭)

- `startRelevantMemoryPrefetch`(attachments.ts:2361):
- **总开关**:`getFeatureValue_CACHED_MAY_BE_STALE('tengu_moth_copse', false)`——**feature gate,默认 OFF**(实验功能,非默认行为;首次核实时误判"源码未见该 gate",子代理报告正确,特此修正记录)。
- **语义 gate**:单字 prompt(无空格 `/\s/`)跳过——没有上下文可提取关键词。
- **频率**:每 user turn 一次(query.ts:301 注释:"Fired once per user turn — the prompt is invariant across loop iterations, so per-iteration firing would ask sideQuery the same question N times");async 非阻塞,`using` 绑定生命周期随 turn 销毁。
- **选择器**:Sonnet sideQuery(`SELECT_MEMORIES_SYSTEM_PROMPT` + JSON schema `output_format`,max_tokens 256)——从记忆文件清单选**最多 5 个**("Be selective and discerning",只选确信有用的;不确定就不选;最近使用的工具文档不选,但 warnings/gotchas 选)。
- **节流**:会话累计 `MAX_SESSION_BYTES = 60KB`(attachments.ts:288)超限停止 surfacing。
- **单文件截断**:`MAX_MEMORY_BYTES = 4096` + 行数限制,截断时提示 "Use the FILE_READ_TOOL_NAME tool to view the complete file"。
- **去重**:`alreadySurfaced`(选过的路径不再选,5-slot 预算花新候选)+ `readFileState`(模型已读文件不再塞)。
- **@-mention 隔离**:提及 agent 时只搜该 agent 的记忆目录,否则搜 auto-memory 目录。
- **compact 重置**:compact 后 old attachments 从 transcript 消失 → re-surfacing valid again(注释原文)。

## 3. 7 层记忆架构

CLAUDE.md(人写) → Auto Memory(AI 写)→ Background Extract → Session Memory(单会话,`~/.claude/projects/<slug>/<sessionId>/session-memory/summary.md`)→ Agent Memory → Relevant Memories(prefetch)→ Auto Dream(空闲整合)。

## 4. 对本项目的启示

1. **"常驻索引 + 自主取回"是生产实践**(MEMORY.md 常驻 + FileRead 取全文)——我们选 Claude 路线(索引常驻 + 全文按需),比 Letta(内容常驻)token 更省。
2. **prefetch 默认关**——记忆读回未优化前不自动灌入,支持我们"常驻索引 + 自主取回"的简化设计(不学 prefetch 自动注入)。
3. **negative constraints 与正向触发并重**——memory_commit 提示词必须写"何时不要调用"(待定项 T1)。
4. **硬预算防膨胀**——文件数 200 / 会话 60KB / 单文件 4096B,支持我们配置项草案(sessionBudgetBytes)。
5. **fork 提取的权限最小化**——提取对话只允许只读 + 记忆目录写入,`Bash rm` 禁用;我们的一次性 LLM 调用同理应只写记忆库。
6. **extractMemories 每次 turn 结束时无条件提取** vs 我们的**决策门控**(只在 memory_commit 表态时)——我们的更省(Claude 是事件驱动,我们是模型自决)。
47 changes: 47 additions & 0 deletions docs/memory-research/05-plain-language-summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 跨会话长期记忆:一页纸设计方案

## 我们在解决什么问题

现在的 AI 助手每次会话结束就"失忆"了。今天聊的结论、踩的坑、做的决定,明天开新会话全不记得,得重新讲一遍。

我们想给它加一个"笔记本":**重要的事记下来,下次会话能想起来用**。

## 核心流程:三步

```
第 1 步:记
模型觉得"这段值得记住" → 说一声"我要记这个"
→ 系统悄悄让模型自己把内容提炼成一条笔记 → 存进笔记本
(不影响当前对话,也不占用当前对话的记忆)

第 2 步:存
笔记本 = 一个普通的文件夹,里面是 Markdown 文件
→ 人能直接打开看、改、删(不是黑盒)
→ 每条笔记带"记于什么时候、来自哪次会话"(可追溯)
→ 每条笔记有"保质期":一直没被用到的笔记,权重越来越低,自动让位给有用的

第 3 步:用
新会话开始时,系统只给模型看一个"目录"(每条笔记一句话)
→ 模型知道"哦,我有这些历史笔记"
→ 处理相关任务时,模型自己决定:打开哪条笔记看全文
→ 用过的笔记,下次目录里权重降低,让其他笔记也有机会露面
```

## 三个关键设计(为什么这样做)

**1. 记笔记不打断当前对话**
模型只说"我要记",写笔记由后台单独完成。当前对话继续干自己的事——像秘书帮你做会议纪要,不用你停下手头的活。

**2. 笔记人是可读的**
不搞数据库黑盒,就是一个文件夹里的 Markdown 文件。你能直接打开看它记了什么,写错了就改,过时了就删。

**3. 用"目录 + 自己翻"而不是"全塞进去"**
不把全部笔记倒进每次对话(那样上下文会爆)。只给一个一行一条的目录,模型需要哪条自己打开看。目录用"重要性 + 多久没被翻过 + 一点随机"排序,避免热门笔记永远霸榜、冷门笔记永远不见天日。

## 一句话总结

> **给 AI 加一个人类可读的笔记本:重要的事自动记下来,下次会话给个目录、需要时自己翻——人随时能打开修改。**

---

*完整技术设计见 `docs/cross-session-memory-design.md`(含内核/宿主职责划分与实施顺序)。*
92 changes: 92 additions & 0 deletions docs/memory-research/05-upstream-proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 跨会话长期记忆:上游(acp-kernel)提案

> 给 acp-kernel / billion-context-pi 作者的方案稿。想法来自 billion-context-dsh 的 P1-4 缺口:**会话内压缩我们做得很好了,但会话结束即失忆**——跨会话记忆这块,希望 kernel 来拥有(算法归 kernel,宿主只做集成)。

## 一、问题

ACP 解决的是"一个会话内上下文无限膨胀"。但它不解决"下一个会话不知道上个会话发生了什么":

- 今天聊定的设计决策、踩的坑、用户偏好,明天开新会话全没了
- 业界(mem0 / Letta / Claude Code Auto Memory / Cline Memory Bank)都有跨会话记忆,但都独立于压缩体系

**核心认知:每次压缩生成的块(如 tier-1 块 b1)本身就是适合长期存储的内容**——压缩把已消费对话提炼成精炼摘要,这个摘要天然就是长期记忆的候选。所以跨会话记忆不该是另起炉灶的独立系统,而应该是压缩体系的**自然延伸**:记忆沉淀复用压缩产物,模型决定哪些块/内容值得沉淀(不搞引擎自动归档——沉淀决策始终是模型自决)。

**一句话**:把"跨会话记忆"和"压缩"做成同一套机制的两面——压缩是会话内折叠,记忆是跨会话沉淀;都复用 kernel 的蒸馏/ref/搜索基础设施。

## 二、核心机制:nudge 表态 + fork 提取(模型自决写入)

当模型判断"这段有值得跨会话记住的内容"(无论是对话内容,还是某个压缩块 b1 的摘要值得沉淀)时:

```
nudge 触发(kernel 已有)
→ 模型回顾上下文(本来就在做)
→ 模型判断:这段有值得跨会话记住的吗?
有 → 调用轻量工具 memory_commit(type, note) 【纯表态,不写内容】
→ 引擎 fork 一个同模型子对话(同文本 + 一句指令"提取当前需要的记忆,重点:{note}")
→ fork 会话最后:模型用 memory_write 把提炼结果写入记忆库
→ fork 使命结束(可归档/删除),主对话继续压缩
```

**三个工具,权限分离**:

| 工具 | 谁可见 | 干什么 |
|---|---|---|
| `memory_commit(type, note)` | **主对话** | 表态"这段值得记"(type 枚举 + note 聚焦提示);不写内容 |
| `memory_recall(query)` | **主对话** | 读回:检索记忆库取全文(见 §三) |
| `memory_write(content)` | **仅 fork 会话** | 真正写记忆库;**主对话物理上无写权限** |

主对话只有"请求写"的能力、没有"写"的能力——写记忆库只能通过 fork 会话的 memory_write 完成,权限天然隔离(fork = 带 memory_write 工具定义的一次性 LLM 调用,宿主把工具输出落库)。这比"fork 直接落文件"更符合工具面架构,也让三工具语义进 kernel 接口定义。

**为什么这样设计**:
- **时机是现成的**——nudge 时模型正在做全局回顾,对"这段讲了什么、什么值得留"认知最佳(比 Claude Code 的"每轮结束自动提取"更省,比 mem0 的"应用层另起 LLM"更模型自决)
- **决策与执行分离**——模型只做轻量"要不要记"的判断(一次工具调用,note 是聚焦提示),繁重的提炼下放给 fork(不打断主对话、不占主对话上下文)
- **fork 缓存命中**——同模型同文本(+一句指令),prompt cache 共享

## 三、读回:常驻索引 + 自主取回(两时机,不搞多层)

```
时机① 常驻索引:记忆库目录(一句话/条 + 类型 + 上次读取时间)注入 system prompt,每次 turn 都在
→ 模型"知道有什么"(存在性提示,解决"想不起来去翻")
时机② 自主取回:模型看到相关条目 → 调 memory_recall(query) 取全文
→ 细节按需取,不一股脑全塞
```

这正好是 Claude Code 生产实践的形态(MEMORY.md 常驻 + FileRead 取全文),两大主流系统(Claude Code / Letta)独立收敛到同一形态,不是我们发明。

## 四、选择与衰减:一个公式两用

**问题**:纯"被读频率"优先 → 富者愈富(被读 → 频率升 → 优先被读 → 更被读;没被读的永远不见天日)。

**机制**(字段全是记忆条目自带的,不需要 embedding/向量):
```
score = importance + min(距上次读取天数 / 30, 5) + uniform(0, 0.5)
```
- `importance`(1-10):模型 memory_commit 表态时顺带评分(一次性评估,Generative Agents 做法)
- `anti-recency`:**越久没被读优先级越高**——刚读过的降权让位,把正反馈变负反馈
- `random jitter`:随机打破"永远同一批"的锁定
- **top-N = 5**(对齐 Claude 5-slot);会话累计 surfacing 上限对齐 Claude 60KB

**这一个公式同时是"可见性调度"和"保质期"**:不被读的记忆不是消失,而是权重回升等机会——Ebbinghaus 遗忘曲线反过来用,一个机制两用。

## 五、记忆条目 = kernel 的 tier-3 蒸馏块(不造新格式)

- 记忆条目数据模型直接复用 kernel 分层蒸馏产物(Agentic Memory 论文明确:tier-1/2/3 蒸馏 ≡ short→long 迁移,tier-3 超浓缩就是天然长期记忆条目)
- 检索复用 kernel `searchBlocks` hybrid 基础设施(扩展一个记忆库文档源即可)
- 记忆库本身 = 人类可读 markdown 目录(人能看能改 = 外部纠错通道)+ 每条带 citation(来源会话+seq,延续可回溯哲学)

## 六、kernel 要做什么(A 类)/ 宿主要做什么(B 类)

| 归 kernel(上游实现) | 归宿主(DSH 集成) |
|---|---|
| `memory_commit` 的决策时机挂钩(nudge 文案加记忆引导) | 工具注册(DSH 工具面) |
| fork/一次性 LLM 提取的接口定义 | 实际 LLM 调用(宿主运行时) |
| 记忆条目数据模型(= tier-3 蒸馏块扩展) | markdown 文件存储 + citation(seq 方言) |
| score 选择机制 + Ebbinghaus 衰减 | 常驻索引注入 system prompt |
| `memory_recall` 检索(searchBlocks 扩展) | `/acp memory list` 命令 |
| | nudge → memory_commit 的时机接线 |

**实施顺序:kernel 先行,宿主后随**——kernel 发布记忆能力后,宿主 bump kernel 并全部调 kernel API(符合我们既有的"缺陷走 upstream、不在宿主 patch kernel"约定)。

## 七、一句话

> **把"跨会话记忆"做成压缩体系的自然延伸:nudge 时模型表态 → fork 提取 → 记忆库 = tier-3 蒸馏块 + 人类可读 markdown;读回 = 常驻索引 + 自主取回;选择/衰减 = importance + 反新鲜度 + 随机,一个公式两用。算法归 kernel,宿主只做集成。**
19 changes: 19 additions & 0 deletions docs/memory-research/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 跨会话记忆调研资料(Memory Research)

> 本目录存放跨会话长期记忆(P1-4)的设计调研原始资料,供 `docs/cross-session-memory-design.md` 追溯。
> 调研日期:2026-08 | 状态:已随设计文档定稿归档

| 文件 | 内容 | 来源 |
|---|---|---|
| [01-autonomous-memory-papers.md](01-autonomous-memory-papers.md) | 论文向 17 篇:自主记忆保存机制(触发六分类/Ebbinghaus 衰减/检索共识) | 子代理调研,arXiv 实测 |
| [02-autonomous-memory-implementations.md](02-autonomous-memory-implementations.md) | 实现向 8 产品:保存时机/工具描述原文/检索格式/会话边界(5 工程模式) | 子代理调研,产品源码/文档 |
| [03-memory-readback-timing.md](03-memory-readback-timing.md) | 读回时机 25 源:四模式谱系(预注入/按需/自主/常驻)+ 混合收敛证据 + 预算研究 | 子代理调研,论文+产品 |
| [04-claude-code-source-verification.md](04-claude-code-source-verification.md) | Claude Code 记忆机制源码核实(写入/双层读取/7 层架构/启示)——`/tmp/claude-code-analysis` 重启即失,此为提炼存档 | 本地源码核实 |

## 与本目录相关的设计决策摘要

- **写入**:nudge 时机 → `memory_commit(type, note)` 表态 → 引擎**一次性 LLM 调用**提取 → 写记忆库(带 citation)→ 主对话继续压缩。
- **读回**:延迟读回,两个时机——常驻索引(每次 turn)+ 自主取回(`memory_recall` 取全文)。Claude Code 生产实践背书。
- **选择/衰减**:`score = importance + min(days/30,5) + uniform(0,.5)`——打破富者愈富,与 Ebbinghaus 保质期同一套字段。
- **用户侧**:仅 `/acp memory list`;导入导出 = 自然语言,模型自主。
- 完整细节见 `docs/cross-session-memory-design.md`(头部含 3 个待定项)。
Loading