AgentSession — Auto-Retry(自动重试)

文件位置:F:\Pi\packages\coding-agent\src\core\agent-session.ts

依赖的配置类型:F:\Pi\packages\coding-agent\src\core\settings-manager.tsRetrySettingsgetRetrySettings()

UI 事件消费:F:\Pi\packages\coding-agent\src\modes/interactive/interactive-mode.tsauto_retry_start / auto_retry_end 事件处理

编写目的:详细拆解 AgentSession 中 Auto-Retry(自动重试)机制的完整实现,包括 agent-session.ts 中的编排方法、settings-manager.ts 中的配置管理,以及 interactive-mode.ts 中的 UI 消费端。


一、Auto-Retry 概述

Auto-Retry(自动重试)是 AgentSession 中用于自动恢复 LLM 请求失败的机制。当 LLM 提供商返回临时性错误(过载、限流、网络波动)时,Pi 不会直接放弃,而是自动等待一段时间后重试。

设计目标

  1. 零干扰恢复:对于临时性的服务端错误,用户无需手动操作,系统自动重试
  2. 指数退避:重试间隔逐次翻倍,避免过载时雪上加霜
  3. 非重试错误不重试:配额耗尽、上下文溢出等不可恢复错误直接跳过
  4. 用户可控:用户可在重试倒计时期间按 Escape 取消,并可配置是否启用

什么错误会被重试

错误类型重试?示例
服务端过载overloaded, service unavailable, 503
限流rate limit, 429, too many requests
网络瞬断connection refused, connection lost, fetch failed
超时timed out, timeout, stream ended before message_stop
服务器内部错误500, 502, 503, 504, server error
WebSocket 异常websocket closed, other side closed
配额耗尽quota exceeded, insufficient_quota, available balance
上下文溢出由 Compaction 处理,不是重试的职责
语法/参数错误重试也没用

默认配置

1
2
3
4
5
{
  enabled: true,     // 默认开启
  maxRetries: 3,     // 最多重试 3 次
  baseDelayMs: 2000  // 基础等待 2 秒,逐次翻倍
}

二、AgentSession 中的 Auto-Retry 方法

2.1 _isRetryableError(message) — 判断是否可重试

1
private _isRetryableError(message: AssistantMessage): boolean

位置agent-session.ts 第 2495 行

功能:判断一个 assistant 消息的 stopReason === "error" 是否属于可重试的错误

执行流程

1. stopReason !== "error" 或没有 errorMessage → 不重试
2. 上下文溢出(isContextOverflow)→ 不重试(交给 Compaction)
3. 不可重试的配额限制 → 不重试(见 _isNonRetryableProviderLimitError)
4. 正则匹配以下错误类型 → 重试

正则匹配的可重试错误模式

overloaded
provider.?returned.?error
rate.?limit | too many requests | 429
500 | 502 | 503 | 504
service.?unavailable | server.?error | internal.?error
network.?error | connection.?error | connection.?refused | connection.?lost
websocket.?closed | websocket.?error | other side closed
fetch failed
upstream.?connect | reset before headers | socket hang up
ended without | stream ended before message_stop
http2 request did not get a response
timed? out | timeout | terminated
retry delay

可重试错误完整清单

下表列举 _isRetryableError() 正则匹配的所有错误模式,每条给出触发示例和重试意义:

类别匹配模式典型错误示例重试原因
服务端过载overloaded"overloaded_error: server is overloaded"服务端负载高,等待几秒后可能恢复
服务端错误provider.?returned.?error"provider returned error: internal error"通用的上游服务端异常,通常是临时的
限流rate.?limit"rate limit exceeded, retry after 30s"短时间请求过多,等待后配额重置
限流too many requests"too many requests, please slow down"同上,不同格式
限流429"429 Too Many Requests"HTTP 429 状态码,标准限流响应
服务端错误500"500 Internal Server Error"HTTP 500,服务端临时不可用
服务端错误502"502 Bad Gateway"网关/代理层临时故障
服务端错误503"503 Service Unavailable"服务正在重启或维护
服务端错误504"504 Gateway Timeout"上游服务响应超时
服务不可用service.?unavailable"service temporarily unavailable"服务端临时不可用(同 503 但文本格式)
服务端错误server.?error"server error occurred, please retry"通用服务器错误
服务端错误internal.?error"internal error encountered"内部错误,通常是瞬时的
网络错误network.?error"network error: connect ETIMEDOUT"网络层故障,瞬时可恢复
连接错误connection.?error"connection error: socket hang up"连接异常断开
连接被拒connection.?refused"connect ECONNREFUSED"目标端口未监听,服务正在重启时常见
连接丢失connection.?lost"connection lost while reading response"长连接意外中断
WebSocketwebsocket.?closed"websocket connection closed unexpectedly"流式连接非正常关闭
WebSocketwebsocket.?error"websocket error: unexpected frame"WebSocket 协议层错误
连接异常other side closed"other side closed the connection"对端主动关闭连接
HTTP 层fetch failed"fetch failed: aborted"请求在传输层失败
HTTP 层upstream.?connect"upstream connect error or disconnect/reset before headers"反向代理层连接上游失败
HTTP 层reset before headers"stream reset before headers received"HTTP/2 流在收到响应头前被重置
HTTP 层socket hang up"socket hang up"Node.js HTTP 请求的经典超时错误
流式错误ended without"response ended without completing"LLM 流式响应未完成就结束
流式错误stream ended before message_stop"stream ended before message_stop"SSE 流在收到停止标记前中断
HTTP/2http2 request did not get a response"http2: request did not get a response"HTTP/2 连接复用中的响应丢失
超时timed? out"connection timed out"请求在超时周期内未完成
超时timeout"stream timeout after 30000ms"流式读取超时
终止terminated"request terminated by server"服务端主动终止请求
退避retry delay"retry delay not yet elapsed"SDK 内部建议等待后重试

不可重试错误完整清单

下表列举 _isNonRetryableProviderLimitError()_isRetryableError() 明确排除的所有错误模式:

排除方式匹配模式典型错误示例不重试原因
_isNonRetryableProviderLimitErrorGoUsageLimitError"GoUsageLimitError: rate limit exceeded"免费套餐额度已用尽,充值前无法恢复
_isNonRetryableProviderLimitErrorFreeUsageLimitError"FreeUsageLimitError: free tier limit reached"同上
_isNonRetryableProviderLimitErrorMonthly usage limit reached"Monthly usage limit reached, upgrade to continue"月度额度耗尽
_isNonRetryableProviderLimitErroravailable balance"insufficient available balance"账户余额不足
_isNonRetryableProviderLimitErrorinsufficient_quota"insufficient_quota: you have exceeded your quota"API 配额用完
_isNonRetryableProviderLimitErrorout of budget"out of budget, please upgrade your plan"预算超限
_isNonRetryableProviderLimitErrorquota exceeded"quota exceeded for this API key"同上,不同格式
_isNonRetryableProviderLimitErrorbilling"billing error: account past due"计费账号异常
_isRetryableError 前置排除isContextOverflow"context tokens exceeded limit"由 Compaction 机制处理,重试同样会溢出
_isRetryableError 前置排除stopReason !== "error"(正常回复或中止回复)非错误消息不需要重试
_isRetryableError 前置排除!message.errorMessage(无错误信息的 error)缺乏可判断的错误信息

设计意图:这些错误都是临时性的。服务端过载时等待几秒可能就恢复了;网络抖动是偶发的;限流等待退避后可能解除。相比直接失败,重试的收益远大于成本。


2.2 _isNonRetryableProviderLimitError(errorMessage) — 不可重试的配额限制

1
private _isNonRetryableProviderLimitError(errorMessage: string): boolean

位置agent-session.ts 第 2488 行

功能:判断错误消息是否属于"配额耗尽"类型的不可重试错误

匹配的错误模式

GoUsageLimitError
FreeUsageLimitError
Monthly usage limit reached
available balance
insufficient_quota
out of budget
quota exceeded
billing

设计意图:配额耗尽不是临时性问题,单纯等待不会恢复,重试只会浪费 token 和 API 费用。必须由用户通过 /login 充值或切换模型解决。


2.3 _prepareRetry(message) — 准备重试

1
private async _prepareRetry(message: AssistantMessage): Promise<boolean>

位置agent-session.ts 第 2514 行

功能:执行重试前的准备工作,包括递增计数器、指数退避等待、移除错误消息。返回 true 表示调用者应继续 agent 循环(即执行重试),false 表示已达最大重试次数或已取消。

完整执行流程

_prepareRetry(msg)
    │
    ├─ settings.enabled === false ────→ return false
    │
    ├─ this._retryAttempt++
    │
    ├─ this._retryAttempt > maxRetries ──→ this._retryAttempt--, return false
    │                                      (保留已完成的次数,用于发出 finalError 事件)
    │
    ├─ 计算延迟:
    │   delayMs = baseDelayMs * 2 ^ (attempt - 1)
    │            = 2000ms → 4000ms → 8000ms (默认配置、3次重试)
    │
    ├─ 发出 auto_retry_start 事件(含 attempt、maxAttempts、delayMs、errorMessage)
    │
    ├─ 从 agent.state.messages 中移除错误消息
    │   (保留在 session 文件中用于历史,但 agent 状态中清除,避免重试后消息重复)
    │
    ├─ 创建 retryAbortController
    │
    ├─ await sleep(delayMs, signal) ── 被中止 ──→ 发出 auto_retry_end 事件
    │                                              _retryAttempt = 0
    │                                              return false
    │
    └─ return true(调用者应继续 agent 循环)

指数退避公式

重试次数等待时间
第 1 次baseDelayMs * 2^0 = 2000ms = 2s
第 2 次baseDelayMs * 2^1 = 4000ms = 4s
第 3 次baseDelayMs * 2^2 = 8000ms = 8s

设计意图:指数退避是分布式系统中处理临时故障的经典策略。第一次重试等 2 秒(服务端可能立即恢复),第二次等 4 秒(需要更多恢复时间),第三次等 8 秒。逐次翻倍避免在服务端压力大时进一步加剧负载。


2.4 abortRetry() — 取消重试

1
abortRetry(): void

位置agent-session.ts 第 2549 行

功能:中止正在进行的重试等待。由 UI(用户按 Escape)或 dispose()/abort() 等清理方法调用。

1
2
3
abortRetry(): void {
  this._retryAbortController?.abort();
}

2.5 isRetrying / autoRetryEnabled / setAutoRetryEnabled() — 状态查询与开关

1
2
3
get isRetrying(): boolean
get autoRetryEnabled(): boolean
setAutoRetryEnabled(enabled: boolean): void

位置agent-session.ts 第 2553-2590 行

功能

方法返回说明
isRetryingboolean当前是否有重试正在进行(_retryAbortController 非空)
autoRetryEnabledboolean委托给 settingsManager.getRetryEnabled(),返回配置中的启用状态
setAutoRetryEnabled(enabled)void调用 settingsManager.setRetryEnabled(enabled) 持久化设置

2.6 _willRetryAfterAgentEnd(event) — Agent 结束时判断是否要重试

1
private _willRetryAfterAgentEnd(event: Extract<AgentEvent, { type: "agent_end" }>): boolean

位置agent-session.ts 第 562 行

功能:在 agent_end 事件处理中,判断是否应该在 agent 循环结束后启动重试。这个判断是只读的,真正的重试启动在 _handlePostAgentRun() 中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private _willRetryAfterAgentEnd(event): boolean {
  // 1. retry 未启用或已达最大次数 → 不重试
  if (!settings.enabled || this._retryAttempt >= settings.maxRetries) return false;

  // 2. 找最后一条 assistant 消息
  for (let i = event.messages.length - 1; i >= 0; i--) {
    if (message.role === "assistant") {
      return this._isRetryableError(message);  // 判断是否可重试
    }
  }
  return false;
}

返回值用于:agent_end 事件的 willRetry 字段,让 UI 端(以及 extension)可以知道 agent 结束是因为失败后将重试,还是真正的结束。


三、Auto-Retry 在 Workflow 中的实例实现

3.1 完整调用链

用户输入 prompt
     │
     ▼
prompt() → _runAgentPrompt()
     │
     ▼
agent.prompt(messages)
     │
     ▼
  [LLM 请求失败,返回错误消息]
     │
     ▼
_handleAgentEvent()
  → message_end 事件
    → 记录 _lastAssistantMessage
    → 如果 stopReason !== "error",重置 retryAttempt
     │
     ▼
agent_end 事件
  → _willRetryAfterAgentEnd() 计算 willRetry 字段(用于 UI 显示)
     │
     ▼
_runAgentPrompt() 的 finally 块执行完毕
     │
     ▼
_handlePostAgentRun()
  → 检查 _lastAssistantMessage
  → _isRetryableError() → true
  → _prepareRetry()
    → 递增计数器
    → 发出 auto_retry_start 事件
    → 从 agent.state 中移除错误消息
    → 等待指数退避(可被 abort 取消)
  → return true(告诉循环调用 agent.continue())
     │
     ▼
agent.continue()
  → 重新发送 LLM 请求(重试)
     │
     ├─ 成功 → 正常处理回复,发出 auto_retry_end(success=true)
     │
     └─ 再次失败 → 再次进入 _handlePostAgentRun()
                    → 重试次数已达上限
                    → 发出 auto_retry_end(success=false, finalError)
                    → 返回 false,循环结束

3.2 _handlePostAgentRun() 中的重试挂载点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private async _handlePostAgentRun(): Promise<boolean> {
  const msg = this._lastAssistantMessage;
  this._lastAssistantMessage = undefined;
  if (!msg) return false;

  // ★ 重试在这里被处理 — 在 compaction 之前
  if (this._isRetryableError(msg) && (await this._prepareRetry(msg))) {
    return true;  // 告诉上层调用 agent.continue() 重试
  }

  // 重试全部失败后,发出失败事件
  if (msg.stopReason === "error" && this._retryAttempt > 0) {
    this._emit({
      type: "auto_retry_end",
      success: false,
      attempt: this._retryAttempt,
      finalError: msg.errorMessage,
    });
    this._retryAttempt = 0;
  }

  // ★ 然后才检查 compaction
  if (await this._checkCompaction(msg)) return true;

  return this.agent.hasQueuedMessages();
}

优先级顺序

后处理链优先级:
1. Auto-Retry → 2. Compaction → 3. Queue drain

重试的优先级高于 Compaction,因为重试是轻量级操作(只是重新发请求),而 Compaction 是重量级操作(调用 LLM 生成摘要)。如果一个错误是可重试的,先重试,只有重试全部失败后才考虑其他路径。

3.3 成功回复后重置计数器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 在 _handleAgentEvent 中的 message_end 处理:
if (event.message.role === "assistant") {
  const assistantMsg = event.message as AssistantMessage;

  // ★ 成功回复(非 error)立即重置计数器
  if (assistantMsg.stopReason !== "error" && this._retryAttempt > 0) {
    this._emit({
      type: "auto_retry_end",
      success: true,
      attempt: this._retryAttempt,
    });
    this._retryAttempt = 0;
  }
}

设计意图:一个 Assistant 消息回复可能会产生多次 LLM 调用(steer/followUp 队列)。计数器在成功回复后立即重置,防止跨多个 LLM 调用累积计数。


四、Interactive Mode 中的 UI 处理

4.1 auto_retry_start 事件

位置interactive-mode.ts 第 2999 行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
case "auto_retry_start": {
  // 1. 劫持 Escape 键 → 调用 session.abortRetry()
  this.retryEscapeHandler = this.defaultEditor.onEscape;
  this.defaultEditor.onEscape = () => { this.session.abortRetry(); };

  // 2. 显示重试指示器
  this.statusContainer.clear();

  // 3. 创建倒计时
  //    显示:"Retrying (1/3) in 2s... (Escape to cancel)"
  //    每秒更新剩余秒数
  this.retryCountdown = new CountdownTimer(
    event.delayMs,
    (seconds) => { retryLoader.setMessage(retryMessage(seconds)); }
  );

  // 4. 将指示器添加到状态栏
  this.statusContainer.addChild(this.retryLoader);
}

UI 效果:用户看到状态栏显示 Retrying (1/3) in 2s... (Esc to cancel),倒计时每秒更新。按 Escape 可立即取消重试。

4.2 auto_retry_end 事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
case "auto_retry_end": {
  // 1. 恢复 Escape 处理
  if (this.retryEscapeHandler) {
    this.defaultEditor.onEscape = this.retryEscapeHandler;
  }

  // 2. 停止倒计时和指示器
  this.retryCountdown?.dispose();
  this.retryLoader?.stop();
  this.statusContainer.clear();

  // 3. 失败时显示错误
  if (!event.success) {
    this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError}`);
  }
}

UI 效果

结果表现
重试成功倒计时消失,用户看到正常回复内容,无异常提示
重试全部失败显示红色错误信息,如 “Retry failed after 3 attempts: overloaded”
用户取消显示 “Aborted after 1 retry attempt”

五、配置管理

5.1 RetrySettings 接口

位置settings-manager.ts 第 21 行

1
2
3
4
5
6
7
export interface RetrySettings {
  enabled?: boolean;     // 默认 true — 是否启用自动重试
  maxRetries?: number;   // 默认 3 — 最大重试次数
  baseDelayMs?: number;  // 默认 2000 — 基础等待时间(毫秒)
                         // 实际等待 = baseDelayMs * 2^(attempt-1)
  provider?: ProviderRetrySettings;  // SDK 层面的重试配置
}

5.2 getRetrySettings() 实现

1
2
3
4
5
6
7
getRetrySettings(): { enabled: boolean; maxRetries: number; baseDelayMs: number } {
  return {
    enabled: this.getRetryEnabled(),           // 默认 true
    maxRetries: this.settings.retry?.maxRetries ?? 3,
    baseDelayMs: this.settings.retry?.baseDelayMs ?? 2000,
  };
}

注意getRetrySettings() 返回的是简化版本,仅包含 AgentSession 自身需要的字段。ProviderRetrySettings(含 timeoutMsmaxRetryDelayMs)是 SDK 层面的重试配置,不在 AgentSession 的 Auto-Retry 中使用。


六、与其他机制的交互

6.1 与 Compaction 的关系

机制处理场景优先级
Auto-Retry临时性服务端错误(过载、限流、网络波动) — 先尝试重试
Compaction上下文溢出(context overflow) — 重试不处理时才检查

_handlePostAgentRun() 中,重试检查在 Compaction 之前。这意味着:

  • 如果一条错误消息既是可重试的,又触发了上下文溢出 → 先重试
  • _isRetryableError() 中明确排除了 isContextOverflow → 所以上下文溢出错误会直接跳到 Compaction

6.2 与 Dispose/Abort 的关系

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dispose(): void {
  this.abortRetry();     // 清理中止重试
  this.abortCompaction();
  this.abortBranchSummary();
  this.abortBash();
  this.agent.abort();
}

async abort(): Promise<void> {
  this.abortRetry();     // 中止正在进行的重试
  this.agent.abort();
  await this.agent.waitForIdle();
}

设计意图:在 session 清理或手动 abort 时,确保重试也被一并中止,避免重试在后台继续运行。

6.3 与 Disconnect/Reconnect 的关系

1
2
3
4
5
6
private _disconnectFromAgent(): void {
  if (this._unsubscribeAgent) {
    this._unsubscribeAgent();
    this._unsubscribeAgent = undefined;
  }
}

当需要断开 agent 事件订阅(如 compact() 期间),重试状态(_retryAttempt_retryAbortController不受影响,因为这些状态存储在 AgentSession 实例中,而非 agent 事件流中。


七、设计总结

Auto-Retry 是 Pi 中最小但最有效的容错机制:

方面设计
触发条件LLM 请求返回 stopReason: "error",且错误匹配可重试模式
不可重试配额耗尽、上下文溢出、语法错误
等待策略指数退避:baseDelayMs * 2^(attempt-1)
默认配置3 次重试,2s/4s/8s 间隔
可取消用户按 Escape → abortRetry()
UI 反馈倒计时指示器 + 可取消提示
生命周期成功回复后立即重置计数器
优先级高于 Compaction,低于正常回复处理

一句话总结:Auto-Retry 是 Pi 在面对 LLM 临时性故障时的自动恢复机制——使用指数退避等待、在用户可见的倒计时内自动重试,成功后对用户完全透明,失败时提供明确的错误信息。


附录:错误匹配实现的三层架构

Auto-Retry 的错误匹配逻辑并非集中在一个文件中,而是分布在三个不同的层次,各自服务于不同的目的:

┌──────────────────────────────────────────────────────────────────┐
│  应用层 — AgentSession Auto-Retry                               │
│  F:\Pi\packages\coding-agent\src\core\agent-session.ts          │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ _isRetryableError()             通用正则匹配               │  │
│  │ _isNonRetryableProviderLimitError()                        │  │
│  └────────────────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────────────────┤
│  AI 层 — Provider 溢出检测                                      │
│  F:\Pi\packages\ai\src\utils\overflow.ts                       │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ isContextOverflow()             按 Provider 分类的溢出模式  │  │
│  │ OVERFLOW_PATTERNS[19]           19 个 Provider 特定正则     │  │
│  └────────────────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────────────────┤
│  SDK 层 — OpenAI Codex 请求重试                                  │
│  F:\Pi\packages\ai\src\api\openai-codex-responses.ts           │
│  ┌────────────────────────────────────────────────────────────┐  │
|  │ isRetryableError(status, text)  基于 HTTP 状态码的重试      │  │
|  │ isTerminalRateLimitError()      429 + 配额用尽 = 不重试     │  │
|  │ getRetryAfterDelayMs()          读取 Retry-After 响应头     │  │
|  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘

第一层:应用层 — AgentSession(通用模式匹配)

文件F:\Pi\packages\coding-agent\src\core\agent-session.ts 第 2482-2540 行

这一层的两个函数是完全通用的正则匹配,不区分具体 Provider。所有模式均为硬编码在 agent-session.ts 中的正则表达式:

_isNonRetryableProviderLimitError(errorMessage)

1
2
3
4
5
private _isNonRetryableProviderLimitError(errorMessage: string): boolean {
  return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|
           available balance|insufficient_quota|out of budget|
           quota exceeded|billing/i.test(errorMessage);
}

这些模式覆盖了主流 LLM 提供商的配额耗尽信息:

  • AnthropicGoUsageLimitError(免费额度用完)、FreeUsageLimitError
  • OpenAIinsufficient_quotaquota exceededbilling
  • Googlequota exceededout of budget
  • 通用Monthly usage limit reachedavailable balance

_isRetryableError(message)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private _isRetryableError(message: AssistantMessage): boolean {
  // 1. 不是 error 或无 errorMessage → 不重试
  if (message.stopReason !== "error" || !message.errorMessage) return false;

  // 2. 上下文溢出 → 不重试(由 Compaction 处理)
  const contextWindow = this.model?.contextWindow ?? 0;
  if (isContextOverflow(message, contextWindow)) return false;

  // 3. 配额耗尽 → 不重试
  if (this._isNonRetryableProviderLimitError(err)) return false;

  // 4. 其他情况 → 匹配通用错误模式
  return /overloaded|provider.?returned.?error| ... /i.test(err);
}

这一层的特点是不关心具体 Provider,只做通用判断。唯一的 Provider 相关知识来自 isContextOverflow() 的调用——但那是委托给下层的。


第二层:AI 层 — Provider 溢出检测(按 Provider 分类)

文件F:\Pi\packages\ai\src\utils\overflow.ts

这一层是 Auto-Retry 中唯一真正按 Provider 分类的过滤逻辑isContextOverflow() 维护了两组正则数组:

OVERFLOW_PATTERNS — 19 个覆盖主流 Provider 的正则

序号Provider正则示例错误
1Anthropicprompt is too long"prompt is too long: 213462 tokens > 200000 maximum"
2Anthropicrequest_too_large"413 Request exceeds the maximum size"
3Amazon Bedrockinput is too long for requested model"input is too long for requested model"
4OpenAIexceeds the context window"Your input exceeds the context window of this model"
5OpenAI/LiteLLMexceeds.*maximum context length"exceeds the model's maximum context length of 131072 tokens"
6Google Geminiinput token count.*exceeds the maximum"The input token count exceeds the maximum number of tokens"
7xAI Grokmaximum prompt length is"maximum prompt length is 131072 but request contains 537812 tokens"
8Groqreduce the length of the messages"Please reduce the length of the messages"
9OpenRoutermaximum context length is"maximum context length is 200000 tokens"
10OpenRouter/Poolsideexceeds.*maximum allowed input length"Input length X exceeds the maximum allowed input length of Y"
11Together AIinput.*tokens.*longer than.*context length"The input (X tokens) is longer than the model's context length"
12GitHub Copilotexceeds the limit of"prompt token count of X exceeds the limit of Y"
13llama.cppexceeds the available context size"the request exceeds the available context size"
14LM Studiogreater than the context length"tokens to keep is greater than the context length"
15MiniMaxcontext window exceeds limit"context window exceeds limit"
16Kimi For Codingexceeded model token limit"exceeded model token limit: X (requested: Y)"
17Mistraltoo large for model with.*context length"Prompt contains X tokens ... too large for model with Y context length"
18z.aimodel_context_window_exceededz.ai 将溢出暴露为 finish_reason
19Ollamaprompt too long; exceeded.*context length"prompt too long; exceeded max context length by X tokens"
20context_length_exceeded通用回退模式
21too many tokens通用回退模式
22token limit exceeded通用回退模式
23Cerebras`^4(?:0013).*\(no body\)`

NON_OVERFLOW_PATTERNS — 防止误杀的反向排除

这些模式虽然可能匹配 OVERFLOW_PATTERNS,但实际上是非溢出错误(如限流),需要排除:

模式来源 Provider原因
Throttling error: / Service unavailable:AWS BedrockBedrock 的限流信息格式为 “Throttling error: Too many tokens…",会误触 too many tokens 模式
rate limit通用限流信息有时包含 token 相关的描述
too many requests通用HTTP 429 中的通用描述

三种溢出检测模式

isContextOverflow() 实现了三种不同的溢出检测逻辑,分别应对不同 Provider 的行为:

检测模式适用 Provider触发条件
Error-basedAnthropic, OpenAI, Google, xAI, Groq, Mistral 等stopReason === "error" + 匹配 OVERFLOW_PATTERNS
Silent overflowz.aistopReason === "stop"input + cacheRead > contextWindow
Length-stopXiaomi MiMostopReason === "length" + output === 0 + input + cacheRead >= contextWindow * 0.99

文件源码引用:overflow.ts 第 1-178 行为正则定义与注释(每个 Provider 的典型错误消息都写在注释中),第 180-227 行为 isContextOverflow() 实现,第 229-234 行为测试用导出函数。


第三层:SDK 层 — OpenAI Codex 请求重试(HTTP 状态码)

文件F:\Pi\packages\ai\src\api\openai-codex-responses.ts 第 113-142 行

这一层与 Auto-Retry 机制正交。它是 OpenAI Codex Responses API 的 SDK 实现内部的请求重试逻辑,处理的是 HTTP 层面的瞬态错误:

isRetryableError(status, errorText)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function isRetryableError(status: number, errorText: string): boolean {
  // 429 + 配额用尽 → 不重试
  if (status === 429 && isTerminalRateLimitError(errorText)) {
    return false;
  }
  // 429, 5xx → 重试
  if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) {
    return true;
  }
  // 正则匹配补充
  return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
}

与 AgentSession Auto-Retry 的区别

维度AgentSession _isRetryableErrorSDK isRetryableError
层面应用层—LLM 消息层错误SDK 层—HTTP 请求层错误
输入AssistantMessage(含 stopReason)number status + string errorText
重试机制移除 agent state 中的错误消息,调用 agent.continue()循环 fetch,递增指数退避
退避计算baseDelayMs * 2^(attempt-1)优先读 Retry-After 响应头,否则 BASE_DELAY_MS * 2^attempt
覆盖 Provider通用(不区分)仅 OpenAI Codex Responses API
是否使用 isContextOverflow是(在 _isRetryableError 中调用)

两者的关系是串联的:

  1. SDK 层首先在 HTTP 层面处理网络抖动和限流(可能重试 2-3 次)
  2. 如果 SDK 层重试全部失败,错误最终会以 AssistantMessage.stopReason === "error" 的形式到达 AgentSession
  3. AgentSession 的 _handlePostAgentRun() 检查是否可重试,启动应用层的指数退避重试

架构总结:为什么这样分层

层级解决的问题为什么不合并
应用层(通用正则)判断一条 LLM 返回的消息是否值得重试这是语义级判断,与底层传输协议无关。所有 Provider 的临时性错误最终都表现为相似的关键词
AI 层(Provider 分类)如果 SDK 层重试全部失败,错误最终会以 AssistantMessage.stopReason === \"error\" 的形式到达 AgentSession\n3. AgentSession 的 _handlePostAgentRun() 检查是否可重试,启动应用层的指数退避重试\n\n—\n\n### 架构总结:为什么这样分层\n\n层级