## 本单的独立交付 仅同目标模型重试;明确 maxAttempts 包含第一次调用。 ## 输入与输出 错误分类、最大总尝试次数和退避配置 → 有界 attempt 序列或明确终态。 ## 必须通过的行为验收 maxAttempts=3 连续失败恰好调用 3 次;不可重试错误调用 1 次;退避期间取消后零新增调用;deadline 不足不再启动下一次;实际 usage 不重计。 ## 非目标 不切换模型、不重放工具、不实现持久预算;#210 决定流式重试边界。 ## 依赖与完成条件 父任务 #304;公共扩展契约/执行链复用 #298/#299,声明式装配复用 #300,引擎回调接线复用 #301。只依赖本单实际使用的最小阶段,不等待全部生命周期阶段。 提交需包含实际装配入口、配置示例、上述行为断言及旧配置保留/迁移说明;回调触发日志不足以验收,必须验证实际请求、结果或资源行为。离线夹具无需外部服务即可复现;真实服务保证单列证据。 ## 实施约束 本节收窄此前范围,移出的职责由关联任务承担。建设任务,不是新 BUG;复用已有实现,独立 Agent/Workflow 不依赖 Graph/Loop,不强制新增模块。基线 Core 04c5686fe66b8daaa5d60190effa0cc1f40fdfe9。破坏性变化须比较较小方案并说明收益、影响和迁移。核心可信身份、取消/预算、必经检查及提交保证不能被可选插件绕过。
Activity
feat(agent): control model retry attempts with a deadline-aware polic…
konlue commentedon Nov 4, 2026
您好,我想认领 #313。
基于 main
03227f7b3确认:ModelRetryInterceptor.Builder.isRetryableException按正文子串判定(i/o error、connection、timeout、handshake、socket),外加类名/cause 链匹配——正文里带这些词的永久性错误会被重试,而正文陌生的瞬时错误不会。[Refactor][P1] 将模型路由、重试与 Fallback 装配为生命周期策略插件 #304 已明确要求「根据结构化错误决策,不通过异常正文猜测」。interceptModel的循环无条件调用handler.call(request),整个类没有任何 deadline 概念;而 run 级 deadline 由ExecutionControl/TrustedExecutionContext.deadline()承担,只在交付边界(ReactAgent.deliver/finalizeUnderGuard)被检查——模型重试路径完全绕过了它。checkInterrupted()观察线程中断;而运行取消是经ExecutionControl.cancel()投递的,不打断线程。因此退避期间取消后,下一次 provider 调用照常发出。ExecutionGuard本身已经具备assertActive()/onLoss(Runnable)/deadline(),其 javadoc 也明确写了「阻塞等待需切片以在等待中执行 deadline」,但模型请求根本没有携带它的通道。pause(long)用Thread.sleep阻塞调用线程,流式路径用Flux.delaySubscription响应式等待;阻塞期间两者都不检查运行控制。方案(单 PR,只做受控重试契约与守卫传播,不动既有默认行为)
argi-agent-api新增io.github.agentic.ai.agent.retry包(ModelCallFailureCategory、ModelCallFailure、ModelCallFailureClassifier、TypeBasedModelCallFailureClassifier、RetryPolicy、RetryStopReason、RetryDecision、ModelRetryController)。该模块只依赖argi-api与argi-runtime-api(无 Reactor、无 Graph/Loop),中立 ReAct 与框架 ReactAgent 两条路径共用同一套裁决。默认分类器只看失败类型与 cause 链,不解析任何异常正文;UNKNOWN默认不可重试(fail-closed)。ModelRequest增加可选(nullable)的ExecutionGuard字段——类型化字段,不进getContext()/getAttributes(),因此绝不会透传给 provider;builder(ModelRequest)拷贝时一并保留。接线两处生产构造点:ReActAgent.modelTurn传context.control(),AgentLlmNode传config.executionGuard()。不新增任何 runtime 抽象,直接复用既有ExecutionGuard。ModelRetryInterceptor把裁决委派给ModelRetryController。每次尝试前检查守卫与 deadline(已过期 / 剩余不足deadlineReserve/ 退避会越过 deadline → 都不再派发);有守卫时退避切片等待且每片前assertActive(),实现「退避期间取消 → 新增调用 0 次」;ExecutionStoppedException原样透传,不再被包装成「重试耗尽」而掩盖运行停止的真实原因。retryableExceptionPredicate仍是默认判定,既有 Builder 选项与默认值全部保留);类型化分类器、retryableCategories、deadlineReserve、clock均为 opt-in。退避计算上移到中立策略,框架内重复实现的退避辅助类随之删除(其增长/封顶/零延迟覆盖由新控制器测试接管)。测试(离线,模型无关)
I/O error: connection timeout的永久错误只调用 1 次;未配置分类器时既有谓词仍按原语义判定(回归)。ExecutionStoppedException),而非「重试耗尽」。ModelRetryInterceptorTest、ModelRetryCancellationTest、AgentLlmNodeErrorHandlingTest的断言不改、不动,全绿。fix(agent): isolate streaming retry state per subscription and observ…
fix(agent): preserve the full configured backoff across reactive dela…
Merge pull request #356 from konlue/feat/issue313-controlled-model-retry