告诉编码 agent:重试是找回同一项任务,还是再创建一项

使用 AI 辅助编写与复核。

本文目录

CueSpec 研究编辑团队撰写,CueSpec 的 AI 辅助编辑团队。

应用没有响应时,“再试一次”可能有两种含义:找回你已经发出的请求的结果,或者再要一个结果。在要求编码 agent 添加重试之前,先告诉它你想要哪一种。

关键区别在于用户打算执行的操作。按钮点击相同或输入相同,都未必能表达这种意图。一个人可以有意请求两项完全相同的任务;一次网络重试也可以重复同一项任务请求,而不要求再创建一项。

本文使用一个原创、虚构的预览任务应用。以下事件序列和结果都是拟议需求,尚未实现或测试,也不描述 CueSpec 的生产问题。

为操作划定一个清楚的小范围

设想一个内置配色方案的应用。用户选择 coast 和 small,然后选择 Create preview job。该操作创建一条草稿任务记录;生成预览在之后进行。

示例使用以下名称:

对象 示例 它标识什么
操作 create-preview-job 请求执行的动作类型
命名空间 board-a 示例中请求身份的作用范围
请求 ID req-101 一次有意发起的创建,以及它的重试
有意义的输入 palette=coast; size=small 所请求任务的设置
任务 ID job-17 该操作创建的记录

这里的命名空间是示例名称,不是一套访问控制设计。实现仍需定义谁可以在这个范围内创建或找回任务。

拟议契约将操作、命名空间和请求 ID 的组合视为同一个请求身份。在该身份支持的有效期内,匹配的重试应找回原来的创建结果,而不是再生成一条记录。

AWS 的工程文章解释了,为何请求标识符比根据相同参数推断重复请求更能清楚表达意图。文章还讨论了等价的重试响应、迟到请求,以及不同服务的不同要求。这些是设计概念,并不是每个 API 都会提供的保证。AWS:Making retries safe with idempotent APIs

序列 1:任务已创建,但响应丢失

用户想要一项预览任务。

  1. 应用提交 req-101,输入为 coast/small。
  2. 服务提交 job-17,以及它与 req-101 的关联。
  3. 响应没有到达应用。仅凭没有响应,应用无法判断创建是否发生。
  4. 用户选择 Check or retry this request。应用保留 req-101 和相同输入。
  5. 只要请求身份记录仍被保留,服务就将 job-17 识别为原来的创建结果,不再创建任务。

拟议的可见结果是“Your original job is job-17”,随后在能够获取信息时,显示该任务当前是否可用或进行到了哪一步。创建回执并不意味着渲染已经完成。

可以给 agent 这样一条指令:

保留结果尚未确定的请求身份。已知原任务时,找回它。不要因响应不确定就生成新的请求 ID,把它变成一次新的创建。

禁用按钮可能有助于避免意外重复点击,但无法确定响应丢失后,或者通过另一条客户端路径重试请求时,应当发生什么。

序列 2:用户有意再创建一项任务

用户已经请求了一项预览,现在希望使用完全相同的设置,再创建一项独立任务。

  1. 原请求 req-101 创建了 job-17。
  2. 用户选择 Create another。
  3. 应用为这次新的创建意图分配 req-102。
  4. 应用提交相同的 coast/small 输入。
  5. 拟议结果是另一项任务 job-18,与 req-102 关联。

这里允许相同输入,因为用户表达了第二次操作的意图。如果复用 req-101,按这份契约,其含义就是找回第一次操作的结果。

要求 agent 同时设计这两个动作:Check or retry this request 保留未确定操作的 ID;Create another 开始一次有意发起的新操作。这些示例标签只是建议,不要求每个界面都照搬文案。

序列 3:旧请求迟到

现在考虑一种情况:原任务的状态已经变化,重放请求才到达。

  1. req-101 创建了 job-17,服务保留了两者的关联。
  2. 一次使用相同 ID 和输入的重试在传输途中延迟。
  3. 在此期间,用户从这个虚构应用中移除了 job-17。
  4. 延迟请求到达时,请求身份记录仍被保留。
  5. 按拟议契约,它识别的是原操作,不会创建一个替代任务。

应用应区分“这个请求原本创建了 job-17”和“job-17 现在可用”。如果已知它被移除,就报告这一点;如果无法确定当前可用性,就保留这种不确定性。

删除任务与遗忘其请求身份,是两个独立的设计决定。本示例要求关联保留足够长的时间,以支持约定的找回行为,但没有规定无限期保留。

身份记录过期后,本示例不承诺防止重复创建。服务一旦遗忘了请求,就不能仅凭记录不存在,判断自己遇到的是一个新 ID,还是一次旧请求重放。找回设计需要明确的有效期与对账政策。AWS 同样指出,保留期限取决于具体服务。请求有效期与迟到请求

将意图、身份和结果放在同一张矩阵中

所有行都描述这份虚构契约。

情况 请求 ID 和输入 预期结果 必须明确的区别
首次创建 新的 req-101,coast/small 创建 job-17 并记录关联 创建回执不等于渲染完成
响应丢失后进行匹配重试 相同的 req-101、相同输入 识别 job-17;不增加任务 没有响应不能证明首次尝试失败
有意进行第二次创建 新的 req-102、相同输入 创建独立的 job-18 相同设置仍可表达新的意图
任务移除后,仍在保留期内的延迟重放到达 相同的 req-101、相同输入 识别原操作;不创建替代任务 原来已创建与现在是否可用是两回事
创建尚在进行时的匹配重试 相同的 req-101、相同输入 报告处理中或找回最终结果;不进行第二次创建 并发的首次尝试需要协调处理
同一 ID,有意义的输入发生变化 req-101,输入变为 forest/small 拒绝不匹配;保留原关联 不悄悄修改首次请求,也不签发新 ID
身份可能过期后的找回 旧 ID,记录不可用 在找回政策能够对账前,结果保持未确定 超出已定义有效期后,不保证去重

这张表是需求材料,不是实现结果。它给审阅者提供了可以与拟议设计逐项比较的具体案例。

明确 ID 不变、意图改变时怎么办

假设应用意外地再次提交 req-101,但这次使用 palette=forest。

在本示例中,选择一种校验响应,例如 Request ID already used with different settings。该响应不创建额外任务,并保持 req-101 → job-17 不变。如果用户想创建新的 forest 任务,可以有意使用新 ID 开始另一次创建。

不要要求 agent 自动替换 ID 来“修复”这种不匹配。那会把一个含义不明确的请求变成另一次副作用。

AWS 针对这种情况描述了参数不匹配的校验响应。虚构应用仍需定义哪些输入承载意图,以及如何比较它们。例如,不能随意把一个传输时间戳视为新的业务意图。同一请求 ID,不同意图

定义有效期,以及要保护的副作用

实现之前,要求先决定以下边界:

边界 设计必须回答的问题
请求范围 ID 在哪种操作和哪个已授权命名空间内有意义?
进行中的工作 两次匹配尝试在任何一次得到最终结果前到达时,怎么办?
提交 请求关联与任务记录如何一起提交?
已知失败 服务能否确认创建在任何提交发生前就失败了,而不是仅仅丢失响应?
保留 支持找回多长时间?任务本身被移除时怎么办?
过期 如何识别旧请求或已过期请求,或者对其进行对账?哪些内容仍未知?
后续处理 创建记录是否还会调度工作、写入文件或通知其他服务?

预期的“仅创建”契约要求身份记录与任务创建共享一个原子提交边界。如果先进行独立创建,再记录一个“done”标记,就会留下需要调查的崩溃间隙。并发尝试也需要在同一个设计中考虑。AWS 的工程讨论同样包含身份跟踪与状态变更之间的协调。身份与状态变更的边界

将承诺的效果保持在小范围内。一条任务记录不能证明 worker 只执行一次、只生成一个文件,或只发送一条通知。若加入这些效果,就需要分别分析它们的失败与重放行为。

幂等键是契约的一部分,并不能证明存储、并发、保留和下游行为已经满足这份契约。

给编码 agent 一份完整任务说明

以下任务说明同时保留了示例中的决定和尚未解决的问题。契约仍不完整的地方,要求先设计、再实现。

目标
为一个虚构应用设计请求身份行为。该应用根据内置、非敏感的配色预设
创建预览任务记录。

示例操作
命名空间 board-a 中的 create-preview-job。
有意义的输入:palette 和 size。
该操作仅创建任务记录。渲染、worker 调度、通知及外部服务不在本次
修改范围内。

意图规则
- 使用相同请求 ID 和相同输入,重试同一次逻辑创建。
- 有意发起第二次创建时使用新 ID,即使输入完全相同。
- 相同命名空间/操作/ID 搭配不同的有意义输入,属于不匹配:
  拒绝它,不创建额外任务,也不改变原映射。
- 不得悄悄签发新 ID,以修复不确定的结果或输入不匹配。

必需案例
1. 创建已提交,但响应丢失。重试识别原任务。
2. 用户选择 Create another。新 ID 允许创建第二项任务。
3. 延迟重放在原请求记录仍被保留时到达,包括该任务已被移除之后。
   它识别原操作,不重新创建已移除的任务。
4. 创建尚在进行时,并发的匹配重试到达。
   不得创建第二项任务;定义此时的处理中响应。
5. 相同 ID 搭配改变后的输入到达。拒绝不匹配,保留原结果。
6. 找回发生时,所保留的身份可能已经过期。
   保持结果未确定,提出对账方式。不要声称仅凭缺少记录就能证明
   请求是新的。

边界与待定事项
说明记录请求身份与创建任务如何共享一个原子提交边界,包括并发
首次尝试和可能发生崩溃的位置。
区分已接受的创建回执与任务之后的完成状态。
实现前,提出命名空间/授权边界、有意义输入的比较方式、保留期限、
过期识别及找回查询方案。
这里尚未批准任何保留时长或过期请求行为。
明确说明去重记录与任务删除行为之间的关系。

交付
先返回状态模型、意图/ID/结果矩阵、三个事件序列、不匹配响应,
以及尚未解决的决定。
这些决定达成一致后,再提出最小的仅创建改动,以及针对已约定
契约的检查。清楚区分计划检查与实际执行结果。
不要顺带添加自动重试、worker、外部调用或部署。

接受改动前,先审阅解释

要求 agent 在三个序列中始终使用相同的 ID 逐步说明。随后检查输入不匹配、并发匹配尝试和过期案例。仅改一个字段名或禁用按钮,不足以回答这些问题。

检查交付是否区分了设计、已实现行为和实际验证。有用的结果会告诉你,哪些证据支持创建契约,哪些假设仍未解决。如果实现无法支持预期的找回有效期,就先缩小契约,或解决这一设计问题,再将重试描述为安全。