Agent-CLI设计七原则:让AI工具真正好用的核心实践

翻到的内容跟预期一样:优秀的 CLI 设计资源,全都是给人看的。它们面向的消费者是坐在终端前的人——能读提示词、做判断、肉眼解析格式化的表格。Agent 在交互式运行时能处理一部分,但一旦 Agent 或 skill 启动后台子 Agent,交互式 CLI 就失效了。没有办法把交互式提示向上穿透这个调用链。彩色输出浪费 token。无限制的输出占用上下文窗口。人类优先的 CLI 设计假设,在 Agent 这里无法很好工作。
Anthropic 发过一些指导,但那是通用的工具设计原则,不是专门针对 CLI 的。CLI设计还缺乏一个实用标准,用来评估一个 CLI 是否对 Agent 友好。
所以我自己写了一个。把那些来源里的内容跟我自己构建 Agent 对接 CLI 的经验综合了一下,总结出七条原则,填补”能跑”和”跑得好”之间的差距。还给 CLI 写了一组评估脚本,来自动检查 CLI 源码是否遵循这些原则。
先说个背景:有人可能会问,既然有 MCP 了,为什么还要关注 CLI。MCP 有 MCP 的用处,但 CLI 本身是 text in, text out,天生可组合。LLM 从训练数据里已经知道常见 CLI 工具,不需要额外 schema 开销。MCP 服务器光加载工具定义就要烧掉几万 token。MCP 的复杂度是值得的,如果你需要 per-user 认证和结构化治理,但对于开发者日常构建和使用的工具,设计良好的 CLI 更快、更便宜、更可靠。
但CLI 设计不好,还是会让 Agent 出问题。以下是避免这些问题出现的一些CLI编写原则。
为什么 CLI 优于 MCP?
CLI 天然是 text in, text out,天生可组合。LLM 从训练数据里已经知道常见 CLI 工具,不需要额外 schema 开销。MCP 服务器光加载工具定义就要烧掉几万 token,问出第一个问题之前什么都没干。MCP 的复杂度是值得的,如果你需要 per-user 认证和结构化治理,但对于开发者日常构建和使用的工具,设计良好的 CLI 更快、更便宜、更可靠。
CLI 还是会在某些地方让 Agent 出问题。下面的原则就是这些问题所在。
CLI问题严重程度评分
在讲原则之前,先讲讲这套标准怎么用。CLI问题严重程度有三个:
Blocker:这个问题阻止了可靠的 Agent 使用。命令挂起、需要人工介入、或者产出 Agent 无法恢复的输出。
Friction:Agent 能用,但效率低下。更多重试、浪费 token、脆弱的解析、多余的工具调用。
Optimization:CLI 没问题,但对 Agent 消费者来说可以更快、更便宜、更可靠。
原则一:默认非交互
任何 Agent 可能自动化的命令都应该无需提示即可运行。交互模式可以为人保留,但应该是可选路径,而不是唯一路径。
这是最常见的 Blocker。当一个 skill 启动子 Agent,这个子 Agent 再调 CLI,没有办法把交互式提示向上穿透。命令就挂在那里等输入,处于无限等待中。
就算在交互式 Agent 会话里,提示也会造成 Friction:多余的往返、模糊的菜单导航、浪费 token。如果 stdin 不是 TTY,命令就不该提示。这是基本要求。
修复方法:支持 --no-input 或 --non-interactive,检测 TTY vs 非 TTY 在 stdin 非交互时抑制提示,接受 --yes / --force 跳过确认,通过 flag、文件或 stdin 接受结构化输入。
验证方法:断开 stdin 并强制超时。
python3 - <<'PY'import subprocess, syscmd = ["blog-cli", "publish", "--content", "my-post.md"]try: result = subprocess.run( cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10, ) print("exit:", result.returncode) print("PASS: command exited without hanging")except subprocess.TimeoutExpired: print("FAIL: command hung waiting for input") sys.exit(1)PY
会挂起等输入的命令是 Blocker。有些提示能跳过但行为在各子命令间不一致是 Friction。完整 flag 覆盖加全局非交互模式是 Optimization 目标。
原则二:结构化、可解析输出
返回数据的命令应该暴露稳定的机器可读表示。
Agent 需要数据契约,不是展示格式。带 ANSI 颜色的漂亮对齐表格对人类很棒,对试图提取 post ID 的 Agent 没用。如果唯一输出是正文或装饰性表格,Agent 只能去 scrape 自己的工具,脆弱又浪费token。
最后一点容易忽略。很多 CLI 正确检测了 TTY 来处理提示,但仍然往管道输出ANSI 转义码。Agent 解析 \x1b[32m✓ Published\x1b[0m 就是在浪费token。
原则三:快速失败,报错可操作
命令失败时,报错应该教 Agent 怎么在下次尝试成功。
这是大多数 CLI 对 Agent 最弱的地方。人类能从模糊的报错信息推断出了什么问题。Agent 不能。”Error: missing required arguments” 告诉 Agent 的信息几乎为零。”Error: –content is required” 告诉它具体要修什么。
好的报错做四件事:命名具体问题、展示正确调用形状、在验证失败时建议有效值、包含示例。收到这个报错的 Agent 可以一次重试就自我修正。收到”missing required arguments”的 Agent 得猜,意味着多余的工具调用、浪费 token,还有再次猜错的机会。
模糊或静默失败是 Blocker。报了问题但不报怎么修是 Friction。报错含完整纠正路径是 Optimization 目标。
原则四:安全重试,变更边界明确
Agent 会重试、恢复、有时重放命令。变更命令应该在可能时让这些操作安全,危险变更应该明确。
这对 Agent 比对人类更重要,因为 Agent 更可能自动重试。人类跑两遍命令会注意到重复。Agent 在重试循环里不会,除非 CLI 告诉它发生了什么。
目标不是到处严格幂等。对 create/update/deploy 命令,让重复应用变成空操作或明显可检测价值高。对 append/send/trigger 命令,精确幂等可能做不到,但 CLI 至少要让变更边界明确,返回足够的状态标识符让 Agent 判断是否重复工作了。
重试变更命令时静默重复或损坏状态是 Blocker。脚本化 destructive 命令几乎没有预览或状态反馈是 Friction。安全重试加显式危险 flag 和审计友好标识符是 Optimization 目标。
原则五:渐进式帮助发现
Agent 不会一开始就读完 CLI 的完整文档。它们先探顶层的 –help,再探子命令的帮助。支持这种增量工作流。
从顶层 –help 开始理解命令表面。然后钻进特定子命令。需要从”这个工具能做什么”到”怎么调用这个具体命令”,两步,不是五步。
每个子命令的帮助应该包含四样:一行目的、具体调用模式、必需参数或 flag、最重要的修饰符或安全 flag。缺任何一样,Agent 都得猜或者多打几个电话来推断调用形状。
Anthropic 自己的工具设计指导表明,具体示例能改善 Agent 使用工具的效果。没有示例的帮助页会强迫 Agent 从 flag 描述里综合调用,能用但浪费 token 还容易出错。
难发现的子命令或缺 –help 是 Blocker。帮助存在但缺调用模式或必需参数是 Friction。分层的、示例驱动的帮助加指向更深文档的链接是 Optimization 目标。
原则六:可组合、结构可预期
Agent 通过链式命令解决问题。它们受益于接受 stdin、产生干净 stdout、使用可预期命名和子命令模式的 CLI。
Agent 是天生的管道工。它们把一个命令的输出 chain 到另一个命令的输入,跟 shell 脚本一样。但它们比人类更不容忍不一致,因为它们靠模式匹配结构而不是读文档做判断。
无法参与管道是 Blocker。跨子命令命名和结构不一致是 Friction。规律的模式加干净的 stdin/stdout 是 Optimization 目标。
原则七:bounded、高信号响应
Agent 每一行输出,都需要耗费token成本。大量输出有时候合理,但 CLI 应该只输出和任务相关的内容。
这一点和人来使用CLI不一样:人扫过 500 行输出,视觉上找到自己需要的。而而Agent 把 500 行全部吃进上下文窗口,每 token 都付费,然后还得想哪行重要。
重要的设计动作是:当 CLI 截断时,它教 Agent 怎么缩小查询。”Showing 25 of 312 posts” 加上一条建议的缩小命令,给 Agent 提示下一步该怎么做。
原则指南作为独立参考文档打包在同一个 PR 里。每条原则对人类也更友好。结构化输出、可操作报错、有界响应、非交互自动化路径:这些不是对 Agent 的妥协。它们是良好的 CLI 设计,只是我们之前没有用好,因为人类足够宽容,能绕过这些缺陷。
把 Agent 当一等公民来设计,节约token,CLI 最终对用户使用也会更友好。
夜雨聆风