一次任务的骨架就三段:读(看文件和报错)、改(写文件)、验(跑命令)。 三段里最值钱的是「验」——它是唯一能让它自己发现错误的一环。 它什么时候停下来问你,取决于审批策略;而大多数人把策略设得太松。
前两篇讲完了「在哪儿跑」。这一篇讲「跑起来长什么样」。 把一次任务拆开看,会发现它其实是个循环: 读 → 想 → 改 → 验 → 再读 → 再改……直到验通过,或者放弃。 这个循环就是「agent loop」,第 2.2 节的「反馈回路」在这里落地。 理解了它,你就知道该在哪一步加人、在哪一步加约束。 这一篇的 Demo 真写了一个极简 agent 循环:三个 bug、三轮修复、第四轮验收,全部真跑。 它会告诉你这个循环消耗了什么:三轮改动、读进 481 字符、约 190 token。
一、从一句话到一份计划
你给它的是一句话,它要变成一份计划才能动手。 这一步在界面上可能只是一闪而过(「正在分析…」),但它确实存在。 计划的质量取决于两件事: ① 它读到了什么(上下文够不够);② 你的话里有多少可执行的约束(第 1.3 节的五个要素)。 计划这一步值得停一下看。 很多工具会把「我打算改这三个文件」打出来—— 这时候你发现「怎么没有那个文件?」,还来得及纠正;等它改完再看,就是返工。 如果工具不显示计划,就主动问一句。 「先说一遍你打算改哪几个文件」——这句话没有成本,但能省一整轮。
二、读、改、验三段
| 段 | 它做什么 | 你能看到什么 |
|---|---|---|
| 读 | 读文件、跑命令看报错 | 「正在读取 xxx」「正在运行 xxx」 |
| 改 | 写文件(或给出 patch) | diff |
| 验 | 再跑一遍命令,看退出码 | 测试输出、退出码 |
三段里,「读」和「改」是手段,「验」是目的。 因为没有「验」,前两段做得再漂亮也没法判断对错。 而有了「验」,前两段做歪了它会自己发现。 这三段在一个任务里会重复很多次。 Demo 里三个 bug 花了三轮,每轮都是「读报错 → 定位 → 改 → 再读」。 真实项目里十轮二十轮很正常——判断「它是不是卡住了」的标准,是看「每轮有没有新信息」。 怎么判断「卡住了」? 连续两轮的报错一模一样,就是卡住了。 这时候该做的不是「再试一次」,是「把报错改清楚」或者「把范围缩小」。 第 2.2 节说过:回路转不动,多半是「结果读不懂」。
三、它什么时候会停下来问你
这取决于审批策略(approval policy)。 常见几档,从松到紧:
| 策略 | 什么时候停 | 适合 |
|---|---|---|
| 从不问 | 一路跑到底 | 只读任务、只在自己分支上折腾 |
| 失败时问 | 遇到错误才停 | 试点期 |
| 动手前问 | 每次写文件/跑命令前停 | 正式项目 |
| 只读不写 | 根本不给写权限 | 陌生仓库、第一次接触 |
大多数人把策略设得太松。 理由是「老停下来太烦」——这个理由在熟悉之后成立,在第一天不成立。 第一天的正确设置是「动手前问」,等你看它干过十次活、都靠谱了,再放松。 还有一个常被忽略的点:审批点设太多等于没有。 每次写文件都问你,问二十次之后你就会闭眼点「通过」——那时候审批就不是防线,是橡皮章。 正确做法是把审批设在「影响面大」的动作上:跑命令、删文件、改配置,而不是每一行代码。
四、动手 Demo:写一个极简 agent 循环:读、改、验
这个 Demo 不用 Codex,用四十行 Python 写一个 agent 循环。 它没有模型——「想」那一步用规则代替(从报错里抓函数名)。 但「读」「改」「验」三段是真的:真读文件、真写文件、真跑测试。 任务:三个函数各有一个 bug,让循环自己修。 它应该在三轮之后收敛。 这个 Demo 的价值是让你看见「轮数」「上下文大小」「token」这三个数是怎么产生的。
for r in range(1, 6):
rc, out = py(base, "test.py") # 读:跑测试看报错
if rc == 0:
break # 验:绿了就停
target = None
for k in FIX:
if k + " 期望" in out: # 想:从报错里抓函数名
target = k
break
body = get(base, "calc.py") # 读:拿当前内容
put(base, "calc.py", newbody) # 改:整段替换
实跑输出:
--------------------------------------------------------------------
写一个极简 agent 循环:读、改、验
--------------------------------------------------------------------
① 造一个有三个 bug 的小项目
--------------------------------------------------------------------
初始状态:通过 0 / 失败 3
三个函数各有一个 bug:f1 该乘 2、f2 该加 1、f3 该返回 x。
② 一个 agent 循环要有什么
--------------------------------------------------------------------
| 环节 | 它做什么 | 这里的等价物 |
|---|---|---|
| 读 | 看文件、看报错 | 跑 test.py,读输出 |
| 想 | 决定改哪一处 | 从报错里抓函数名 |
| 改 | 写文件 | 把那个函数换掉 |
| 验 | 跑测试 | 再跑一次 test.py |
这四步就是一个 agent 循环的全部。真机上的「想」是模型,「读」和「改」是工具,「验」是命令。
③ 真跑:三轮收敛
--------------------------------------------------------------------
第 1 轮 读:通过 0 / 失败 3
第 1 轮 想:报错里提到 f1
第 1 轮 改:把 f1 整段换掉
第 2 轮 读:通过 1 / 失败 2
第 2 轮 想:报错里提到 f2
第 2 轮 改:把 f2 整段换掉
第 3 轮 读:通过 2 / 失败 1
第 3 轮 想:报错里提到 f3
第 3 轮 改:把 f3 整段换掉
第 4 轮 读:通过 3 / 失败 0
第 4 轮验过了,循环停止。
最终:通过 3 / 失败 0
④ 这个循环消耗了什么
--------------------------------------------------------------------
| 指标 | 数值 |
|---|---:|
| 改动轮数 | 3 轮,另加 1 轮验收 |
| 读进的字符 | 481 字符 |
| 粗算 token | 约 190 |
这就是一次真实任务的成本结构:轮数 × 每轮读进去的东西。
轮数取决于「报错说得多清楚」;每轮读多少取决于「你给它多大范围」。
两个数都能压——压轮数靠好报错,压每轮靠好范围。
! 三轮收敛算顺利的。真实项目里卡在同一个错上绕十几轮很常见,那时候该做的是改报错,不是再试一次。
三轮变动、第四轮验收,一共读了 481 字符、约 190 token。
这三个数就是一次任务的成本结构。
轮数 × 每轮读进去的东西 = 总量。
注意第四轮的输出:第 4 轮 读:通过 3 / 失败 0。
它还是要读一次才知道「好了」——这一轮不是浪费,是「验收」本身。
真实场景里,最后这一轮经常被省掉(人觉得「改完就好了」),而坏提交恰恰出在这里。
两个数都能压:
压轮数靠「好报错」——报错里带函数名、带期望值,它一轮就能定位;
压每轮靠「好范围」——只给它相关文件,下一节会量出这个差距有多大。
最后那句「卡在同一个错上绕十几轮很常见」值得当真。
判断卡住的标准是「连续两轮报错一样」——
这时候继续重试是浪费额度,正确动作是改报错或者缩小范围。
30 秒实验
- 下次看它干活,数一数它跑了几轮。 超过五轮还没收敛,去看报错是不是「读不懂」。
- 把你的审批策略从「从不问」调到「动手前问」,试一天。 烦不烦?烦的话说明该改成「只在大动作前问」。
- 给它一个任务,故意不给测试。 看它怎么判断「做完了」——没有验证手段的时候,它只能靠「看起来对」。
常见误区
- 「它说改完了就是改完了。」 「改完了」是它自己说的,「验过了」才是环境说的。 两者不是一回事——第 1.2 节的四样证据里,第二样就是退出码。
- 「审批太烦,全关掉。」 全关掉等于「它跑命令不需要你同意」。 刚开始用就全关,是把方向盘交给一个你还没观察过的司机。
- 「轮数越少越好。」 轮数少有两种可能:一次做对,或者根本没验。 最后那一轮验收不能省——省掉它,你得到的是「看起来做完了」。
自测三问(点开对答案)
1. 读、改、验三段里,哪一段最值钱?为什么? —— **验。** 没有它,前两段做得再漂亮也没法判断对错; **有了它,前两段做歪了它会自己发现**——这是唯一能「自我纠错」的一环。 2. 怎么判断它「卡住了」? —— **连续两轮的报错一模一样,就是卡住了。** **这时候该改报错或者缩小范围,不是再试一次**——回路转不动,多半是「结果读不懂」。 3. 审批策略为什么不能「每次写文件都问」? —— **问二十次之后人就会闭眼点通过**,那时候审批就不是防线,是橡皮章。 **审批该设在影响面大的动作上:跑命令、删文件、改配置。**下一篇 3.4 讲「用哪个模型」: 这一节的结论可能跟直觉相反——「给它什么」比「用哪个模型」影响大得多。
- ☐ (本篇完,下一篇 3.4 模型选择)