导学阶段-Coding Agent 的工作模式
本文整理自 CNB 上的开源课程 《Vibe Agent:从零搭建 Coding Agent》。课程讲义、示例代码与练习仓库均在 CNB 上公开,文中示例出自该仓库对应章节。
把一个编程任务交给 Agent,看到的是连续的工具调用:列目录、读文件、改几行、跑一次命令。这一串动作不是随机的,它对应人写代码时的固定路径。人在 IDE 里改一个函数,流程也差不多:展开目录看结构,用 Cmd+P 或全文搜索定位文件,打开文件读上下文,改那几行,跑测试确认。Coding Agent 的工作模式就是这五步,只是每一步换成了工具调用。
五个阶段与对应的工具
| 阶段 | 人做什么 | 工具 |
|---|---|---|
| 浏览 | 展开目录树,了解项目结构 | list_dir |
| 定位 | 按文件名找文件,按内容搜文本 | glob、grep |
| 理解 | 打开关键文件读代码 | read |
| 修改 | 新建文件,改某一段代码 | write、edit |
| 验证 | 跑测试,执行脚本 | bash |
这五个阶段会反复循环。测试失败,回到修改;改完再跑,直到通过。Cursor、Claude Code、Devin 的核心循环都落在这五个阶段上,差别在实现精度。
课程列出的必做工具是五个:list_dir、read、write、edit、bash。glob 与 grep 属于定位阶段的补强,不是闭环的必需品,靠 list_dir 一层层看下去也能找到文件,只是慢。五个工具覆盖了最低限度的能力。
按阶段分类,不按文件操作分类
按文件操作的动词来切工具,是很容易踩的坑:给模型一组 open、close、read、write,看起来齐全,实际多出几个用不上的选项。
open 与 close 就没有必要。模型不需要持有文件句柄,read 一次把内容读回来就够,读过之后的”打开状态”没有任何用处。
delete 也没有放进最小工具集。删文件是高风险操作,误删之后很难恢复,它也不属于五个阶段里的任何一个。模型真要删,可以用 bash 执行 rm,但那就进入了另一个受约束的通道。
move 同理,bash 里一条 mv 就能完成,不值得单独开一个工具。
工具集的边界由工作流决定,不由文件系统的接口决定。每多一个工具,模型的选择空间就多一份误判的可能;参数越相似,选错的代价越高。工具越多,每轮请求携带的工具描述也越长,这部分 token 每轮都要付一次。
工具的输出是给模型看的界面
五个工具在数据流上是一条链:list_dir 的输出决定下一步 read 哪个文件,read 的输出决定 edit 改哪一段,bash 的输出去决定要不要再改一轮。这条链能跑通的前提是上一步的输出格式刚好能喂给下一步。
| 工具 | 输出设计 | 服务对象 |
|---|---|---|
| read | 每行带行号 | 让模型能引用具体位置,让报错定位更准 |
| list_dir | 区分文件与目录 | 让模型知道下一步是 read 还是继续往下列 |
| bash | 附带退出码 | 让模型判断命令是成功还是失败 |
read 返回的格式是这样:
1 | 1:def greet() -> None: |
行号在这里的作用是让模型能说”第 4 行调用有问题”,而不是”文件末尾附近有问题”。read 还接受 offset 与 limit,大文件可以只读一段,避免一次把上下文塞满。list_dir 用 [D] 与 [F] 前缀区分目录和文件,模型据此决定是继续浏览还是开始阅读。bash 在命令返回非零时把退出码放在输出开头,模型据此判断测试是否通过;没有退出码,它只能从文本里猜,猜错的概率不低。
设计输出格式时,要问的是下一个工具能从这段输出里拿走什么,打印出来好不好看无关紧要。
修改用 edit,新建用 write
一个常见的用法是让模型每次都用 write 重写整个文件,哪怕只改一个变量名。每改一行都要重写全篇,成本随文件大小线性上涨,而模型按 token 计费。长文件写到后面容易漏段,输出长度有上限,少写的函数要等运行报错才会暴露。整篇重写之后也看不出这一次到底改了什么,等价于用 git commit -a 却不写提交信息。
区分标准只有一条:文件已存在,用 edit 改目标段;文件不存在,才用 write 从头建。
1 | edit( |
old_content 必须与文件里的内容逐字符一致,包括缩进和换行。这个参数怎么定位、为什么要强制唯一,下一节展开。
走一遍完整流程
任务可以很小:给 main.py 里的 greet 函数加一句开场白,再运行确认。
1 | list_dir(".") 看到 main.py 与 utils.py |
执行到 bash,看到输出,任务结束。输出不符合预期就退回 edit,重读、重改、重跑。这四步构成的循环,就是 Agent 写代码时反复做的事。
循环不能无限转。模型可能一直改不到点上,也可能反复跑同一条命令,所以 Agent 的 run 方法带最大轮次参数,课程示例的默认值是 12 轮,到顶就停下来交还结果。
把这一串工具调用交给模型,配上 tool_calls 协议,循环由模型自己驱动,第 5 章的 Coding Agent 就是按这个结构组装的。
工具集变大之后,模型在错误的阶段绕圈的机会也会变多。工具按阶段切分、输出按下一步的需要设计,循环才会收敛。
这对搭 Agent 意味着什么:写代码这件事可以拆成有明确输入输出的阶段,把工具看作这条数据流上的节点,比把它当成一份能力清单更有用。先想清楚阶段,再决定每个工具收什么参数、返回什么格式,比先堆功能再补流程省事得多。
工具的参数和返回值还有一层设计,专门用来兜住模型常犯的错误。下一节拆开其中四个关键选择。


