Agent 工具调用超时后,怎样避免重复创建工单

19 阅读4分钟

工具调用超时以后,运行器需要保留一种状态,结果未知。请求可能没有执行,也可能已经执行成功,只是响应没有回来。这个区别会直接影响下一次调用能不能发出去。

我在 sunday面试指南整理这道题时,把查询和写入分开讨论。AWS Builders' Library 对安全重试的说明也强调了调用方请求标识与业务意图的关系。下面用一个假设的工单系统,把设计过程走一遍。

先沿着一次超时找副作用

用户要求为设备故障创建工单。应用发出请求,下游完成写入,响应途中连接断开。调用方等到超时,只知道没有拿到确定的结果。

如果此时反馈给模型“创建失败,请重试”,模型可能再次创建,用户的一次需求就对应了两张工单。运行器要让调用结果能够表达不确定性,模型的下一步才有可靠依据。

查询工单状态的工具,如果确认没有写入副作用,可以在次数和总时长预算内重试。创建工单的工具则先检查接口有没有幂等能力,或者能否凭操作标识核对状态。工具名称含有 query,也不能替代对实现语义的检查。

幂等键跟着同一个业务动作走

应用可以在首次执行前,为这一次已经确认的创建意图分配 operationId,并保存起来。假设下游明确支持 idempotencyKey,第一次请求和后续安全重试使用同一个值。

{
  "operationId": "ticket-intent-42",
  "idempotencyKey": "ticket-intent-42",
  "action": "create_ticket",
  "arguments": {
    "deviceId": "device-8",
    "summary": "设备无法连接"
  }
}

这是应用与工具之间的教学数据结构,需要按真实接口调整。用户明确提出第二个新的创建动作时,要生成新的标识。不能仅凭参数长得一样,就把两次合理的业务需求合并。

下游也必须真正实现去重。只在调用方生成一个随机 ID,却没有服务端处理规则,重复调用仍然可能重复写入。幂等记录与业务写入还要协调,避免工单已经生成,去重记录却没有保存。

接口约定需要说明键的作用域和有效期。同一个键带着不同参数再次提交时,应按契约拒绝或处理冲突。超过服务端的保留期限,也不能继续假设这个键仍然保证去重。

让未知状态限制下一步动作

超时后,运行器可以返回下面这样的结果。

{
  "operationId": "ticket-intent-42",
  "status": "unknown",
  "allowedNextActions": ["query_operation_status", "request_review"],
  "reason": "下游响应超时,尚未确认写入结果"
}

具体状态名称可以自行设计,关键是区分已确认成功、已确认失败和结果未知。这段数据只是协议示例,真正禁止重复创建的约束还要落实在执行器里。模型选择了未获允许的动作,工具执行层仍应拦住。

如果下游支持幂等,运行器可以按照接口约定复用原键重试。能够查状态时,先检查同一操作的结果。查不到结果也有不同含义,可能仍在处理,可能查询数据还没有同步。需要达到接口定义的确定条件,才能认定没有执行。

下游既不支持幂等,又无法核对状态,就停在待核对。继续查询也要有限制,避免把 Agent 运行变成一串没有结束条件的网络请求。

用丢失响应的测试检查一次

测试环境里可以故意让下游完成创建,再让调用方收不到响应。后续重试后,检查实际工单数量与操作记录。这能验证最容易重复写入的情况。

另外检查两个新的创建意图是否会得到不同工单,以及同一意图多次提交是否只产生一个结果。最后让状态查询一直超时,确认系统会在预算用尽后退出,并保留待核对状态。

测试要核对服务端的实际记录。客户端只拿到一次成功响应,仍不足以证明下游只写入一次。幂等保留期限之外怎样恢复,也需要单独检查,不能把有限范围的去重概括成所有情况下都能恰好执行一次。

我是程序员Sunday。完整原理和面试追问见 sunday面试指南的工具超时与幂等解析。

参考 AWS 关于幂等 API 与安全重试的说明。