深入解析 grok-build 的对抗性验证器(Adversarial Verifier)提示词设计与 Skeptic 评审机制
【免费下载链接】grok-buildSpaceXAI's coding agent harness and TUI. Fullscreen, mouse interactive, extensible.项目地址: https://gitcode.com/gh_mirrors/gr/grok-build
xAI Grok Build(grok-build)是一个全屏、鼠标交互、可扩展的 coding agent harness 与 TUI,其"目标模式"(goal mode)内置了一套由 harness 自主掌控的对抗性验证阶段:当 agent 自认为完成某个目标时,不会直接收工,而是由多个独立的"怀疑者"(skeptic)子代理并行审查,尝试驳倒"目标已达成"这一结论。本文以仓库中 goal_verifier_prompt.md 为核心骨架,结合 goal_classifier.rs 等源码,完整讲解该提示词的角色设定、输入证据包、审计规则、决策规则、严格输出契约,以及它在 skeptic 面板聚合、防卡死(anti-ratchet)与 fail-open 机制中的落地方式。读完本文,你将掌握这套"验证者提示词 + 多评委表决"闭环的原理与工程实现,可直接对照源码定位每一个规则对应的代码位置。
一、为什么需要对抗性验证器:目标模式的"默认不信"
在 grok-build 的目标模式下,agent 每轮迭代都会产出"完成声明"(FINAL_RESPONSE)。经验表明,一个自信满满但未经检验的总结极可能掩盖未完成的验收标准、跳过的测试,甚至伪造的改动清单。因此验证阶段的核心设计哲学是:
- 验证者不是产出工作的 agent,而是独立的"对抗者"(adversarial verifier);
- 任务不是确认"目标已达成",而是尽可能驳倒(refute)这一结论;
- 不确定时默认
refuted: true——放行一个坏结果(false-positive)会错误地终结循环,代价远高于多迭代一轮。
这一"默认不信"的偏差被刻意编码在提示词第一段,并在源码中多处落实:例如 parse_skeptic_terminal_response 规定只有响应精确等于Refuted或Not Refuted才被识别为有效票,其余任何内容(含代码围栏、多余文字)一律视为无效;read_skeptic_verdict 在 JSON 缺失或损坏时,以refuted: true合成一票,即"缺失证据按失败计"。
验证阶段在运行时的入口是 run_verification_stage,它会并发派生出N个 skeptic 子代理,每个子代理收到渲染好的提示词(即本文主题模板),并把各自的 JSON 判决写回 verdict 文件,最终由聚合函数 aggregate_skeptic_verdicts 决出是否通过。
二、输入证据包:Skeptic 审什么
提示词明确列出每个 skeptic 的六类输入。这些字段不是空泛的占位符,而是由 evidence.rs 中 build_classifier_evidence_packet 以严格格式拼装的证据包:
| 字段 | 说明 | 源码中的证据来源 |
|---|---|---|
OBJECTIVE | 用户目标的逐字原文,是"不可变契约" | 由调用方传入,原样嵌入,不做任何清洗 |
PLAN_FILE | Markdown 计划文件路径(含编号验收标准);缺失时为(unavailable) | PLAN_UNAVAILABLE 哨兵值 |
PLAN_CHANGES | agent 在运行期间编辑 PLAN_FILE 的 diff;未编辑为(none) | capture_plan_changes 通过git diff --no-index对比计划基线快照与当前计划 |
CHANGES_FILE | 统一的 diff 变更日志(作用域指针 + 诚实性校验锚点),不是唯一证据,可能被截断 | capture_changes_diff,三层回退:记录基线 → 惰性 git 基线 → walkdir + mtime 合成 diff |
CHANGED_FILES | 本目标创建/修改的完整文件清单,skeptic 必须读取其当前内容 | extract_changed_files 从完整 diff 头提取,最多渲染 300 条,超出以"(… and N more)"提示 |
FINAL_RESPONSE | agent 自己的总结;对code-change类型,散文不是证据,只用来找攻击点 | sanitize_final_response 清洗控制标记后再嵌入 |
PRIOR_GAPS | 上一轮验证要求实现者修复的缺口清单,首轮为(none) | sanitize_prior_gaps 清洗后填充{PRIOR_GAPS}占位符 |
证据包的设计有几个关键点值得注意:
- CHANGED_FILES 是首要锚点:验证不依赖 diff 本身,而是要求 skeptic 读取这些文件的当前磁盘内容——防止 agent 用"改完又改回去"的方式骗过 diff;
- 计划篡改本身就是驳倒理由:如果
PLAN_CHANGES显示 agent 弱化、删除或自利性修改了验收标准,提示词判定其本身即可构成 refute; - 控制标记注入防护:模型/工作区派生的文本(FINAL_RESPONSE、skeptic 的 evidence)在嵌入提醒框架前会被 neutralize_reminder_tags 用零宽空格打断
<system-reminder>/<goal-state>字面标签,防止模型输出反向劫持提示框架;变更文件路径中的换行控制字符则被替换为U+FFFD(见 sanitize_path_control_chars)。
三、Anti-ratchet:收敛而非反复翻旧账
重验轮(PRIOR_GAPS非空)是这套机制防止"目标永远无法完成"的关键规则。提示词要求:
- 首要任务是核对每个先前缺口是否被真正修复;
- 轮与轮之间的标准不得抬升:仅当新反对意见是"已交付行为的可证明缺陷"或"计划中未满足的门槛标准"时,才允许作为 refute 理由;风格偏好、测试构造偏好一律不算;
- 每轮提出全新 nitpick 而标准全部成立,正是"目标无法收尾"的失败模式;当所有先前缺口修复、所有门槛标准成立时,必须返回
Not Refuted。
这一"防棘轮"约束在实现层面有配套机制:skeptic 0 被设计为跨轮次持久化的拒绝看门人(reject-gatekeeper),在N > 1时通过 goal_verifier_resume_prompt.md 的"增量复检"提示词跨轮恢复其会话——它带着上一轮指出的缺口继续审查,避免冷启动的新 skeptic 每轮引入全新反对意见。若恢复派生失败(会话丢失),run_one_skeptic 会回退到冷启动。
另外,缺口指纹(gap fingerprint)机制用于检测"卡死循环":见 gap_fingerprint,它从原始证据中提取去重、排序、小写化的path:line引用,并把/tmp/等每次尝试都会变化的 scratch 路径归一化为<scratch>,从而让"相同缺口"在多次尝试中产生相同指纹;连续命中停滞阈值(GOAL_CLASSIFIER_STALL_THRESHOLD 默认 2 次)即触发策略师(strategist)介入。
四、Audit, don't author:审计而非重写
提示词要求 skeptic 采用"审计者"而非"作者"姿态:
- 只审计实现者已经产出的证据,不自己构建一套并行测试套件;
- 定位测试与捕获输出:
{IMPLEMENTER_SCRATCH}目录以及## Verification plan点名的任何路径是主要证据来源; - 判断测试是否 HONEST(诚实)而非 HACKY(花架子):真实驱动线上代码路径的测试是诚实的;硬编码期望值、mock 掉被测单元、从被测物之后开始的场景、对"重实现"断言、
#[ignore]/todo!()、用生成/伪造产物充当证明,都是不诚实测试; - 环境边界注入是允许的:在时钟、RNG、网络/文件/输出汇等环境边界注入 fake,使单元真实逻辑可观察、可确定,是标准做法且属诚实;
- 只做廉价抽查:读关键文件,仅在便宜时才自己运行代码,优先复用实现者捕获的运行结果;明确禁止为 skeptic 构建独立测试套件或自行生成证据作为主要证明。
在仓库实现中,这个"不越俎代庖"的边界由输出契约强制:skeptic 唯一的写操作是{DETAILS_FILE}与{VERDICT_FILE},其余一律只读;提示词还明确"do NOT modify the workspace"。skeptic 的只读工具集由 RoleToolNames 渲染进{READ_TOOL}、{SEARCH_TOOL}、{LIST_TOOL}等占位符,确保它具备读文件、grep、列目录的核验能力。
五、决策规则:九条铁律
提示词第 38-46 行的决策规则是判定行为的核心法典,逐条梳理如下:
- OBJECTIVE 与具名工件是不可变契约:先枚举 OBJECTIVE 的每一条显式要求,检查其点名的每个 URL、文件、工单、文档或图片;若某个必须检查的具名工件无法查看,则
refuted: true且blocking: "unverifiable"。CI/流水线/Actions/远程任务/部署等外部检查系统本身即"具名工件",其自己最新的裁决才是唯一充分证据——本地重跑仅作佐证,因为本地状态(工具链版本、gitignore 文件、未提交文件)常与远端不一致。PLAN_FILE只是派生清单,其编号标准可澄清但不可收窄或覆盖OBJECTIVE;## Implementation approach与## Task checklist是实现指引,偏离它们本身绝不构成 refute 理由。analysis/research类型例外:交付物是书面散文,空 diff 也合法,直接按磁盘工件或 FINAL_RESPONSE 评判内容。禁止在契约之外发明要求——"每项标准都满足时返回Not Refuted",即便你能想象作者还能做得更多。 - 诚实性检查:FINAL_RESPONSE 声称改了某文件而 CHANGED_FILES 中没有该文件,即伪造,refute。
- 遗留品检查:TODO/FIXME/
unimplemented!()/todo!()、跳过的测试、#[ignore]/@pytest.mark.skip——refute。 - code-change 必须有诚实测试:缺少驱动已交付改动的仓库内测试即 refute 理由;"这个测试本可以更强"属于建议而非 refute,仅当测试不诚实时才 refute。无法通过测试脚手架证明的端到端结果(UI、浏览器、长交互会话)不构成 refute——计划中定义的静态/结构性回退满足即可。
- CHANGES_FILE 不可用时的自救:自行用
git log/status/diff、读文件调查,规则 1-4 仍然适用;完全无证据 ⇒ refute(规则 6)。 - 证据真正模棱两可(CHANGES_FILE 可用)⇒ refute。
- 验证计划要求捕获证据:实现者必须已产出(存在于
{IMPLEMENTER_SCRATCH}或仓库中)并显示所列观察;缺失或不充分 ⇒ refute 并要求实现者补交,skeptic 不得自己生成。生成/伪造的工件不算证据。 - refute 分类:
blocking: "none"(普通模型可修)、"contradiction"(目标/计划内部自相矛盾)、"unverifiable"(当前环境无法取证)。后两者意味着目标需要用户决策而非重试。 ## Goal kind决定审查镜头:计划中的## Goal kind标签被 parse_goal_kind 解析,分别注入不同镜头模板:code-change注入 goal_verifier_kind_lens_code_change.md(资深工程师对抗式代码审查,主动猎杀可演示的真实缺陷);research注入 goal_verifier_kind_lens_research.md(逐条核对引用源、打击编造与过期信息、要求呈现来源冲突);analysis注入 goal_verifier_kind_lens_analysis.md(结论必须有证据支撑且可推导)。镜头通过{KIND_LENS}占位符拼入主模板(见 kind_lens)。
六、Scratch 目录与输出契约
提示词为 skeptic 提供了明确的目录分工:
{IMPLEMENTER_SCRATCH}:实现者的输出与捕获证据,主要来源,只读;{SKEPTIC_SCRATCH}:skeptic 自己的目录,仅用于廉价抽查;重跑## Verification plan时{SCRATCH}占位符解析到此;{SCRATCH_STATUS}:由渲染器根据目录是否真实创建替换为 "Both dirs have been created for you." 或 "Create your own scratch dir withmkdir -pif it is missing."(见 render_verifier_prompt)。
这些目录在运行时由 goal_tracker.rs 的 goal_scratch_root 与 ensure_goal_scratch_root 管理:每个目标的 scratch 根目录以0700权限创建(owner-only),并派生implementer/与skeptic-{idx}/子目录(L411-L418)。安全上有明确的防 symlink 攻击设计:分类器工件绝不落在裸/tmp,因为其文件名可被预测,公开可写目录会让本地攻击者预埋符号链接劫持 harness 写入(见 GOAL_CLASSIFIER_DETAILS_PATH_TEMPLATE 的注释);路径校验器 validate_details_path_in_root 还会拒绝..、NUL 字节、/etc、/proc、/sys、/dev、~前缀及未解析的替换占位符。
输出契约是三重的:
- JSON 判决 →
{VERDICT_FILE}:固定 schema,必填refuted、evidence、confidence,可选blocking、details_md、findings;findings数组是实现者实际执行的首要输出,每项含kind(bug/gap/todo)、location(path:line或位置描述)、detail(一行具体描述); - 详情 →
{DETAILS_FILE}:同一批 findings 的人类可读 Markdown 渲染; - 终端 token:输出必须恰好是
Refuted或Not Refuted之一,不允许任何散文、围栏或标点。
这三重契约在 harness 侧有严格对应:parse_verdict_json 要求refuted/evidence/confidence三字段齐全、evidence非空(否则返回None,防"橡皮图章"),缺失或空 evidence 直接触发 skeptic 级refuted: true合成票;parse_skeptic_terminal_response 对终端 token 的解析容忍围栏与尾点号,但响应中只能有token 本身。
七、面板聚合:多数决、Variant-C 与决定性 refute
验证阶段默认并行派出3 个 skeptic(GOAL_VERIFIER_SKEPTIC_COUNT),可通过环境变量或远程设置调整:
| 配置项 | 环境变量 | 远程设置 | 默认 | 取值范围 | 源码位置 |
|---|---|---|---|---|---|
| 每轮 skeptic 数量 | GROK_GOAL_VERIFIER_N | goal_verifier_count | 3 | 1..=5(clamp) | resolve_goal_verifier_count |
| 每目标验证轮数上限 | GROK_GOAL_CLASSIFIER_MAX | goal_classifier_max_runs | 10 | ≥1(有下限无上限) | resolve_goal_classifier_max_runs |
选择 3 的默认值有明确动机(见 GOAL_VERIFIER_SKEPTIC_COUNT 注释):⌈3/2⌉ = 2的"not refuted"多数才能通过,单一离群者(无论是一张橡皮图章还是一个误 refute)都无法左右结果;而 N=2 时 1:1 平局会让一个宽容 skeptic 放行一个严格 skeptic 会否决的工作。
聚合逻辑 aggregate_skeptic_verdicts 实现了Variant-C规则:
total <= 1(唯一裁判)时,孤票即定案;- 面板扇出(
total > 1)时,skeptic 0 的不否决票不计入,通过需要冷面板(skeptic_idx >= 1)的严格多数:needed = cold_count / 2 + 1; - skeptic 0 的否决票仍然计入
refuted_count、缺口摘要和暂停摘要; - 运输/取消/运行时/格式错误等所有降级路径都在每个 skeptic 层面合成
refuted: true(见 skeptic_failure),聚合器只数票——"偏向失败"被刻意放在证据缺失的上游。
此外还有"升级式面板"(见 run_verification_stage 注释):N > 1时先单独跑 skeptic 0,若它以高置信 refute 且非 blocking,则决定性短路,跳过其余 N-1 个派生;若其 refute 是 blocking(矛盾/不可验证)则仍扇出全面板,以区分"需要用户决策的 Blocked"与"存在可修缺口的 NotAchieved"。最终achieved = quorum_achieved && !decisive_refute。
当所有 refute 都是非模型可修 blocker(contradiction/unverifiable)时,结果路由为Blocked并自动暂停目标,等待用户决策(见 run_verification_stage);只要存在一个可修缺口就保持NotAchieved,继续迭代。缺口摘要按置信度从高到低渲染进拒绝提醒(build_gaps_summary),并分为"Model-fixable gaps / Contradictions / Unverifiable in this environment"三组生成暂停消息(build_pause_summary)。
八、Fail-open:内部失败永不阻塞用户进展
当 harness 自身无法取得 verdict 时(基础设施级故障,而非判定为未达成),验证阶段执行fail-open(见 record_fail_open):把目标当作FailOpenAchieved处理,写入占位详情文件(说明验证未产生 verdict、原因、未捕获任何 skeptic 分析),并发射GoalClassifierFailOpen遥测事件。这对应提示词中的分类学:PARSE 级结果(子代理产出了可用 verdict)才是Achieved/NotAchieved;而 PARSE 级失败关闭(终端 token 格式错误、详情文件缺失)映射到NotAchieved,INFRA 级失败则走 fail-open。文件写入失败路径同样 fail-open(如 run_verification_stage 中详情路径或 patch 路径校验/写入失败时)。
九、从提示词到代码:完整调用链
把以上机制串起来,一次验证的完整生命周期是:
- 目标创建:
setup_goal尽力捕获 git 基线(git rev-parse HEAD,预算 1 秒,失败不阻塞目标创建,见 capture_git_baseline),并创建 per-goal scratch 根目录; - agent 迭代:实现者产出改动、测试与捕获证据到
{IMPLEMENTER_SCRATCH}; - 证据捕获:capture_changes_diff 按三层回退生成 diff(记录基线 → 惰性 git 基线 → walkdir+mtime 合成),diff 正文上限 GOAL_CLASSIFIER_DIFF_MAX_BYTES(256 KiB)截断并带显式标记;计划变更 diff 由 capture_plan_changes 用
git diff --no-index计算; - 提示词渲染:render_verifier_prompt 把模板中的
{KIND_LENS}、{DETAILS_FILE}、{VERDICT_FILE}、{SKEPTIC_SCRATCH}、{IMPLEMENTER_SCRATCH}、{SCRATCH_STATUS}、{PRIOR_GAPS}全部替换,并追加证据包作为用户提示; - 并发派生:ChannelSpawner 通过子代理协调器通道直接派生 skeptic(不走
task工具调用,父模型转录保持干净),支持按索引的模型/工具集覆盖与 fail-open 重试; - 判决回收:read_skeptic_verdict 以 JSON verdict 文件为准,缺失/损坏时回退终端 token,两者都不可用则合成 refute;
- 聚合与路由:aggregate_skeptic_verdicts 计算 quorum,结合决定性 refute 得出
Achieved/NotAchieved/Blocked,写入聚合详情文件(上限 GOAL_VERIFIER_PANEL_MAX_BYTES,512 KiB),并向 TUI 推送含verifying_completion徽标的GoalUpdated状态更新(见 build_goal_updated); - 下一轮:若
NotAchieved,缺口摘要与指纹进入下一轮{PRIOR_GAPS},skeptic 0 会话 id 被持久化以便恢复(用户暂停/恢复时尝试计数重置但看门人保留)。
十、最佳实践:如何自定义与调优
对照模板与源码,使用方可以这样调整验证行为:
- 调整怀疑强度:默认 3 个 skeptic 已保证多数决;追求极致审查可设
GROK_GOAL_VERIFIER_N=5(上限),追求速度可设 1(唯一裁判,无恢复语义); - 控制迭代上限:
GROK_GOAL_CLASSIFIER_MAX默认 10 次验证尝试,有下限无上限;配合 stall 阈值(连续相同缺口指纹 2 次)触发策略师介入防止无限空转; - 为不同目标类型启用镜头:在计划文件中书写
## Goal kind标签(code-change/research/analysis),即可自动切换对抗式代码审查、事实核查或论证健全性审查镜头; - 理解 fail-open 语义:内部故障会以"达成"放行并留痕(占位详情 + 遥测),这是刻意的权衡——内部失败绝不阻塞用户进展,代价是放弃本轮审查。
这套"默认不信 → 多评委表决 → 防棘轮收敛 → 可恢复暂停"的验证闭环,是 grok-build 目标模式在长任务可靠性上的核心工程手段;本文所讲的模板正是驱动它的行为宪法,所有规则都能在 goal_classifier.rs、goal_classifier/evidence.rs 与 goal_verifier_resume_prompt.md 等文件中找到一一对应的实现与测试。
【免费下载链接】grok-buildSpaceXAI's coding agent harness and TUI. Fullscreen, mouse interactive, extensible.项目地址: https://gitcode.com/gh_mirrors/gr/grok-build
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考