跳到主要内容

Agent 友好型 API:当 AI 成为客户端时,后端工程师常犯的错误

· 阅读需 13 分钟
Tian Pan
Software Engineer

在 2024 年,自动化机器流量在互联网上首次超过了人类流量。Gartner 预测,到 2026 年,超过 30% 的新 API 需求将来自 AI Agent 和 LLM 工具。然而,只有 24% 的组织在设计 API 时明确考虑了 AI 客户端。

这一差距正是生产系统崩溃的地方。并不是因为 LLM 本身表现不佳,而是因为为人类开发者构建的 API 包含了一些默认假设,当调用者是自主 Agent 时,这些假设会悄无声息地失效。Agent 无法请求澄清,无法阅读文档网站,也无法自行判断 422 错误是指“修改你的请求”还是“几秒钟后重试”。

这篇文章是写给那些刚刚发现自己的服务正被 AI Agent 调用,或者即将构建此类服务的后端工程师的。

核心问题:API 是为人类设计的,而非机器

当人类开发者遇到模糊的错误时,他们会查阅文档、检查状态页,并根据经验推断下一步该做什么。当 AI Agent 遇到同样的错误时,它必须根据眼前的信息进行推理:HTTP 状态码、响应体,以及它在任务执行过程中积累的所有上下文。如果这些信号含糊不清,Agent 就会陷入猜测——而且是按照机器的速度进行猜测。

考虑一个在读取响应体之前立即调用 response.raise_for_status() 的 Python SDK。Agent 收到:"HTTP Error 422: Unprocessable Entity"。它真正需要的是:"Field 'first_name' required — provide a first_name string"。当 SDK 被修复以显示完整的错误体时,Agent 立即进行了自我修正。在错误模糊的情况下,它陷入了循环。这并非个案——这是大多数语言中大多数 HTTP 错误处理模式的默认行为。

同样的逻辑也适用于分页、身份验证流程、速率限制和 Schema 设计。你的 API 对调用者具备人类判断力所做的每一个假设,都是 Agent 系统中潜在的失效模式。

错误响应是 API 的核心部分,而非事后补充

你能为 AI 客户端做的杠杆率最高的改变就是改进错误响应。不是针对正常路径(happy path)——Agent 处理正常路径通常没问题。真正让 Agent 与人类行为产生灾难性偏差的是错误处理路径。

对于 AI Agent 来说,一个好的错误响应需要是机器可解析的,并且具有行动指导性。由 Stripe 推广的黄金标准通过以下方式构建错误:一个机器可读的 code 字符串(不仅仅是 HTTP 状态码)、一个人类可读的 message、一个指明导致问题的特定输入的 param 字段,以及一个直接链接到恢复文档的 doc_url。此外,还要增加两个 Agent 特别需要的字段:is_retriable(布尔值)和 retry_after_seconds(整数)。

有了这些字段,Agent 的错误处理就变成了一个确定性的决策树:读取 is_retriable → 如果为 false,停止并上报;如果为 true,等待 retry_after_seconds 秒,然后使用相同的幂等键(idempotency key)重试。如果没有这些,Agent 就只能仅凭状态码来推断是否可以重试——而这种推断经常出错。一个 409 Conflict 可能意味着“资源已存在,不要重试”、“存在并发写入,立即重试”或者“存在版本冲突,先获取最新状态”。三种完全不同的恢复路径,却对应同一个模糊的状态码。

RFC 9457 (application/problem+json) 标准化了响应封装格式。采用它只需要一天的工作量,就能立即让你所有的错误信息对任何理解该标准的 Agent、SDK 或监控系统变得可解析。

对于 Agent 调用者,幂等键(Idempotency Keys)是必选项

根据多个 Agent 框架的生产遥测数据,AI Agent 重试工具调用的概率约为 15-30%。重试发生的原因往往是 Agent 无法控制的:请求中途超时、瞬时网络错误、模型对前一次调用是否完成的不确定性,以及编排器重启。如果你的状态变更端点不支持幂等键,那么每一次重试都是一次潜在的重复操作——重复付款、重复发送电子邮件、重复的数据库记录。

实现模式已经非常成熟:客户端在第一次尝试前生成一个 UUID,在每次重试时将其作为 Idempotency-Key 请求头发送,服务器将该键对应的响应缓存 24 小时,并在收到重复提交时返回缓存的结果。生产环境中让团队栽跟头的关键细节是:内存缓存无法在 Pod 重启后存活。分布式 Agent 工作负载需要 Redis 或具有原子性 SET NX 语义的等效数据存储,这样当两个 Pod 同时收到带有相同键的请求时,只会执行一个。

根据重试语义对操作进行标记也很重要。“重试安全”与“幂等”并不等同——GET 请求是重试安全的,而带有幂等支持的写入操作也是重试安全的。Agent 需要知道哪些操作可以安全地重新发起,而无需上报给人类进行确认。

不以“人类正在观察”为前提的分页

基于偏移量(Offset-based)的分页有一种微妙的失效模式,人类 API 用户很少察觉,但智能体(Agent)却经常遇到:当页面之间有记录被创建或删除时,结果集中的项会发生偏移。第 2 页可能会返回已经在第 1 页出现过的项,或者跳过本应出现的项。对于人类使用的分页组件,这只是偶尔的视觉小瑕疵;但对于自主遍历整个数据集的智能体来说,这就是隐形的数据损坏。

基于游标(Cursor-based)的分页消除了这类失效。游标编码了结果集中的一个稳定位置,不受并发写入的影响。对智能体要求的契约应是精简且一致的:每个分页响应都需要 data(结果数组)、next_cursor(字符串,耗尽时为 null)和 has_more(布尔值)。有了这三个字段,智能体就可以编写一个通用的分页循环,适用于你 API 中的每一个端点。

脆弱性源于不一致性。如果你一半的端点使用游标分页,另一半使用偏移量分页,智能体就无法编写统一的可重用遍历模式。每个新端点都需要专门实现。在整个 API 层面标准化分页行为,其价值远超任何单一的性能优化。

速率限制应作为实时状态,而非事后惩罚

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates