AI agent 的长期记忆越攒越多,怎样用它又不把上下文窗口塞爆?启动时只加载很小的一层常驻记忆;其余内容通过一份索引路由,agent 先读索引,再只打开描述和当前任务对得上的一两个文件。在 nestwork 作者自己的 nest 上,这让会话启动从约 69,600 token 降到约 640 token,一次典型任务也只读约 3,600 token。
下面讲清楚两层结构、让第二层可导航的主题索引、索引检查能拦住什么,以及为什么上下文不是越多越好。
「全量加载」为什么走不通
大多数记忆方案的第一版,都是每次会话把记忆文件整个注入。文件小的时候没问题,之后会在两个地方撞墙。
第一,记忆涨得比窗口快。作者的 nest 在 2026 年 9 月用 o200k_base 分词器实测:
| 场景 | 文件数 | 体积 | token |
|---|---|---|---|
| 2.x 式全量启动(规则、策略、全部共享与 agent 记忆、方法论) | 37 | 224 KB | 约 69,600 |
| 3.x 启动,只读常驻层 | 2 | 2.6 KB | 约 640 |
| 3.x 任务:一次 git 操作(常驻 + 索引 + 一个主题) | 4 | 11.8 KB | 约 3,600 |
| 整个 nest 的所有记忆文件 | 180 | 1.2 MB | 约 369,000 |
整个 nest 已经装不进大多数上下文窗口,挑着读不是可选项。
第二,就算装得下,多塞也有代价。Liu 等人的研究发现,当模型需要从长上下文的中间位置取用关键信息时,表现会明显下降(Lost in the Middle)。Anthropic 工程团队把这种现象叫作 context rot,建议找到「尽可能小的一组高信号 token」(Effective context engineering)。nestwork 协议把它写成设计原则:文件大小按检索质量调,而不是按窗口大小调,因为注意力会随 token 数增加而衰减。
第一层:常驻
从协议 3.0 开始,会话启动只读三个文件(AGENTS.md 第 1 节):
queen/agent-rules.md,你的核心行为规则shared/resident.md,跨 agent 的少量当前事实和检索指针(可选)agents/<host>/<agent-id>/resident.md,本实例的少量事实和指针(可选)
每个文件都有字节预算:规则 4,096 字节,共享常驻 4,096 字节,每个 agent 的常驻 2,048 字节,一次启动合计不超过 10,240 字节。提交前用 python3 scripts/maintenance/check-resident.py 检查,它从不截断内容;超了就得人工审一遍、挪出去,而不是悄悄砍掉。
装了 SessionStart hook 的工具,会把常驻路径打印成 READ-ON-START 清单,其余打印成 READ-ON-DEMAND。打印的是路径不是内容,所以哪怕某个文件超大,也挤不掉清单里的其他条目。
什么该常驻:必须在没人去查的时候就生效的事实、关键边界、少量指针。项目 backlog、事故经过、过期状态都不该常驻。协议给的判断标准是:这条内容是否必须在「没人去找它的时候」也起作用。拿不准,就放按需层。
第二层:按需,由主题索引路由
其余全部按需:策略、历史记忆、项目、方法论、carryover、信箱。agent 从当前任务出发,搜标题或关键词,只读匹配的段落。
文件少时这样够用,文件上百就需要结构了。协议 3.1 加入可选的主题记忆:一个记忆作用域(shared/ 或某个 agent 目录)在自己的 memory.md 里放两个标记,就算开启:
# Shared memory
<!-- nestwork:topic-index:begin -->
<!-- generated by scripts/maintenance/memory-index.py; edit topic front matter, not this block -->
- [`engineering.md`](engineering.md) — 构建、测试、发布约定;改 CI 前读 (2026-09-20, 6.1 KB)
- [`hosts.md`](hosts.md) — 各机器的怪癖、端口和路径;动某台机器的环境前读 (2026-09-27, 3.4 KB)
<!-- nestwork:topic-index:end -->
开启后,memory.md 就是一张路由表,事实都放在主题文件里。每个主题文件开头有 front matter:
---
description: 各机器的怪癖、端口和路径;动某台机器的环境前读
updated: 2026-09-27
---
description 是 agent 决定要不要打开这个文件前唯一能看到的东西,所以要写成触发条件(什么时候该读),而不是标题。检索路径因此变成:常驻摘要 → memory.md 索引 → 描述对得上的一两个主题文件。
路由方式和 skill 一样
用过 Claude Code skill 的人会觉得眼熟。Claude Code 文档说,skill 的描述会载入上下文,完整内容只在调用时才加载(Claude Code skills)。Claude Code 自带的 auto memory 也类似:启动时读 MEMORY.md 的前 200 行或前 25KB,主题文件按需读取(Claude Code 记忆文档)。
nestwork 把同样的思路用在一个跨工具、跨机器共享的记忆库上,区别在于:索引是生成的,不是手写的,所以不会和它描述的文件脱节。
memory-index.py --check 能拦住什么
索引由 scripts/maintenance/memory-index.py 生成。改完主题文件就跑一次;提交前或 CI 里加 --check:
python3 scripts/maintenance/memory-index.py # 重新生成索引
python3 scripts/maintenance/memory-index.py --check # 不写文件,有问题就失败
--check 在这些情况下失败:
- 索引过期,和主题文件的 front matter 对不上
- 某个主题文件没有
description,检索时等于隐形 - 主题文件超过 32 KB(超过 16 KB 先警告)
- 嵌套深于
<topic>/<subtopic>.md
另外,描述超过 300 字符、或生成的索引块超过 8 KB 时会警告,这两者都说明主题该合并了。标记之外的文字原样保留;resident.md、outbox/、local/、carryover/ 以及以 _ 或 . 开头的路径永远不算主题。
主题怎么切才有用
生成的索引只有在主题切得好时才有用。协议的规则(AGENTS.md 第 6 节):
- 先复用再新建。 先读索引,往已经覆盖这件事的主题里写。
- 按「什么时候需要」切,不按「谁写的」切。 一个主题就是 agent 做某一类任务时要加载的那一块。
- agent 只整理自己的作用域。
shared/里新建、改名、合并主题,只在经过审核的蒸馏里发生,免得 30 个 agent 各自长出三份差不多的用户画像文件。
把现有的大文件迁过来是手工、需审核的:按原有标题拆分 memory.md,原文照搬;给每个文件写触发式描述;把 memory.md 换成标题加两个标记;跑索引脚本和 --check;让 resident.md 指向索引。完整步骤见 context loading 文档。
想看自己 nest 的数字:
python3 scripts/maintenance/measure-context.py --task shared/<topic>.md
字节数是精确值;token 在装了 tiktoken 时用它计算,否则用校准过的估算。
常见问题
这不就是 RAG 吗?
是检索,但没有向量嵌入。agent 读一份简短、人能看懂的索引,按描述挑文件。好处是透明、能在 git 里排查,代价是没有模糊的语义匹配。
必须切到主题记忆吗?
不必。它按作用域自愿开启。没有标记的作用域继续用单文件记忆,不会自动迁移;从 3.0 升到 3.1 也不需要刷新各工具的引导。
agent 选错了主题怎么办?
多半是描述写得不好。把它改成「什么时候该读」的触发句,再重新生成索引。常驻文件也可以直接指向最常用的几个主题。
窗口越来越大,以后还需要这套吗?
大概率需要。窗口变大并不能阻止注意力随 token 累积而衰减,而作者完整的 nest 已经远大于常见窗口。
相关阅读
给你的 agent 一份记忆
nestwork 把一个私有 git 仓库变成 Claude Code、Codex、Gemini、Kimi 等 agent 共享的长期记忆。
用模板创建我的记忆仓库 → ★ 去 GitHub 点个 Star