第01章 Agent时代为什么还要CLI

作者:光谷老亢 | 源码地址:https://github.com/kang-airtc/cli-mini-book

打开任何一台开发者的电脑,Claude Code、OpenCode、Codex、Gemini CLI 这些 AI 编程助手都以同一种形态存在,即终端里一个可执行命令。在浏览器插件、桌面应用和 IDE 集成都已成熟的今天,头部 Agent 产品不约而同地选择了命令行接口(Command Line Interface,CLI)作为主分发形态。这不是历史惯性,而是 Agent 与外部系统协作模式的必然结果。

理解这一选择背后的逻辑,是设计自己的 Agent CLI 之前必须看清的前提。下文先定位 CLI 在 Agent 生态中的具体角色,再回顾命令行工具四十年的演化主线,最后通过一组对比说明 CLI 形态与 Web 服务在被程序编排时的差异。

1.1 CLI在Agent生态中的位置

1.1.1 头部Agent产品的共同形态

考察当前主流的 AI 编程产品,可以列出一份清单。其中 Claude Code 通过 npm install -g @anthropic-ai/claude-code 安装;Gemini CLI 通过 npm install -g @google/gemini-cli 安装;OpenCode 提供 curl -fsSL https://opencode.ai/install | bash 一键脚本;Codex 同样提供 npm install -g @openai/codex 与官方安装脚本两条路径。

这些产品的共同点不是用了同一个 SDK,也不是用了同一种语言实现,而是都把对外提供能力这件事统一为一条命令行入口。读者使用它们时,首次接触的就是终端里敲下产品名加子命令。

在这里插入图片描述

如图 1-1 所示,Agent CLI 在生态中扮演的不是单纯的终端工具,而是一个枢纽。向下,它通过子进程或 API 调用模型服务、文件系统、版本控制系统等具体工具。向上,它既可以被人类用户在终端敲命令直接驱动,也可以被另一个 Agent 编排程序当作子命令调用。这种人机双向接口属性,是其他形态难以同时具备的。

1.1.2 CLI承担的双向接口

笔者最早把一个调用大模型的脚本封装成命令行工具时,初衷只是图自己用着顺手。把脚本发给同事之后才意识到,它的真正价值不在于节省自己敲键盘的时间,而在于同事可以把它接到自己的脚本里,流水线可以把它放到 CI 步骤里,另一个 Agent 可以把它列到自己的工具清单里。命令行接口的本质,是给程序提供一个稳定的、与编程语言无关的调用契约。

下面这段示例同时展示了三种调用模式,三种模式共用同一个二进制与同一套参数解析逻辑:

# 模式1 人类直接调用
opencode chat "帮我重构这个函数"

# 模式2 被 Shell 脚本编排
git diff HEAD~1 | opencode chat --stdin "总结这次提交的改动"

# 模式3 被另一个 Agent 当工具调用
echo '{"tool":"opencode","args":["status"]}' | agent-runner

三种调用形态共享同一份退出码定义,使一份实现可以同时服务于三类调用者。这一特性是 Agent 产品集中投入做 CLI 的根本原因。

注意:本书所讨论的 Agent CLI 与传统命令行工具的区别,不在于实现技术,而在于设计意图。传统 CLI 主要面向人类操作者,Agent CLI 则从设计之初就需要兼顾被另一个程序调用的场景,这一差异会反复出现在后续章节关于输出格式、退出码、错误信息的讨论中。

1.2 命令行工具的演化主线

CLI 并不是一项新发明,但每一代主流产品都对它进行了重新定义。回看四十年来的演化,可以看到一条清晰的主线。

在这里插入图片描述

1.2.1 演化的四个阶段

如图 1-2 所示,命令行工具的演化经历了四个阶段。

1. 操作系统时代

代表是 DOS、Unix 内置命令。这一阶段 CLI 是操作系统暴露给用户的唯一界面,命令的设计目标是操控本机资源,代表性命令包括 ls、cp、grep。读者今天使用的绝大多数 Shell 内建命令都来自这一时期。

2. 云服务时代

代表是 AWS CLI、Azure CLI、kubectl。云服务把数据中心抽象成一组 API,但开发者并不愿意手写 HTTP 调用。于是出现了把 API 封装为子命令的 CLI 工具。命令的设计目标从操控本机变成操控远端,同时引入了配置文件、身份凭证、区域等概念。AWS CLI 至今仍是这一阶段最完整的范例。

3. 平台聚合时代

代表是 GitHub CLI、GitLab CLI、Vercel CLI。这一阶段的 CLI 不再只是 API 的包装,而是把日常工作流沉淀为更高层的子命令,例如 gh pr create 把创建分支、推送、调用 API 建 PR、填模板这些步骤压缩为一条命令。设计目标是减少认知负荷,贴近开发者真实使用场景。

4. Agent时代

代表是 Claude Code、OpenCode、Gemini CLI。CLI 第一次承担起对话角色,用户输入的不再只是结构化参数,还可以是自然语言指令,输出也不再只是数据,而是模型生成的多模态结果。同时,CLI 自身成为一个可被其他程序编排的能力单元。

从操控本机到操控远端,再到沉淀工作流,最后到承载对话与编排,四十年里 CLI 的内涵不断扩展,但终端入口这一形态始终保留。理解这条主线,有助于读者判断自己正在做的 Agent CLI 应当借鉴哪一代产品的设计经验。

1.3 同一能力Web服务与CLI的对比

要进一步理解 Agent 为何选择 CLI 而非 Web 应用,可以把同一份能力以不同形态对外做一组对比。假设有一个能根据自然语言生成提交信息的小工具,分别以 Web 服务和 CLI 两种形态实现,关键差异如表 1-1 所示。

表 1-1 同一能力两种形态的关键差异

对比维度 Web服务形态 CLI形态
安装成本 注册账号、申请密钥、保管密钥 一行安装命令
调用方式 HTTP 请求、JSON 序列化 进程调用、参数与标准输入
数据流向 必须离开本地环境 默认在本地处理
集成成本 需要写客户端代码 Shell 管道直接串联
离线能力 依赖网络可达 可降级为本地模型
被Agent编排 需要 SDK 或 HTTP 客户端 子进程调用即可

如表 1-1 所示,核心差异在于 CLI 把调用契约压缩到了进程边界这一最薄的接口上。读者要让另一个程序调用 CLI,只需要 fork 进程、传递参数、读取标准输出,这套约定从 Unix 时代延续至今,几乎所有编程语言都自带支持。相比之下,Web 服务的调用契约横跨网络协议、序列化格式、认证机制,集成时需要写更多代码。

笔者在做内部 Agent 平台时观察到一个现象。同样的能力,做成 Web API 时只有少数熟悉它的同事会用,做成 CLI 后用户数翻了几倍。原因不在于功能差异,而在于 CLI 的接入成本被压到了打开终端敲一行的程度,这一成本差异决定了一个工具能否真正进入日常工作流。

注意:本节并非否定 Web 服务的价值。对于需要图形界面、需要面向非开发者用户或需要严格集中管控的场景,Web 应用依然是更合适的选择。本书讨论的 Agent CLI,前提是目标用户具备终端使用习惯,且需要被其他程序编排调用。

1.4 本章小结与后续章节路径

回到本章开头的问题,Agent 时代为何还要 CLI。答案可归纳为三点。

第一,命令行接口具有人机双向友好的天然属性,同一份实现可以同时服务于终端操作、脚本编排、Agent 调用三种场景。第二,从 DOS 到 Claude Code,CLI 的内涵随技术阶段不断扩展,Agent 产品继承的是一条经过四十年验证的分发与集成模式。第三,在被另一个程序调用成为常态的 Agent 生态里,CLI 的低集成成本带来直接的用户增长。

理解了这一动机,后续章节的路径自然展开。下一章先把用户敲下一行安装命令之后到底发生了什么拆解清楚,这是设计自己的 CLI 之前必须看清的底层机制。然后从空目录开始动手,先用 Node.js 实现一个能在终端运行的最小 CLI,再发布到 npmjs。接着用同一份逻辑切换到 Python,体验另一条生态的打包发行方式。最后通过 install.sh 把发布路径打通到 GitHub Releases。

注意:本书配套的源码仓库位于 https://github.com/kang-airtc/agent-cli-demo,Node.js 实现位于 my-agent-node-cli,Python 实现位于 my-agent-python-cli。建议读者边读边操作,后续章节的所有代码片段都可在该仓库中找到对应的完整文件。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐