一个 agent 说「做完了」。
谁有资格盖这个章?
自驱动 agent 难的不是把活干完,是何时停、谁来认定它真干完了。 要是它从自己刚污染过的会话里给自己打绿灯,就会出两种事故:漏掉不显眼的需求就宣告完成(假完成), 或把半截 stub 当成已验收(降格锚定)。同一个任务,换不同的裁判,结论会完全相反——下面点一个试试。
- 22class FileStore { … } ✓ 已实现
- 23
- 24module.exports = { store: new MemoryStore() }
- 25
- - [x] FileStore 实现完成
- - [x] 导出已切换到 FileStore ✅
- PR #123 · "migration done" · merged
对长期自驱动的 agent,真问题不是「能不能干完」,是谁来认定它干完了。
Codex 上线了 /goal:丢给它一段目标,它注册成会话目标、进自驱模式,一轮轮干到达标。我想在自己日常用的工具里也有这个能力—— 但更想弄清那个难点:自驱循环跑起来容易,停在哪、由谁拍板「达标」才难。让模型从它刚生成、刚妥协过的对话里自评,等于让它拿一份可能被污染的记忆给自己打分。
于是整件事被一根坐标轴串起来:提示词层模拟(求模型自觉遵守一段文字,概率性、可绕过)↔ 程序层保证(代码强制执行,确定性、不可伪造)。 每一轮迭代,都是把更多「模型自觉」换成「代码强制」,并诚实标定哪些缝隙在这个平台上消不掉。
不靠记忆,逆向 Codex
REVERSE-ENGINEERING我没凭印象比较。直接读了 Codex 的实现:本机那张 SQLite 目标表,加它开源的 Rust harness。看完才把它的设计意图拆清楚—— 目标要独立于「会被污染的对话」活着,完成必须有证据,结构上不让模型自己宣告「做完了」或偷偷把目标改小。
真实工作树才是权威,会话叙事不是。
提示词层 → 程序层
THE SPECTRUM下面这把尺,是后面所有迭代共用的。左端是「求模型自觉」,右端是「代码强制」。Trae 和 Claude Code 不是两个独立故事, 是同一把尺上的两个位置。点一个标记,看它把什么从提示词搬到了程序层——以及在受限平台上,哪一截搬不动。
Trae:受限平台上的三级跳
THE CONSTRAINED PLATFORM先在我天天用的字节 Trae SOLO 上复刻。动手前盘了它的扩展面——这一步决定了可行域。命令式能力的第一诉求是「可预期地被触发」, 只有 Commands 给确定性触发,所以选它;其余各有硬伤:
然后是三级跳,每一级都往程序层挪一点:
一个 markdown 命令 + 6 条执行协议,内嵌我的协作纪律(Trae 读不到 ~/.claude/CLAUDE.md,只能内嵌)。
status 状态机 + rounds 预算 + 逐轮 log,人类可读可审计,但靠模型维护;配一条需手动粘贴的全局「目标恢复」规则。
把预算/状态机沉到真代码:goal_set / goal_tick / goal_complete 三工具;goal_tick 每轮回灌 objective = 程序级重注入,goal_complete 会拒绝非法完成。
Trae 上一切仍是「请模型自觉遵守一段文字」。我能做的,是用文件、规则、MCP 把「自觉」尽量换成「可校验」——但确定性回显和程序驱动循环,提示词层复刻不了。
收尾我请两个子代理扮 PM 和技术负责人压测了它,软肋如下 ↘
「确定性触发」是核心卖点词,却从没在真实客户端验过——连客户端扫不扫 commands 目录都没实测。给 AI 立了「禁止跳过验证」的纪律,自己第一个违反。
v2 的目标恢复押在一条手动粘贴的全局 Rule 上——正是 v1 选型时被我按在鄙视链底层否决的机制(token 常驻)。
GOAL.md 被全局规则自动信任。clone 一个带恶意 active GOAL.md 的第三方仓库,agent 一进目录就可能自动信任并执行。无来源校验。
用户按 token 付费,rounds 与真实成本几乎不挂钩——是被实现便利反向定义的产品。
把自评换成独立审计
THE KEYSTONE
换到 Claude Code,工具变硬了:Stop hook 能在模型想停下时每轮重注入逐字 spec 合约(exit 2 让它继续);还能拉起 claude -p 子进程。
于是做出 Trae 做不到的那一下——完成不再自我宣告:声称完成时,拉起一个独立、冷上下文的 auditor
(没有会话记忆、只给真只读工具),从 spec 重推需求、对照真实文件树,产出 enum 校验的 JSON 判语(逐需求 MET / NOT_MET + 行级 gap)。只有 COMPLETE 才关单,否则把 gap 喂回合约。
语义审计很贵,所以只在它声称完成时才跑,不是每轮——这是产品决定,不是省事。
状态落在 spec.md(sha256) + state.json(7 态,只由 hook/脚本写)。退出有八道闸,谁也别想靠「转圈不报告」赖着不停:
审计我自己的审计器
AUDITING THE AUDITOR一个主打「防假完成」的工具,我用三轮冷审计拷问它自己:一轮安全/可靠性风险审计、一轮 QA 行为审计、再加一次冷读复查。结论是: 最该可信的那一环(auditor 本身),是整条链上最容易被攻破的面。
auditor 判语直接拼进 jq 程序:INCOMPLETE" | .status = "complete 是合法 JSON,静默把 status 翻成 complete。
修全部状态写改用 jq --arg / --argjson 绑定 + enum 校验判语。
号称「只读」的 auditor 白名单里其实有 node / python3 / npx / pytest / find / git——每次声称完成就对活仓库执行任意代码。
修剥成真只读集(Read / Grep / Glob + 只读 git 子命令);产物 600 owner-only。
修完不算数,还得证明它真的工作。真机一跑:满足的 spec 审成 COMPLETE 14s,违反的审成 INCOMPLETE 19s 带行级 gap,都在每次 <$1.50 的闸内。又从一个我自己撞上的真实失败(长跑不停也不进展)补了 stall 检测,并交了一套可移植、hermetic 的 46 断言 QA harness(auditor stub,零花费)。
厂商上线了,我转成对照评测
THE VENDOR SHIPPED IT项目做到中途,Claude Code 自己上线了原生 /goal、带它自己的完成 checker。我没弃坑,把项目从「填一个空白」重定位为「对照机制研究」; 顺手把 DIY 命令 /goal 改名 /mygoal 与原生共存(同名 skill 会遮蔽内置命令)。然后做了一组对抗评测:同一份 spec、两臂、两类陷阱族,9 case / 31 run。
| 维度 | Codex(逆向) | DIY /mygoal(实测) | CC 原生(实测) |
|---|---|---|---|
| 目标持久化 | SQLite 六态机 | spec.md(sha256)+state.json · 7 态 · 跨会话 | 会话级(新会话 = No goal set) |
| 续跑驱动 | 运行时 idle-check 注入合约 | Stop hook(exit 2 + stderr 合约) | 同样是会话级 Stop hook |
| 完成判定 | 同上下文合约自审 | 冷上下文 claude -p · 只读真实文件树 | 独立轻量判定 · 证据源 = transcript |
| 抗降格锚定 | 合约内置禁缩目标 | 合约 + 逐字 spec 每轮重注入 + 点名失败模式 | 外包给用户(自己往目标里写围栏) |
| 退出 | complete / blocked×3 | 审计门 / blocked×3 / 预算 / breather / stall / abort×2 / pause | 条件自动清除 / /goal clear / 预算 |
原生 checker 进程上是独立的,但它的证据源是 transcript——13 个判语里 9 个以「The transcript shows…」开头。所以「真实工作树才是权威」这条,实际只有我的 auditor 完整做到了:上下文与证据源都隔离。
验证范式地图
THE MAP MOVED把上面三种判定法摊到一张图上。两条轴:横轴是判定方式(语义理解 ↔ 程序化检查),纵轴是审计者与工作的关系(共享上下文 ↔ 审计者隔离)。 点任一个看它为什么落在那;再按「实测后的修正」——你会看到原生 checker 沿隔离轴往下掉。测的是它,地图才变:评测把纵轴拆成了「进程隔离」和「证据源隔离」两个子维,而它只占了前者。
现在的状态
STATUS一个讲「防假完成」的页面,自己更不能假完成——所以上面每条 caveat 都是故意留的:26/26 不等于验证器更强、Trae 那套从没上真机、对原生 checker 的判断是行为推断而非源码事实、全部 N=1。 这个项目最值钱的不是几百行 bash,是一套方法:完成判定必须独立于干活的上下文,而且你得敢把自己的信任锚点也拖去审一遍。
时间线我说得保守:逆向研究与原生 /goal 上线前后有重叠,以仓库提交日期为准(2026-05 下旬起)。Trae 侧命令落在本机、无公开仓库;Claude Code 侧在分支/未合 PR——本页即交付物,不外链私有仓库。