做编码 Agent 的人很快会撞上一个问题:模型到底该通过一个万能的 bash 工具去操作世界,还是应该把每个动作拆成一个个 typed 的专用工具(read_file、edit_file、run_tests、send_email…)?这不是审美问题,而是 harness 工程的核心权衡。这篇把判据讲清楚。
bash 给的是"广度"
给模型一个 bash 工具,本质上是把整台机器的能力一次性交出去。模型能 grep、能 sed、能跑构建、能调 curl、能写临时脚本,几乎任何你想不到的组合它都能拼出来。广度是 bash 无可替代的优势——你不需要为每个新场景预先设计工具 schema,模型自己会用 shell 拼。
但代价是:harness 拿到的只是一个不透明的命令字符串。
1 | { "tool": "bash", "input": { "command": "git push origin main && rm -rf build/ && curl -X POST https://api.foo/notify" } } |
对 harness 而言,这一串和 ls 长得一模一样:都是 bash 调用,都是一段 string。它无法在执行前知道这条命令会 push、会删目录、还会发外部请求。于是四件事全做不了:拦截(不知道哪条危险)、渲染(没法给用户画出"即将发送邮件给 X"的确认卡片)、审计(日志里只有命令文本,没有结构化语义)、并行(不知道哪些命令能并发、哪些有副作用必须串行)。
"提升为专用工具"给的是钩子
把一个动作从 bash 里拎出来,做成带 typed 参数的专用工具,本质是给 harness 一个可编程的介入点。send_email(to, subject, body) 和 bash("echo ... | sendmail") 的运行效果可能一样,但前者让 harness 在参数层面看见 to 字段,能弹确认、能脱敏、能记审计、能限流。
什么时候值得提升?有四条清晰的判据。
1. 安全边界:可逆性是判据
不可逆或会外发的动作必须门控。判据很简单——这个动作能不能撤销?
read_file、grep:可逆(其实是只读),不必门控。edit_file:基本可逆(有备份/git),轻度门控。send_email、rm、POST到外部 API、git push:不可逆或外发,必须做成专用工具并加确认/策略门。
走 bash 你做不到精准门控——要么全量拦截(模型每跑一条 ls 都要人确认,体验崩溃),要么放行(危险动作裸奔)。提升为专用工具,才能只在 send_email 这一类工具上挂确认逻辑。
2. Staleness 检查:bash 做不到的状态校验
专用 edit_file 工具能记录"这个文件上次被模型读取的版本",并在写入时校验:如果文件自上次读取后已被改动(用户手改了、另一个进程改了),拒绝写入并要求重新读。
1 | def edit_file(path, old, new): |
bash("sed -i ...") 没有这个记忆,它会盲目覆盖,把用户的并发修改冲掉。staleness 检查是专用工具独有的安全网。
3. 渲染:把动作变成交互
有些"工具"的价值不在执行,而在交互呈现。最典型的是"向用户提问":做成 ask_user(question, options) 工具后,harness 可以弹出一个带选项的对话框、阻塞 Agent 循环、等用户点选再把结果回灌。
1 | ask_user("用哪种迁移策略?", ["蓝绿", "滚动", "停机"]) |
这套渲染/阻塞/回灌的循环,靠一个 bash 里的 echo 是组织不出来的。
4. 调度:让 harness 知道什么能并行
只读工具(glob、grep、read_file)可以在 schema 层标记为"可并行",harness 就能把模型在一轮里发出的多个只读调用并发执行。
而走 bash,harness 无从区分一条可并行的 grep 和一条绝不可并行的 git push——它只看见两个 bash 调用,为了安全只能串行。把只读操作提升为专用工具,等于把"可并行性"这个元信息显式交给调度器。
| 维度 | bash(不透明命令串) | 专用工具(typed 参数) |
|---|---|---|
| 广度 | 极高,几乎万能 | 受 schema 限制 |
| 拦截/门控 | 全或无,难精准 | 参数级精准门控 |
| Staleness 校验 | 做不到 | 可做 |
| 渲染/交互 | 弱 | 强(弹窗/阻塞/回灌) |
| 并行调度 | 只能串行 | 可标记并发 |
| 审计 | 仅命令文本 | 结构化语义 |
经验法则
不要从第一天就把所有东西都做成专用工具——那会牺牲 bash 的广度,还要养一大堆 schema。先用 bash 求广度,当某个动作需要门控、需要渲染、需要审计、或需要并行时,再把它提升为专用工具。 提升是有成本的(定义 schema、写执行器、维护),所以让上面四条判据来决定,而不是凭直觉一刀切。
一句话总结:bash 是默认的"广度底座",专用工具是为门控、渲染、审计、并行付费换来的"控制力",边界就在你需不需要这四种控制。