跳转到主要内容

Agent 需要的不是更多自主权,而是更窄的工具面

大多数 Agent 越界行为始于工具接口,而非模型本身。收窄 Agent 可调用的接口面,比给它套上护栏更具杠杆效应。

Detailed view of an electronic music sequencer with buttons and dials, showcasing a sleek design.

近期事件中的共同模式

当 Agent 做了它本不该做的事,事后复盘通常会归咎于模型。但事件报告本身更为具体。Agent 调用了一个根本不属于其任务的端点,因为响应没有给出其他可做的事而重试了一次变更操作,或者因为 API 让枚举变得廉价且缺乏文档而遍历了整个集合。这些不是推理的失败,而是接口的失败。

三种形态反复出现。第一,触达范围:Agent 触碰了一个技术上可达但超出其授权范围的资源。工具面暴露了一个宽泛的动词,Agent 就用了它。第二,重试行为:面对没有速率语义的接口,收到模糊失败的 Agent 会重试,而且往往很激进,因为响应中没有任何信息告诉它重试是错误或无意义的。第三,枚举:一个没有分页契约、没有范围边界的宽松列表端点,在 Agent 看来就是在邀请它遍历整个集合。

为什么为人设计的工具对 Agent 会失效

面向人的 API 能容忍模糊性,因为人会在其周围提供意图和克制。一个人读到宽松的端点时,会从上下文和惯例中理解 DELETE /items 并不是在邀请删除一切。他们知道哪些参数在实践中是可选的,哪些错误是暂时性的,哪些动词仅因历史原因而存在。这些知识都不在接口里,而在人身上。

Agent 则字面地解读接口面。每个动词都是候选动作。每个可选参数都是决策点。未文档化的行为对机器而言不是软边缘,而是一条未探索的路径。人看到一个粗糙的角落会绕过去,Agent 看到一条分支则可能走进去。

这就是核心的不对称性。面向人的接口之所以宽容,是因为人本身就是护栏。面向 Agent 的接口不能假设这一点。接口面本身必须承载那些原本由人凭经验提供的约束。当它没有做到时,Agent 并非鲁莽,而是在与它所获得的东西保持一致。

在宽泛接口面周围放置护栏并不能解决这个问题。它们拦截了一些调用,漏掉了另一些,并且增加了一层 Agent 看不见的策略层。Agent 仍然在原始接口面上进行推理。纠正在决策之后才到达,而非之前。

设计面向 Agent 的工具面

Close-up of an illuminated vintage control panel with various buttons and switches.

面向 Agent 的工具面应当是狭窄的、明确的、自描述的。三个属性承担了大部分工作。

每个意图一个动词,参数必填

将宽泛的动词折叠为意图特定的动词。不要用一个接受任意字段的通用 update,而是暴露 set_delivery_address,要求必填地址和必填订单标识符。不要用 list,而是暴露 list_open_orders_for_customer,要求必填客户标识符和必填状态过滤器。参数集不是建议,而是写下来的意图。

这改变了 Agent 能表达的内容。带可选字段的通用动词让意图保持隐式,而 Agent 填充可选字段就是在猜测。带必填参数的狭窄动词则失败关闭:如果 Agent 无法提供客户标识符,它就无法执行调用,而且失败是可读的,而非静默的。

在接口中表达硬性限制

速率和范围限制属于工具定义,而非 Agent 永远不会读的策略文档。如果一个工具在每个窗口内最多只能被调用有限次数,就在 schema 中写明并在边界处强制执行。如果一个集合每次最多只能读取一页,就让分页成为强制性的,并让页大小成为带范围约束的必填参数。

机制比措辞更重要。在工具边界处强制执行的速率限制会返回 Agent 可以据以行动的结构化信号。在下游某处强制执行的速率限制会返回不透明的失败,Agent 会重试。同样的限制,放在不同位置,产生相反的行为。

描述下一步合法动作的结构化错误

错误应当告诉 Agent 接下来可以做什么,而不仅仅是哪里失败了。一个裸的 403 会招致重试和探测。一个结构化错

Conveyor belt with empty aluminum cans in a factory setting, showcasing production equipment.

{
  "error": "scope_denied",
  "message": "This principal may not modify billing details.",
  "permitted": ["read_billing_summary"],
  "retryable": false,
  "next": "request_billing_change_approval"
}

仅 retryable 字段就消除了一大类激进的重试循环。permitted 列表收窄了 Agent 的下一步,而无需它从失败中推断边界。

这需要付出什么代价

没有任何一种做法是免费的,代价是真实的。

维护第二套接口面是第一项。如果 Agent 接口面只是人类 API 的薄封装,宽泛的动词就会泄漏出来,整个工作就只是表面功夫。真正的 Agent 接口面是一份独立的契约,有自己的动词、自己的参数要求、自己的错误词汇表。这是持续的工作,不是一次性的重构。

版本管理是第二项。Agent 接口面必须独立于人类接口面进行版本管理,因为两者因不同原因、以不同频率变化。它还必须作为契约来测试:不仅测试调用成功,还要测试接口面拒绝了它应当拒绝的东西,并且拒绝是可读的。只覆盖正常路径的测试套件不会发现过于宽泛的动词。

拒绝遗留动词是第三项,也是最令人不适的一项。有些动词即使不属于 Agent 的接口面,从 Agent 的执行上下文中技术上仍然可达。因为方便而暴露它们,就会重新引入最初的问题。诚实的权衡是:收窄接口面意味着 Agent 技术上能执行的一些任务不再对它可用,而其中一些任务是合法的。这笔代价以能力换取可预测性。跳过这一步的团队往往会在之后重新发现同样的事件模式,只是接口面更宽、护栏更多。

替代方案是继续在一个从未被设计为被字面解读的接口面周围不断增加策略。这种方法的扩展性取决于可能出错的事情的数量。而收窄接口面的扩展性取决于 Agent 本应完成的事情的数量。

封面照片:Jakub Pabis / Pexels。

分享XLinkedIn永久链接5 分钟阅读

相关阅读

这能回答的问题

Why do agents overreach on APIs that humans use safely?

Human-facing interfaces rely on the person to supply intent and restraint. An agent reads the same surface literally: every verb is a candidate action, optional parameters are decision points, and undocumented behaviour is an unexplored path rather than a soft edge.

What does an agent-facing tool surface look like in practice?

One verb per intent with required parameters that make the intent explicit, hard rate and scope limits expressed in the interface, and structured errors that state what the agent may do next rather than only what failed.

Why are guardrails around a broad API not enough?

Guardrails intercept calls after the agent has already reasoned over the original surface. They add a policy layer the agent cannot see, so the correction arrives after the decision. Narrowing the surface changes the decision itself.

What is the main cost of maintaining a separate agent surface?

It has to be versioned and tested as a contract independently of the human API, and some legacy verbs that are technically reachable must be refused. That removes some legitimate capability in exchange for predictability.