· Paul Lukic · 1 分钟阅读 · coographclaude-codeagentsguardrailscontext-budget

你的代理从没看到的那条警告:指令预算、看不见的 hook,以及一次吃掉自己产出的同步

在真实项目上跑 Coograph 的一天:一行代码都还没读,指令就已经占了 120,000 个 token;护栏发出的警告,模型从没收到过;还有一次同步,覆盖了它自己的 retro 提出的修复。我们发现了什么、怎么证明的,以及 1.8.5 发布了什么。

本文目录

我们发布的东西,大多来自在自己的项目上跑 Coograph、看着它出问题。这次发布格外集中:针对一个 monorepo 提的一个 issue,在一天之内变成了四条发现,其中两条是那种只有直接问模型”你看到了什么”才能找到的。

第一行代码之前的 120,000 个 token

这个 issue 是带着数字来的。一个大约十天前接入 Coograph 的 monorepo(一个 Web 应用、一个原生应用、一个共享核心和一组云函数),在一个典型的 Web 任务里,代理还没读任何代码,就加载了大约 120,000 个 token 的指令文件。其中一个工作区地图,也就是告诉代理 Web 应用结构的那个文件,单独就有 91,645 个 token。

没有人故意写一个 90,000 token 的文件。它是长出来的。每次改动都追加一个带日期的小节(“登录,见 openspec 2026-09-01”),于是一个界面的当前行为散落在许多小节里,代理得把它们全读一遍。在一条 Web 与原生对齐的规则下,一个功能会在几份地图里产生三到五个小节。硬规则被复制进三个根文件,于是要付三次钱。还有一个 6,400 字符的表格单元格,让格式化工具把一张宽表的每一行都补齐了空格:那个 363 KB 的文件里有 228 KB 是空格。

Retro 是 Coograph 用来衡量护栏是否守得住的循环,本该抓到这件事。它没有,因为它量错了文件。它的 token 预算只算了根文件和 .github/instructions/,别的一概不算:不算工作区地图,也不算任何通过 @ 导入拉进来的东西。那份 91,000 token 的地图,对这个唯一负责发现它的工具来说是隐形的。

所以 1.8.0 给指令文件定了形状,也给了一把尺子。一共三层:始终加载的根文件(合计 9,000 token,导入的内容也算在内),每个工作区一个简短的路由文件(2,000),以及每个产品领域一份功能文档(8,000),只在代理需要时才读。Gotcha 有自己的文件,从不预先加载;当代理编辑某条 gotcha 路径下的文件,或者运行它所针对的命令时,一个 hook 才把那一条展示出来。layout.py --budget 把这一切都量进去,还有一个 CI 检查:如果一个 pull request 改了某份功能文档覆盖的代码却没动那份文档,检查就失败。

对于已经膨胀得太厉害的文件,/coograph-docs-restructure 会先写一个 OpenSpec,把旧地图里的每一条事实按来源列出来,然后每个代理迁移一个领域,逐条对照代码核实。在那个 monorepo 上,它把始终加载的部分从 30,745 token 降到了 7,914,把那份 91,645 token 的 Web 地图降到了 591;现在没有任何工作区地图超过 700。Coograph 自带的模板现在用掉 9,000 token 预算中的 5,510;以前,它自己的三个根文件各自把同样的硬规则完整重复了一遍。

没人读到的警告

做 gotcha hook 意味着要弄清楚 hook 是怎么把文字送到模型面前的,而这一查,查出了一个让人不舒服的发现。

Coograph 有四个警告 hook。一个在编辑超出已批准改动范围时警告,一个在没有提案却编辑了第二个源文件时警告,一个在添加依赖时警告,一个在提交没有经过评审时警告。四个做的都是同一件事:把消息打印到 stderr,以退出码 1 退出。消息出现在了对话记录里,所以我们以为它是起作用的。

我们用唯一能下定论的方式测试了它。我们在一个一次性项目里启动了一个嵌套会话,让它做一次范围 hook 必然会警告的编辑,然后在不提供任何其他工具的情况下,让模型逐字引用那次编辑之后它收到的、所有包含 “scope” 或 “OpenSpec” 的文字。它的回答是:没有。hook 触发了;对话记录里有那段文字,标注为一个非阻塞的 hook 错误。模型从来没有收到它。

同一个下午写的 gotcha hook 用的是另一个通道,additionalContext,在同样的探测里,模型逐字引用了它的条目,包括一个它不可能猜到的 token。所以修复方向很清楚。四个警告 hook 现在都通过 additionalContext(给模型)和 systemMessage(给终端)说话,并以 0 退出。我们在修好的 hook 上重新跑了探测:模型逐字引用了范围警告。

那次重跑还揪出了一件事。第一次尝试又失败了,对话记录显示了原因:已安装插件里自带的那份 hook 副本,还是旧版本,抢在项目的副本之前认领了这个事件。Coograph 让每个事件只有一份 hook 副本生效,这是对的,但这意味着只有两份副本都更新了,修复才算真正生效。现在插件从一个 GitHub marketplace 安装,所以 claude plugin update 能更新到它。

一条凭空升级的护栏

看不见的警告还有一个二阶代价,也是最让我们担心的那个。

当证据表明警告被忽略时,Retro 会把一条规则从警告升级为阻止:规则触发了,没人修,它又触发了。在那个 monorepo 上,一次 retro 正是这么做的。它读到不得新增依赖这条规则”5 次结果中忽略率 0.8”,于是把它改成了对安装的硬阻止。表面上看很合理。但那五次结果中的每一次,跟在的都是一条代理从没被展示过的警告。阻止本身生效了;阻止的理由却根本不存在。

所以 Retro 现在只从一条规则的 outcomes_since 日期开始统计它的结果,也就是 hook 行为最后一次改变的时间。通道修复之前的结果描述的是没人读到的警告,它们再也不能把一条规则推向阻止。在那个 monorepo 上,这条规则退回成了警告,而代理现在真的能看到它了,下一次 retro 会在诚实的证据上重新做决定。last_changed 在仅仅改个措辞时也会被设置,现在它完全不碰证据;这个修复的第一版用的正是它,我们的评审发现,一次只改了一句话的 retro 会把一条规则的历史清空。

删掉自己产出的同步

最后一条发现,责任在我们自己。合并这些改动会运行 Coograph 的同步,它把模板文件复制到每个已注册的项目里。那天晚上,那个 monorepo 自己的提交在它的工作区里被还原了:一次 retro 带来的 hook 升级、对范围 hook 的精度修复、布局检查器里的一个守卫选项。同步把每个模板文件都覆盖到了项目的文件上,而 Retro 提议修改的恰恰就是这些文件。这个工具在删除它自己反馈循环的产出。

现在同步绝不会悄悄覆盖本地编辑。每个它要替换的文件,都会和它上次写入的内容、以及 Coograph 发布过的每一个版本做比较。两者都不匹配的文件会被保留,新的上游版本会放到 .coograph/upstream/,日志里写着 KEPT <path> (local edit)。在那个 monorepo 上,它恰好保留了有真实本地改动的那十五个文件,其余的都更新了。这些本地改动中通用的修复,在同一个版本里被合并回了上游,所以现在每个项目都能拿到。

这次发布里还有

  • OpenCode 成为一等工具。 同步会生成一个 opencode.json,包含代码图服务器和 skills 路径。端到端验证过:OpenCode 连上了服务器,一次代理对 graph_stats 的调用读到了真实的图。我们也试过用相对于项目的路径让这个文件可移植:OpenCode 在项目根目录启动时可以连上,在子文件夹里启动时就失败了,所以这个文件保留本机的绝对路径,不应提交到仓库。
  • 一个能在锁紧的 Windows 上活下来的启动器。 在开启了智能应用控制(Smart App Control)的机器上,uv 自己下载的 Python 可能被禁止加载,代码图服务器一启动就挂。现在启动命令把 uv 固定到系统上 3.10 或更新版本的 Python。
  • OpenSpec 门禁的警告点名的是规则,不是某个文件,因为在新的布局里,这条规则住在 AGENTS.md 里,而不是 CLAUDE.md。

这些没有解决什么

预算只能告诉你一个文件太大了;它不能告诉你哪些事实仍然成立。重构 skill 会对照代码核实每一条事实,但代码无法确认的事实,比如一个部署步骤或一项控制台设置,会被保留并打上标记,而不是被核实。文档检查刻意做得很粗:一份功能文档覆盖若干路径,这些路径下的改动必须动到这份文档,但它完全不管那次编辑对不对。另外,警告 hook 现在看得见了,也就更吵了;这份吵闹值不值得,正是接下来几次 retro 要衡量的。

共同的形状

这四条发现里有三条同一个病因:某样东西起作用了,也记录了自己,却从没被那个唯一要紧的读者看到。一个跳过了最大文件的预算。一条写进模型不读的通道的警告。一次建立在那条没人读的警告所产生结果之上的升级。每一次,对话记录看起来都没问题。

每一次的修复,都是不再相信记录,而是去问读者。量代理实际加载的文件。问模型它收到了什么。只统计代理本可以据此行动的证据。

这些全部随 Coograph 1.8.5 发布。指令预算和 gotcha 的文档在 /zh-CN/docs/layout/,证据如何运作见 /zh-CN/docs/retro/。

削减你的 AI 编程账单 67–78%。Coograph 采用 MIT 许可、永久免费。Pro 提供定制服务。