AI Agent 时代的命令行工具新思考
1. CLI 简介
什么是 CLI?
CLI(Command-Line Interface,命令行接口)是一种通过文本命令与计算机交互的方式。用户输入指令,程序解析并执行,返回结果。
2. 为什么 CLI 突然"火"了?
2.1 AI Agent 的核心能力:调用工具
AI Agent 不能只"说话",必须能执行动作。工具是 Agent 能力的延伸。
~/ai-agent
传统 AI:理解 → 回复
Agent AI:理解 → 规划 → 调用工具 → 执行 → 反馈
2.2 CLI 是最优的工具接口
CLI 的优势:
1. 零部署 — 无需运行服务,直接命令行调用
2. 自描述 — --help 让 AI 知道能做什么
3. JSON 输出 — 机器可解析的结构化数据
4. 幂等性 — 同样输入,产出一样(确定性)
5. Unix 哲学 — 一个命令做一件事
2.3 典型场景
~/ai-agent
# AI 理解到需要计算
用户:帮我算 2 的 10 次方
Agent → 调用 CLI → mycli calc "2 ** 10" → 返回 1024
# AI 理解到需要搜索文件
用户:在项目里找所有包含 TODO 的文件
Agent → 调用 CLI → mycli search . -k "TODO" --json → 返回文件列表
# AI 理解到需要系统信息
Agent 需要知道当前环境 → 调用 CLI → mycli sysinfo --json
3. 项目结构
~/ai-agent
/Users/kirito/repos/ai-agent/cli/
├── python-demo/ # Python 版 (mycli)
│ ├── mycli/
│ │ ├── __init__.py
│ │ ├── main.py # 入口 + 命令注册
│ │ ├── utils.py # 统一输出格式化
│ │ └── commands/
│ │ ├── __init__.py
│ │ ├── calc.py # 数学计算
│ │ ├── search.py # 文件搜索
│ │ └── sysinfo.py # 系统信息
│ └── pyproject.toml
│
└── go-demo/ # Go 版 (gocli)
├── main.go # 入口 + 命令注册
├── output/
│ └── output.go # 统一输出格式化
├── calc/
│ └── calc.go # 数学计算 + 递归下降解析器
├── search/
│ └── search.go # 文件搜索
├── sysinfo/
│ └── sysinfo.go # 系统信息
└── go.mod
4. Python 版:mycli
4.1 技术栈
| 组件 |
选型 |
说明 |
| CLI 框架 |
Click |
Python 最流行的 CLI 框架 |
| 输出美化 |
Rich |
彩色输出、表格、面板 |
| 解析器 |
AST (内置) |
Python 内置的 AST 模块 |
4.2 核心代码架构
main.py — 命令注册
~/ai-agent
@click.group()
def cli():
"""mycli - Agent-Friendly CLI Tool"""
pass
cli.add_command(calc) # 注册 calc 子命令
cli.add_command(search) # 注册 search 子命令
cli.add_command(sysinfo) # 注册 sysinfo 子命令
utils.py — 统一输出格式
~/ai-agent
def make_success(data: Any, **meta) -> dict:
"""构建成功响应"""
return {"success": True, "data": data, **meta}
def make_error(message: str, **meta) -> dict:
"""构建错误响应"""
return {"success": False, "error": message, **meta}
def output_result(data: dict, json_output: bool, rich_formatter=None):
"""根据 --json 标志决定输出格式"""
if json_output:
click.echo(json.dumps(data, ensure_ascii=False, indent=2))
else:
rich_formatter(data) if rich_formatter else click.echo(data)
calc.py — 安全数学计算(核心)
~/ai-agent
# 使用 Python 内置 AST 模块做安全求值
tree = ast.parse(expression, mode="eval") # 解析为 AST
def _eval_node(node):
if isinstance(node, ast.Constant):
return node.value
elif isinstance(node, ast.BinOp):
left = _eval_node(node.left)
right = _eval_node(node.right)
return OPERATORS[type(node.op)](left, right) # 白名单操作符
elif isinstance(node, ast.Call):
# 只允许白名单函数
if node.func.id not in SAFE_FUNCTIONS:
raise ValueError(f"Unsupported: {node.func.id}")
return SAFE_FUNCTIONS[node.func.id](*args)
4.3 启动方法
~/ai-agent
cd /Users/kirito/repos/ai-agent/cli/python-demo
# 开发模式安装(可编辑)
pip install -e .
# 验证安装
mycli --version
原理:pyproject.toml 中的 entry points
~/ai-agent
[project.scripts]
mycli = "mycli.main:cli"
安装后,setuptools 会在 bin 目录创建 mycli 可执行文件。
4.4 使用方法
~/ai-agent
# 查看帮助
mycli --help
mycli calc --help
# 数学计算
mycli calc "2 ** 10" # 1024
mycli calc "sqrt(144) + pi" # 15.141593
mycli calc --json "log10(1000)" # JSON 输出
# 文件搜索
mycli search . -p "*.py" # 按模式
mycli search . -k "TODO" # 按关键词
mycli search . -p "*.py" -k "class" --json # 组合 + JSON
# 系统信息
mycli sysinfo # 全部信息
mycli sysinfo --section python # 只看 Python
mycli sysinfo --json # JSON 输出
JSON 输出示例
~/ai-agent
{
"success": true,
"data": {
"expression": "2 ** 10",
"result": 1024
},
"command": "calc"
}
5. Go 版:gocli
5.1 技术栈
| 组件 |
选型 |
说明 |
| CLI 框架 |
Cobra |
Go 最流行的 CLI 框架 |
| 输出美化 |
fatih/color |
轻量级彩色输出 |
| 解析器 |
手写递归下降 |
从头实现 token + parser |
5.2 核心代码架构
main.go — 命令注册
~/ai-agent
func init() {
rootCmd.AddCommand(calc.Cmd) // 注册 calc
rootCmd.AddCommand(search.Cmd) // 注册 search
rootCmd.AddCommand(sysinfo.Cmd) // 注册 sysinfo
}
output/output.go — 统一输出格式
~/ai-agent
type Result struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
Command string `json:"command,omitempty"`
}
func MakeSuccess(data interface{}, command string) Result {
return Result{Success: true, Data: data, Command: command}
}
func MakeError(message string, command string) Result {
return Result{Success: false, Error: message, Command: command}
}
calc/calc.go — 递归下降解析器(核心)
Go 没有内置 AST,需要手写 tokenizer + parser:
~/ai-agent
表达式: sqrt(144) + pi
Step 1: Tokenizer(分词)
[Ident("sqrt"), LParen, Number("144"), RParen, Operator("+"), Ident("pi")]
Step 2: Parser(递归下降)
parseExpression()
└─ parseAddSub()
├─ parseMulDiv()
│ └─ parsePower()
│ └─ parseUnary()
│ └─ parsePrimary()
│ └─ parseFunctionOrConst() → sqrt(144) → 12.0
├─ Operator("+")
└─ parsePrimary() → parseFunctionOrConst() → pi → 3.14159...
结果: 12.0 + 3.14159... = 15.14159...
关键代码结构:
~/ai-agent
type token struct {
typ tokenType // NUMBER, OPERATOR, IDENT, LPAREN, RPAREN...
val string
}
type parser struct {
tokens []token
pos int
}
// 递归下降语法分析
func (p *parser) parseExpression() (float64, error) {
return p.parseAddSub()
}
func (p *parser) parseAddSub() (float64, error) {
left, _ := p.parseMulDiv()
for p.peek().typ == tokenOperator && (p.peek().val == "+" || p.peek().val == "-") {
op := p.next()
right, _ := p.parseMulDiv()
left = applyOp(op.val, left, right)
}
return left, nil
}
5.3 启动方法
~/ai-agent
cd /Users/kirito/repos/ai-agent/cli/go-demo
# 构建(如果还没有二进制文件)
go build -o gocli .
# 直接运行
./gocli --help
./gocli --version
优势:单二进制文件,无依赖
- Python 版需要 Python 环境
- Go 版编译后是纯机器码,直接运行
5.4 使用方法
~/ai-agent
# 数学计算
./gocli calc "2 ** 10" --json
./gocli calc "sqrt(144) + pi" --json
./gocli calc "log10(1000)" --precision 4
# 文件搜索
./gocli search . -p "*.go"
./gocli search . -k "TODO" --json
./gocli search . -p "*.go" -k "func" --json
# 系统信息
./gocli sysinfo
./gocli sysinfo --section go --json
./gocli sysinfo --json
JSON 输出示例
~/ai-agent
{
"success": true,
"data": {
"system": {
"os": "darwin",
"machine": "amd64",
"hostname": "KiritodeMacBook-Pro.local"
},
"go": {
"version": "go1.26.1",
"num_cpu": 16
},
"environment": {
"cwd": "/Users/kirito/repos/ai-agent",
"home": "/Users/kirito"
},
"timestamp": "2026-04-24T20:36:28+08:00"
},
"command": "sysinfo"
}
7 CLI与MCP对比
7.1 什么是 MCP?
MCP(Model Context Protocol,模型上下文协议)是 Anthropic 提出的 AI 与工具连接的标准化协议。
7.2 CLI vs MCP 对比
| 维度 |
CLI |
MCP |
| 连接方式 |
subprocess 调用 |
持久化网络/进程连接 |
| 标准化 |
无标准,各自实现 |
Anthropic 统一协议 |
| 适用场景 |
简单独立工具 |
复杂、需要状态的系统 |
| 部署复杂度 |
低(无依赖) |
中(需要 MCP Server) |
| AI 理解成本 |
低(--help 自描述) |
中(需要 MCP SDK) |
| 状态管理 |
无状态(每次独立) |
有状态(可保持连接) |
| 实时性 |
低(每次 fork 进程) |
高(持久连接) |
7.3 使用场景对比
CLI 适用场景:
~/ai-agent
✅ 计算器(mycli calc)
✅ 文件搜索(mycli search)
✅ 系统信息查询
✅ 一次性任务
✅ 工具链简单、无状态
MCP 适用场景:
~/ai-agent
✅ 数据库查询(保持连接状态)
✅ 实时文件监控
✅ 需要事务的操作
✅ 多步骤、需要上下文的操作
✅ 复杂系统集成(如 ERP、CRM)
7.4 架构示意图
7.5 总结
|
CLI |
MCP |
| 定位 |
工具调用 |
协议标准 |
| 复杂度 |
简单 |
中等 |
| 灵活性 |
高(任何语言) |
中(需要 MCP SDK) |
| 适用性 |
独立工具、简单任务 |
复杂系统、需要状态 |
| 发展趋势 |
成熟稳定 |
快速发展 |
结论: CLI 和 MCP 不是替代关系,是互补的。
- 简单工具 → CLI(你的 mycli/gocli)
- 复杂系统集成 → MCP
- 实际项目中 → 根据场景选择,或混合使用
7.6 "CLI 取代 MCP" — 这个说法到底对不对?
营销号的误导
最近 AI 圈冒出很多逆天营销号,标题一个比一个夸张:"MCP 已死,CLI 才是未来!"——说实话,这种标题党害人不浅。
因为这两者根本不是一个维度的东西
MCP 是规定怎么连接的协议,CLI 是跑工具的环境。我完全可以在 CLI 里调用 MCP,MCP 也完全可以在各种不同平台运行使用。冲突吗?不冲突。
这句话的正确理解方式
硬说 "CLI 取代 MCP" 本身就有问题。正确表述应该是:
"在 AI 生产环境下,优先用 CLI 工具调用取代 MCP 协议调用。"
本质上讨论的是"调用方式"的问题,而不是谁替代谁
以目前来看,接下来的图景大概是这样:
本地开发环境 → CLI + Skills,轻量、快速、上下文干净。
云端生产环境 → MCP + Skills,标准化、跨平台、认证完备。
简单场景 → 直连 API,别瞎折腾。
所以,MCP 并没有死。至于上下文膨胀的问题,官方也已经在着手解决了。
它当然并非万能方案,但它正在成为云端 Agent 的标准化接入层。
核心就一句话:因地制宜,因材施教,合适的就是最好的
8. 附录:Agent 调用示例
Python 调用 mycli
~/ai-agent
import subprocess
import json
def call_mycli(command: str, args: list[str]) -> dict:
"""调用 mycli 并解析 JSON 输出"""
result = subprocess.run(
["mycli", command, "--json"] + args,
capture_output=True, text=True
)
if result.returncode == 0:
return json.loads(result.stdout)
else:
return {"success": False, "error": result.stderr}
# 使用示例
result = call_mycli("calc", ["2 ** 10"])
print(result["data"]["result"]) # 1024
Go 调用 gocli
~/ai-agent
package main
import (
"encoding/json"
"os/exec"
)
func callGocli(command string, args ...string) (map[string]interface{}, error) {
cmd := exec.Command("./gocli", append([]string{command, "--json"}, args...)...)
output, err := cmd.Output()
if err != nil {
return nil, err
}
var result map[string]interface{}
json.Unmarshal(output, &result)
return result, nil
}
func main() {
result, _ := callGocli("calc", "2 ** 10")
data := result["data"].(map[string]interface{})
println(data["result"].(float64)) // 1024
}
10. 参考资料
文档生成时间:2026-04-24