跳到主要内容

AI 原生 API 设计:构建智能体真正能调用的后端

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的 REST API 运行良好。文档齐全,错误码一致,每一个经过测试的人工编写客户端都能正常使用。然后你的团队接入了一个 AI 智能体,不到一小时,它就通过不断重试一个不存在的端点的各种变体生成了 2,000 次失败请求——bulk_search_userssearch_all_usersbulk_user_search——每次尝试都触发了真实的下游处理。

这不是提示词工程的失败,而是 API 设计的失败。

REST API 是为能够解析文档、遵守契约、严格调用规范的客户端而构建的。AI 智能体则不同:它们根据名称和描述推断端点可能做什么,在不追踪状态的情况下重试,并将错误信息视为指令而非诊断代码。为智能体调用方设计 API,需要重新审视大多数后端工程师从未质疑过的基本假设。

智能体不是初级开发者

描述 AI 智能体行为时,常见的类比是拟人化的:"把智能体当作初级开发者对待。"但初级开发者会读文档,会提问,在进行破坏性操作前会犹豫。

智能体什么都不做。它们对你的端点描述执行的是近似语义搜索。当智能体需要根据状态检索用户列表时,它会基于概念相似度而非字符串精确匹配来定位端点。如果你的端点名为 /user-list,但智能体的工具使用训练数据将此模式与 /users 关联,它就会请求 /users。如果你的错误信息是"endpoint not found",智能体会尝试另一个看似合理的变体。

第二个关键区别是重试行为。人工客户端在记录特定意图的失败后才会重试。而智能体在所有调用中有 15–30% 的概率会重试——不只是失败的调用——因为模型有时会在确认第一次调用成功前就生成第二次工具调用。这意味着你的 API 需要将重复提交处理为基准流量,而非边缘情况。

第三个区别是错误解读。当智能体收到 HTTP 422 Unprocessable Entity 时,它不会查阅 422 的含义,而是逐字阅读响应体。如果响应体写着"Error 422: Unprocessable Entity",智能体什么也没学到,会用相同的负载重试。如果写着"Missing required field: customer_id — 请提供有效的客户 UUID",智能体会在下一次尝试时自我修正。

错误信息即智能体指令

在面向智能体系统的 API 设计中,投入最不足的地方是错误响应体。标准 API 错误设计优化的是日志查看器中的人类可读性;AI 原生错误设计优化的是自我修正能力。

这两者之间的差距出乎意料地具体。字段级验证错误需要指明字段名,类型不匹配需要指明期望类型,业务逻辑拒绝需要描述有效输入的样子。"invalid request"或"bad input"之类的通用信息会造成死局:智能体无法推断需要修改什么,只能在没有修改的情况下重试,或者幻觉出一个修复方案。

RFC 7807 问题详情格式是一个有用的起点。它将错误构造为带有 titledetailstatus 和可选 extensions 字段的类型化对象。对于智能体调用方而言,关键是将 detail 扩展为包含可操作的具体信息:哪个字段失败了、有效范围或格式是什么,以及在适用时提供包含候选修正方案的 suggestions 数组。收到 {"detail": "start_date 必须早于 end_date;收到 start_date: 2026-06-01,end_date: 2026-05-01"} 的智能体可以立即交换这两个值。而收到 {"message": "日期范围无效"} 的智能体则无法处理。

一个具有较好通用性的模式是在错误响应中加入 recovery_action 字段。这与 HATEOAS 链接是相同的概念,但面向的是智能体的决策循环而非浏览器导航。对于超出配额的错误,recovery_action: "retry_after_seconds: 30" 给智能体提供了具体的下一步。对于使用可刷新令牌的身份验证错误,recovery_action: "refresh_token_then_retry" 能防止不必要的会话销毁和重新授权。

能抵御幻觉的命名

对于面向智能体的 API,端点和字段命名不是风格问题,而是可靠性问题。

智能体会从上下文中预测端点名称。如果你在一个资源上将集合端点命名为 /user-list,在另一个上命名为 /orders,智能体就会建立一个混合命名规范的心理模型,最终会为既不遵循这两种模式的第三个资源调用 /order-list。命名一致性——集合用复数名词、所有端点保持一致的 HTTP 动词语义——能大幅降低智能体幻觉变体的频率。

字段名需要在不依赖外部上下文的情况下实现语义完整。temp 有歧义;temperature_celsius 没有。ts 可能是任意格式的时间戳;created_at_utc_ms 则无歧义。冗长的成本对智能体来说微不足道,对正确性的收益则非常可观。

枚举值需要特别关注。API 中存在但未在 OpenAPI 规范中记录的枚举值对智能体来说是不可见的,这意味着它们会猜测。如果智能体需要设置一个 status 字段,而规范只记录了 activeinactive,而没有 pending_review,智能体要么完全省略 pending_review,要么幻觉出一个近义词。每个有效的枚举值都需要写入规范,最好附带一句话的描述。

错误码同样适用这一原则。如果你的 API 返回应用特定的错误码(如 ERR_QUOTA_EXCEEDEDERR_ENTITY_LOCKED)而不记录其含义,智能体就无法对其采取行动。请为每个错误码记录触发它的状态以及调用方应采取的后续操作。

幂等性是不可妥协的

对于人工编写的客户端,幂等性是最佳实践;对于智能体调用方,它是正确性要求。

数学很简单:如果智能体对所有调用有 15–30% 的重试率,而你的 API 每小时在用户群中有 10,000 次调用,你的非幂等端点每小时会将 1,500–3,000 次重复提交处理为独立操作。重复处理一笔支付是严重 bug,重复创建一条记录会产生孤立状态,可能在未来数月内以微妙的方式浮现。

Stripe 的幂等键模型是正确的方法。调用方在每个 POST(或任何状态变更请求)上包含一个 Idempotency-Key 请求头,其中包含智能体在调用前生成的 UUID。服务器将该键连同响应存储一段时间(通常 24 小时)。使用相同键重试时,服务器返回缓存的响应而不重新执行操作。

幂等键的生成策略很重要。每次重试都生成新 UUID 的智能体会使这个机制失效。键应从操作的意图中派生:输入参数的哈希,或编排器在委托前分配给任务的键。对于由上层编排器驱动智能体的工作流,编排器应生成并提供幂等键,防止内层智能体在重试时无意间生成新键。

这种模式也处理了最危险的智能体失败模式:操作中途的网络超时。智能体不知道连接断开前服务器是否已处理了请求。没有幂等性,它只能在重试(有重复风险)和放弃(有遗漏风险)之间二选一。有了幂等性,重试总是安全的。

为 LLM 解析而优化的 OpenAPI 规范

每个 LLM 函数调用接口——OpenAI、Anthropic、Gemini——都会摄入你的 OpenAPI 规范并将其转换为工具定义。转换质量取决于你的规范写法。

operationId 字段会成为模型调用的函数名。如果你的 operationId 是自动生成的(如 users_get_1users_post_2)而非描述性的(如 getUserByIdcreateUser),智能体的工具选择就会不够精确。每个操作都需要有意义的 operationId 和简洁的 description——不是为了人工文档,而是为了模型的工具选择逻辑。

内联 JSON 示例具有不成比例的价值。模型使用示例来推断负载结构的方式,超越了单纯依赖模式定义。规范中包含格式良好请求示例的端点,收到格式错误负载的频率远低于没有示例的同类端点。对于复杂的嵌套对象以及语义含义不能直接从名称推断的字段,这一点尤为明显。

参数文档需要完整且冗余。指定格式(date-timeuuidemail)、数值的有效范围和字符串的字符限制。同时包含类型约束和字段含义的纯语言描述。模型会同时使用这两者。

新兴的模型上下文协议(MCP)值得作为一个方向去了解。MCP 通过有状态连接上的 JSON-RPC 2.0 标准化了智能体连接外部数据源和工具的方式。自 Anthropic 于 2024 年 11 月发布以来,OpenAI 和 Google 均已采纳。对于构建供多个智能体框架使用的 API 的团队,在 REST API 旁边构建 MCP 服务器,提供了一个更结构化的集成点,受不同模型间提示变体的影响更小。

针对智能体流量模式的速率限制

为人工客户端设计的速率限制会对智能体调用方静默失效。

人工流量受阅读时间和用户思考时间调节。智能体以推理速度发起调用,这意味着单个智能体任务可以在数秒内触发数十次 API 调用。允许每分钟 100 次请求的固定窗口速率限制,会被活跃智能体在复杂任务的最初几秒内耗尽,导致剩余时间一无所有。

解决方案分两部分。首先,以智能体能解读的方式传达限制:在每个响应(而非仅 429 响应)中包含 X-RateLimit-RemainingX-RateLimit-ResetRetry-After 头部。能主动检查这些头部的智能体可以自我调节;只从 429 中了解限制的智能体已经失败,还需要从失败中恢复。

其次,考虑使用令牌桶或滑动窗口限制,而非固定窗口。令牌桶允许受控的突发——短暂的高频序列后跟一段安静期——比均匀的请求间隔更符合智能体的行为模式。固定窗口限制在窗口边界处制造了一个悬崖,智能体流量会猛烈撞上。

当多个智能体共享速率限制配额时,需要对配额追踪进行外部同步。基于 Redis 的配额管理是标准方法:在发起调用序列前,智能体检查整个序列是否有足够的配额,而不仅仅是第一次调用。预检配额检查能防止最糟糕的失败模式:智能体完成了任务中的 8 个步骤,耗尽了速率限制,现在必须决定是重试剩余的 2 步,还是放弃一个半完成的操作。

设计思维的转变

AI 原生 API 设计的根本转变,是将假设从客户端移至服务端。传统 API 设计假设人工编写的客户端会阅读文档、优雅地处理错误、正确实现速率限制逻辑。对于智能体调用方,这些假设会以可预见的方式失效。

自描述错误、一致的命名、带内联示例的完整规范、状态变更端点的幂等键,以及每个响应中的速率限制头部——这些都不是新想法。它们是经过验证的成熟实践,只是 API 设计者以前将其降低了优先级,因为复杂的人工客户端可以弥补这些缺口。智能体无法弥补,它们会字面上遵循契约,契约不完整时就会失败。

好的一面是,AI 原生 API 设计同样提升了人工客户端的可用性。一个智能体能可靠导航而无需幻觉端点的 API,就是一个命名一致、文档完整的 API。智能体并非更苛刻的客户端——它只是更诚实的客户端。

References:Let's stay in touch and Follow me for more thoughts and updates