治理接口超时与重复执行,可以把一次工具调用分成“准备执行、处理中、成功、明确失败、结果未知”五种状态。超时进入“结果未知”,Runtime 先用同一个业务操作编号查询外部结果;确认未执行后才允许重试。发消息、建工单和追加数据等动作,还需要下游系统提供幂等键、唯一约束或事务保护。
ZGI 可以把 Agent、Workflow、工具与运行状态放在同一条执行链中。团队能将“调用接口、等待结果、查询外部状态、人工核对”拆成明确节点,并沿任务记录查看流程停在哪一步。遇到超时后,不必立刻重跑整条任务,可以先进入核对节点,再决定复用结果或继续执行。
超时不能直接记成失败
接口在返回结果之前,往往已经开始执行。创建工单完成后响应包丢失,调用方看到的只有超时;此时再次提交,可能生成第二张工单。把超时直接标记为失败,会让自动重试和人工重试同时放大风险。
任务状态应当单独保留“结果未知”。这个状态不会继续向后推进,也不会立刻重放动作,而是进入结果查询或人工核对。外部系统能按业务操作编号查询时,Runtime 可以重新取得工单号、消息 ID 或写入记录,再把任务更新为成功。
同一次业务动作始终使用同一个编号
业务操作编号用于表达调用意图。第一次准备执行时生成,自动重试、任务恢复和人工重试继续携带同一个编号。接收端发现编号已成功处理,直接返回原结果;编号仍在处理中,返回可查询状态;相同编号对应的关键参数发生变化,则拒绝执行并报告冲突。
编号不能在每次重试时重新生成,否则接收端会把重试识别成新任务。只对全部参数做哈希也不够可靠,两次内容相同的通知可能本来就需要分别发送。稳定编号应当关联调用方、目标对象、动作类型和一次明确的业务意图。
| 动作类型 | 超时后的处理 | 下游保护方式 |
|---|---|---|
| 查询资料 | 在次数上限内退避重试 | 限流与超时控制 |
| 更新固定记录 | 使用原操作编号重试 | Upsert、版本条件或唯一约束 |
| 创建记录 | 先查询已有结果 | 幂等键与原结果复用 |
| 发消息、扣款 | 无法确认时暂停 | 服务端查重或人工核对 |
重试规则要看错误能否自行恢复
参数错误、权限不足和业务规则冲突,重复请求通常不会得到新结果,应当停止并返回可处理的原因。网络中断、限流和临时服务错误,可以采用次数上限、指数退避和随机抖动。超时则先查询,因为调用方无法仅凭超时判断外部动作有没有发生。
Runtime 保存每次尝试时,至少记录任务编号、操作编号、调用目标、关键参数摘要、开始时间、结果状态和外部结果标识。参数中涉及凭据或敏感数据时,只保留排查需要的摘要或脱敏字段。运行记录用于串起一次任务,不能把密钥写进日志。
用 ZGI 缩短超时排查路径
Agent 可以判断何时调用工具,Workflow 可以把高风险动作放在审批之后,并在超时后进入核对节点。工具适配层负责把稳定的操作编号传给外部接口,Runtime 承接任务状态与节点执行过程,接收端再用幂等约束控制重复结果。ZGI 把这些环节接在同一工作区后,定位时可以从当前节点直接追到调用目标和后续处理路径,减少在 Agent、脚本和业务系统之间来回确认。
这里需要避免一个误区:工作流中出现重试字段,不能自动证明所有节点都已经具备安全重试。上线前应使用真实的副作用动作验证完整路径,包括接口写入完成后主动丢弃响应、运行进程在调用后中断、两次请求并发到达,以及人工在“结果未知”状态点击重试。
验证结果要同时查看 Runtime 运行记录和下游业务数据。页面只显示一次“成功”,下游却出现两条记录,防重复约束仍未生效。确认同一操作编号在各种中断条件下都只对应一个业务结果后,再逐步开放自动重试。
GitHub:github.com/zgiai/zgi
Gitee:gitee.com/zgiai/zgi