论Design for Agent(一):API、CLI 与Tools

过去十五年,开发者体验(DX)基本只意味着一件事:为坐在终端前的那个人做优化——可读的报错、可交互文档、七种语言示例。

但这个假设正在改变。

  • Cloudflare 数据显示,自动化 Bot 流量首次超过了人类流量,2025 年初基于 RAG 的 Agent 流量单季度增长 49%。
  • 当开发者对 Cursor 或 Claude 说一句「帮我加上 Stripe 支付」,Agent 会自己抓文档、挑 API、写集成代码、调试报错——开发者甚至还没有看到任何一行代码。
  • Ramp 报告,过去三个月内通过 Claude、ChatGPT 等 Agent 进入产品的 MCP 周活用户增长了 10 倍;Salesforce 干脆推出 Headless 360,把整个平台暴露成 API/MCP/CLI,让 Agent 不打开浏览器就能完成全部操作。

这意味着出现了一类新的API消费者:Agent。

面向Agent的这一层新的设计面,Stripe称之为Agent Experience(AX)——不是替代 DX,而是叠在它上面的下一层。

刚好最近一直在研究Application Design for Agent ,所以借这个机会整理一下基于Anthropic和Stripe等公司的技术分享,AX设计的几个关键要点。


第一层:构建适合 Agent 使用的 API

没有一份好的 API 描述,所有上层(MCP、Skills、CLI)的努力都是在补漏。

1. OpenAPI 描述:Agent 路由的「语义信号」

当 Agent 决定该调哪个 endpoint,它做的事情接近一次针对你的描述文档的语义检索:读 spec → 把用户意图与可用 operation 做匹配 → 挑一个最接近的。描述写得好不好,直接决定它能不能选对。

对比两份描述的差距:

Gets the dataReturns a paginated list of invoices filtered by status and date range, sorted by created_at descending. Requires accounting:read scope. Use the cursor parameter for pagination.

每一个字段、每一个枚举值、每一个 endpoint 的描述,都是 Agent 用来路由的信号。如果 spec 不能用来做契约测试,那就意味着对 Agent 也一样不可用。

Stripe 的实用建议:
  • 先把最重要的 10 个 endpoint 逐字段、逐参数地补完描述
  • 明确每个枚举值的含义、缺省行为、依赖的 scope
  • 标清楚 deprecated: true 并指向替代方案——废弃 API 是一个会随时间累积变糟糕的 AX 问题

2. 错误响应:把「失败路径」变成 Agent 的「决策树」

Agent 与人类处理错误的方式根本不同:

  • 人类:读错误 → 理解 → Google → 回来修
  • Agent:读错误 → 必须在同一个执行上下文里当场决定下一步

如果错误模糊,Agent 要么瞎猜,要么直接挂掉。所以有两件必要的事:

  1. 加上 documentation_url / doc_url——Stripe 多年前就做了,几乎零成本,却给了 Agent 自主拉文档、自我纠错的能力。
  2. 加上机器可读的恢复元数据——is_retriableretry_after_secondsalternative_action 这些字段把错误从「失败模式」变成 Agent 可自主导航的决策树。

错误描述的具体性同样关键:

Invalid requestThe 'amount' field cannot be emptyYou passed payment_method_types: ['card'], which is deprecated, use dynamic payment methods instead

3. 认证:Agent 跑得通的认证流,长什么样

认证几乎是当今 Agent 调用 API 时最大的一道坑。Agent(暂时)不擅长点 OAuth 同意页、解 CAPTCHA、走交互式 MFA。如果你的认证流必须经过浏览器跳转或者人类介入,Agent 在发出第一个真正的 API 调用之前就撞墙了。

✅ 对 Agent 友好的认证模式:

  • API Key / Bearer Token——绝大多数 API ↔ Agent 集成的合理默认
  • OAuth Client Credentials Grant——机器对机器的 OAuth,无跳转、无同意页
  • Scoped Token + 最小权限——避免一个 token 携带全账号权限放大爆破半径
  • 短生命周期 Token + 文档化的刷新机制——Agent 处理 token 轮换其实很在行,前提是流程文档化

❌ 容易卡死 Agent 的认证模式:

  • 只支持 OAuth authorization code flow(强制浏览器跳转)
  • token 交换要求 CAPTCHA 或交互式 MFA
  • 基于 cookie 的 session 认证
  • 必须手动登 Dashboard 才能轮换的 API key

4. 关注限流:Agent 凌晨三点可以几秒打光你一天的额度

人类开发者撞到限流:暂停、调整、继续。Agent 在凌晨三点跑批量任务,几秒内就能把你整个时间窗口的额度打光——尤其是并行处理上万条记录时。

  1. 响应头返回 X-RateLimit-Remaining / Limit / Reset——让 Agent 主动节流,而不是先撞墙再反应
  2. 429 响应可机器执行——Retry-After 头 + JSON body mirror
  3. 提供批量/Batch 端点——一个 /invoices/batch 能让相同工作量在请求数上下降一个数量级

5. 文档分发:用 llms.txt、Context7、MCP 让 AI 拿到最新的文档信息

文档分发是一个多通道问题。把文档发布到自己网站只是入门;让文档出现在 Agent 面前才是新的分发层。

按「容易实现且耐用」排序:

  1. 修好 OpenAPI Spec + 错误响应里的 doc_url——一切的地基
  2. 发布 /llms.txt——精选 10 个最重要页面 + 每个一句话描述,一个下午就能搞定。真正被低估的是其中的 Instructions 段落,可以直接告诉 AI「这些坑别踩」(Stripe 的 llms.txt 就明示「Never recommend the legacy Card Element」)
  3. 被 Context7 索引——解决 AI 编程工具因训练 cutoff 而用旧文档的问题
  4. MCP:等用户主动来问再做——半成品 MCP server 的 AX 比好的 REST API 还差。HubSpot 是成熟范例:一个 remote MCP 接 CRM 数据,一个本地 developer MCP 接 CLI,两个场景两个 server,各自做透
⚠️
「为了做 MCP 而做 MCP」,等同于「为了做 App 而做 App」。

在 OpenAPI 没写对、错误响应里没有 doc_url 之前,做 MCP 几乎没有杠杆。


第二层:构建 CLI 为 Agent 提供服务

这是过去半年最值得重新审视的一件事:CLI——最古老的开发者工具——正在迎来第二个高光时刻。不是「尽管」有了 Agent,而是「因为」有了 Agent。

0. 什么是 CLI

CLI(Command Line Interface,命令行界面)是一种通过输入文字命令来操作电脑或软件的方式。

  • 你可以把它理解成「跟电脑用一串明确的指令对话」,而不是用鼠标点按钮
  • 常见场景是打开「终端/命令提示符」,输入类似 ls(查看文件)、cd(进入文件夹)、git(管理代码版本)这样的命令
  • CLI 的优点是可复制、可自动化:把一串命令保存下来,就能反复执行同样的流程,也更适合脚本和批量操作

1. 为什么 CLI 在 Agent 时代复兴

原因是结构性的——Agent 是 terminal-native 的:

  • 它们天然会跑 --help
  • 会用 pip / npm 装包
  • 会用管道串联工具
  • 会解析输出

它们不需要为你的服务做一层定制集成——直接就用。

Karpathy 演示过:Claude 装了新的 Polymarket CLI,3 分钟搭出了一个展示成交量最高预测市场及 24 小时价格变化的终端 Dashboard。再配上 GitHub CLI,Agent 就能在一条自治流水线里导航 repo → review PR → 基于真实信号采取行动——不需要任何自定义集成层,不需要 MCP server,什么都不用维护。

Simon Willison 总结得很尖锐:几乎所有你想用 MCP server 实现的事,都可以用一个 CLI 来做——因为 LLM 本来就会调 cli-tool --help

2026 年 3 月,Google 用 Google Workspace CLIgws)正式印证了这条思路:

  • 默认结构化 JSON 输出——Agent 不必自己 hack 解析
  • 自描述子命令——Agent 跑 --help 就能渐进式发现能力
  • 预配置 OAuth——认证不再依赖浏览器交互
  • 命令面通过 Discovery Service 运行时动态生成——Google 加新 endpoint,gws 自动跟上
  • 预置 100+ 个 Skill,覆盖 Gmail/Drive/Calendar 常见工作流

更重要的是,一个 gws 命令消耗的 token,只是同等功能 Workspace MCP server 的一小部分。

为什么?

差别来自「上下文加载方式」和「输出处理方式」两端的结构性不同。

1. 工具定义的加载方式:预加载 vs 按需发现

MCP server 上所有工具的 schema(名字、描述、参数、类型、枚举值……)必须在会话开始时全部预加载进 context window,Agent 才能知道有哪些工具可调。Workspace 那种覆盖 Gmail / Drive / Calendar / Docs / Sheets…… 的 MCP,工具数量动辄上百,光是工具定义本身就能吃掉几万 token。

CLI(以 gws 为例):Agent 只需要先知道「有一个叫 gws 的命令」。具体子命令、参数怎么用,是它跑 gws --helpgws gmail --help 时才渐进式获取的。不用的能力,不进 context。

这是最大的一块差异——MCP 是「把整本说明书塞进脑子再开始干活」,CLI 是「需要哪一页翻哪一页」。

2. 返回数据的体量:结构化 JSON vs 可裁剪输出

MCP 工具的返回通常是完整的结构化 JSON:一个 Gmail 消息会带上 message_id、thread_id、label_ids、history_id、internal_date、payload headers、MIME 树……大量字段对当前任务没有信号但必须穿过模型。

CLI 的输出是流式文本,Agent 可以在 shell 层就用 | jq| grep| head-fields=subject,from 之类先过滤再嗂给模型。gws 默认 JSON 输出也支持字段裁剪。

这正是 Anthropic 在工具设计里反复强调的:优先返回高信号、低 token 的语义化字段,光是把 UUID 换成可读名字就能显著省 token。

3. 工具粒度:CRUD 一比一 vs 任务级聚合

很多 Workspace MCP 是把 Google API 一比一包装:messages.listmessages.getmessages.modifylabels.list…… 完成「整理一下今天客户邮件」可能需要串好几次工具调用,每次都带上一整轮工具调用的 overhead(参数 + 返回 + 思考)。

CLI 倾向任务级命令:gws gmail search "from:client after:today" --json 一条命令搞定。调用次数少 → token 累计也少。

4. 协议/会话开销

MCP 每次调用都伴随 JSON-RPC 协议层的 envelope、错误结构、能力声明等元数据;CLI 就是「命令 → stdout」,没有这层包装税。

「同等功能」时,CLI 把「能力定义」「数据传输」「调用粒度」三件事都做得更紧凑。而且应用方不需要为任何一家 AI 厂商定制集成就能直接被使用。

2. 一个 CLI 「对 Agent 友好」的检查表

参考 Google gws 和 Karpathy 的归纳,几件让 CLI 真正对 Agent 友好的事:

  • 提供 --json flag,或默认 JSON 输出——避免 Agent 解析人类可读字符串
  • 子命令遵循 tool noun verb 风格(如 gh repo listgh pr view),让 --help 一目了然
  • 用环境变量做认证——Agent 不需要交互式输入凭据
  • 错误信息说清楚出了什么问题,而不是只甸一个 exit code
  • 自描述能力——一个好的 --help 输出,让 Agent 第一次遇到也能渐进式探索

这正是 REST API 不天然具备、CLI 却天然具备的优势:Agent 第一次碰到 gh --help 自己就能搞清楚相关子命令;Agent 第一次遇到一个没有描述的 REST API,直接撞墙

3. CLI vs MCP:方向相反的两条触手

很多人会问:CLI 和 MCP 到底什么关系?一句话讲清:

CLI 是给「人」用的入口,朝外;MCP 是给「Agent」用的接口,朝里。
维度CLIMCP
本质用户界面 + 运行容器通信协议
位置Agent 的外壳(用户那一头)Agent 的触手(外部系统那一头)
解决什么问题让用户能用上 Agent让 Agent 能用上外部服务
对 token 的影响低——子命令按需展开高——工具定义需预加载
典型例子Claude Code、Codex CLI、gwsSlack MCP、GitHub MCP、Notion MCP

对应用方的战略读法不是「CLI vs MCP 二选一」,而是「CLI 正在成为基础接口,MCP 在它之上、在合适的地方再叠加」。


第三层:构建有效的工具为 Agent 提供能力

这一层是 Agent 真正「动手」的最小单位。Anthropic 在《Writing effective tools for AI agents》里是这样定义的:

工具是一类全新的软件——它是「确定性系统」和「非确定性 Agent」之间的契约

传统软件你写的是「确定性系统对确定性系统」的契约:getWeather("NYC") 永远以相同方式取 NYC 天气。

但当用户问 Agent「我今天要不要带伞?」,Agent 可能:

  • 调用 weather 工具
  • 凭通识直接回答
  • 反过来追问位置
  • 偶尔甚至会幻觉、误用工具

这意味着:为 Agent 写工具,不能再按「给开发者写函数」的方式写——要重新思考。

1. 选对要做的工具(比做更多更重要)

常见的错误:把已有的每个 API endpoint 一比一包装成一个 tool——不管它适不适合 Agent。这忽略了一件事:Agent 的「上下文」是有限的稀缺资源,而程序内存几乎无限。

经典例子:在地址簿里查联系人。

  • list_contacts()——让 Agent 把所有联系人 token-by-token 读一遍,像从头翻到尾找电话簿
  • search_contacts(name="Jane")——直奔主题

更进一步,让一个工具承担多个步骤的合理组合:

  • 与其 list_users + list_events + create_event 三件套,不如直接给一个 schedule_event
  • 与其 read_logs,不如 search_logs(只返回相关行 + 上下文)
  • 与其 get_customer_by_id + list_transactions + list_notes,不如 get_customer_context

这正是 Stripe 在做 MCP 时的「精选」哲学:与其把 200 个 endpoint 暴露成独立 CRUD 工具把 Agent 的 context 撑爆,不如蒸馏成 5–30 个围绕业务结果的「块状」工具——把对账、报销、订阅等场景的调用链好好编排起来。这就是「给 Agent 一袋 HTTP 动词 vs. 给它可被推理的能力」的差别。

2. 命名空间:用前缀区分能力边界

当 Agent 同时拿到几十个 MCP server 和几百个工具时,重名 / 含糊的工具描述会直接让它选错。命名空间是低成本高收益的解法:

  • 按服务:asana_search vs jira_search
  • 按资源:asana_projects_search vs asana_users_search

前缀 vs 后缀命名风格在评测里有非平凡的差异,建议用自己的 eval 决定。

3. 返回有意义的上下文(而不是 UUID 海洋)

工具实现应当只把高信号信息返还给 Agent——优先「上下文相关性」,少给低层级技术标识(uuid256px_image_urlmime_type)。nameimage_urlfile_type 这类字段对 Agent 的下游行动直接有用。

Anthropic 自己的经验是:仅仅把神秘 UUID 替换成语义化名称,就能显著提升 Claude 在检索任务上的精度、减少幻觉

更优雅的做法是给工具加一个 response_format 参数:

enum ResponseFormat {
   DETAILED = "detailed",  // 含 channel_id / user_id 等 ID
   CONCISE  = "concise"    // 只返回内容
}

Slack 工具的示例显示,concise 响应可以用大约 1/3 的 token 完成相同任务。

4. 为 token 效率而设计

上下文不是免费的。要为「响应可能很大」的工具内置默认参数:

  • 分页 / range 选择 / 过滤 / 截断
  • Claude Code 默认把工具响应限制在 25,000 token 以内
  • 如果截断,显式告诉 Agent 用更精准的多次小搜索,而不是一次大搜索

错误响应也是 prompt——好的错误响应能让 Agent 自我纠错:

  • Invalid date
  • Date '2022-01-01' is in the past. Please provide a future date.

这条原则与第二节 API 错误设计完全同源:好的错误信息不仅告诉 Agent「出错了」,还告诉它「错在哪」以及「如何修复」

5. 把工具描述当 Prompt Engineering 来做

Anthropic 的判断是:Prompt-engineering 你的工具描述和 spec,是提升工具最有效的方法之一。Claude Sonnet 3.5 在 SWE-bench 上拿到 SOTA 的一个关键,就是对工具描述做了精确打磨,错误率显著下降。

实务建议:

  • 想象你在给一个新入职同事解释这个工具——他不知道你脑里那些隐含语境
  • 参数命名要消歧义:user ❌ → user_id
  • 用严格的数据模型把输入输出锁死,避免歧义
  • 把专有术语、查询格式、资源间关系显式写出来

6. 整套打磨方法:评测驱动 + Agent 协作

Anthropic 给出的工作流非常值得照搬:

  1. 快速跑 prototype——用 Claude Code 一次性把工具脚手架搭出来,包成本地 MCP server 或 DXT,先在 Claude Desktop 里手感测一下
  2. 构建评测——基于真实工作流生成几十个 task(甚至几十次工具调用一个 task),每个配一个可验证的预期输出
  3. 跑 eval 收指标——准确率 + 总 token + 调用数 + 错误数 + 单次工具运行时长
  4. 让 Agent 自己复盘——把评测 transcript 嗂回 Claude Code,让它指出哪些描述歧义、哪些字段冗余、哪些常见调用模式应该合并
  5. 守住 held-out test set,避免对训练 eval 过拟合

这件事的根本心智模型是:软件开发实践需要从「可预测的确定性」转向「面向非确定性的迭代评估」


选择正确的实现顺序:从 Markdown 到 MCP

Karpathy 给企业的归纳同样适用于 API 公司——按「容易实现且耐用」排序,让你的产品对 Agent 可用的层级是:

Markdown 文档 → Skills → CLI → MCP

每一层都在前一层基础上复利:

  1. Markdown 文档(OpenAPI 描述 + documentation_url + /llms.txt + Context7)——让 Agent 能读懂你
  2. Skills(机构知识层)——让 Agent 以你的方式做你的事;与 MCP 互补:MCP 是能力层,Skill 是知识层
  3. CLI(执行面)——Agent 能立刻安装、立刻探索,完全不需要和任何 AI 厂商协调
  4. MCP(标准化连接层)——等用户主动来问、出现清晰集成模式时,再针对具体场景做

上下文缺口与反馈循环:服务同一个用户

最后一个常被忽略、但极其本质的观点来自 Ramp:

在任何 Agent 交互中,你的系统掌握一些调用方智能体不知道的上下文;调用方智能体也掌握一些你的系统不知道的上下文。设计这些交互时,你应该清楚地判断:哪一方在哪些信息上更有优势。

比如一个差旅报销场景:

  • 用户的 AI 幕僚知道:日历、邮箱里的酒店/航班、Slack 里的客户邀约、收据照片
  • 费用管理系统知道:原始交易、公司报销政策、GL 科目、历史归类习惯

传统 API 会把问题丢回给用户:「这里有一笔交易需要填写 GL code,请从 150 个里挑一个。」

设计良好的 Agent ↔ Agent 交互会反过来:不索要 GL code,而索要上下文——「这是客户餐、团队餐还是个人?」用户的 Agent 从日历或 Slack 里查到答案,费用系统再据此自动套用科目。Diego 和他的 Agent 都不需要知道 GL code 是什么。

承认你的 Agent 在某些事情上不擅长是完全可以的——因为你们其实在服务同一个用户

这件事的最后一层落地是反馈循环。Ramp 的做法很值得拄:

  1. 每次工具调用要求 Agent 填一个 rationale——你看不到聊天内容,但能从理由日志重建用户意图
  2. 提供一个独立的 feedback 工具——Agent 卡住时可以用它告诉你它原本想做什么、试了什么、卡在哪
  3. 在特定工具里加上下文种子参数——把以后会有用的上下文一并捕获

当你反复在理由日志里看到「正在生成事故报告」「正在起草事故摘要」时——这就是一个新功能的信号。Agent 比真人用户更具体、更一致地告诉你下一步应该构建什么。


写在最后

DX 是「为人类移除摩擦」。AX 是把同样的纪律,应用到「无法主动求助、也无法适应你 API 把它带去意外地点」的自治消费者身上

如果把这篇文章浓缩成三句话:

  1. API 层:先把 OpenAPI 描述、错误响应(带 doc_url + 恢复元数据)、机器友好的认证和限流做对——这是地基,没有这一层,上面所有努力都是补漏。
  2. CLI 层:CLI 在 Agent 时代正在复兴。一个默认 JSON、子命令可组合、--help 写得好、用环境变量认证的 CLI,杠杆往往比同等规模的 MCP server 更高——因为 Agent 本来就会用 CLI。
  3. 工具层:工具不是 API 的 1:1 包装,而是为非确定性消费者重新设计的契约。选对要做的工具 > 做更多工具;让工具响应回传高信号、低 token、语义化的上下文;用评测驱动迭代,把工具描述当 prompt engineering 来做。

那些过去让 API 对开发者「好用」的特性——具体、语义一致、错误友好——在没有人类在回路中为模糊性兜底时,只会更加重要。

好消息是:让你的 API 对机器好懂的方法,和让它对一个不熟悉你系统的新人开发者好懂的方法,本质上是同一件事——从这里开始就好。

参考资料

如果这篇文章对你有帮助,欢迎点个赞 :)