近几年,许多团队把“把所有功能做成 CLI,让 AI agent 去跑”当成默认方法:Agent 在终端里跑命令、解析输出、根据 exit code 重试、再把结果写回。不过实践证明,CLI 虽然在快速原型阶段方便,但作为长期、可扩展、可被 agent 安全调用的「工具契约(tool contract)」时,存在不少隐患和工程成本。本文讲清原因,并给出把 CLI 漸进迁移成 API 的实务建议与样例代码。


核心观点(Summary)

  1. CLI 的天然优点:易上手、实现快、对人类开发者友好;agent 很容易通过 shell 拼命令并运行。

  2. 但 CLI 的弱点很重要:非结构化文本、易受输入注入、错误语义不稳定、难以做版本与权限控制。

  3. API(REST/HTTP/gRPC)更适合长期服务化:类型化输入输出、机器可解析、可做授权、限流与可观测。

  4. 最佳实践:先把最关键的能力用 API 包成“机读契约”,同时保留 CLI 做为便捷入口;或在 CLI 内部调用 API、把 CLI 作为 API 的轻薄封装。


为什么 CLI 对 agent 看起来很“自然”但并不可靠

1) 文本解析 vs 结构化输出

CLI 常输出纯文本(human-friendly),agent 需要解析自然语言或自行写正则来提取数据,这对 LLM 来说增加了出错面。相反,JSON / protobuf 等结构化响应,能被 agent 直接消费与验证。

2) 注入与安全风险

当 agent 把用户输入直接拼到命令里,会发生类似 shell 注入的问题(LLM 生成意外参数或恶意 payload),这种风险在 CLI 环境里更严重。把功能暴露为接口可以在服务端集中做参数校验与速率限制。

3) 可观测性、权限与错误语义不足

CLI 退出码与 stderr 信息远不能替代 HTTP status、错误码、trace id、指标与审计日志。对于生产级 agent 执行,运维/安全团队需要更多可追溯的信息。API 更容易接入监控与鉴权体系。


架构上的建议:API-first,但保留 CLI(可选)

推荐两条工程路线(按成熟度):

  1. API-first(推荐)

    • 先把核心能力做成 REST/gRPC API(类型化请求/响应、示例、错误码、版本化)。

    • Agent 与前端都调用这些 API;把 CLI 做成 API 的轻量封装(CLI 内部仍向 API 发请求)。

    • 好处:统一契约、便于安全与监控、便于自动化工具生成(如从 OpenAPI 生成 client)。

  2. 渐进式迁移(从 CLI 到 API)

    • 现有是成熟 CLI:在 CLI 里增加 --json 输出、稳健的 exit code、严格参数校验;同时在后台逐步抽出业务逻辑为服务并暴露 API。

    • Agent 先使用 --json 的 CLI 输出,随后切换到 API 以获得更好健壮性。


具体对比:CLI vs API(快速清单)

  • 可机读性:CLI(差) ← JSON 输出可改善 → API(强)

  • 安全(注入、权限):CLI(易受攻击) → API(集中鉴权更好)

  • 错误处理:CLI(靠 exit code 和 stderr) → API(HTTP status + 业务错误码)

  • 可监控性:CLI(本地难埋点) → API(内置 tracing / metrics)

  • 版本/兼容性:CLI(flag 变化碎片化) → API(语义版本控制更明确)


实战示例:把 repo 操作从 CLI 迁移到 API

下面以“查询 PR 列表并合并”为例,示范 CLI 使用与 API 使用的对比。

1) 现有 CLI(人类/agent 可能这样调用)

# agent 拼命令并解析输出(不稳定)
gh pr list --repo acme/project --state open
gh pr merge 123 --merge

缺点:输出是表格/文本;合并失败时信息不结构化;agent 需要解析并推断下一步。

2) 推荐的 API(JSON + 结构化错误)

请求:

curl -X POST "https://api.acme.com/v1/prs/merge" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"repo":"acme/project","pr":123,"strategy":"merge"}'

响应(成功):

{
  "status": "ok",
  "merged": true,
  "merge_commit": "abc123",
  "trace_id": "req-20260225-xyz"
}

响应(失败):

{
  "status": "error",
  "code": "merge_conflict",
  "message": "Merge conflict with file X",
  "hint": "Run 'refresh-branch' then retry",
  "trace_id": "req-20260225-xyz"
}

好处:agent 可以基于 code 做分支逻辑(比如遇到 merge_conflict 则去 rebase),并把 trace_id 传给运维以便追踪。


迁移与实作步骤(工程清单)

  1. 能力梳理:列出目前 CLI 提供的所有功能(子命令与 flags)。

  2. 确定契约:为每个关键功能设计 API 输入/输出 schema(JSON Schema / protobuf),包含错误码与示例。

  3. 实现服务端:把业务逻辑从 CLI 抽离到可复用的服务层(library),并用 HTTP/gRPC 包装。

  4. CLI 适配:让 CLI 内部调用新 API(而不是重复实现),同时保持 --json 输出兼容旧 agent。

  5. 测试与兼容:并行发布、为 agent 制定迁移计划(比如先切到 --json,再切到 API)。

  6. 文档与示例:提供 OpenAPI、示例请求/响应、速查表与错误处理指南,方便 agent 或工程师接入。


给 Agent 开发者的实践建议(Agent 端)

  • 偏好结构化调用:当你控制 agent 的 instruction 时,优先调用 POST /v1/... 类型的 API,避免直接通过 shell 拼接复杂命令。

  • 参数验证:agent 在发送请求前尽量做本地校验(类型、必填项),同时在收到错误码时按错误码逻辑分支(不要只看 message 文本)。

  • 幂等与重试策略:设计 API 时明确哪些是幂等(GET/PUT)并为非幂等操作提供幂等 key,agent 重试时使用相同 key。

  • 审计与可控:对可能产生破坏性操作(deploy、delete)强制二次确认或设置审批流程,而不是直接开放给 agent 无限制调用。


常见反对与反驳

反对:我需要快速原型,API 太慢。
原型阶段 CLI 确实快,但生产化或多人协作时,API 的长期成本远低于维持一堆 brittle CLI 的代价。建议:原型用 CLI,但设计时同步暴露 API(或把 CLI 做成 API 的 thin wrapper)。

反对:我们已有大量 CLI,迁移成本高。
可采用渐进式迁移:先增加 --json、标准 exit code、并把内部逻辑抽到 library,随后逐步启用 API。这样风险最小,交付可控。


总结:把“可用给 Agent 的东西”当成产品来设计

CLI 是极好的开发工具,但把它当作长期对 agent 的「接口」容易出问题。对 agent 最友好的接口不是任凭 agent 去解析自然语言,而是清晰、类型化、可验证的契约 —— 也就是 API。把关键能力先做成 API,再用 CLI 做外部封装,既能兼顾开发效率,也能保证安全、可运维与长期演进性。

Logo

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

更多推荐