为 Agentic 客户端设计 API 规格

为 Agentic 客户端设计 API 规格
Spec Coding 编辑部 · Spec-First 工程实践内容

大多数 API 规格是写给人类开发者的——他们会认真读文档、从上下文推断意图,遇到危险动作会停下来想一下。智能体客户端什么都不会做。如果你的规格假设读者是一个谨慎的人,那么 LLM 驱动的 agent 会精准找到其中的歧义,然后毫无迟疑地在 400 次循环里直接撞上去。

发布于 2026-03-10 · ✓ 已更新 2026-05-06 · 阅读约 6 分钟 · 作者:Spec Coding 编辑部 · 审校:编辑与事实核查政策

内容复查说明

复查日期:2026-05-06。本文已作为可索引的专题参考重新放出,并与 AI 编码治理 Hub 形成内链。内容保留了可复用的评审材料、失败模式和下一步阅读路径,适合直接用于实际团队讨论。

调用方不再是人

我花了十年时间给人类写 API 文档。一个叫 id 的字段写成这样就够了,因为任何开发者读到它,都会顺手跑一条 curl 示例,把格式猜出来。把同样的字段摆在 agent 面前,前十次调用里你就会看到三种不同的幻觉形态:UUID、整数、模型根据资源名瞎编出来的邮箱地址。调用方是一个统计模式匹配器,它对"猜"这件事毫无心理负担。仅此一个变化,就让我们过去依赖的大多数隐性约定全部失效。

我现在默认假设,每一个参数都会被某个东西读到,而只要描述写得不够,它就会自信地编一个值。这个假设改变了我在规格页面上写什么。

描述承载全部契约

我做过最大、最有效的一次升级,就是给每一个字段、参数和操作都写满一整句语义描述。customer_id: string 是废话。customer_id: Stripe 客户 ID,格式为 cus_XXXX,区分大小写,前缀后 14 到 18 个字符;通过 /customers.list 获取,绝对不要编造 才是可用的。"绝对不要编造"这句不是摆设。Agent 真的会读这类指令并服从,比你以为的频率要高得多,尤其是当描述里明确写出上游来源的时候。

同样的原则也适用于枚举。不要只列 ["active", "pending", "cancelled"] 就结束。要描述每个状态意味着什么、哪些状态转换是合法的、agent 在当前场景下最可能需要哪一个。只要你把话说出来,模型就会走到正确的值上去。

幂等性不再是可选项

人类客户端会重试一次,最多两次,然后升级给人处理。Agent 会非常激进地重试,而且经常是在应该被当成成功的模糊响应上重试。我现在要求所有非 GET 接口都必须带 Idempotency-Key 头部,没带就直接拒绝。服务端把第一次响应缓存 24 小时,重复请求直接回放。这不是面向 agent 的功能,是自保。没有它,一次被误读的 502 就会变成三笔重复扣款和一张退款工单。

破坏性操作默认 dry-run

我当前这套 API 里每一个破坏性操作都接受 ?dry_run=true。响应体结构和真实调用完全一致,只是多了一个 "dry_run": true,且没有任何副作用。Agent 被 system prompt 和 tool description 训练成:对陌生调用先 dry-run 一次。这能在"我觉得这个会删一行数据,但让我试试看"这类错误演变成事故之前就拦住它。

规格必须写清楚:dry-run 是免费且安全的。如果 agent 以为 dry-run 会消耗一次计费请求、或者会占用 rate limit 配额,那它在压力下就会跳过这一步。

不可逆操作走两阶段 confirmation

上个季度,有一个 agent 集成在失控循环里调用了我们的 DELETE /projects/{id} 接口 400 次。触发这次循环的是一个 planner,它把"archived"误读成"deletable",然后一直调用直到被 rate limiter 挡住。我们没丢任何数据,因为这个接口背后已经有两阶段 confirmation 模式兜底,但事后复盘依然很难看。

这套模式是这样的:第一次 POST 返回 HTTP 202,带上 confirmation_token、一个描述"到底会销毁什么"的 summary 字段,以及 expires_in_seconds(我用 30)。Agent 必须用这个 token 再提交一次完全相同的调用,才能真正执行。Token 单次使用,且绑定到具体的资源和 agent 身份。一个陷在紧密循环里的 agent,会在每一对调用的第一次就把 token 烧掉,永远走不到真正执行那一步。就这一个模式,把一次差点翻车的事件变成了一条日志。

Given an agent calling DELETE /projects/prj_123
  When the first request arrives without a confirmation_token
  Then return 202 with { confirmation_token, summary, expires_in_seconds: 30 }
  And record no destructive side effect

Given a second request arrives with a valid, unexpired token
  When the token matches the exact resource and agent identity
  Then execute the deletion and invalidate the token

Given a second request arrives with an expired or mismatched token
  When the server validates the token
  Then return 409 with a human-readable explanation and require a fresh confirmation

能力协商与可发现性

Agent 不会去翻你的文档站。它只加载 system prompt 指向的那个东西,然后从那里开始工作。我提供了一个 /_capabilities 接口,返回操作列表、调用方身份当前的 rate limit、每次调用的 token 或分成本,以及当前生效的 feature flag。那些会先查这个接口的 agent,错误调用数量会大幅下降。

与此同时,我的 OpenAPI 规格中 operationId 一律写成动词加名词的形式(list_projectsarchive_project,而不是 projectsGet2)。Agent 在自己的工具列表里看到的就是 operationId,所以对模型而言,operationId 才是这个接口真正的名字。

每个响应都要暴露成本与配额

人类是通过被限流一次学到 rate limit 的。Agent 需要这个数字直接出现在响应里。我的每一个响应都包含 X-RateLimit-RemainingX-RateLimit-Reset,付费接口还多一个 X-Cost-Units。规格把这些字段写成契约的一部分,而不是顺手加的头部。当 agent 看到自己只剩 4 次调用、60 秒后才会重置时,它会退让。看不到,就不会。

错误消息要告诉 agent 怎么修

我把所有 4xx 响应体重写成一个固定结构:{ error_code, message, suggested_fix, docs_url }suggested_fix 是一句 agent 能直接照做的指令。"Invalid customer_id format"会变成"Invalid customer_id;期望格式 cus_XXXX,收到 [email protected];调用 /customers.search 并传入该邮箱来解析它"。Agent 原本要五轮才能从错误中恢复,这样写之后一轮就行,而且永远不会把内部堆栈泄露给终端用户——因为根本没有内部堆栈可泄。

针对 agent 流量的可观测性

我给每个请求都打上 agent 身份标签(通过一个必填的 X-Agent-ID 头部),并按身份记录 dry-run 比例、confirmation token 烧掉的速率、以及重试次数。每当一个新集成上线,我就盯着这三个数字。一个健康的 agent 集成在探索阶段 dry-run 比例是 30% 到 60%,进入稳态后降到 10% 以下,并且几乎不会在 confirmation 第二阶段失败。任何落在这几个区间之外的数据,都是规格问题,不是 agent 问题,并且它会精确告诉我哪一段描述需要重写。

如果回到一年前,我会告诉自己

面向 agent 的规格不是"规格加示例"。它的默认项完全不同:幂等必须,dry-run 免费,破坏性操作两阶段,每个字段一整句描述,能力可发现,成本可见,错误可操作,身份有标签。一旦我接受"API 现在就是 prompt",其他所有决策都变简单了。

评审时看什么

这篇文章适合用在给 Agentic 客户端设计 API时。别从“原则”聊起,直接拿一条真实改动来对照,看看规格里还缺什么。

面向 agent 的 API 不能只服务人类文档。它要让模型少猜,让人类保留控制权。

给 agentic client 的接口要暴露“后果”

人类看到删除按钮会犹豫,智能体只会看到一个 endpoint。面向 agentic client 的 API spec 要把危险等级、幂等键、dry-run 和确认字段写进契约,让客户端知道什么时候必须停下来问人。

Endpoint safety contract:
DELETE /workspaces/{id}
- risk: destructive
- requires_confirmation: true
- dry_run: GET /workspaces/{id}/delete-preview
- idempotency_key: required
- human_check: required when workspace has active billing
- audit_event: workspace.delete.requested

边界:不是每个接口都要做成“智能体友好”。读接口、搜索接口和无副作用校验可以保持简单。真正需要加护栏的是会花钱、删数据、发通知或改变权限的操作。

可复制产物:契约评审包

当工作涉及 API 行为、schema、事件、重试或消费者预期时使用。它会把兼容性和发布证据提前摊开。

API 契约评审包:为 Agentic 客户端设计 API 规格

本次要做的决策:
- 确认契约变化是否兼容,消费者需要什么迁移动作,发布后如何观察风险。

责任人检查:
- 产品责任人:
- 工程责任人:
- QA 或运维评审:

范围边界:
- 本次包含:
- 本次不包含:
- 仍需确认的假设:

验收证据:
- 测试或 fixture:
- 日志、指标或截图:
- 人工复核步骤:

契约边界:没有兼容性分类、消费者影响、重试行为和回滚说明,不进入发布。

评审追问:
- 没参加需求会的人还会误解哪里?
- 哪个证据能证明这次改动足够安全,可以发布?

编辑复核记录

复核日期:2026-05-06。本次补充了专题阅读路径,按相关主题 Hub 检查文章定位,并收紧下一步链接,让页面更像可操作参考。

关键词:agentic API 设计 · LLM 工具调用 · Idempotency-Key · 两阶段 confirmation · OpenAPI operationId · dry-run 接口

编辑说明与免责声明

最近复核:2026-05-06。编辑部检查了示例、专题内链和可复制评审片段,确保内容更适合真实项目使用。

本文用于软件工程教学与实践参考,不构成法律、税务或投资建议。示例场景用于解释规格方法,不对应真实客户数据。