Agent 开发者的工具箱:标准比清单重要

An Agent-First Toolchain — Criteria Matter More Than the List

前几天有人给我发了一份「2026 年 Agent 开发者工具箱」清单,整整 15 个名字:git、gh、docker、uv、node、pnpm、nvm、rg、jq、curl、fzf、just、direnv、python、go。分层清晰,还配了星级。

我顺手在自己这台跑 Agent 的机器上核对了一遍,结果很有意思:15 个里我只装了 7 个。

for t in git gh docker uv node pnpm nvm rg jq curl fzf tmux direnv just mise; do
  command -v "$t" >/dev/null && echo "$t: yes" || echo "$t: NO"
done
# generated by hugo AI

git、gh、uv、node、pnpm、rg、curl 在;docker、jq、fzf、tmux、direnv、just、mise 一个都没有。机器没罢工,Agent 每天照样在上面干活。

所以我没有急着去补齐那 8 个缺口,而是先问了一个更基本的问题:这份清单是按什么标准选的?是给人用的,还是给 Agent 用的?

这两个问题的答案,会导出两份完全不同的清单。

An Agent-First Toolchain — Criteria Matter More Than the List

一个真实的破绽:fzf

让我意识到这件事的,是一次具体的失败。

上个月我让 Agent 排查一个 bug:在一堆 JSONL 格式的 eval 结果日志里,找出所有失败用例的共性。代码搜索没问题,ripgrep 两秒出结果。但到了结构化处理这一步,Agent 卡了——机器上没有 jq,它只能用 python3 的 json 模块现写一段脚本,又长又脆,改了两轮才跑通。(jq 至今没装在这台机器上,这是真实状态。)

同一天我还注意到另一件事:清单里的 fzf,我从来没想过给 Agent 装。原因很简单,fzf 的灵魂是交互式模糊搜索——你敲几个字符,它在屏幕上实时收窄候选,你用方向键选。这套交互的前提是:有一个人在键盘前

Agent 没有键盘。给它一个只能交互式使用的工具,等于给它一把没有锁孔的钥匙。

那一刻我意识到,所谓「Agent 开发者的工具箱」,真正的分水岭不在工具名本身,而在一个更底层的标准:Agent 能不能确定性地调用它

这个标准可以拆成三条,每一条都有明确的判定方法:

标准判定方法人类优先的反例
确定性同样的输入永远产生同样的输出和退出码fzf:输出取决于人的击键
结构化输出有 JSON / 机器可读格式,不只是给人看的漂亮排版只有彩色表格输出的 CLI
幂等性重复调用不产生副作用,失败可以安全重试没有查重机制的推送命令

顺手验证一下这个标准在你机器上的表现——下面这段代码是真的会去调用工具的:

from dataclasses import dataclass
import shutil
import subprocess


@dataclass
class ToolCheck:
    """一个工具的 Agent 友好性速查。"""

    name: str
    json_output: bool
    non_interactive: bool
    idempotent: bool

    @property
    def agent_friendly(self) -> bool:
        """三条标准全过,才算 Agent 可以放心调用的工具。"""
        return self.json_output and self.non_interactive and self.idempotent


def probe_version(name: str) -> str:
    """真的调用一次工具:拿不到版本号,Agent 调用时同样会卡住。"""
    if not shutil.which(name):
        return "missing"
    try:
        out = subprocess.run(
            [name, "--version"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        return "ok" if out.returncode == 0 else f"exit {out.returncode}"
    except Exception as exc:  # noqa: BLE001 - 演示用,速查不求全面
        return f"error: {exc}"


TOOLS = [
    ToolCheck("rg", json_output=True, non_interactive=True, idempotent=True),
    ToolCheck("gh", json_output=True, non_interactive=True, idempotent=True),
    ToolCheck("fzf", json_output=False, non_interactive=False, idempotent=True),
]

for t in TOOLS:
    verdict = "agent-ready" if t.agent_friendly else "human-only"
    print(f"{t.name:6s} {verdict:11s} probe: {probe_version(t.name)}")
# generated by hugo AI

我机器上的真实输出:

rg     agent-ready probe: ok
gh     agent-ready probe: ok
fzf    human-only  probe: missing
# generated by hugo AI

注意第三行:fzf 在这台机器上根本不存在(probe: missing)——但这不是重点。就算装上了,Agent 拿到的输出仍然取决于人的击键,probe: ok 也没用。工具能跑 ≠ 工具可被 Agent 调用,这是两件事。

顺带说一个常见误区的反面:三条标准不是要你把所有工具都换掉。不达标的工具通常有便宜的出路——包一层脚本把输出转成 JSON、用 --no-pager 或非交互模式替代默认行为、或者干脆在 Harness 里给它配一个 Agent 专用封装。判断要不要换,看的是包装成本是否高于收益。

用三条标准,重审那份清单

拿着这三条标准回头看,那份 15 个工具的清单里,问题不止 fzf 一个。

Docker 的五星,需要一个但书。 Docker 提供的是环境隔离,不是安全沙箱——容器共享宿主机内核。当你要跑的是不可信的、由 Agent 现场生成的代码时,裸 Docker 挡不住逃逸。生产级的 Agent 沙箱,实际在往 gVisor、Firecracker 这类 microVM 方案走,E2B、Daytona 这些产品底层都是这个路线。一句话:Docker 是构建和分发格式,microVM 才是执行边界。 把这两件事混在一起,是很多人做 sandbox 设计时踩的第一个坑。当然,如果你的 Agent 只跑自己仓库里的测试,裸 Docker 完全够用——标准取决于你让 Agent 碰什么。

mise 和 uv 不是二选一。 常见误读是「mise 能统一管一切,所以可以替换 uv」。实际上 mise 管的是 runtime(node、go、rust、java),uv 管的是 Python 的包、虚拟环境和 lock 文件——mise 自己处理 Python 时,底层反而会委托给 uv。正确的姿势是:mise 替换 nvm、fnm、pyenv,uv 原样保留。 「没必要为了统一而强行迁移」这个结论是对的,但理由不是习惯,而是 uv 的 lock 和 venv 能力 mise 替代不了。

Evals 和 Observability 只有名词,没有名字。 清单里写了「Agent Evals」「OpenTelemetry」,但落到选型,至少要说出具体选项。Evals 这边:promptfoo 轻、适合接进 CI;Inspect 是 UK AI Safety Institute 开源的框架,强在轨迹级评估;LangSmith、Braintrust 走平台路线。观测这边:OpenTelemetry 的 GenAI 语义约定是事实上的采集标准,Langfuse 可以自托管。要说一句实话:这块工具还在剧烈演化,现在押注单一平台是冒险的,自建一层薄适配更稳——这是我基于自己踩坑的判断,不是定论。

最大的缺口是 Secrets。 direnv 只解决本地开发的环境变量。而生产环境的 Agent 会把 key 写进 prompt、带进日志、留在 trace 里,一把静态 API key 落到 Agent 手里,爆炸半径比人使用时大一个量级。这里真正需要的是 1Password CLI、Doppler、Vault 这类工具,加上短时效凭证和按工具粒度发放的 token。原文的生产层其实提到了 Secrets,但 Top 15 里一个对应工具都没有——这是整份清单最实质的洞。

按同样三条标准,我的补位清单是:

出局补位理由
fzftmux / duckdb / yqfzf 是交互式的;Agent 缺的是会话管理、结构化查询、YAML 处理

tmux 管长任务和交互式 CLI——Agent 跑一个需要终端的程序、或者挂一个几小时的任务,都需要一个不随调用方断开而消失的会话;duckdb(或 sqlite3)是 Agent 的本地数据库,eval 结果、会话状态这种 JSONL 数据,一句 SQL 比十行 Python 稳;yq 补 jq 管不了的 YAML——MCP 配置、CI 文件、k8s 清单,全是 YAML。

消费者变了,标准才会变

你可能会问:这三条标准是不是太苛刻了?人类开发者用工具,从来不需要「非交互、JSON 输出、幂等」同时成立。

没错——因为人类有眼睛、有手、有判断力。彩色输出是给人看的,交互确认是给人兜底的,偶尔重复执行一次,人能当场发现、当场补救。

但 Agent 没有这些兜底机制。它只有 stdin、stdout 和退出码。人类靠感官兜底的地方,Agent 只能靠协议。 这就是为什么同样一份工具清单,第一用户一旦从人换成 Agent,标准就必须重写。

回头看,这事早有伏笔。我在 AI Agent 架构的终局,是 Unix 哲学的回归 里写过,Skill 之于 Agent 就是 Unix 命令——而 Unix 命令之所以能被管道组合,恰恰是因为它们非交互、吃 stdin、吐 stdout。今天讨论的工具三条标准,其实是同一条脉络的另一端:Unix 哲学当年为「可组合性」做的设计,在 Agent 时代变成了「可调用性」的底线。

而 Harness 那一层做的事,可以看成这个逻辑的延伸。好的 Runtime 不挑模型 里我说,Harness 的价值在于把「Agent 怎么干活」标准化;AutoHarness 那篇 里 Warp 甚至让 Agent 通过改 Skill 来改进自己的 Harness。Harness 解决的是「怎么干」,而工具箱的 Agent 化解决的是「拿什么干」——两件事合起来,才是完整的生产力闭环。

再往后看一步,我有个还不确定的推测:工具生态本身可能会分层演化。一层是存量工具的「Agent 化改造」——加一个 –json、加一个非交互模式;另一层是原生为 Agent 设计的新工具,从第一天就不考虑人类交互界面。至于最终哪一层赢,我还没有答案,但观察 六大 Coding Agent 的八月 里各家都在做 Skill 生态,方向大概不会错:谁能成为 Agent 的第一用户,谁就占住下一波工具生态的位。

一个检验问题

所以下次再看到任何一份「Agent 开发者必备工具」清单,先别急着安装,问一个问题:

这份清单里的每一个工具,Agent 能不能确定性地调用?

答不上来的那些,大概率是给人类准备的。而你的 Agent 没有键盘。

你的工具箱里有没有混进「人类工具」?或者你发现过哪些被低估的 Agent 友好工具?欢迎留言讨论。


See also