如何设计一个Agent友好的CLI工具?

如何设计一个Agent友好的CLI工具? 随着 Agent 技术走向成熟越来越多的产品需要同时面对人类和 Agent 两类用户而 CLI命令行工具正是更容易被 Agent 调用的形态。未来可能所有产品或系统都需要实现自己的 CLI作为面向 Agent 的统一能力入口封装自身核心能力供 Agent 使用。本文以近期实践的开源 CLI 为例从编程语言、登录授权、命令设计、Skill、分发和安全六个方面探讨如何设计一个对 Agent 友好的 CLI 工具。为什么是 CLIAgent 访问外部能力主要有三种方式调用 API、通过 MCP、以及在终端执行命令。三者其实底层都是对远程接口的封装但 CLI 对 Agent 有两个独特优势一是几乎所有通用 Agent 都自带 Bash 工具 (例如 Codex、Claude Code、CowAgent、OpenClaw 等)无需服务提供方搭建 MCP server二是它把鉴权、参数组装、分页、错误处理等封装进命令Agent 不用自己构造 HTTP 请求也不用把整份接口文档塞进上下文只读一份精简的命令说明就能按需决策省 token 也更准确。但随之而来的是使用者的形态也发生了改变过去 CLI 的主要用户是坐在终端前的开发者现在多了一个 读取输出 - 做决策 - 执行下一条命令 的 Agent。两类用户的诉求有诸多差异如何同时满足人类和 Agent 的使用体验就成为了设计上的一个重要课题。下文以一个平台产品的 CLI 设计实践为例对过程中的关键设计决策进行介绍整体思路如下图一、编程语言CLI 在开发语言选择上有几个硬指标• 单文件且无运行时依赖Agent 的运行环境不可预测可能是本地计算机、Docker 容器、远程 Linux 等无法假设它装了正确的 Node 或 Python 版本最好是下载一个二进制就能跑• 可交叉编译需要覆盖 macOS / Linux / Windows × amd64 / arm64支持一套代码跨平台编译• 启动快Agent 会高频反复调用命令静态语言是首选几个候选对照• Node / TypeScript生态好、写得快但依赖目标机器有 Node 运行时打包成单文件产物大• Python同样受运行时和版本问题限制分发是大问题• Rust单二进制、性能好各项都满足但是开发效率和编译速度偏低• Go单静态二进制设置 CGO_ENABLED0 后零依赖交叉编译支持好启动毫秒级开发效率不错最终选择 Golang 作为开发语言并使用 Cobra 作为 CLI 框架处理命令解析和路由。由于产物是纯二进制分发时可以用一套 GoReleaser 配置同时产出 npm 包、Homebrew Cask 和 GitHub Release。语言选择本身没有标准答案取决于团队技术栈但如果首要用户是 Agent则必须优先考虑能编译成零依赖单二进制的语言分发的便捷程度直接决定 Agent 能不能自己把它快速装上。二、登录与授权最简单的 CLI 授权方式是让用户直接填写 API Key但问题也很明显API Key 通常长期有效、全量权限、明文存储一旦泄露风险很大也无法按操作细分权限。对于一个会被 Agent 自动调用、管理各类资源的 CLI 来说这种 一把钥匙开所有门 的方式风险太高。所以实践中采用了 OAuth 2.0 Device Authorization Grant设备码流程 的方案用户在浏览器登录并按 scope 授权CLI 拿到的是短期有效、可刷新、可精细授权、服务端可随时吊销的 access token。整个流程分三步另外很多传统 CLI (例如 gh、gcloud ) 会有另一种 OAuth 的实现通过本地起一个临时 HTTP server 接收浏览器回调的方式。但它对 Agent 其实并不友好因为 Agent 可能运行在没有浏览器和图形界面、端口不开放的服务器环境。而设备码流程支持授权过程在任意一台机器的浏览器上完成CLI 侧只做发起和轮询。在整个授权过程有两个关键阶段1. 分阶段轮询人类用户手动登录时CLI 可以自动打开浏览器并一直轮询等待。但 Agent 是「执行命令 → 拿结果 → 决定下一步」的循环如果在同一次工具调用里既要输出登录链接又要轮询链接就没法通过模型返回给用户从而阻塞整个会话。所以在实现中将 Agent 的登录拆成了异步的两阶段。第一阶段立即返回返回的 JSON 除了验证 URL 和设备码还带一个给 Agent 看的 next_action明确告诉它下一步该跑什么命令linkai auth login --no-wait --json第二阶段开始轮询每次最多等待若干秒后返回超时未完成时 Agent 可以通过下一次工具调用再次轮询linkai auth login --device-code code --wait60 --json这里使用 --wait 参数区分了两类用户不传默认阻塞到设备码过期走人类的交互式登录传 --wait 则切到 Agent 的有界轮询路径。2. 授权页面授权登录页是用户直接交互和做决策界面需要把三件事呈现清楚• 是哪个 账号/设备/CLI 在请求授权• 这次申请了哪些权限逐条列出不同模块的 scope让用户清楚这次授权到底给了 CLI 什么能力• 授权成功后提示可以回到终端因为终端还在轮询用户需要知道流程已走完三、命令与参数这一节介绍命令参数设计的细节对于同一条命令开发者希望看到清晰的表格和流式回复而 Agent 则更需要一次性返回可解析的结构化数据。1. JSON 格式输出--json 是一个全局参数让命令可以输出结构化的 JSON 格式数据这样只需在给 Agent 的 Skill 中约定 总是加 --json 参数就会让 Agent 能拿到稳定可解析的结构化数据。2. 流式与非流式与大模型交互的这类命令人类习惯流式SSE的打字机效果但 Agent 通过 Bash 调用时输出会被管道重定向流式会把内容切成碎片不利于解析它更需要的是一次性的完整回复。这里没有把流式参数 (--stream/--no-stream) 作为必选项而是根据运行环境自动选择默认值在终端下默认流式管道或重定向Agent 的典型场景下默认非流式同时 --json 时强制非流式显式传参可以覆盖。3. 写操作试运行对于破坏性/变更类的命令支持 --dry-run可以不实际发起请求只打印将要发送的 HTTP 请求。这样 Agent 可在真正执行前预检查参数是否正确也增加了一道安全校验。4. 输出分流与退出码结果走标准输出stdout过程信息走标准错误stderr。像等待进度、更新通知、交互确认的问句这些过程信息如果输出到 stdout会影响 Agent 对 JSON 结构的解析分流之后 Agent 只需处理标准输出即可。同时对退出码也做了区分让 Agent 能在失败时判断应该重试还是停止在实现中设计了这样几个退出码0 成功、1 一般错误、2 参数错误、3 认证/权限问题、4 网络异常。其中权限不足exit 3时会在错误信息中给出命令提示如 linkai auth login --scope ...Agent 根据提示会重新拉起授权而非反复重试。四、Skill 设计CLI 通过命令实现了一系列接口的封装下一步需要解决的是如何让 Agent 了解如何正确调用这些命令。如果只靠反复 --help 来查看参数显然试错成本太高所以需要一个 Skill 来配套使用这是一份写给 Agent 看的 CLI 说明书。Skill 的结构尽可能做得扁平一个主 SKILL.md 作为入口各个模块的命令说明则放到references/ 目录中。这样的结构安装到 Agent 中只会有一个目录原则是优先让 Agent 看主 Skill 文件需要了解模块细节的再深入 references 中查看。有些 CLI 会把每个子模块都安装为一个 Skill这样其实并不友好一是 Agent 没有对整个 CLI 的全局视角二是大量的子 Skill 会增加 Agent 的上下文消耗。在构建中Skill 会通过 go:embed 打包进二进制与 CLI 的版本严格锁定同时实现了 skill install 命令可以一键安装技能。另外还有一个细节优化在主 SKILL.md 里内置了一段安装说明当 Agent 先拿到 Skill 时 (例如从 Skill Hub 中直接下载)也能根据引导顺利安装 CLI 二进制。五、分发与更新CLI 的分发设计对易用性的提升很关键主要分发两部分内容CLI 二进制和 Agent Skill。理想状态是给 Agent 一句话就能自己装好。在实现上建议尽量支持多种渠道要让不同平台和系统下的开发者及 Agent 都能够找到合适的渠道顺利下载.对于有 Node 环境的机器通过 npm 安装是最便捷的因为可以屏蔽不同操作系统和平台架构的差异自动选择安装合适的 CLI 二进制并加入环境变量。而一键安装脚本则是更好适配无依赖的环境除了下载 CLI还会把 Skill 一键分发到各常用 Agent 的技能目录中 (Codex、Claude Code、Cursor、Openclaw、CowAgent 等)。为了进一步让 Agent 能一句话装好一份清晰的安装说明 (install.md) 也至关重要将从零安装的完整步骤写成指南用户只要把一句话发给 Agent 就能一键完成 CLI 和 Skill 的初始化例如阅读 https://cdn.link-ai.tech/cli/install.md 并按其中步骤安装 CLI 与 Skill然后开始使用。版本更新是分发的延续做了两层实现• 被动通知命令启动时后台拉取最新版本号并缓存命令结束时在 stderr 输出一行提示引导完成更新• 主动更新update 命令自动检测初始安装方式调用对应包管理器升级并自动同步 Skill六、安全性登录授权只是第一道防护在 Agent 使用场景还有几个关键点1. 权限最小化 权限 scope 用 资源:动作 格式例如 app:read、db:write默认只授予只读和内容生成类权限对于变更、删除等高危操作须显式设置这样即使 Agent 误操作也无法越过 CLI 的权限管控2. Token 存储 在 macOS 下优先将凭证存入系统钥匙串其他平台存储至文件权限 0600使用服务端可撤销的 opaque token而非自解析 JWT在登出时同步吊销服务端 token3. 设备绑定 每个请求携带设备 ID服务端将 token 与设备绑定降低 Token 泄露后的盗用风险4. 危险字符过滤 Agent 接收的内容可能来自不可信来源网页、外部输入等存在不可见攻击字符的风险例如Bidi 覆盖、零宽字符、ANSI 转义等可能改变命令语义或制造终端欺骗因此在 CLI 输入侧就拒绝这类危险字符并在输出侧同步剥离。小结CLI 的设计思路和 GUI 产品大相径庭而同样是 CLI面向 Agent 和面向开发者也有不少差异。回顾全文可以归纳为几条思路• 语言选择优先选择能够编译成单二进制的静态语言• 登录授权采用设备码流程权限确认在浏览器完成并面向 Agent 提供分阶段登录• 命令与参数为开发者提供清晰展示为 Agent 提供结构化数据同时容忍多余参数、支持操作预演、错误时给出下一步引导• Skill 配套作为给 Agent 的说明书采用单目录、扁平结构降低理解和上下文成本• 分发安装支持多渠道分发并配一份能发给 Agent 的安装手册• 安全防护除登录授权外还要支持权限最小化、凭证保护、设备绑定、风险内容过滤等这些思路其实不限于 CLI。结构化输出、容错设计、配套的 Skill、顺畅的分发、最小权限同样适用于 API、SDK 以及其他面向 人与 Agent 双用户 的产品。