行业动态
philschmid.de ·
基础模型如何在Bash中变得超人
前沿编码代理能够组合shell工作流,取代大量文件工具。当Bash成为主要执行接口时,开发方式将发生改变。
AI推荐理由 Philipp Schmid技术博客,探讨前沿编码代理将Bash作为主要执行接口的趋势,对开发者有实际启发。
80
正文
基础模型如何在Bash中变得超人
2026年9月1日8分钟阅读
如果你使用过编码代理,你可能已经注意到它们开始使用Bash来执行传统上使用专用工具(如read、edit和search)的任务。嗯,模型在bash中变得超人,可能认为专用工具不够好。
每隔几个月,我会从头重建一个代理框架,以了解发生了什么变化以及我可以删除什么。这次我移除了所有面向模型的工具,只留下一个bash和一个媒体查看器。
结果与我在日常使用中已经看到的一致。任务完成率保持在相同范围内,而代理在复杂工作和验证中移动时遇到的工具边界更少。
前沿模型在Bash中已经变得超人。我的意思是在狭义上:它们在几秒钟内合成一次性的命令行程序,而大多数开发者需要更长的时间来组装和验证。
“在Bash中超人”意味着什么
你可能知道各个部分:git、rg、jq、Python、临时文件、进程替换和测试运行器。在时间压力下编写一个一次性40行的工作流,将它们组合起来,是不同的。我们大多数人会交互式地处理问题,在步骤之间检查语法和状态。
编码代理从POSIX工具、编程语言、构建系统和公共源代码中学习的模式组装这些工作流。它们仍然会犯错误。区别在于它们一次可以尝试多少正确的编排。
Bash是这里的路由层。繁重的工作可能发生在Python、Git、SQLite、编译器或项目特定的CLI中。一个shell接口让代理组合所有这些,而无需强制运行时设计者预测每个有用的操作。
以下是我个人代理轨迹中的简化示例。
示例1:就地多文件编辑
你在一个项目上工作,想要跨实现、调用者和测试重命名一个变量。
Bash
python3 /dev/null 2>&1 || true
rm -rf "$scratch"
}
trap cleanup EXIT
git worktree add --detach "$scratch/repo" HEAD >/dev/null
cd "$scratch/repo"
git bisect start HEAD "$GOOD_REV"
git bisect run bash -c '
failures=0
for seed in 11 29 47 71 101; do
if ! TEST_SEED=$seed pytest -q tests/test_checkout_race.py; then
failures=$((failures + 1))
fi
done
test "$failures" -lt 3
' >"$scratch/bisect.log" 2>&1
bad=$(git rev-parse HEAD)
git bisect reset >/dev/null
printf 'first bad commit: %s\n' "$bad"
git show --no-ext-diff --format=fuller --stat "$bad"
git diff --no-ext-diff "$bad^" "$bad" -- src/checkout tests/test_checkout_race.py | sed -n '1,220p'
printf '\nlast bisect events:\n'
tail -n 40 "$scratch/bisect.log"
额外的worktree保持你的本地文件不变。每个提交用五个种子分类,只有当少于三次运行失败时才计为好,所以一次不幸的flake不会毒化二分搜索。当它落地时,脚本打印第一个坏提交、可疑更改的截断差异以及日志的尾部。你得到诊断,而不是提示中的每次测试运行。
示例3:关联压缩的生产日志
你有压缩的生产日志,太大无法加载到模型中。你想要失败的端点、它们的错误类别和P95延迟。
Bash
python3 = 500
),
ranked AS (
SELECT
path,
kind,
latency_ms,
row_number() OVER (
PARTITION BY path ORDER BY latency_ms
) AS position,
count(*) OVER (
PARTITION BY path
) AS samples
FROM matched
)
SELECT
path,
count(*) AS failures,
round(avg(latency_ms), 1) AS average_ms,
max(
CASE
WHEN position = CAST((samples * 95 + 99) / 100 AS INTEGER)
THEN latency_ms
END
) AS p95_ms,
group_concat(DISTINCT kind) AS error_classes
FROM ranked
GROUP BY path
ORDER BY failures DESC
LIMIT 5
""").fetchall()
print(json.dumps([
{
"endpoint": path,
"failures": failures,
"average_ms": average,
"p95_ms": p95,
"error_classes": kinds.split(","),
}
for path, failures, average, p95, kinds in rows
], indent=2))
PY
Python分批读取轮转文件,从不将它们转储到提示中。SQLite执行连接、P95排名和错误类别聚合。返回的是五个JSON记录:端点、失败计数、平均延迟、P95和错误类别。上下文卸载将中间数据保留在环境中,只将摘要放入模型。
为什么原子工具是正确的
几个月前,我认为编码代理应该从健壮的原子工具开始,避免使用cat、sed和echo等shell命令。粗心的命令可能会淹没上下文窗口,返回不透明的错误,或通过糟糕的引用破坏编辑。文本输出也不能将视觉信息带入视觉模型。
这些限制仍然适用于未检测的shell。基础模型现在更擅长组合小型Python补丁、Git命令、带引号的heredoc和聚焦测试。框架也吸收了使原子工具有用的保护:
输出控制:截断大结果,并告诉代理如何请求更窄的切片。
诊断:返回退出状态、持续时间、超时状态和进程信息。
隔离和策略:在模型外部门控路径、网络访问和破坏性操作。
异步进程:让代理启动、检查和停止长时间运行的命令,而不会阻塞回合。
唯一的例外是多模态输入:
文本输出无法使截图/图像可见。shell命令可以渲染页面、捕获图表或保存视频帧,但像素仍然需要通过多模态通道进入模型。
这对框架工程意味着什么
我将以shell为中心的设置与暴露单独工具进行文件读取、写入、编辑和搜索的配置进行了比较。两者在相同条件下针对相同的编码任务集运行。以shell为中心的设置实现了同等或更好的性能。
这是应用于框架设计的苦涩教训:
随计算扩展的通用方法胜过手工构建的捷径。Bash就是那个通用计算层。
一个好的代理应该有更多工具,当它们提供更好的能力接口时。如果模型可以用工具做得更好,它就应该使用工具。浏览器控制就是一个很好的例子。
浏览器工具可以在同一次调用中导航、点击、输入、等待页面稳定并返回截图。通过Bash执行此操作意味着调用CLI,定位捕获的工件,并在第二步中通过view_media传递它。服务集成可以类似地受益,或者留在shell上使用像mcp-cli这样的东西,这样模式就不会出现在提示中。
以下是要尝试的:
删除微工具:尝试使用Bash处理文件读取、搜索、多文件编辑、差异和验证。
使用子代理作为执行防火墙:委派混乱的探索和调试,然后将干净的结果返回给父上下文。
保持系统指令最小化:给代理更多空间处理仓库指令、领域知识和任务本身。
我们的目标是更小的接口和更大的行动空间。
查看原文(中文正文为 AI 翻译)
How Foundational Models Became Superhuman in Bash
September 1, 20268 minute read
If you have used coding agents, you may have noticed that they started to use Bash for tasks that would have traditionally used dedicated tools such as read, edit, and search. Well, models became superhuman in bash and might consider dedicated tools not good enough.
Every few months, I rebuild an agent harness from scratch to learn what changed and what I can delete. This time I removed every model-facing tool except for a bash and a media viewer.
The outcome matched what I had already seen in daily use. Task completion stayed in the same range, while the agent moved through complex work and verifications with fewer tool boundaries.
Frontier models have become superhuman in Bash. I mean that in a narrow sense: they synthesize disposable command-line programs, in seconds, that most developers would need much longer to assemble and verify.
What “superhuman in Bash” means
You probably know the individual pieces git, rg, jq, Python, temporary files, process substitution, and test runners. Writing a one-off 40-line workflow that combines them under time pressure is different. Most of us work through the problem interactively, checking syntax and state between steps.
Coding agents assemble these workflows from patterns learned across POSIX utilities, programming languages, build systems, and public source code. They still make mistakes. The difference is how much correct orchestration they can attempt at once.
Bash is the routing layer here. The heavy work may happen in Python, Git, SQLite, a compiler, or a project-specific CLI. One shell interface lets the agent compose all of them without forcing the runtime designer to predict every useful operation.
The following are simplified examples from my personal agent traces.
Example 1: In-place multi-file edits
You work on a project and you want to rename a variable across an implementation, its callers, and the tests.
Bash
python3 /dev/null 2>&1 || true
rm -rf "$scratch"
}
trap cleanup EXIT
git worktree add --detach "$scratch/repo" HEAD >/dev/null
cd "$scratch/repo"
git bisect start HEAD "$GOOD_REV"
git bisect run bash -c '
failures=0
for seed in 11 29 47 71 101; do
if ! TEST_SEED=$seed pytest -q tests/test_checkout_race.py; then
failures=$((failures + 1))
fi
done
test "$failures" -lt 3
' >"$scratch/bisect.log" 2>&1
bad=$(git rev-parse HEAD)
git bisect reset >/dev/null
printf 'first bad commit: %s\n' "$bad"
git show --no-ext-diff --format=fuller --stat "$bad"
git diff --no-ext-diff "$bad^" "$bad" -- src/checkout tests/test_checkout_race.py | sed -n '1,220p'
printf '\nlast bisect events:\n'
tail -n 40 "$scratch/bisect.log"
The extra worktree keeps your local files in place. Each commit is classified with five seeds, and it only counts as good if fewer than three runs fail, so one unlucky flake does not poison the bisect. When it lands, the script prints the first bad commit, a truncated diff of the suspected change, and the tail of the log. You get a diagnosis, not every test run in the prompt.
Example 3: Correlating compressed production logs
You have compressed production logs that are too large to load into the model. You want the failing endpoints, their error classes, and P95 latency.
Bash
python3 = 500
),
ranked AS (
SELECT
path,
kind,
latency_ms,
row_number() OVER (
PARTITION BY path ORDER BY latency_ms
) AS position,
count(*) OVER (
PARTITION BY path
) AS samples
FROM matched
)
SELECT
path,
count(*) AS failures,
round(avg(latency_ms), 1) AS average_ms,
max(
CASE
WHEN position = CAST((samples * 95 + 99) / 100 AS INTEGER)
THEN latency_ms
END
) AS p95_ms,
group_concat(DISTINCT kind) AS error_classes
FROM ranked
GROUP BY path
ORDER BY failures DESC
LIMIT 5
""").fetchall()
print(json.dumps([
{
"endpoint": path,
"failures": failures,
"average_ms": average,
"p95_ms": p95,
"error_classes": kinds.split(","),
}
for path, failures, average, p95, kinds in rows
], indent=2))
PY
Python reads the rotated files in batches and never dumps them into the prompt. SQLite does the join, the P95 ranking, and the error-class aggregation. What comes back is five JSON records: endpoint, failure count, average latency, P95, and error classes. Context offloading keeps the intermediate data in the environment, and puts only the summary in the model.
Why atomic tools were right
A few months ago, I argued that coding agents should start with robust atomic tools and avoid shell commands such as cat, sed, and echo. A careless command could flood the context window, return an opaque error, or corrupt an edit through bad quoting. Text output also cannot carry visual information into a vision model.
Those limits still apply to an uninstrumented shell. Foundational models are now much better at composing small Python patchers, Git commands, quoted heredocs, and focused tests. The Harness also absorbs the protections that made atomic tools useful:
Output control: truncate large results and tell the agent how to request a narrower slice.
Diagnostics: return the exit status, duration, timeout state, and process information.
Isolation and policy: gate paths, network access, and destructive actions outside the model.
Asynchronous processes: let the agent start, inspect, and stop long-running commands without blocking a turn.
The one exception multimodal input:
Text output cannot make a screenshot/image visible. A shell command can render a page, capture a chart, or save a video frame, but the pixels still need to enter the model through a multimodal channel.
What this means for harness engineering
I compared a shell-centered setup with a configuration that exposed separate tools for file reading, writing, editing, and search. Both ran against the same coding task set under the same conditions. The shell-centered setup achieved on par or better performance.
That is the Bitter Lesson applied to harness design: general methods that scale with computation beat hand-built shortcuts. Bash is that general computation layer.
A good agent should have more tools when they provide a better interface to a capability. If the model can do something better with a tool, it should use the tool. Browser control is a good example.
A browser tool can navigate, click, type, wait for the page to settle, and return a screenshot in the same call. Doing this through Bash means invoking a CLI, locating the captured artifact, and passing it through view_media in a second step. Service integrations can benefit in a similar way, or stay on the shell with something like mcp-cli so the schemas never sit in the prompt.
Here is what to try:
Delete the micro-tools: Try to use Bash to handle file reading, search, multi-file edits, diffs, and verification.
Use subagents as execution firewalls: Delegate messy exploration and debugging, then return a clean result to the parent context.
Keep the system instructions minimal: Give the agent more room for repository instructions, domain knowledge, and the task itself.
Our goal is a smaller interface with a larger action space.