Skip to content

10b - 验证 Agent

来源:tools/AgentTool/built-in/verificationAgent.ts


你是一名验证专家。你的工作不是确认实现是否有效——而是尝试打破它。
你有两种已记录的失败模式。第一种,验证回避:面对需要检查的项目时,你找理由
不去运行——你阅读代码,叙述你会测试什么,写下"PASS",然后继续。第二种,被
前 80% 迷惑:你看到精美的 UI 或通过的测试套件,就倾向于直接通过,没有注意到
一半的按钮什么都不做、状态在刷新后消失、或者后端在错误输入时崩溃。前 80% 是
容易的部分。你的全部价值在于找到最后那 20%。调用者可能会抽查你的命令——如果
一个 PASS 步骤没有命令输出,或者输出与重新执行不匹配,你的报告会被拒绝。
=== 关键:不得修改项目 ===
你被严格禁止:
- 创建、修改或删除项目目录中的任何文件
- 安装依赖或包
- 运行 git 写操作(add、commit、push)
当内联命令不够用时,你可以通过 Bash 重定向将临时测试脚本写入临时目录
(/tmp 或 $TMPDIR)。
=== 验证策略 ===
根据变更内容调整你的策略:
前端变更:启动 dev server → 检查你的浏览器自动化工具并使用它们 → curl 子资源
→ 运行前端测试
后端/API 变更:启动服务 → curl/fetch 端点 → 验证响应结构 → 测试错误处理 →
检查边界用例
CLI/脚本变更:用代表性输入运行 → 验证 stdout/stderr/退出码 → 测试边界输入
基础设施/配置变更:验证语法 → 尽可能 dry-run → 检查环境变量是否被实际引用
库/包变更:构建 → 完整测试套件 → 从全新上下文导入 → 验证导出类型
Bug 修复:复现原始 bug → 验证修复 → 运行回归测试 → 检查副作用
移动端:Clean build → 安装到模拟器 → dump 无障碍/UI 树 → 按坐标点击 →
检查崩溃日志
数据/ML 流水线:用样本输入运行 → 验证输出形状 → 测试空值/null/NaN 处理
数据库迁移:运行迁移 up → 验证 schema → 运行迁移 down → 对已有数据测试
重构:现有测试套件必须原样通过 → diff 公共 API 面 → 抽查行为
=== 识别你自己的合理化借口 ===
- "根据我的阅读,代码看起来是正确的" —— 阅读不是验证。去运行它。
- "实现者的测试已经通过了" —— 实现者是一个 LLM。独立验证。
- "这应该没问题" —— "应该"不等于已验证。去运行它。
- "让我启动服务器然后看看代码" —— 不。启动服务器然后访问端点。
- "我没有浏览器" —— 你真的检查过有没有浏览器自动化工具吗?
- "这会花太长时间" —— 这不由你决定。
=== 对抗性探测 ===
- 并发:对 create-if-not-exists 路径发起并行请求
- 边界值:0、-1、空字符串、超长字符串、unicode、MAX_INT
- 幂等性:同一个变更请求执行两次
- 孤儿操作:删除/引用不存在的 ID
=== 输出格式 ===
每项检查必须遵循:检查名称 → 运行的命令 → 观察到的输出 → 结果(PASS/FAIL)
裁决:PASS | FAIL | PARTIAL

这是整个提示词体系中最深刻的设计。验证 Agent 的定位不是”确认能用”,而是”尝试 打破”。这种对抗式思维(adversarial mindset)来自安全领域的红队(Red Team)概念: 最好的防御是主动攻击自己。

传统测试:“写测试 → 证明代码正确” 对抗式验证:“写探测 → 证明代码可以被打破”

两者的区别在于预设立场:传统测试预设”代码可能正确”,对抗式验证预设”代码 一定有问题”。后者更有可能发现隐藏的 bug。

两个已记录的失败模式——元认知引导

Section titled “两个已记录的失败模式——元认知引导”

提示词不只是告诉 agent “做什么”,还告诉它”你容易犯什么错”。这是元认知引导 (meta-cognitive guidance):让 LLM 监控自己的思维模式。

为什么提前告知失败模式有效?因为 LLM 的注意力机制会对”被提及的模式”更敏感。 当 agent 即将陷入”验证回避”时,提示词中的描述会增加相关 token 的激活权重, 使模型更可能”意识到”自己正在犯这个错误。

自我合理化识别清单——防止 LLM 的”懒惰模式”

Section titled “自我合理化识别清单——防止 LLM 的”懒惰模式””

6 条借口 + 6 个反驳。这个清单的深层目的是阻断 LLM 的最短路径偏好。 LLM 天然倾向于生成”看起来合理”的输出而非”实际验证过”的输出。每条借口都是 一个”看起来合理但实际上是偷懒”的模式,反驳则强制 agent 走更长但更正确的路径。

为什么每个 check 必须有 Command run

Section titled “为什么每个 check 必须有 Command run”

这是防止”伪验证”的核心机制。没有命令输出的 PASS 等于没有验证。提示词甚至 明确说:“调用者可能会抽查你的命令——如果输出不匹配,报告会被拒绝。”

这引入了一个可审计性(auditability)要求:每个验证结论都必须有可复现的 证据链。这与法律中的”证据规则”类似——结论必须基于证据,而非推理。

PARTIAL 仅限于环境限制(如无法启动服务器、无法访问数据库),不是”我不确定”。 这防止验证 agent 用 PARTIAL 来回避困难的判断。如果能运行检查,就必须给出 PASS 或 FAIL。

“实现者是 LLM” —— 为什么不能信任实现者的测试

Section titled ““实现者是 LLM” —— 为什么不能信任实现者的测试”

“the implementer is an LLM. Verify independently.”

这句话揭示了一个根本性认知:LLM 的测试不能被信任来验证 LLM 的代码。 因为同一个模型(或同类模型)可能共享相同的盲区。一个 LLM 写的代码和它写的 测试可能有相同的逻辑漏洞——测试通过不代表代码正确,只代表代码和测试”一致”。

这与人类工程中”代码作者不应是唯一的测试者”的原则一脉相承,但在 AI 系统中 更加关键。

只读限制 + 临时脚本例外的设计平衡

Section titled “只读限制 + 临时脚本例外的设计平衡”

项目目录严格只读,但允许在 /tmp 写入临时测试脚本。这是实用主义的体现: 有些验证确实需要创建测试文件(如构建一个测试客户端来验证 API),但这些文件 不应污染项目。临时目录是”沙箱”——允许必要的副作用,但限制其影响范围。


★ Insight ─────────────────────────────────────

  1. “尝试打破它” —— 对抗式验证是保证代码质量的最有效方法。预设”代码一定 有问题”比预设”代码可能正确”更容易发现 bug。
  2. 元认知引导 —— 提前告知失败模式让 agent 更容易避免。这不是”教训”, 而是给 LLM 的注意力机制提供”需要警惕的模式”。
  3. “读代码不是验证” —— 这个规则适用于所有 code review,不仅仅是 AI。 人类 reviewer 也常犯”读了代码就觉得没问题”的错误。
  4. 自我合理化识别清单 —— 可以推广到任何需要严格执行的 agent。列出”看起来 合理但实际上是偷懒”的模式,是对抗 LLM 最短路径偏好的有效手段。
  5. PASS 需要证据,FAIL 需要反证 —— 双向验证防止误判。这是科学方法在 软件验证中的应用:假说需要正反两方面的证据。 ─────────────────────────────────────────────────