尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Spring AI遇到429或超时后为什么重复执行工具?重试边界与幂等完整排查

Spring AI遇到429或超时后为什么重复执行工具?重试边界与幂等完整排查
📅 发布时间:2026/8/1 1:19:42

文章摘要

AI接口出现429、超时或连接中断后,开发者通常会增加自动重试。但在Tool Calling场景中,如果重试包裹了整个Agent流程,退款、发送邮件、创建工单、写数据库等工具可能被重复执行。更隐蔽的情况是模型请求超时,但工具其实已经完成;客户端重试后,模型再次发起相同工具调用。本文从模型层、Agent层、工具层和HTTP层四个重试边界出发,给出幂等键、状态机、结果查询和可重试错误分类的完整方案。

一、典型事故

用户说:

给客户创建一个售后工单

执行链路:

模型选择create_ticket → 工具创建工单成功 → 返回模型时连接超时 → Agent整体自动重试 → 再次调用create_ticket → 创建第二个工单

从用户视角只发了一次请求,系统却产生两个业务对象。

如果工具是:

  • 退款;
  • 支付;
  • 发券;
  • 发邮件;
  • 删除数据;
  • 创建订单;

后果会更严重。

二、为什么“重试一次”会跨越多个层级

一个AI请求可能同时存在:

网关重试 HTTP客户端重试 Spring AI Provider重试 Resilience4j重试 Agent步骤重试 工具SDK重试 消息队列重投

如果每层都重试3次,最坏情况不是3次,而可能是乘法放大。

例如:

网关2次 × 应用3次 × 工具SDK3次 = 18次潜在调用

必须明确每层的职责。

三、四种重试边界

1. 模型调用重试

适合:

  • 429;
  • 暂时性5xx;
  • 连接建立失败;
  • 无副作用的模型请求。

风险:

如果模型调用发生在工具执行后,重试可能重新生成工具调用。

2. Agent步骤重试

适合:

  • 结构化输出解析失败;
  • 计划校验失败;
  • 可恢复的推理错误。

风险:

整个步骤可能包含多个工具副作用。

3. 工具调用重试

适合:

  • 只读查询;
  • 明确幂等写入;
  • 服务端支持幂等键。

4. 业务流程重试

适合:

  • 有持久化状态机;
  • 可以查询当前执行状态;
  • 能从检查点继续。

不能简单重新运行整个流程。

四、哪些错误可以自动重试

通常可重试

429 rate_limit_exceeded 502 503 504 连接被拒绝 短暂DNS失败 读超时且确认无副作用

通常不可直接重试

400参数错误 401认证失败 403权限不足 404资源不存在 insufficient_quota 内容安全拒绝 业务校验失败

状态未知

最危险的是:

请求超时

超时只说明客户端没有按时收到结果,并不说明服务端没有执行。

写操作超时后应该:

先查询执行状态 → 再决定是否重试

五、幂等键必须在模型之外生成

不要让模型自己生成随机幂等键。

模型可能每次重试都生成不同值。

正确做法:

业务请求进入 → 应用生成operationId → 同一个业务动作的所有重试复用

例如:

StringidempotencyKey=String.join(":",tenantId,conversationId,requestId,"create_ticket");

如果一次请求中允许创建多个工单,还要加入业务对象标识或步骤编号。

六、工具服务端如何实现幂等

表结构:

CREATETABLEtool_idempotency(idempotency_keyVARCHAR(200)PRIMARYKEY,tool_nameVARCHAR(100)NOTNULL,request_hashVARCHAR(128)NOTNULL,statusVARCHAR(30)NOTNULL,result_jsonTEXT,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL);

状态:

PROCESSING SUCCEEDED FAILED_RETRYABLE FAILED_FINAL

执行流程:

收到请求 → 插入PROCESSING → 已存在则读取状态 → SUCCEEDED直接返回历史结果 → PROCESSING返回处理中 → 可重试失败按规则执行

伪代码:

@TransactionalpublicToolResultexecute(Stringkey,ToolRequestrequest){Optional<IdempotencyRecord>existing=repository.findById(key);if(existing.isPresent()){returnrestore(existing.get(),request);}repository.insertProcessing(key,hash(request));try{ToolResultresult=doExecute(request);repository.markSucceeded(key,result);returnresult;}catch(RuntimeExceptionex){repository.markFailed(key,ex);throwex;}}

七、相同幂等键但参数不同怎么办

攻击或代码错误可能发送:

相同key +不同参数

例如第一次退款100元,第二次使用同一个key退款200元。

服务端必须比较request_hash。

如果不同:

返回409 Conflict

不能把第二次请求当成第一次的成功结果。

八、模型返回的tool_call_id能不能当幂等键

不建议单独使用。

tool_call_id通常只在一次模型响应中唯一。

Agent整体重试后,模型可能生成新的ID。

更稳定的是:

业务operationId +工具名 +步骤ID

可以把tool_call_id作为追踪字段,而不是唯一业务幂等依据。

九、重试应该包裹哪一层

错误:

@Retry(name="ai")publicStringrunAgent(Stringmessage){returnagent.run(message);}

如果agent.run()内部执行写工具,整个流程会重跑。

更安全:

模型只读推理调用 → 可重试 工具写操作 → 幂等执行 最终回答生成 → 可重试,但复用工具结果

将流程持久化:

PLANNED TOOL_EXECUTED ANSWER_GENERATING COMPLETED

最终回答失败后,从TOOL_EXECUTED继续,不再重复执行工具。

十、检查点设计

publicrecordAgentCheckpoint(StringexecutionId,Stringstate,StringtoolName,StringtoolResultLocation,intmodelAttempt,inttoolAttempt){}

执行:

模型选工具 → 保存计划 → 工具执行 → 保存结果 → 模型生成回答

任何一步失败都从最近检查点恢复。

十一、只读工具是否可以随便重试

只读工具通常风险较低,但仍可能有:

  • 外部API计费;
  • 强限流;
  • 数据查询压力;
  • 非稳定快照;
  • 重复下载大文件。

建议设置:

最大重试次数 指数退避 随机抖动 总超时 并发限制

十二、指数退避与Jitter

固定间隔:

1秒、1秒、1秒

大量实例会同时重试,造成惊群。

推荐:

1秒 2秒 4秒

并加入随机抖动。

Resilience4j示例:

resilience4j:retry:instances:aiModel:max-attempts:3wait-duration:1senable-exponential-backoff:trueexponential-backoff-multiplier:2retry-exceptions:-java.io.IOException-java.util.concurrent.TimeoutException

异常列表需要按实际Provider SDK调整。

十三、429的Retry-After要不要遵守

如果响应提供:

Retry-After: 10

应优先遵守。

但还要区分:

rate_limit_exceeded → 等待后重试 insufficient_quota → 不重试

两者都可能是HTTP 429。

十四、熔断器应该包在哪里

建议在模型Provider适配层设置熔断:

业务Service → ModelGateway → Circuit Breaker → Provider

不要用一个熔断器同时覆盖:

  • 模型;
  • 向量库;
  • 所有工具;

否则其中一个工具失败会关闭整个AI系统。

按依赖隔离:

openai-chat qdrant-search order-tool mail-tool

十五、降级策略

模型不可用:

Sol → Terra → Luna → 规则模板

RAG不可用:

生成回答 → 降级为关键词搜索结果

写工具不可用:

自动执行 → 创建待办 → 转人工

降级不能绕过审批和权限。

十六、需要记录哪些指标

model_retry_count tool_retry_count agent_restart_count idempotency_hit_count idempotency_conflict_count unknown_execution_status_count circuit_breaker_open_count fallback_model_count duplicate_business_object_count

重点告警:

同一operationId出现多个业务对象

十七、完整排查清单

□ 是否同时存在多层重试 □ 重试是否包裹整个Agent □ 写工具是否支持幂等键 □ 相同业务动作是否复用同一个key □ 是否保存request_hash □ 超时后是否先查询状态 □ 最终回答失败是否重复执行工具 □ tool_call_id是否被误作唯一幂等键 □ 429是否区分限流与额度不足 □ 熔断器是否按依赖隔离 □ 是否有检查点和状态机

总结

Tool Calling场景中,最大的错误不是“没有重试”,而是:

在错误的边界重试

生产级方案应该做到:

模型调用可重试 +工具写操作幂等 +业务流程有检查点 +超时先查状态 +最终回答复用工具结果

只有把模型推理和业务副作用分开,自动重试才不会变成重复执行。

相关新闻

  • 2026年8月海南省联通300M单宽带申请避坑实录 - 找卡家园
  • 2026年8月贵州省移动1000M宽带办理攻略 - 找卡家园
  • 2026年8月杭州市电信300M单宽带怎么办理 - 找卡家园

最新新闻

  • TSB空间斩特效:本地部署与游戏开发集成实践
  • 特殊字符串处理全攻略:从编码安全到工程实践
  • 2026 哈尔滨市道里区正规管道疏通优质服务商家详解 全域街道乡镇上门服务全覆盖 - 园子一号
  • 如何3步实现智能图片分层:Layerdivider的终极效率指南
  • 10101010
  • GNN预测分子爆炸性:原理、实现与工业应用

日新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号