给 Agent 的工具加上重试之前,先想想会不会做两遍
用创建笔记的例子解释超时、幂等键、任务查询,以及重试为什么可能造成重复写入。
目录
请求超时以后,再发一次,大多数时候看起来都没问题。可如果刚才那次已经创建了文件,只是回复没传回来,重试就会再创建一份。
Agent 很容易遇到这种情况。它看见工具没有返回成功,于是换个参数、换个标题,继续尝试。最后任务完成了,文件夹里也多了三个版本。
超时只说明没有按时收到答复
假设工具叫 create_note,负责把整理结果存成一条笔记。一次调用可能经过这些步骤:收到请求、写入数据库、提交事务、返回笔记编号。
如果断线发生在提交之后,客户端看到失败,服务端却已经做完。模型再聪明,也不能仅凭“timeout”判断那条笔记存在不存在。
因此我会让工具明确区分:确定没有执行、确定已经完成、结果未知。最后一种状态需要查询,不能直接等同于失败。
把同一次操作认出来
幂等键的作用,是给一次业务操作一个稳定编号。比如“把本次任务的最终摘要存为笔记”对应一个键。它在网络重试时不变,用户明确要求另建一份时才创建新键。
POST /notes
Idempotency-Key: task-7f3a-final-note
{"title": "胶片扫描记录", "body": "……"}
这个接口是设计示例,不对应现成服务。服务端应把键、请求内容摘要、执行状态和结果编号关联起来。同一个键配同一份内容,再次请求时返回已有结果;同一个键配不同内容,拒绝并要求调用方核对。
不要让模型每次重试都生成随机键,那只是给重复操作换了身份证。键最好由掌握任务状态的程序生成。
最容易漏的是两个请求同时到达
先查询“有没有这个键”,再写入,还不够。两个请求可能同时查到没有,然后各创建一份。
需要数据库唯一约束、事务或服务端等价机制保证只有一个操作获得执行权。如果创建笔记还依赖另一个外部系统,单个数据库事务未必能覆盖整个过程,就要保存中间状态,并使用对方提供的幂等能力或事后对账。
记录也不能刚成功就删除。键的保留时长至少要覆盖可能发生的重试窗口,过期后旧请求再次出现的行为需要有明确约定。
给模型的说明要能直接指导下一步
工具说明里写“创建一条笔记”太少。更有用的是补上三句话:会产生外部写入;超时后先按操作编号查询;只有确认未执行才考虑重新提交。
返回值也应包含可用信息:
{
"status": "completed",
"operation_id": "task-7f3a-final-note",
"note_id": "note-42",
"replayed": true
}
replayed 表示这次拿到的是已有操作的结果,不是又写了一条。这样的字段比“操作成功啦”更容易交给后续程序处理。
对于文件编辑,也可以记录原文件版本或内容摘要,写入前检查是否发生变化。这里解决的是另一类冲突:任务暂停时,人可能已经改过文件。防止重复写入和防止覆盖新内容,都需要工具层的约束。
Agent 的重试策略最后仍要落回普通软件工程。读操作可以按错误类型重试;写操作先确认有没有执行。把这条线画清楚,比在提示词后面多加几个“务必谨慎”更有用。