# 工程上下文已经留在 GitHub，Agent 还需要独立记忆吗？Beacon 与 Checkpoint 对照实测

> Canonical: https://vxi.me/posts/beacon-local-memory-capture-causality/
> Locale: zh-cn
> Published: 2026-10-27
> Updated: 2026-10-27
> Markdown: https://vxi.me/posts/beacon-local-memory-capture-causality.md

<figure class="agent-memory-cross-harness-hero not-prose" lang="zh-cn">
<picture><source media="(prefers-reduced-motion: reduce) and (max-width: 640px)" srcset="/images/posts/agent-memory-cross-harness-beacon/agent-memory-counterexample.mobile.still.zh-cn.svg" type="image/svg+xml"><source media="(prefers-reduced-motion: reduce)" srcset="/images/posts/agent-memory-cross-harness-beacon/agent-memory-counterexample.still.zh-cn.svg" type="image/svg+xml"><source media="(max-width: 640px)" srcset="/images/posts/agent-memory-cross-harness-beacon/agent-memory-counterexample.mobile.zh-cn.svg" type="image/svg+xml"><img src="/images/posts/agent-memory-cross-harness-beacon/agent-memory-counterexample.zh-cn.svg" alt="Antigravity 无记忆组与 MCP 记忆组的公开 CSV 测试都为 3/3 PASS，但两组独立隐藏检查都 FAIL；记忆正文提到 CSV 和 TSV，适用范围却只标 TSV。范围失配是候选解释，不是已确认根因。" width="1200" height="700" loading="eager" fetchpriority="high" decoding="async" style="display:block;width:100%;height:auto"></picture>
<figcaption style="font-size:0.8rem;line-height:1.5;opacity:.78;margin-top:.35rem">图：两组公开测试都通过，独立隐藏检查却都失败；适用范围失配是候选解释，不是已确认根因。</figcaption>
</figure>

前两篇先把两个基础问题拆开了：[《把工程上下文留在 GitHub：AI 编程跨工具接力与多分支协作》](https://vxi.me/posts/github-first-ai-coding-context-handoff-workflow/) 讨论工程状态如何持久化、如何让不同 AI 工具接力；[《代码不上云，网页端 AI 能参与到哪一步？Local-first 与 GitHub-first 的接入边界》](https://vxi.me/posts/local-first-vs-github-first-architecture/) 划定数据访问与执行权限的边界。

在这两条边界之上，第三篇要回答的不是记忆能不能被保存，而是：**Agent Memory 到底能补充什么？它能否保存当前仓库无法推导的历史知识，并在新会话里带来可测量的额外价值？**

先给不想看实验细节的朋友交个底：**现阶段，真别指望靠加一层独立 Agent Memory 来帮写代码提效。** 只要是 Agent 翻翻当前仓库就能自己看懂的规矩，还是老老实实写进 README、AGENTS.md、Makefile 和测试里比什么都强；只有那些翻遍代码也查不到的“历史恩怨”和妥协决策，才值得考虑塞进独立记忆。这次我轮番折腾了三轮实测，结果现实很骨感：跨工具（Harness）读记忆确实跑通了，但它并没能让新任务多过哪怕一个隐藏测试。

三篇串起来，是一个工程判断：工程状态由谁保存 → 数据和执行权限在哪里 → 额外的 Agent 记忆究竟有没有必要。

我用三轮合成实验把问题拆成两半：前两轮测“仓库自己就能推导的规则”，第三轮测“只有历史纠正才知道的跨格式隐性约定”。

真正需要分开验收的是三件事：记忆有没有保存下来、另一个 Harness 有没有读到、读到之后是否真的改善了任务结果。三者不能互相替代。本文所说的 Harness，是运行 Agent 的工具/宿主环境，例如 Codex Desktop 与 Antigravity。

## 仓库能教会 Agent 的事，不该被算成记忆的功劳

第一轮实验使用 Beacon 1.3.46（build `f8177a68`）和 Codex CLI 0.162.1，运行在 macOS arm64，目的地明确设为 Local。合成项目通过 `api.yaml` 描述接口，README 说明生成客户端已检入，Makefile 提供生成目标，测试则检查生成客户端与 API 定义的一致性。

第一轮刚跑完，其实挺容易让人产生幻觉的。当时无记忆的对照组 A0 挂了，而走了 Beacon 记忆路径的会话 3/3 测试全过。我复盘时越看越不对劲：第一轮的 A0 居然跑在只读沙箱里，连文件都改不了——好比把对照组的手绑住，然后宣布实验组跑得更快，属于典型的无病呻吟。再加上第一轮的报错信息里直接把 `make gen` 给漏出来了，就算做对了，也根本分不清是记忆的功劳还是报错提示喂到了嘴边。

等到第二轮把沙箱改成可写、把报错泄漏堵上之后，**打脸来得非常直接**：没有任何记忆的 A0 自己翻了翻 README、Makefile 和测试，顺理成章地推导出生成规则，1/1 测试一把过。

下表中的“独立隐藏检查”是实验控制器在公开测试之外执行的任务验收，不是 Beacon 内置组件。各轮使用不同的合成夹具和测试套件，1/1、2/2、3/3、4/4 的分母不能横向比较；尤其第三轮 TRAIN 测的是 TSV，两个消费者组测的是 CSV。

| 实验 | 实际结果 | 允许得出的结论 |
|---|---|---|
| 第一轮：A0 / Beacon 路径 | A0 的只读沙箱无效；Beacon 路径人工候选获批、Skill 物化，新会话读取后 3/3 通过 | A0 不提供 Agent 失败证据；第一轮也不能证明记忆带来任务收益 |
| 第二轮 A0：无记忆、可写 | 从 README、Makefile、测试推导规则，`get_metrics` 的 1/1 测试通过 | 该任务能靠仓库内信息完成，不能把成功归因于记忆 |
| 手工 Checkpoint | Agent 读取 `CHECKPOINT.md`，最终 2/2 通过 | 手工交接也能完成任务；没有同条件耗时基准 |
| 第三轮 TRAIN：Codex | TSV 公开测试 3/3 通过，实验控制器的隐藏检查 PASS | 训练任务通过；不代表自动蒸馏或跨 Harness 复用通过 |
| 第三轮消费者：Antigravity A0 / MCP | 两组 CSV 公开测试均 3/3 通过；隐藏检查均 FAIL | MCP 读取真实发生，但本次消费者任务的增量收益未得到证明 |

看到这个结果，我默默把原本准备写的“记忆复用成功”给划掉了。给我提了个醒：如果一件事情仓库自己已经写得明明白白，Agent 做对了那是它本来就会看代码，真别硬往 Memory 脸上贴金。

## Checkpoint 与 Agent Memory，保存的不是同一种东西

这里我得先按住想看跑分对比的冲动：这轮手工 `CHECKPOINT.md` 和 Beacon 组并不是严格控制变量的竞速赛，中间的纠错过程和统计口径都不一样，我当时也没拿秒表和统一口径去掐两边的耗时和 Token。

现在事后硬凑两个数字出来比谁更省钱省时间，那就纯属是忽悠了。

抛开跑分，这两种做法在手感上的工程差异其实非常直观：Checkpoint 是摆在明面上的交接单，跟着 Git 走，谁都能审；而 Agent Memory 却像是自动攒经验的错题本……

| 知识类型 | 优先放在哪里 | 需要额外记忆吗？ |
|---|---|---|
| 当前代码约束、生成步骤、接口不变量 | README、AGENTS.md、Makefile、测试 | 通常不需要重复存一份；仓库应是当前事实的权威来源 |
| 当前任务进度、已验证 SHA、下一步和阻塞 | Issue、PR、Checkpoint、CI 记录 | 只有在跨会话恢复确有缺口时再补充 |
| 仓库中没有的历史决策、为什么曾经否决某个方案 | 可审查的决策记录或经批准的 Agent Memory | 可能有价值，但要写清来源、适用范围和证据限制 |
| 已经变化的规则、临时例外 | 更新当前仓库事实，并明确废止旧规则 | 不能只追加新记忆；旧记忆必须能被发现、限制或退役 |
| 不能跨数据边界的敏感上下文 | 留在获准的本地边界内 | 不应为了“记住”而默认复制到云端 |

这也是三部曲之间的关系：工程状态由谁保存，数据和执行权限在哪里，最后才是独立记忆层有没有必要。GitHub-first 不是说所有信息都要塞进仓库；它要求项目当前事实可读回、可审查。Agent Memory 也不应成为第二个彼此矛盾的 README。

## 第三轮：Antigravity 读到了记忆，任务却没通过

前两轮回答了仓库可推导约定的问题，却没有检验历史知识能否跨 Harness 被读取并用于新任务。第三轮换成合成 Python 导出任务：先检验经人工审核批准的历史兼容记忆能否被 Google Antigravity 通过 Beacon Local MCP 真实读取，再观察读取是否改善任务验收。目的地仍是 Local，没有启用 Beacon Cloud；没有安装 Skill，也没有运行 Beacon 评估器。

与前两轮的 `make gen` 不同，这条兼容规则来自训练会话中的历史纠正，而不是 README、AGENTS.md 或当前测试能直接推导出来的仓库约定。规则是：CSV 和 TSV 导出都应把 Python `None` 序列化为字面文本 `\N`（一个反斜杠加大写 N）；空字符串仍为空字段，字符串 `NULL` 保持原样。第三轮的训练 Harness 是 Codex Desktop 0.164.0-alpha.11.1（版本来自本地 Trace 元数据）；消费者是 Google Antigravity 桌面版，但精确版本没有记录。现有实验记录也没有保存两侧底层模型名称、推理档位/温度或完整提示词，因此这不是只改变“是否有记忆”一个变量的严格同条件基准。训练组中，Codex 完成 TSV 任务，公开测试 3/3 通过；实验控制器返回的独立隐藏检查回执也报告 PASS。训练结束后，实验执行了 `scrub-train`。现有回执只记录它把合成仓库恢复到初始源码，没有说明该步骤是否触及 Beacon memory store；批准记忆是在这之后创建并批准的，因此本轮不能用这一步证明记忆库被清空或完全未被触及。

到了最关键的第三轮 MCP 消费者组，我盯着本地回执看：Antigravity 确实老老实实调了 `get_memory` 和两次 `search_memory`，把那条“遇到 `None` 要转成 `\N`”的历史记忆完完整整读进来了。公开测试 3/3 绿灯。结果一跑独立隐藏检查——**又是一个FAIL。**

| 消费者组 | 是否读取 Beacon 记忆 | 公开 CSV 测试 | 独立隐藏检查 |
|---|---|---:|---|
| A0：Antigravity 无记忆 | 否 | 3/3 PASS | FAIL |
| MCP：Antigravity 有记忆 | 是，实际调用 `get_memory` / `search_memory` | 3/3 PASS | FAIL |

**最戏剧性的地方就在这：它明明读到了标准答案，为什么还是交了白卷？**

翻看 Antigravity 的执行摘要，它的逻辑让人没法反驳：虽然记忆的正文里写了 CSV 和 TSV 都要遵守 `None → \N`，但这条记忆的 `applicability`（适用范围）元数据里，只标了 `render_tsv` 或 TSV 兼容性。再加上 README 里说两个导出函数是独立实现的，Antigravity 就非常正经地认为：既然标签写的是 TSV，那我写 CSV 的时候就不瞎套用了。

这当然还算不上板上钉钉的唯一根因（毕竟隐藏检查器只吐了 FAIL，没给具体挂在哪个断言），但它把做 Agent Memory 一个痛点扒了个底朝天：**记忆的适用范围到底该怎么划？**

划太宽，上次修 A 模块留下的特殊癖好就会去污染 B 模块；划太窄，正文里明明写了通用规则，就因为元数据标签只打了 TSV，换个 CSV 任务它就装看不见。呵呵

每个消费者臂都只有一次合成任务，而且 A0 与 MCP 组的隐藏检查均失败。两组同为 FAIL，既不能说明记忆带来了改善，也不能说明读取记忆让结果变差。本轮能确认的是：**跨 Harness 的记忆读取发生了，但消费者任务的独立验收结果没有改善。** 样本不足以估计 Beacon 的总体效果；如果把“检索成功”直接当成“复用成功”，就会把工具调用链误认为有效的工程交付。

这组观察仍有复核边界：筛选后的 Beacon Trace 能看到提示词、补丁调用和测试命令，但没有保留对应的 `tool_result` 或 `error` 事件；完整原始 MCP JSON-RPC、隐藏检查器的内部断言和本机运行轨迹也没有作为公开复现包发布。因此，本文记录的是有本地执行摘要与控制器回执支撑的单次观察，不是一键可复现的实验包。

## 过期记忆能被 Agent 绕过去，不等于 Beacon 会治理它

另一个测试把 Makefile 目标从 `gen` 改成了 `generate`，但旧 Skill 仍然写着 `make gen`。带着旧规则的会话先执行了过期命令，看到：

```text
make: *** No rule to make target 'gen'. Stop.
```

它随后重新查看 Makefile，改用 `make generate`，该轮 `make test` 的 4 项测试全部通过；恢复 Makefile 后，独立的 `python3 -m unittest discover -s tests` 也报告 4 项通过。这里的 4/4 属于过期记忆夹具，不是第三轮 CSV/TSV 测试套件。这说明 Agent 能在命令报错后回头读取当前仓库，并自行恢复；它不说明 Beacon 自动更新了旧 Skill。过期记忆仍在，真正纠正行为的是 Agent 重新检查当前事实。

这一区别影响工程设计。如果某条记忆会随着代码变化而失效，就不该只依靠模型“应该会发现冲突”。至少需要明确的适用范围、来源和时间边界；当前事实仍以仓库和测试为准。更关键的历史约束应进入版本化决策记录，或在冲突时要求 Agent 停下来核对，而不是继续把旧记忆当作事实。

## Local 日志也不是通用的秘密检测器

实验使用合成标记检查本地日志，未放入真实凭据，也没有连接 Beacon Cloud。第一轮在 `~/.beacon/endpoint/logs/runtime.jsonl` 中统计到：

```console
$ grep -c 'PLUMOTTER9917' ~/.beacon/endpoint/logs/runtime.jsonl
7
$ grep -c 'sk-canaryfake' ~/.beacon/endpoint/logs/runtime.jsonl
0
$ grep -c 'REDACTED' ~/.beacon/endpoint/logs/runtime.jsonl
1
```

第二轮结束后，累计计数变为 25、4、6。普通文本标记 `PLUMOTTER9917` 在本地日志中逐字出现；形状类似 Token 的 `sk-canaryfake` 匹配行数从 0 变成 4。但我没有检查这四行的内容，因此无法判断命中来自待测值、脱敏后残留的前缀，还是日志中被记录的测试/检索文本。canary 的脱敏结论仍是不确定。

固定版本源码显示，脱敏依赖字段名和已知模式匹配，而不是对任意自然语言进行通用秘密识别。可以直接检查 [脱敏实现](https://github.com/Asymptote-Labs/agent-beacon/blob/d7a3a72fdc0a864655cf2e85fb7de5ba974cde72/pkg/asymptoteobserve/privacy.go#L322-L395) 与[内容保留逻辑](https://github.com/Asymptote-Labs/agent-beacon/blob/d7a3a72fdc0a864655cf2e85fb7de5ba974cde72/pkg/asymptoteobserve/content.go#L159-L197)。本地日志出现某段文本，也不能证明它被转发到云端；本轮没有连接 Cloud。

同样需要分清源码审计与运行期测试。我把 Beacon 源码固定在 [提交 d7a3a72](https://github.com/Asymptote-Labs/agent-beacon/tree/d7a3a72fdc0a864655cf2e85fb7de5ba974cde72)。其 [Harness 捕获矩阵](https://github.com/Asymptote-Labs/agent-beacon/blob/d7a3a72fdc0a864655cf2e85fb7de5ba974cde72/README.md#L313-L321)、[Codex hooks](https://github.com/Asymptote-Labs/agent-beacon/blob/d7a3a72fdc0a864655cf2e85fb7de5ba974cde72/cli/beacon/internal/endpoint/hooks/codex.go#L65-L81) 和 [OTLP 事件转换器](https://github.com/Asymptote-Labs/agent-beacon/blob/d7a3a72fdc0a864655cf2e85fb7de5ba974cde72/collector-builder/exporter/beaconjsonexporter/internal/beaconevent/converter.go#L534-L598)没有给出 Codex File/MCP 事件的对应捕获映射。这张矩阵描述 Beacon 从各 Harness 采集哪些运行事件，不是 Harness 作为 MCP 客户端调用本地 MCP 服务的能力；两者不是同一条路径。这是固定版本源码检查，不是逐类运行抓包。

Jev 的评估实现也只做了源码核查；没有配置密钥，没有真正运行打分，所以没有评估器分数或自动蒸馏精度数据。在本次使用 tarball 安装 Beacon 1.3.46 并配置 Local endpoint 的本机环境中，未找到 `beacon-memory-distill` Skill；第二轮隔离子 shell 又因该环境下 `beacon` 不在 PATH 而无法运行蒸馏。会话恢复参数也曾因 CLI 参数不匹配和 trusted-directory 检查失败，导致纠正没有送达目标会话。这些是本次安装路径和实验编排中的具体观察，不代表所有安装路线都会失败。

## 记忆应该补仓库的缺口，而不是复制仓库

三轮实验没有证明 Agent Memory 能普遍提高任务成功率，也没有证明它毫无价值。它们把值得付出维护成本的范围缩小了：

- **仓库已经能回答的问题**，放在 README、AGENTS.md、Makefile、测试和版本化决策记录里。这样当前事实可以被代码审查和 CI 验证，避免多份记忆互相冲突。
- **仓库没有记录的历史知识**，才是独立记忆更有希望发挥作用的地方。比如某项兼容性决定为何做出、曾经否决过什么方案、某个外部流程里有哪些不明显的约束。但记忆必须保留证据来源，并把适用范围写得足够准确。
- **过期或互相冲突的记忆**，必须允许被识别、限制和退役。Agent 偶然在命令报错后重新读了 Makefile，不等于系统已经建立了可靠的过期治理。
- **数据边界之外的信息**，不应该为了跨会话便利就自动外发。Local 日志、Local MCP 和托管评估器是不同路径，必须分别核对实际配置与数据流。

第一篇讨论把工程上下文放进 GitHub，第二篇讨论数据与执行权限的边界；这次实验补上了最后一个判断：独立记忆不是工程事实的替代品，而是对那些当前仓库无法表达的历史知识的一种候选补充。它是否值得保留，不能只看捕获数量、候选条数或一次成功的读取调用，必须看它能否在明确适用范围内改变新任务的结果，并且经得起独立验收。

实验跑到这我就停了，没有再去手动改那条记忆的 `applicability` 标签来强行刷出一个 PASS。道理很简单：如果我看着隐藏测试挂了，再人工进去把标签改宽、直到喂到 CSV 任务跑通为止，那我测的根本不是“Agent Memory 能不能从历史会话里自然捕获并跨端复用”，而是在测“我本人对着答案调优提示词的能力”——这就是纯属自我意淫自己骗自己了。

与其硬凑一个大团圆结局，不如把话停在证据能撑住的地方：**仓库里写清楚的规矩，不需要额外记忆来抢功；跨工具读记忆的路子确实通了，但怎么让读出来的历史记忆真正帮新任务拿分，这还没跑通。**

相关背景：[把工程上下文留在 GitHub：AI 编程跨工具接力与多分支协作](https://vxi.me/posts/github-first-ai-coding-context-handoff-workflow/)、[代码不上云，网页端 AI 能参与到哪一步？Local-first 与 GitHub-first 的接入边界](https://vxi.me/posts/local-first-vs-github-first-architecture/)，以及 [Coding Agent 记住上次怎么改代码，就能省 Token 吗？我拆了 Singularity 的检索逻辑](https://vxi.me/posts/singularity-agent-memory-cue-matching/)。

实测时间说明：第一、二轮于 2026 年 10 月 26 日完成，Antigravity MCP 消费者实验于 2026 年 10 月 27 日完成。
