一、CLI 是什么
CLI,全称 Command-Line Interface(命令行界面)。你打开终端,敲一行字,按回车,程序干活,打印结果,退出------这就是 CLI。
$ ls -l ← 输入
total 24
drwxr-xr-x 5 user staff 160 Jun 1 10:00 src/
-rw-r--r-- 1 user staff 120 Jun 1 10:00 README.md
↑ 输出
$ ← 退出了,等下一个命令
CLI 和 GUI(图形界面)的区别:GUI 一直开着,你点按钮它响应;CLI 敲完就跑,结果打出来就结束。
CLI 和 Web 服务的区别:Web 服务一直等请求,每个请求进来响应完继续等下一个;CLI 一次执行完毕就退出,不保持连接。
CLI 的三个本质特征:输入(命令行参数)→ 处理(程序逻辑)→ 输出(stdout + 退出码)。 少了任何一个都不算 CLI。
二、CLI 的零件
一个完整的 CLI 由以下几个部件组成。
解剖图
┌─────────────────────────────────────────────────────┐
│ CLI Tool Anatomy │
├─────────────────────────────────────────────────────┤
│ │
│ $ my-cli calc add 1 2 --verbose │
│ │ │ │ │ │ │
│ │ │ │ │ └── 选项参数 │
│ │ │ │ └─────── 位置参数 │
│ │ │ └───────── 子命令 │
│ │ └───────────── 子命令组 │
│ └─────────────────── 主命令 │
│ │
│ ┌─────────┐ ┌────────┐ ┌──────────────────┐ │
│ │ Parser │─▶│ Logic │─▶│ Output │ │
│ │ (拆参数) │ │ (干活) │ │ │ │
│ └─────────┘ └────────┘ │ stdout → 结果 │ │
│ │ stderr → 错误 │ │
│ │ exit 0 → 成功 │ │
│ │ exit 1 → 失败 │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
五要素
子命令体系 :类似 git commit、docker ps。一个 CLI 可以拆成多个子命令,每个子命令做一件事。
参数解析:分两种------
- 位置参数:
calc add 1 2里的1和2,按顺序匹配 - 选项参数:
--output json、-v,通过名字匹配
stdout 和 stderr:标准输出(结果)和标准错误(日志/错误信息)走两个通道,分开处理。
退出码:0 表示成功,非 0 表示失败。这是程序化的判断依据------无论是人还是 AI,都通过退出码知道命令有没有跑通。
--help :每个合格的 CLI 都必须能打印帮助信息。calc --help 要告诉用户有哪些子命令,每个子命令要什么参数。
举个例子
假设我们要设计一个计算器 CLI:
python
# 伪代码示意
$ calc add 1 2 → 3
$ calc multiply 3 4 → 12
$ calc --help → 列出 add, multiply, divide...
这就是 CLI 的全部零件。不多不少。
三、"写脚本"和"做 CLI 程序"的区别
很多初学者觉得"写一个 .py 文件跑起来"就是 CLI 了。对,但也不对。随手写的脚本 和正式发布的 CLI 程序之间,差了一套工程规范。
随手写脚本:python cli.py 才能跑,依赖手动安装,没有 --help,没有退出码。
正式 CLI 程序:my-cli 直接跑,pip install 一键安装,自动生成 --help,严格退出码。
| 特性 | 随手脚本 | 正式 CLI 程序 |
|---|---|---|
| 运行方式 | python cli.py |
my-cli |
| 安装 | 手动装依赖 | uv tool install . |
| --help | 没有 | 自动生成 |
| 输出 | print() 随便打 |
stdout(结果)/ stderr(错误) |
| 退出码 | 没有 | 0=成功,1=错误,2=参数错 |
| 依赖管理 | 全靠 README 写 | pyproject.toml 声明 |
关键在于:这些标准不是 AI 领域发明的,而是 Python 包管理的通用规范。
你写一个 FastAPI Web 服务、一个数据处理脚本、一个 CLI 工具------它们用的都是同一套 pyproject.toml 标准。区别只有一行配置:
toml
# Web 服务
[project.scripts]
uvicorn = "myapp.main:app"
# CLI 工具
[project.scripts]
my-cli = "my_cli.cli:main"
这一行决定了 pip install 之后,用户敲的是什么命令。
四、从 pyproject.toml 倒推目录结构
很多教程会直接甩给你一个目录结构,说"这就是标准"------但初学者看不懂为什么。我们反过来,从"想要什么效果"倒推。
第一步:想让用户敲 my-cli 就能用。
需要在 pyproject.toml 里声明入口点:
toml
[project.scripts]
my-cli = "my_cli.cli:main"
这告诉 Python:用户敲 my-cli 时,执行 my_cli/cli.py 里的 main() 函数。
第二步:需要一个包名,不和别的项目冲突。
于是有了:
src/my_cli/
├── __init__.py
└── cli.py
用 src/ 包装一层(src-layout),是为了避免项目根目录的 .py 文件和别的包路径冲突。
第三步:子命令多了,一个文件装不下。
于是拆出 commands/ 目录:
src/my_cli/
├── __init__.py
├── cli.py
└── commands/
├── __init__.py
├── calc.py
└── config.py
第四步:想让用户也能 python -m my_cli 跑。
加一个 __main__.py:
python
from .cli import main
main()
每一步都是"因为想要某个效果,所以这么做",而不是"标准就是这样"。
真实案例:项目中的 mcp-server
你可以在项目源码 mcp-server/pyproject.toml 里看到实际配置:
toml
[project.scripts]
mcp-server = "mcp_server.main:main"
安装后直接 uv run mcp-server 就能启动 MCP 服务。这就是一个标准的 Python CLI 入口。
五、AI 时代给 CLI 加了几条要求
CLI 本身没变。但 AI Agent 作为新的"用户",对 CLI 的输出方式提出了额外要求。其实就三条:
1. --output json
AI 看不懂"结果是: 3 🎉",但 AI 看得懂 {"result": 3}。
bash
# 给人看
$ calc add 1 2
3
# 给 AI 看
$ calc add 1 2 --output json
{"result": 3, "status": "ok"}
所以所有 AI 兼容的 CLI 都应该支持 --output json(或 --json)选项。默认给人看可读文本,加 --output json 给程序解析。
2. 严格退出码
AI 通过退出码判断命令是否成功:
- 0 = 成功
- 1 = 通用错误
- 2 = 参数错误
如果所有情况都返回 0,AI 无法知道操作是否真的完成了。
3. 无交互式提示
bash
# ❌ AI 没法回答这个
$ calc add
请输入两个数字: _
# ✅ 所有参数一次给全
$ calc add 1 2
3
AI 不会弹对话框,不会选择 Y/N。CLI 必须一次性接收所有必要参数。
就这三条。其他所有规范(pyproject.toml、src-layout、入口点)都和 AI 无关,是 CLI 本身就该有的。
六、AI 时代的完整链路:CLI → MCP → Skill
现在我们把 CLI 放到更大的图景里看。
三层架构
┌──────────────────┐
│ Skill │ ← 操作手册:告诉 Agent 怎么用工具
│ (指导层) │ 什么时候该用、参数怎么填
└────────┬─────────┘
│
┌────────▼─────────┐
│ MCP │ ← 通信协议:Agent 和工具之间的标准接口
│ (协议层) │ tools/list → 发现工具
│ │ tools/call → 调用工具
└────────┬─────────┘
│
┌────────▼─────────┐
│ CLI │ ← 工具本身:真的在干活的那个
│ (工具层) │ 人敲命令也是它,AI 调也是它
└──────────────────┘
关键原则:每一层不依赖上层,可以独立存在。
- CLI 不需要 MCP 也能用------人直接敲命令
- MCP 不需要 Skill 也能用------Agent 可以自己决定怎么调工具
- Skill 只是给 Agent 提供更多上下文,让 Agent 决策更准确
用项目实际案例理解
在 nacos-learn-example 项目中:
CLI 层 :mcp-server 模块。一个 Python 程序,注册了三个工具(hello、calculator、weather)。人可以直接启动它,也可以把它当普通 CLI 用。
MCP 层 :nacos-mcp-router 模块。它作为 MCP 协议代理,一端连接 Nacos MCP Hub(服务注册中心),一端通过 SSE 协议暴露 MCP 接口。Agent 通过它发现和调用服务。
Skill 层 :项目中没有显式的 Skill 定义,但 agentic 模块的 Planner 和 Validator 承担了类似角色------它告诉 LLM"有哪些工具可用、每个工具做什么用、什么场景该用什么工具"。
调用流程
人用 CLI: $ mcp-server calculator add 1 2 → 3
AI 用 CLI: Agent → MCP tools/list → 发现 calculator
Agent → MCP tools/call calculator →
MCP Server → 执行逻辑 → 返回结果给 Agent
底层干活的那个没变------无论是人还是 AI,最终执行的都是同一段业务逻辑。
CLI、MCP、Skill 的本质
| 概念 | 本质 | 谁在用 | 创新程度 |
|---|---|---|---|
| CLI | 命令行工具,跑完就退 | 人和 AI | 1970s Unix 就有 |
| MCP | AI 调工具的标准化协议 | AI Agent | 新标准(2024) |
| Skill | 给 AI 的操作手册 | AI Agent | 新模式,框架+自由决策 |
CLI 没有创新,它是基础设施。MCP 是真正的创新------它让所有 Agent 用同一种方式发现和调用工具。Skill 是 MCP 之上的辅助层,让 Agent 用得更对。
七、参考案例:飞书 lark-cli
飞书官方 CLI(github.com/larksuite/cli)是"给人和 AI 用的 CLI"的标杆实现,13k stars,Go 语言编写。
三层命令体系
lark-cli calendar +agenda ← Shortcut(快捷命令,含智能默认值)
lark-cli calendar calendars list ← API 命令(1:1 映射平台接口)
lark-cli api GET /open-apis/... ← Raw API(全覆盖 2500+ 接口)
层级递进:从最常用的快捷操作,到精准的 API 调用,再到任意接口访问。
Skill 机制
lark-cli 提供了 24 个领域 Skill,每个 Skill 是一个模块:
| Skill | 覆盖的功能 |
|---|---|
| lark-calendar | 日历事件创建、议程查询 |
| lark-im | 消息发送、群聊管理 |
| lark-doc | 文档读写、搜索 |
| lark-base | 多维表格操作 |
| lark-task | 任务管理 |
Agent 按需加载 Skill,不会一次性暴露所有工具给 AI。
AI 友好设计
--format json是默认输出 ,--format pretty给人看- 输出格式可选:json / ndjson / table / csv
- OAuth 登录:支持交互式(人用)和非交互式(AI 用)两种登录流程
- Dry-run 模式:有副作用的操作先预览再执行
八、动手建议
想学 CLI,最好的方式不是看教程,而是做。
从小做起
找一件你日常重复做的事------文件重命名、批量处理图片、查询系统状态------把它做成 CLI。
推荐工具链
bash
uv init my-tool # 初始化项目
uv add typer # 安装 CLI 框架
# 写代码...
uv sync # 测试
Typer 是最适合初学者的 Python CLI 框架,4 行代码就能跑起来:
python
import typer
app = typer.Typer()
@app.command()
def hello(name: str, count: int = 1):
for _ in range(count):
print(f"Hello {name}!")
if __name__ == "__main__":
app()
不要一开始就搞 MCP
先把 CLI 本身做对------子命令、参数解析、退出码、--output json。这些都是通用的 CLI 技能,不管以后要不要对接 AI,都值得掌握。
MCP 和 Skill 是锦上添花,CLI 才是那匹锦。