文章提出为仓库中的 Agent 上下文生成可校验指纹,并在源文档变更但派生配置未更新时令 CI 失败。这样能避免编码智能体依据格式正确却已经过期的路径、检查命令和职责边界自信地改错代码。
一个没有上下文的 coding agent,通常会犹豫、搜索,或者提出问题。
而一个拿着过期上下文的 coding agent,可能会自信得多。
这才是危险的情况。
文件仍然存在。指令看起来也是经过深思熟虑的。生成的 JSON 完全有效。Agent 严格照着执行——结果却进入了一个早在两周前就不再负责该功能的 package。
在代码已经被改到错误的位置之前,一切看起来都没有问题。
我希望仓库上下文能带有一种可由 CI 验证的过期信号,而不是依赖某个人记得去检查日期。
设想一个 monorepo,其中 packages/auth 负责 token 验证。仓库发布了以下机器可读的交接信息:
{
"startHere": "docs/for-agents/packages/auth.md",
"editRoots": ["packages/auth"],
"checks": ["pnpm --filter @example/auth test"]
}
后来,token 验证迁移到了 packages/security。维护者更新了源文档,却忘记重新生成交接索引。
于是,同一个仓库里出现了两个内部自洽的答案:
源文档指向 packages/security;
生成的 Agent 上下文仍然指向 packages/auth。
旧答案的格式没有任何问题。恰恰因为如此,它才格外危险。
我使用 Doc Bridge 中公开的 fixture 对此进行了测试,版本为 1.2.6。第一次执行索引和新鲜度检查时顺利通过:
Index is fresh
expected: 359355e5...
actual: 359355e5...
接着,我修改了一份面向 Agent 的源文档:
- Package: packages/os-core
- Layer: L1
+
+Token validation now belongs to packages/security.
我没有改动生成的索引。下一次检查返回了退出码 1:
ak-docs gate run index-freshness
Index is stale. Run: ak-docs index
expected: b099695d...
actual: 359355e5...
之后,我运行了 ak-docs index,检查生成的变更,再次运行 gate。此时两个 hash 一致,检查顺利通过。
这些 hash 并不是为了证明文档内容是正确的。任何 checksum 都做不到这一点。它们证明的是一个范围更窄、但很实用的事实:已提交的 Agent 上下文,确实是根据当前配置的输入生成的。
有一种很简单的办法,可以让每次新鲜度检查都变成绿色:
- run: ak-docs index
- run: ak-docs gate run index-freshness
但这也是掩盖上下文漂移的好办法。
如果 CI 在检查之前重新构建索引,那么分支中生成的文件就不再是被测试的证据。这项任务只能证明 runner 可以生成一份新鲜索引,却无法证明 reviewer 看过或提交了发生变化的路由上下文。
对于一个采用 fail-closed 策略的 pull request 检查,执行顺序应该是:
检出分支;
安装固定版本的工具;
在修改索引之前,验证已经提交的索引;
如果按照当前配置的输入生成了不同的 hash,则检查失败。
修复应该在本地完成:
检查 .doc-bridge/ 和 llms.txt 下的 diff;
确认所有权、起始文档和检查项的变化都是有意为之;
提交生成的产物;
让 CI 验证这个已提交的状态。
Doc Bridge 的 GitHub Action 遵循的正是这种模式:
name: Documentation gate
on: [pull_request]
permissions:
contents: read
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: AgentsKit-io/doc-bridge@v1.2.6
with:
config-path: doc-bridge.config.json
这个 Action 会在重新构建任何内容之前检查已提交的状态。过期索引会成为 PR 中清晰可见的失败项,而不是在 runner 内部被悄悄修复。
这个区别足够重要,值得再强调一遍。
一份新鲜索引,仍然可能忠实地编码了错误的所有权决策。一个有问题的检查项,也可以刚刚被编入索引。一份面向人的指南可以是最新的,却依然表述不清。
这份生成的上下文,是否来自我们正在审查的仓库输入?
这些输入是否正确描述了系统?
第二个问题仍然需要通过 code review、架构决策和测试来回答。这个 gate 的价值在于,reviewer 不必再面对源内容与生成上下文之间不可见的不一致。
我不认为每个仓库都需要先构建一套庞大的知识系统,才能为 Agent 提供更安全的上下文。从一份小型、确定性的契约开始就足够了:
Agent 应该从哪里开始阅读;
任务拥有哪些路径;
哪些检查能够提供证据;
哪份面向人的文档描述了相同的领域;
这份契约是否根据当前输入生成。
在此之后,搜索和 RAG 依然很有用。它们可以找到相关的设计说明、迁移记录和调用点。但语义相关性不应该悄悄覆盖仓库已经明确知道的所有权边界。
我更倾向于采用以下顺序:
verify freshness
→ resolve the ownership handoff
→ read the starting documents
→ search for implementation context
→ edit the declared scope
→ run the declared checks
如果所有权答案存在歧义,或者变更确实需要跨越多个 package,就应该触发范围更广的交接或交由人来决策。Gate 应该暴露不确定性,而不是制造虚假的信心。
提交生成的上下文会增加 review 工作量。维护者必须检查 diff。工具版本需要固定。所有权输入需要持续维护。大型文档库可能还需要缓存,才能保证验证速度。
但另一种选择同样存在成本:Agent 会根据已经无法描述当前仓库的上下文自信地行动,却没有任何可见信号表明上下文已经发生漂移。
对于允许 Agent 修改生产代码的仓库,我宁愿为一个小而明确的 diff 付出成本,也不愿去调试一个实现本身正确、却被放在错误边界之后的功能。
这一确定性验证层不需要 model 或 API key:
npm install --save-dev @agentskit/doc-bridge@1.2.6
npx ak-docs init
npx ak-docs index
npx ak-docs gate run index-freshness
接着,在不重新生成索引的情况下,修改一份已经配置的 Agent 文档,然后再次运行 gate。真正有价值的演示并不是看到绿色检查,而是看到仓库拒绝接受昨天的上下文。
我是 Emerson Braun,Doc Bridge 的创建者和维护者,因此我显然是这种方法的利益相关方。相关命令、fixture、gate 实现和测试都是公开的。如果你能让新鲜度检查在输入与已提交索引不一致时仍然通过,那么这样的反例比一句赞美更有价值。
准备工作说明:我使用了 AI 工具来协助组织和审视这篇草稿。我亲自运行了相关命令,复现了 gate 从红色失败到绿色通过的过程,对照公开源代码核验了文中的说法,并审阅了最终文本。
如果需要采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。