Table of contents
Open Table of contents
起因
同时开着四个 session, 每个都在等我按 y. 我在它们之间来回切, 看 diff, 确认, 再切回去. 一小时以后我发现自己什么也没想, 只是在点确认.
这不是在用 agent. 这是在给 agent 当保姆.
于是把最近攒的六篇读完了. Cursor 的 self-driving codebases, Uber 的 software factory, Josh Rosen 的 LLM-as-judge, 还有三篇 X 上的长文. 领域和体量差得很远, 但说的是同一件事: 怎么把人从每一步里拿出来, 只留在最后一步.
Loop: check 才是全部
Hanako 那篇的定义很干净. 一个 loop 是四件事: produce, check, correct, 重复到绿. 剩下三件都不重要, check 才是全部.
因为如果没有一个能在你不在场时判定失败的东西, 你没有 loop, 你有的是一个 scheduler.
几乎所有人都是先写活儿, 再在末尾补一个 review, 而那个 review 又是另一个模型去看输出. 两个乐观主义者互相同意.
Anthropic 那套说得更直接: 永远不要让模型给自己的活儿打分. 同一个 context 生产出来的东西, 再由同一个 context 去审, 一定偏正面. 这不是模型的毛病, 是自审这件事本身的结构问题 —— 盲区之所以是盲区, 就是因为从那个位置看过去它是好的.
所以 check 必须写在前面, 而且要写成程序能算的形状:
GREEN 测试套件 exit 0
GREEN 每一条 claim 都带 source line
GREEN diff 只碰 plan 里列出的文件
NOT A CHECK 输出看起来不错
NOT A CHECK 模型说它有信心
NOT A CHECK 没有报错
最后一条最阴. 没有 error 不是正确的证据. 建在这上面的 loop 会非常自信地重复同一个错误, 直到预算烧完, 而且日志全程干干净净.
Graph: loop 的天花板
一个 loop 只能把一个单元做好. 它决定不了有哪些单元, 也决定不了顺序.
于是你得到一个很优秀的 agent, 在按错误的顺序, 一件一件地做三件错误的事. 每一步都对, 结果又慢又不对形状. 调 loop 是没用的, 因为问题不在任何一个单元里面.
Graph 就是这一层: 什么在跑, 什么并行, 什么等待, 什么根本不该跑.
- 一个 node 是一个有边界的活儿, 一进一出.
- 一条 edge 是依赖, 上一个的输出喂给下一个的输入.
所有多余的等待都来自把”然后”当成了 edge. “总结这个文件, 然后查天气” —— 天气不消费那份总结, 这中间没有 edge, 它俩只是被你打字的顺序串起来了. 拿这个问题去问现有 pipeline 上的每一根箭头: 下一步真的读了上一步的输出吗? 说不出是哪个变量穿过去的, 那就没有 edge.
还有一句我很喜欢: code node 不是模型. 合并, 排序, 去重, 比对前后的 export —— 这些不是推理, 每一个都只有一个正确答案, 每一个都是几行代码. 塞给模型只是给一个本来没有方差的步骤加上成本, 延迟和方差.
判断很简单: 如果能不用 judge / decide / assess / summarize 这几个词把这个变换描述清楚, 那它就是代码.
loop 和 graph 的关系是一句话: loop 活在 node 里面, graph 活在 node 之间.
两条回边
这是我读下来收获最大的地方.
- correction edge 很短. gate 打回一个单元, 回到产出它的那一步. 它修的是这一次运行.
- learning edge 很长. 一个被接受的结果回到 splitter, 变成约束. 它修的是以后每一次运行.
几乎所有人只建第一条. 症状是: 系统很快, 但永远不变聪明.
polydao 给了第二条一个能落地的形式, 一个 CONSTRAINTS.md:
2026-08-19 · 新闻稿不算独立信源. 两篇新闻稿 = 1 个信源.
2026-08-24 · "被收购"需要 filing 或公司声明, 不能用报道.
每次 launch 时载入. 第一周三行, 三个月后三十行. 每一行都是一个不会再被任何 agent 在任何未来的运行里犯第二次的错.
我以前的”纠正”全都活在聊天记录里, 说完就没了, 下周一我又打一遍周五打过的字. 给纠正一个能长期居住的地方, 这件事的价值远大于它看起来的成本.
还有一条代价很高的细节: 打回单元, 不要打回整批. 四个切片, 一个挂了. 如果整批退回, 三个正确的会被重写一遍, 新版本只是不同, 不是更好, 因为它们本来就没问题. 然后你要重新验四个, 这三个里任何一个都可能因为无关的原因这次挂掉. 你把一次失败变成了四个不确定, 还为此付了钱. 一次运行里干两次, 它就再也收敛不了.
从外面看, 这像是模型在反复失败. 其实是回边在摧毁正确的工作.
Judge 正在搬进 runtime
Josh Rosen 那篇讲的是另一件正在发生的事: LLM judge 从离线评测搬进了运行时.
以前 judge 是告诉你昨天那版有没有变差. 现在它开始参与 control flow —— 继续, 重试, 换个模型, 再找点证据, 还是升级给人.
几个我觉得能直接用的:
- 把判断拆开. 不要问”这个输出好不好”. 一个 judge 看有没有回答问题, 一个看 claim 有没有证据支撑, 一个看该做的活儿做完没有. 一个大的模糊判断拆成几个小的清楚判断, 外面用确定性的逻辑组合起来.
- 比较, 而不是打分. 模型分不清 7 分和 8 分, 但很能分清 A 和 B 哪个更好.
- 判过程, 不判答案. 对 chatbot 判最终回复够了. 对一个干了二十分钟的 agent, 最终结果可能看起来完全合理, 但它取错了文档, 忽略了关键信源, 或者绕了一大圈最后蒙对了. 要判的是那些真正做决定的地方 —— research agent 在 synthesis 之前判信源选择, coding agent 在实现之前判方案.
- 分歧比投票更值钱. 三个 judge 2:1 判它好, 这个结论我不太关心. 但它们不一致这件事本身, 是一个很好的升级信号.
代价也说清楚了: 一旦 judge 进了关键路径, judge 的错误就是应用的错误. 一个在离线评测里稍微有点噪的 judge 只是烦人; 同一个 judge 站在每一个重要动作前面, 会造成死循环, 拦住好活儿, 放过坏活儿, 并且给每一次执行都加上延迟.
规模上去以后, 瓶颈换了地方
前面几篇是方法论, Cursor 和 Uber 这两篇是账单.
Cursor 那边: 三层结构, root planner 只拆不写, subplanner 递归往下, worker 做隔离的任务并留下 handoff note. 一周, 一台大 Linux VM, 一千万次 tool call, 大约每小时一千个 commit.
有两个反直觉的结论:
- 瓶颈是 disk I/O, 不是 CPU. 几百个 agent 同时编译, 就是每秒好几个 GB 的构建产物读写. 也就是说, 项目结构和编译开销对效率的影响, 大过 token 本身.
- 不要求 100% 正确. 要求全对会导致严重的串行化. 留一点松弛, agent 才可以相信别的问题很快会被同伴修掉. 错误率保持在小且恒定, 定期清理, 而不是在前面拼命堵.
他们还试错了三种失败的结构: 平权 agent 自己管锁 (锁拿了不还, 二十个 agent 的吞吐退化成一到三个的量), 僵硬的流水线角色 (被最慢的 worker 卡住), 单个全能 executor (同时背太多角色, 开始随机睡觉, 拒绝派活, 谎报完成).
Uber 那边是另一个量级: 超过 70% 的 PR 来自 agent, 3600 个 agent skill, 每天三万次 skill 执行. 半年里用户涨 7 倍, 请求涨 9.4 倍, 而固定住模型看, 每千次请求的成本降了 34%, 每 session 的成本从六月峰值降了 52%.
他们的做法是把一次 session 的开销拆成六个相乘的项, 然后一项一项优化. 最能直接抄的几条:
- subagent 默认用弱模型. 主模型负责拆解和评估, subagent 执行定义明确的活儿, 不需要前沿推理.
- 即使是 1M context 的模型, 也在 400k 自动压缩.
- reasoning effort 默认 Medium.
- 交互 session 用 1 小时的 cache TTL, 因为人经常离开超过 5 分钟; subagent 保持 5 分钟.
- MCP 走 CLI. 一百多个工具的 schema 是 50-70K token, 而且每一轮都重发一次. 改成让模型执行 shell 命令去动态解析, 这部分从 context 里彻底消失.
- code-mode. 一条 SQL 要提交, 轮询两到五次, 再取结果; 每一次轮询都是一个 model turn, 都落进 context. 把这个循环塞进子进程, 只把摘要拿回来, 即使结果集很小也省一半以上的 token, 批量场景省 90% 以上.
还有一个数字我看了很久: 他们建了一张 24M 节点, 80M 边的 context graph. 同一个 prompt, 同一个模型, 接了图的 agent 38 秒给出答案; 没接的那个花了 20 分钟, 开了 2 个 subagent, 撞了 3 次错误, 最后得出”这个数据集查不了”的错误结论.
一个没有 grounding 的 agent 不是便宜地失败, 是昂贵地失败.
所以我应该站在哪
六篇读完, 我要改的其实只有一件事: 把自己从 loop 里挪到 gate 上.
而且 gate 开在哪, 判断标准不是 confidence, 是 blast radius.
confidence 是这个决策里最弱的一个输入, 原因很简单: 它是唯一一个模型能影响的量. 该看的是错了之后有多难收回来.
- 可逆且局部的 —— 文案, 测试, 有覆盖的孤立函数. 错一次的成本是一次 revert. 这条道可以先开.
- 可逆但影响面广 —— 共享工具函数, schema 加字段. 确定性检查加上干净的轨迹才放行.
- 不可逆的 —— 迁移, 删除, 写生产数据, 动钱. 这条道不开.
第三行不是”阈值设得很高”, 是这条道不开. 区别在于阈值会被调, 关着的道不会.
人放在后果最大, 可逆性最低的那一步. 批准合并, 决定哪些修复上线. 不是审中间产物, 不是每一步点确认 —— 人一旦站在 graph 中间, 就变成里面最慢的那个 node, 整张图的速度等于一个人读东西的速度.
写到这里我发现, 这就是去年年底写 burnout 那篇的同一个问题, 换了一层.
那时候算的是: 精力有限的情况下, 花 50% 时间把 A 做到 80% 之后, 不该再花 50% 去补剩下的 20%. 现在是同一道题 —— 注意力有限的情况下, 不该花在每一步的确认上, 该花在那个不可逆的一步, 和那个能失败的 check 上.
放弃那 20% 需要学习. 从 loop 里站起来也需要.
但是
有一件事得说出来, 不然这篇就变成第七篇同样的东西了.
这六篇里, 有三篇是 AI 写的长文, 挂在推特上, 结尾在卖课, 或者引流到电报群. 其中一篇下面最高赞的回复是: this is ai slop article.
而它们都在很认真地告诉你: 永远不要让模型给自己的活儿打分.
它们自己没有被任何人打过分.
Cursor 和 Uber 那两篇不一样, 不是因为写得更好, 是因为它们有疤. Cursor 扔掉过三个结构, 是因为那些结构真的在几百个 agent 上炸过. Uber 会去讲 disk I/O 和 400k 的压缩阈值, 是因为账单真的送到他们手里了. polydao 那篇在三篇里最实, 也是因为它开头说了同一件事: 我今年重建了四次, 扔了三次.
我以前写过, 所有的范式和结构都是为了优化已经存在, 已经被发现的东西. 它们维持稳定, 但不会让你体会到作为人的精神满足.
这六篇加起来是一套非常完整的范式. 我大概会照着改我的 harness, 因为它确实能把我从点确认里解放出来.
但被解放出来的那些时间, 如果只是拿去搭一个更精致的 harness, 那我只是把自己变成了一个执行别人 loop 的 node. 一个很会搭 harness 的 copy.
check 要写在前面. 这条对 agent 成立, 对我自己也成立. 那个 check 大概是: 这一年我有没有做出一件不是从别人的模板里长出来的东西.
来源
- Towards Self-Driving Codebases — Cursor
- Running a Software Factory Efficiently at Uber Scale — Uber Engineering
- LLM-as-Judge Architectures — Josh Rosen
- Loops and Graphs — Hanako
- 300 Agents, One Graph — Mr. Buzzoni
- How to Actually Build AI Agents — CyrilXBT