---
name: save-compact
description: 先把当前会话的关键信息持久化到长期记忆（mem0 与 Claude Code 本地 memory 是冷热分工，两边都写，不是主备），然后自动 /compact 压缩上下文。当上下文过大、收到用量看板提醒、或用户说"存记忆压缩"/"save-compact"时触发。
user-invocable: true
---

# 保存记忆 + Compact 上下文

上下文太大时，先抢救会话里的关键信息到长期记忆，再压缩 —— 避免 /compact 的摘要丢掉重要细节。整个流程你自己完成，不要再问用户确认。

> 一件事已经做完、下一件与之无关时，优先用 `/save-clear`（存档后直接清空，干净且不会越压越失真）；本技能留给**任务做到一半**、对话里还压着大量没落盘细节的时候。

## 第 1 步：挑重点存记忆

从当前会话里挑**值得跨会话记住**的内容（是筛选，不是全部倒进去），先按**生命周期**分三桶，每桶去处不同：

| 桶 | 举例 | 去处 |
|---|---|---|
| **进度 / 检查点**（下周就过时） | 正在进行的任务：目标、当前进度、未了事项、下一步、等谁拍板 | **HANDOFF.md**（一份、覆盖、不进 mem0） |
| **长期事实**（一年后还成立） | 决策 + 理由；踩过的坑 / 走不通的路线；结构性事实、关键路径 / 命令 / 账号对应；用户偏好、规矩、约束 | **mem0**（带 `type`） |
| **不搜也必须知道的结论** | 一句能立刻改变行为的话 | **本地 memory 文件**（热层，条数克制） |

2026-09-06 起（记忆架构评估）进度快照**不再写 mem0**：它们占了每天写入的大半，半个月后全是尸体，把结论挤出 top-10；卫生 cron 会把 14 天前的 `type=progress` 条目直接删掉。

### 写 HANDOFF.md（进度交接：一份、覆盖式）

路径：你系统提示里 memory 目录（`~/.claude/projects/<slug>/memory/`）下的 `HANDOFF.md`。**整文件重写，不追加**——它描述的是「此刻」，不是日志。同目录 `MEMORY.md` 索引里保证有一行 `- [HANDOFF](HANDOFF.md) — 上次会话交接：进度 / 未了 / 下一步`（已有就别重复加）。

模板（≤ 60 行，具体到能直接接着干）：

```markdown
# HANDOFF · <日期时间>
## 在做什么
<任务一句话 + 目标>
## 已完成 / 已定
- <commit / 文件 / 决定>（原话或精确值）
## 进行中 / 阻塞
- <到哪一步、卡在哪、等什么>
## 下一步（按顺序）
1. …
## 等 owner 拍板
- <未回应的按钮 / 问题>
## 关键路径 / 命令
- <重建成本高的东西>
```

SessionStart hook（`src/hooks/recall-hook.ts`，`manager install-hooks` 注册）会在 startup / resume / clear 时把 14 天内的 HANDOFF.md 自动注入开场 context（compact 时不注入，压缩摘要比它新），同时注入 `## mem0 recall` 块（本项目最相关 + 最近的 mem0 记忆）——那只是顶层，做非平凡任务前照样要主动搜。

### 两个后端是冷热分工，不是主备

| | 本地 `memory/`（热） | mem0（冷） |
|---|---|---|
| 怎么到达 | **每次开会话自动注入 context** | 必须主动检索才存在 |
| 代价 | 占 context，条数要克制 | 零 context 成本，但要想起来搜 |

**它们不互为备份**：本地那份的价值是「无需检索就在场」，mem0 给不了；mem0 能装几百条不占 context，本地装不下。判断标准：「**下次开会话时，不主动搜也必须知道吗？**」是 → 本地也要有；否 → 只进 mem0。

同一件事两边都该有时**不是复制粘贴**：进本地的是一句能立刻改变行为的结论，进 mem0 的是完整的论证与数据。

### 写 mem0（冷层：完整事实，5~15 条不心疼）

⚠ **mem0 工具常是「延迟加载」的**——不出现在默认工具列表里，只在 deferred 清单里列个名字。**不要因为「工具列表里没有」就判定 mem0 不可用**（2026-08-11 静默降级两次的教训）。先 `ToolSearch("mem0 memory")` 把本机实际存在的工具拉出来再调用，调不通才算真没有。

- 工具名**因机器/版本而异**（`memory_write` 与 `add_memory` 都存在过），以 ToolSearch 实际拉到的为准，别背工具名。
- `user_id` **必填，不要省略**——取值以你系统提示 / 本机全局 CLAUDE.md 约定的那个为准（不要硬编码别人机器的值）。省略时 MCP 的默认 scope 未必是你的：实测有的机器省略后 search 静默返空 `[]` 不报错——查重永远「没存过」→ 重复写入，且拿不到任何已有上下文。
- **只收长期事实**：决策 + 理由、教训与走不通的路、结构性事实、用户偏好。进度 / 待办 / 检查点一律不写（去 HANDOFF.md）。每条 `metadata` 带 `type`（`decision` | `feedback` | `reference` | `guide` 之一；`progress` 保留给旧数据的 TTL 清理，别新写）和 `project`（服务端 2026-09-06 起会按 cwd 自动填，显式给更稳、跨项目写别人的事实时必须给）。
- 每条独立事实调一次，内容写成**脱离本会话也读得懂**的完整陈述，带日期 / 项目名。2026-09-06 起 `memory_write` **默认原文入库、不走 LLM 抽取**（写什么存什么；传 `infer=true` 才抽取）——别再指望服务端替你归纳，也别把多件事塞进一条。
- **写后判重护栏**（2026-09-06 起，非破坏性）：只有逐字重写（余弦 <0.01）才被丢弃，返回 `DROPPED_NEAR_DUPLICATE` + `duplicate_of`——拿这个 id 去 `memory_update` 并入新信息，别换措辞重写。余弦 0.01~0.08 的近重复**不再自动删**：照常入库，返回里带 `near_duplicate_of` + `existing_memory`（并写进该条 `metadata.near_dup_of` 供周一卫生 cron 复核）。收到 `near_duplicate_of` 时自己判断——同一事实就 `memory_delete` 自己这条并 `memory_update` 旧 id；是新结论取代旧的就 `memory_delete` 旧 id。距离分不清「换个说法」和「改了个数字」（实测只改 219→220 距离仅 0.02），所以这一步不能省。
- **写前先搜**：新事实不会自动淘汰过时事实——搜到矛盾旧条目就地 update/delete，**两条互相矛盾的记忆比没有记忆更糟**。
- 并发写是安全的（server 逐条执行），多条可以一起发不必串行。

### 写本地 memory（热层：几条就够，每条都在花每次会话的 context）

用你系统提示里描述的 memory 目录机制。通用约束：

- **一条事实一个文件**，`MEMORY.md` 索引只放一行指针 + 钩子，正文绝不进索引（索引全量注入 context，正文躺磁盘按需读——实测 63 个文件 267KB 的库，索引只 10KB，比 1:24；正文进索引就白费了索引制）。
- 写前查重：已覆盖的更新原文件，不新建重复条目。
- 旧记忆被本次会话推翻的，就地改掉或删除，别留尸体。

### 两边都不可用（这才是降级档，上面两个是并列的）

追加到项目 CLAUDE.md 的合适段落（新开一个 `## Session notes` 段也行），并在第 3 步的回复里写明哪个后端不可用及原因——静默降级会让用户以为记忆系统挂了。

## 第 2 步：安排自动 compact

存完记忆后，用 Bash 安排一个后台任务，在本轮结束后向你自己的 Claude Code 发 `/compact`：

```bash
if [ -n "$TMUX" ] && [ -n "$TMUX_PANE" ]; then
  nohup bash -c '
    sleep 10
    # copy-mode 守卫(2026-08-30 实证):pane 在 copy-mode 时所有 send-keys 被
    # tmux 吞掉且不报错——先无条件 cancel(不在模式时此命令无害报错,忽略)
    tmux send-keys -t "$TMUX_PANE" -X cancel 2>/dev/null
    sleep 0.2
    # 保留清单(2026-09-03,来自 Anthropic「Prompting Claude Fable 5.1」压缩摘要
    # 一节):/compact 接受自定义摘要指令,明确告诉模型摘要必须留住什么——否则
    # 约束原话、放弃过的路线、精确进度这些最容易被摘掉。必须单行(换行=提前提交)。
    tmux send-keys -t "$TMUX_PANE" -l "/compact 摘要必须让新上下文无需返工、无需重新提供约束就能接着干。务必保留:(1)遇到的难点/问题及其处理与结果;(2)提出、尝试或放弃过的方案及原因;(3)用户要求、决定、同意、否决、确立为偏好/约束/边界的内容——按原话;(4)当前精确进度:已覆盖/已定/已完成的;(5)未了、未决、已承诺、预期接下来发生的事;(6)难以重建的细节——名字、数字、日期、原话、路径、命令、链接——原样保留。这六项宁长勿缺,其余从简;用户说过/要求/确立的贴近原话,我自己的解释推理压缩到结论与产出。"
    sleep 0.4
    tmux send-keys -t "$TMUX_PANE" Enter
  ' >/dev/null 2>&1 &
  disown
  echo "compact scheduled"
else
  echo "not in tmux"
fi
```

- 输出 `compact scheduled` → 本轮结束约 10 秒后 compact 会自动执行(带上面的保留清单指令;Claude Code 的 `/compact <指令>` 会把它交给摘要模型)。
- 输出 `not in tmux`（不在 tmux 里跑）→ 跳过，最后提示用户手动执行 `/compact`。

## 第 3 步：告知结果

最后简要告诉用户（在 Claudestra/Discord 环境用 reply 工具，注意 reply 要在安排 compact 的 Bash **之前或紧接着**完成，别拖到 compact 触发之后）：

- 存了哪几条（分开说：HANDOFF.md 写了什么、mem0 存了什么、本地 memory 动了什么——三处各自成立，都写了才是完整答案）
- compact 已安排（约 10 秒后自动执行）/ 或请手动 `/compact`
