🐸 青蛙编程网
首页 / AI / 脚手架 Harness / 4. 记忆与状态

4. 记忆与状态

这是什么:状态层 = 把「做到哪儿了」「哪些结论不能忘」从对话里搬出来,落到 Agent 读得到的地方。

为什么用:上下文是易失的——压缩会丢、交接会漏、换会话就没了。任务状态必须活在文件里,才能跨轮次、跨会话、跨 Agent 继续。

如何用:本章先分清三种「记忆」,再讲进度文件怎么写、子代理怎么隔离状态,最后说熵的清理。

4.1 三种「记忆」别混

Agent 语境里的「记忆」至少有三个来源,混在一起是大故障的开始:

类型 是什么 例子 该放哪
对话历史 本轮会话的原始消息 模型上一句说了什么 上下文窗口里,短期
任务状态 当前任务做到哪儿 「改完模型层,正在改 API 层」 状态文件,随任务走
长期记忆 跨任务仍然成立的结论 「这个项目用 pnpm 不用 npm」 记忆文件,长期有效

再往外还有第四类:工具日志——它是副产品,不是记忆。放进上下文只会占地方,该落文件就落文件。

Anthropic 的「初始化 Agent + 编码 Agent」两阶段设计就是在分这个层:初始化阶段产出一份规格说明,编码阶段只消费它。中间产物不靠对话记忆传递,靠文件传递。

4.2 任务状态:写成文件

最实用的形式是一个进度文件,放在项目里,Agent 每完成一步就更新:

# .agent/progress.md

## 当前任务
给 `users` 表加软删除字段

## 状态
进行中

## 已完成
- <input type='checkbox' checked disabled> migration 脚本写好并跑通
- <input type='checkbox' checked disabled> 模型层加字段 + 全局过滤器
- <input type='checkbox' checked disabled> 删除接口改成软删除
- <input type='checkbox' disabled> 列表接口接上过滤(`apply_deleted_filter` 已写好,未接入)
- <input type='checkbox' disabled> 补测试

## 当前卡在哪
列表接口的 `get_users()` 有两处调用没走 `apply_deleted_filter`,需要改成统一走 `User.query.filter(User.deleted_at.is_(None))`。

## 关键决策
- 用 `deleted_at IS NULL` 判断,不用布尔字段(避免 NOT NULL 默认值迁移问题)
- 不加 `is_deleted` 索引,等真实数据量上来再看慢查询

## 验证命令
```bash
pytest server/tests/test_users.py -k soft_delete
```

这个文件的价值:

  • 压缩不丢它——它在文件里,不在上下文里
  • 换会话不丢它——新会话第一件事就是读它
  • 人能看懂——出问题时人直接看这个文件判断进度

写进度文件的三条纪律:

  1. 只记「非显然」的事——做了什么可以直接看 git diff,不用记;为什么这么做必须记
  2. 记决策,不记流水账——「改了 3 个文件」没价值,「为什么不用布尔字段」有价值
  3. 每步更新——不是任务结束才写,是每完成一块就改,否则会写成事后总结

4.3 结构化状态:功能清单用 JSON

如果状态里有「很多个功能、各自什么进度」,用 JSON 不用 Markdown。Anthropic 的做法是用 JSON 追踪功能状态,理由很实在:Agent 不太容易乱改结构化数据。

{
  "features": [
    { "id": "soft-delete-model", "status": "done", "notes": "deleted_at + 全局过滤器" },
    { "id": "soft-delete-api",   "status": "wip",  "notes": "删除接口已改,列表接口未接" },
    { "id": "soft-delete-tests", "status": "todo", "notes": "覆盖过滤与删除两条路径" }
  ],
  "updated_at": "2026-10-21T18:40:00+08:00"
}

Markdown 适合人读,JSON 适合机器改。如果状态要由程序更新,就用 JSON;如果要由人审阅,就用 Markdown。

可以再加一个「推进清单」文件,让 Agent 每轮把待办勾掉:

# .agent/todo.md
- <input type='checkbox' disabled> 确认列表接口所有调用点(grep get_users)
- <input type='checkbox' disabled> 统一改成带过滤的查询
- <input type='checkbox' disabled> 补 pytest 用例
- <input type='checkbox' disabled> 跑全量测试确认没回归

4.4 子代理隔离:状态不串味

多 Agent 场景里,一个常见设计是主 Agent 负责规划与验收,子 Agent 负责执行。这样每个子 Agent 只带着一小段上下文干活,干完把结论交回主 Agent。

好处有三个:

好处 说明
上下文干净 子 Agent 只看到自己那摊事,不受主对话污染
能并行 多个子 Agent 同时跑不同的活儿
上下文利用率高 每个子 Agent 的上下文都很短,留在 Smart Zone

代价是状态必须显式传递。主 Agent 不能假设子 Agent「知道」上下文,只能通过文件或结构化消息传。

主 Agent ──派任务──► 子 Agent A(改模型层)
     │                        │
     │                   读 .agent/progress.md
     │                   改完更新它
     │                        │
     ◄──────结果(文件 + 摘要)─┘

约定要先写下来:子 Agent 动手前读哪个文件、做完更新哪个文件、返回什么格式。这一条写进 AGENTS.md,比口头约定可靠。

4.5 上下文压缩的边界

第 2 章讲过压缩,这里补一条实务边界:什么该进摘要,什么必须留在文件里。

内容 压缩时 理由
工具调用原文 丢 日志占地方,细节可回查文件
已读文件的内容 丢 需要时重新读更便宜
试过什么、为什么放弃 保留 这是最贵的经验,别丢
关键决策与理由 保留(或落文件) 决策丢了 Agent 会走回头路
当前任务状态 落文件 压缩不可靠,文件才可靠

一句话:经验压缩,状态落盘。

4.6 垃圾回收:熵不会自己消失

AI 生成的代码越多,低质量实现、重复逻辑、文档不一致也跟着变多。OpenAI 的团队一开始每周要花 20% 的时间手动清理这些生成物,后来把这事自动化了——后台 Agent 定期扫描文档不一致、架构违规和冗余代码,自动提交清理 PR。

这件事跟 Harness 的关系是:它属于 L4(状态与记忆),也是 L5(评估观测)。原因很简单——

生成速度高于清理速度时,重复逻辑和过期文档会持续进入仓库,后续 Agent 检索到的上下文也会随之变差。

也就是说,清理不是锦上添花,它直接决定后续 Agent 的上下文质量。

实操上可以先从低成本的开始:

  1. CI 里加检查:文档与代码不一致、目录里重复的 README,直接让流水线报错
  2. 定期扫一遍:每周跑一次脚本,列出「最近改动最多但引用最少」的文件,人工判断
  3. 再考虑自动化:等规则稳定了,再让后台 Agent 自动提 PR

4.7 L4 小结

  • 分清对话历史 / 任务状态 / 长期记忆 / 工具日志四类东西
  • 任务状态写成文件(.agent/progress.md),记决策不记流水账
  • 结构化状态用 JSON 追踪,人读的用 Markdown
  • 子 Agent 靠文件交接状态,约定写进 AGENTS.md
  • 压缩时:经验留下、状态落盘、日志丢弃
  • 清理是持续工作:生成速度超过清理速度,熵会污染后续上下文

🤝 记忆口诀:经验靠压缩,状态靠文件,熵靠定期清。

动手改

  1. 给你的项目建一个 .agent/progress.md,把当前正在做的事按第 4.2 节的模板写一遍(重点写「关键决策」那一段)
  2. 检查一下你的工作流:任务状态现在存在哪?如果只在对话里,换个会话就断——这就是要修的地方
  3. 挑一个「Agent 反复做同一件事」的环节,把它固化成一个脚本或钩子,顺便看看能不能顺手清掉一批没人维护的旧文件

小结

  • 四类东西要分清:对话历史(短期)、任务状态(随任务)、长期记忆(长期)、工具日志(副产品)
  • 任务状态写文件,记决策和卡点,不记流水账
  • 状态要程序改就用 JSON,要人审就用 Markdown
  • 子 Agent 隔离上下文,靠文件交接状态,约定写进 AGENTS.md
  • 压缩的边界:经验压缩,状态落盘,日志丢弃
  • 熵不会自己消失,清理 Agent 是 Harness 的常驻组件

下一章:约束、验证与恢复,它怎么保证不出事、做对了 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。