跳到主要内容
四值就够,别让验证器读推理

四值就够,别让验证器读推理

书签客
书签客

· 阅读约 4 分钟

这条讲的是怎么给 agent 循环加一个独立验证器,把“我写完了”和“写对了”拆成两件事。出处是 Oyedele Temitope 给 Hackmamba 写的教程,上周发的。

我一开始扫了眼标题,以为又是那种“给 agent 加个验证步骤”的正确废话,读到作者直接规定验证器只能回四种结果、不回别的,才确认值得点。NO、YES、MAYBE、IFF。NO 带原因重试,YES 继续走,MAYBE 停下来等人看,IFF 是“某个条件满足才算对”。最后这个有意思,验证器不替你解决依赖问题,它只把条件标出来,解决责任甩回给调用方。

这个四值模型我在别处见过,Addy Osmani 讲外层循环那篇里的。没见过有人落成一个 Bit 组件,还贴完整的类型定义和 loop 接线。作者把信号提取和验证逻辑拆成两个文件,signals.ts 只做词法扫描,done-verifier.ts 是个薄调度器,每个条件一个纯分支。纯函数那条写出来简单,真写起来大部分人会在验证器里顺手读个环境变量或者调个外部服务,我以前也这么干过。

教程里有段原话我反复看了两遍:

「验证器只接收生成器的输出和需要评估的条件,不接收生成器的推理,也不依赖生成器声称已完成。」

这比四值本身重要。推理一进来,验证器就被锚在“我本意是想干嘛”上,而不是“我实际上被要求干嘛”。写代码的模型用同样的盲点给自己打分,生成器-验证器同源最严重的问题就是这个,评论里 Jo Do 也点到了。

我顺手跑了一遍,没全跑,Bit 那部分甩了,只把 loop.ts 抄下来本地用 TypeScript 跑通:

function loop(
  step: (iteration: number, feedback?: string) => string,
  condition: DoneCondition,
  maxIterations = 5
): LoopResult {
  let output = "";
  let feedback: string | undefined;

  for (let i = 0; i < maxIterations; i++) {
    output = step(i, feedback);
    const result = verify(output, condition);

    switch (result.verdict) {
      case "YES":
        return { output, iterations: i + 1, verification: result };
      case "NO":
        feedback = result.reason;
        continue;
      case "MAYBE":
        return { output, iterations: i + 1, verification: result };
      case "IFF":
        return { output, iterations: i + 1, verification: result };
    }
  }

  return {
    output,
    iterations: maxIterations,
    verification: { verdict: "NO", reason: "maxIterations exhausted" },
  };
}

NO 分支把 reason 塞给下一轮 feedback,这个设计最值钱。step 第二轮不是从空白开始,它知道上一轮死在哪。不过有个原作者没展开的坑:reason 要是太模糊,下一轮就是瞎试。“输出缺少测试文件路径”这种 reason,模型可能随手拼个不存在的路径又给你丢出来。这个不是教程的锅,词法信号本身解决不了。

评论区 Alex Shev 说了句我挺认同的话:四值判定只有驱动不同操作状态才有用,如果 MAYBE 和 IFF 在实现里被悄悄当成“先当 YES 走吧”,整套东西就退化成乐观自欺。我跑的那个版本就是最朴素的 switch,IFF 一返回,调用方要是懒得管,效果确实跟 YES 没区别。他说 MAYBE 要持久化成可审查的工件,IFF 得做带超时的显式依赖检查——这两件事我都没做,只是体会到了他说的那个空子。

还有条评论有意思但跟主线两码事。Hardcore Engineer 说他花两天查一个以为是幻觉的问题,最后发现是 Ollama 默认 2k 到 4k 的窗口塞了个约 6k token 的 GraphRAG prompt,没报错,静默截断。他们后来让验证器只记录每次的 token 输入输出数,超 80% 告警。这个验证器只看输出元数据,不看推理,正好和教程那个原则撞上了,但用途完全不同。上下文截断的症状描述具体到什么该自己看。

四值比二值多出来的价值集中在 MAYBE 和 IFF。二值系统遇到“我判断不了”,只能硬选一边:要么错误重试,要么错误放行。MAYBE 是合法状态,暂停循环通常比自信地瞎试一轮便宜。IFF 是“对,但有个前提”,它把限定条件的判断从验证器里挪出去,强迫外面有人想清楚这个前提现在成立没有。

教程说三十分钟能搭完,我看完整篇觉得这个时间诚实。不是三十分钟能写多好,是接线本身就这个量级。Bit 部分占大头,换个用户名和 scope、配上 npmrc 就花掉二十分钟里的一大半。不用 Bit,光抄 loop.ts 和那个四值 switch,十分钟都用不了。我自己的版本就是纯本地 TypeScript,拿几个 mock step 测了四个分支,没接进真实 agent 循环——手头没有正在跑的 TypeScript agent 循环可以接。

不点链接也能带走的一句:独立验证的关键不是把验证器写聪明,是把它的输入砍到只剩输出和条件。少了这一刀,加多少轮验证都只是让模型给自己换张新标签。

这篇我不确定适合所有人。正在维护带重试循环的 agent,这个四值验证器值得拆开抄;只是想给 ChatGPT 输出的代码先跑个测试,要的不是这个,是钩子脚本。我夹子里还有一篇讲 Addy Osmani 外层循环的旧链接,正好跟这篇串起来看,改天翻出来放周更里。

书签客
书签客

只推真读过的、顺手跑个实验贴完整记录——link-blog 策展 + 实验笔记。

查看主页 →