如何设计Agent友好的API/CLI?

过去我们设计 CLI 和 API,默认使用者是人,或者是一段已经写好的程序。
人会看文档,会理解业务,也知道 customer.owner = alice 的实际含义是“把这个客户分配给 Alice”。程序则更简单,工程师提前把逻辑写死,它只负责执行。
但是Agent 不一样。Agent 需要在运行过程中自己理解系统有什么能力,判断应该调用什么,填写参数,观察结果,遇到失败还要决定下一步怎么办。
所以,真正对 Agent 友好的 CLI 或 API,不只是“支持 JSON”,也不只是“接入 MCP”,而是要让 Agent 能稳定完成一个闭环:
Discover → Understand → Act → Verify → Recover,
这五步基本可以解释 Agent-friendly interface 的全部核心。
1. Agent更容易发现能力:Discover
Agent 首先要知道:这个系统到底能做什么。
传统 CLI 往往只有:
tool --help
这主要是给人看的。然而,Agent 需要的是机器可读的能力目录,例如:
crm capabilities --json
返回:
{
"actions": [
"search_customer",
"assign_customer_owner",
"activate_customer",
"close_opportunity"
]
}
它不应该先读几十页文档,再猜哪个命令能完成任务。
API 也是一样。传统 API 往往从资源出发:
GET /customers
PATCH /customers/:id
POST /orders
人知道这些 endpoint 背后的含义,但 Agent 真正关心的是:
“我能不能分配客户?”
“我能不能关闭商机?”
“我能不能取消订单?”
所以对 Agent 来说,更好的能力暴露方式是直接告诉它:
assign_customer_owner
close_opportunity
cancel_order
这就是 Discover 的关键:能力要显式,而不是隐藏在数据结构和文档里。
2. Agent更容易理解能力:Understand
发现一个动作以后,Agent 还必须知道:这个动作到底意味着什么。
比如一个接口叫:
update_customer(id, value)
虽然参数很简单,但对 Agent 并不好。
因为它不知道 update 什么,也不知道 value 代表什么,更不知道调用以后会产生什么副作用。
如果换成:
assign_customer_owner(
customer_id,
owner_id
)
语义就完全不同了。
Agent 可以直接理解这是:“把一个客户分配给某个销售负责人。”
再进一步,一个更好的 Action Contract 还应该告诉它:
intent:
Assign a customer to a sales representative.
preconditions:
customer.status: active
owner.status: active
effect:
customer.owner_id changes
过去 API schema 主要回答:**参数是什么类型。**Agent 时代还必须回答:**这个动作什么时候应该用,会发生什么。**也就是说,schema 开始从“类型系统”变成“语义系统”。
3. Agent更容易执行能力:Act
Act 执行过程中的关键是Agent 实际执行的动作,应该尽量贴近业务意图。
举一个最典型的例子。假设系统里有一个客户:
{
"id": "cus_123",
"name": "Acme",
"status": "active",
"owner": "bob"
}
现在希望把它分配给 Alice。传统 REST API 很可能是:
PATCH /customers/cus_123
传入:
{
"owner": "alice"
}
这里的 PATCH 意思是:修改 customer 资源的一部分字段。
对程序员来说,这很自然。因为程序员知道:
修改 owner 字段
=
把客户分配给 Alice
但 Agent 要自己完成一次额外推理:
我要把客户分配给 Alice
↓
分配客户意味着修改 customer
↓
要修改的是 owner 字段
↓
owner 填 Alice 的 ID
↓
调用 PATCH customer
真正对 Agent 友好的接口可以直接是:
assign_customer_owner(
customer_id="cus_123",
owner_id="alice"
)
于是整个过程变成:
我要把客户分配给 Alice
↓
assign_customer_owner
底层可能仍然只是执行一次数据库更新,区别只在于业务语义被接口承担了,而不是被 Agent 推理出来。这就是为什么 Agent 时代很多接口会从 Resource-centric 逐渐走向 Action-centric。
过去喜欢:
PATCH customer
PATCH order
PATCH invoice
未来更适合 Agent 的是:
assign_customer_owner
cancel_order
approve_invoice
前者描述“修改什么数据”。后者描述“完成什么事情”。
这并不意味着传统 REST API 不好了。完全可以同时存在两层:
底层:
PATCH /customers/:id
Agent 层:
assign_customer_owner
就像我们不会让 Agent 为了转账,自己执行:
UPDATE account_a ...
UPDATE account_b ...
而更愿意给它一个:
transfer_money(...)
因为事务、权限、约束和正确性应该尽量由系统承担,而不是交给 Agent 自己拼。
4. Agent更容易验证结果:Verify
完成 Act 以后,下一步是 Verify。
这一层特别容易被忽略。很多 CLI 做完事情以后只返回:
Success!
对人类程序员来说似乎够了。但 Agent 会继续产生几个问题:
到底改了什么?
真的发生变化了吗?
之前是什么状态?
现在是什么状态?
所以更好的返回应该是:
{
"status": "success",
"customer_id": "cus_123",
"previous_owner": "bob",
"new_owner": "alice",
"changed": true
}
这样 Agent 一次调用以后,就能确认结果。
一个非常重要的原则是:
Agent 调用 write action 后,最好不用再额外调用一次 read action,只为了确认刚才是否成功。
也就是说,执行结果本身应该具有可验证性。
CLI 也一样。一个 Agent-friendly CLI 最好明确区分:
stdout = 结构化执行结果
stderr = 日志和诊断信息
exit code = 成功或失败状态
而不是把进度条、warning、日志和最终结果全部混在一起。
5. Agent如何从错误中恢复执行:Recover
这一层可能是 Agent-friendly API 和普通 API 差别最大的地方。
传统错误定义经常是:
{
"error": "Invalid request"
}
或者:
{
"error": "Customer status invalid"
}
人看到以后会去看文档。Agent 看到以后只能继续猜。
对于Agent更好的错误定义和描述应该是:
{
"code": "CUSTOMER_NOT_ACTIVE",
"message": "Owner can only be assigned to an active customer.",
"retryable": false,
"required_action": "activate_customer"
}
这样 Agent 可以立即形成下一步:
assign_customer_owner
↓
CUSTOMER_NOT_ACTIVE
↓
activate_customer
↓
assign_customer_owner
这时候错误信息就不再只是“报错”,它实际上是 Agent 执行路径的一部分。
所以 Recover 的核心并不是“错误写得更详细”,而是:错误必须可指引行动。
Agent 应该知道:
为什么失败;
能不能重试;
缺什么前置条件;
应该调用哪个动作恢复。
如果把这五层连起来看,一个真正 Agent-friendly 的接口就非常清晰了。
Discover
系统能做什么?
↓
Understand
每个动作是什么意思?
↓
Act
用最接近业务意图的动作执行
↓
Verify
执行以后明确知道发生了什么
↓
Recover
失败以后知道下一步怎么走
它最终形成的就不只是 API Schema,而是一份完整的 Agent Action Contract。
例如:
name: assign_customer_owner
description:
Assign a customer to a sales representative.
input:
customer_id: string
owner_id: string
preconditions:
customer.status: active
owner.status: active
effect:
customer.owner_id changes
idempotent:
true
output:
customer_id: string
previous_owner_id: string
owner_id: string
changed: boolean
errors:
CUSTOMER_NOT_ACTIVE:
retryable: false
next_action: activate_customer
OWNER_NOT_ACTIVE:
retryable: false
next_action: select_active_owner
这里实际上已经把 Discover、Understand、Act、Verify、Recover 都包含进去了。
name 和 description 帮助 Discover 和 Understand。
input、preconditions、effect 定义 Act。
output 支撑 Verify。
errors 和 next_action 支撑 Recover。
6. 总结:Agentic Action Contract
所以我越来越觉得,未来 CLI、REST API、SDK 和 MCP 不应该被完全分开设计。真正的 one source of truth 可以是一份统一的 Action Contract。
比如系统先定义:
assign_customer_owner
然后由它生成:
CLI:
crm customer assign-owner
HTTP:
POST /customers/:id/assign-owner
SDK:
crm.assignCustomerOwner(...)
MCP:
assign_customer_owner
表面上是四种接口。本质上是同一个业务动作。这是一种很深的变化。过去的软件接口主要围绕:**数据模型。**Agent 时代的软件接口,会越来越围绕:能力模型。
过去我们问:
“有哪些资源,我可以怎样修改它?”
未来更重要的问题会变成:
“这个系统到底能完成哪些动作?”
而判断一个 CLI 或 API 是否真正对 Agent 友好,也可以直接沿着这五步去看:
Agent 能不能发现能力,
能不能理解语义,
能不能低风险执行,
能不能确认结果,
失败以后能不能继续。
如果这五步都成立,那么这个接口系统就不只是“提供了 API”。
它开始真正成为一个 Agent 可以工作的环境。
