[控场AI]
· 10 分钟阅读· 5,286 字

为AI Agent设计API:.NET开发者的5条实战法则

为AI Agent设计API:.NET开发者的5条实战法则

面向AI Agent的API设计:五条原则让你的后端接口对Agent和人类都更友好

本文由一位.NET后端开发者总结,围绕"AI Agent如何消费API"这一核心问题,系统梳理了面向Agent的API设计原则。与人类开发者不同,AI Agent不读文档、不会debug,完全依赖机器可读的spec做决策,且重试速度极快——这要求API具备更强的自描述性与健壮性。文章提炼出五条核心法则:一是为每个字段标注完整的类型、约束与描述;二是保持命名、分页、状态值等全局一致;三是采用RFC 9457结构化错误响应,明确指出错误字段和合法取值;四是通过Idempotency-Key配合原子检查保障重试安全;五是将多步操作封装为语义明确的复合接口,而非堆砌CRUD端点。文章还介绍了MCP协议作为更高层的Agent工具调用标准,并提供了.NET C# SDK的示例及一份可落地的自检清单。

为什么开发者该重新审视API设计

长期以来,API都是为前端、移动端或其他开发者团队设计的。但一个新的消费者正在登场——AI Agent。这篇文章源自一位.NET后端开发者的学习笔记,核心问题很直接:当有人说“AI Agent会调用你的API”时,这究竟意味着什么?我们的API又需要做哪些改变?

答案要从Agent和人类开发者的根本差异说起。人类开发者会读文档、会猜测模糊字段的含义、遇到错误会主动debug,而且动作较慢。AI Agent则完全相反:它主要依赖OpenAPI spec和字段描述来理解接口,容易对模糊字段做出错误推断,错误信息里若没有明确指引就会反复犯同样的错,而且重试速度极快——这意味着它犯错的速度也极快。

AI Agent API设计学习笔记

正是这些差异,推导出了下面这套面向Agent的API设计原则。

原则一:完整的Schema与有意义的描述

Agent不像人类那样会翻阅文档,它几乎完全依赖机器可读的接口描述。因此每个字段都应标注类型、格式、约束和描述。在.NET中,只要给request model加上attribute,这些信息就会自动进入OpenAPI spec:

public record PlaceOrderRequest(
    [property: Description("下单客户的ID")]
    Guid CustomerId,

    [property: Description("商品ID")]
    Guid ProductId,

    [property: Range(1, 1000)]
    [property: Description("商品数量(整数,至少为1)")]
    int Quantity);

端点的描述同样关键。相比“Gets the data”这种空洞的说明,“Returns a paginated list of invoices filtered by status and date range”能给Agent提供强得多的决策信号。原文给出一个假想场景很好地说明了问题:如果spec里quantity没有标注类型,Agent可能发送字符串"2",服务端只回一句{"error": "Invalid input"},Agent无从判断哪里错了,于是重复发送同样的请求,陷入死循环。

原则二:全局一致的约定

Agent会从一个端点学到模式,再套用到其他端点上。如果一个接口的状态值是pending,另一个却是IN_PROGRESS,第三个又变成done,它就会困惑。保持一致性要覆盖多个维度:

  • 命名风格(camelCase或snake_case,二选一,不要混用)
  • ID格式(例如统一使用uuid)
  • 分页方式
  • 错误结构
  • 状态值的书写规范

一致性对人类开发者是“锦上添花”,但对Agent来说往往是“能不能用对”的分水岭。

原则三:结构化的错误信息

错误信息不该只是告知“出错了”,而应该成为Agent的修正指引:哪个字段错了、收到了什么值、哪些值才合法。ASP.NET Core内置了RFC 9457(Problem Details)标准,还能在此基础上扩展自定义字段:

return Results.Problem(
    title: "Invalid field value",
    detail: "Field 'status' received invalid value 'processing'.",
    statusCode: StatusCodes.Status400BadRequest,
    extensions: new Dictionary<string, object?>
    {
        ["field"] = "status",
        ["receivedValue"] = "processing",
        ["allowedValues"] = new[] { "pending", "shipped", "delivered", "cancelled" }
    });

返回的JSON会明确告诉调用方:错误字段是status,收到的值是processing,合法取值是pending/shipped/delivered/cancelled。对比{"error": "Invalid input"},Agent拿到前者,就能在下一次调用中直接发送正确的值,而不是盲目重试。

RFC 9457(Problem Details for HTTP APIs)是IETF于2023年正式发布的标准,前身为RFC 7807。它规定了一种通用的JSON错误响应格式,包含type(错误类型URI)、title(人类可读标题)、status(HTTP状态码)、detail(具体说明)和instance(触发错误的请求URI)五个标准字段,并允许通过扩展字段携带领域特定信息。ASP.NET Core自8.0起将Problem Details作为内置默认错误格式,通过AddProblemDetails()中间件即可启用全局统一的错误响应结构。对Agent的价值在于:结构化的extensions字段(如allowedValues)可以被Agent的推理逻辑直接解析并用于下一次请求的参数修正,而无需依赖自然语言理解去解读模糊的错误文本,这将错误处理从"语义理解"问题降级为"结构化数据读取"问题,大幅提升了可靠性。

原则四:幂等性保障重试安全

Agent(以及任何客户端)在网络超时后会自动重试。如果POST /orders被发送两次,可能就会创建两个订单。解决方案是让客户端携带一个Idempotency-Key请求头,服务端记住这个key,当同一个key再次到来时,不再创建新订单,而是返回之前的结果。

原文给出一个用于理解的简化示例(基于内存,生产环境需用Redis或数据库):

var seenKeys = new ConcurrentDictionary<string, bool>();

app.MapPost("/orders", (HttpRequest http, PlaceOrderRequest body) =>
{
    var key = http.Headers["Idempotency-Key"].ToString();

    // TryAdd 原子操作:同一key的两个并发请求只有一个成功
    if (!string.IsNullOrEmpty(key) && !seenKeys.TryAdd(key, true))
        return Results.Conflict("该key的请求已处理过。");

    // ... 创建订单 ...
    return Results.Ok();
});

这里有两个关键点值得强调。其一是原子性检查:如果拆成“先看是否存在,再写入”两步,两个并发请求可能都通过检查,Redis的SET NX或数据库唯一约束正是为此而生。其二是在真实系统中,应该保存并返回之前成功的响应,而不只是返回Conflict——上面的代码只是用于说明概念。

幂等性(Idempotency)是指对同一请求执行一次与执行多次产生相同结果的性质。HTTP规范中GET、PUT、DELETE天然是幂等的,而POST默认不是。Idempotency-Key模式最早由Stripe在支付场景中推广,现已成为金融和电商API的常见实践。生产级实现通常将key与响应序列化后一起存入Redis,并设置合理的TTL(如24小时),当相同key重复到达时直接返回缓存的完整响应而非Conflict状态码——这样调用方无法区分"首次成功"与"幂等重放",行为完全透明。需要注意key的生成责任在客户端(Agent侧),通常使用UUID v4;服务端只负责检查和存储。对于AI Agent这类高频、无监督的调用者,缺少幂等性保护可能导致数据库在短时间内产生大量重复记录,后果远比人类误操作严重。

原则五:精心设计的操作,而非堆砌CRUD

如果把每个端点都作为tool暴露给Agent,它面前的选项就会过多,选错的概率也随之上升。少而精、描述清晰的操作反而更实用。

举例来说,下单若需要“查客户 → 查库存 → 创建订单”三次调用,不如封装成一个PlaceOrder操作。Agent不必自己编排步骤,中间出错的环节也随之减少。这既降低了Agent的推理负担,也提升了整体可靠性。

MCP:面向Agent的进一步演进

MCP(Model Context Protocol)是一个将AI Agent与外部工具、数据连接起来的开放协议,原文形象地称之为“AI的USB接口”。通过MCP,你可以把操作暴露给支持MCP的Agent,让它们更容易发现和使用。.NET官方提供了C# SDK,一个tool大致长这样:

[McpServerToolType]
public class OrderTools
{
    [McpServerTool, Description("为指定客户创建新订单。")]
    public async Task<string> PlaceOrder(
        [Description("客户ID")] Guid customerId,
        [Description("商品ID")] Guid productId,
        [Description("数量,至少为1")] int quantity)
    {
        // 在此调用你现有的应用逻辑
        return "created";
    }
}

需要提醒的是,SDK变动较快,具体的setup代码应以官方仓库为准。而且第五条原则在这里同样适用:不要把所有端点都做成MCP tool,只暴露Agent真正需要的那些。

MCP由Anthropic于2024年底发布并开源,随后获得OpenAI、Google等主流AI厂商跟进支持,迅速成为Agent工具调用领域的事实标准。它定义了一套标准化的JSON-RPC通信协议,让AI模型能够以统一方式发现并调用外部工具(Tools)、读取资源(Resources)和使用提示模板(Prompts)。与直接调用REST API相比,MCP的优势在于工具的自描述性更强——每个tool都携带结构化的名称、描述和参数schema,Agent无需提前知道调用细节,可以在运行时动态决策使用哪个工具。MCP支持两种传输层:stdio(适合本地进程通信)和SSE/HTTP(适合远程服务),这使它既能嵌入本地开发工具,也能部署为网络服务。理解MCP的定位有助于区分它与OpenAPI的关系:OpenAPI描述的是HTTP接口的结构,MCP则是一层更高抽象的"Agent可调用能力"协议,二者可以共存,MCP server内部可以继续调用你已有的REST API。

一份可落地的检查清单

原文还提供了一份实用的自检清单:

  • OpenAPI spec是否完整,每个字段都有类型、格式、约束
  • 描述是否体现了意图,而不仅是输入输出
  • 错误是否结构化,包含字段名和合法取值
  • 修改状态的端点是否具备幂等性
  • 响应是否小而聚焦
  • 可行之处是否使用了复合操作
  • 分页、命名、状态值是否全局一致
  • 认证与限流是否在spec中说明

作者最后建议动手实践:花1到2小时建一个故意把类型和错误写模糊的POST /orders端点,用支持MCP的客户端去调用,观察它在哪里卡住,然后逐条应用这五条原则,记录每次行为的变化。

结语:好API对人和Agent都友好

过去API是为开发者设计的,如今Agent成为新的消费者——它们不会猜测,也不会debug。一个schema清晰、错误有指引、重试安全的API,对Agent和人类来说都是更好的设计。这或许正是面向Agent时代,后端开发者需要重新建立的工程直觉。

#AI Agent API#API设计#.NET后端#MCP协议#OpenAPI#幂等性#结构化错误#Model Context Protocol
分享:

相关推荐