# Send 失敗與 fallback

模型呼叫逾時、被限流、額度或 context 用盡時的處理，以及 fallback 如何挑下一個模型。

## Send 失敗處理

`Agent.Send()` 失敗採逐級升高處理，而非盲目重試：

| 失敗類型 | 行為 |
|---|---|
| Timeout | 於同一模型最多嘗試 `MaxSendTimeoutRetries`（3）次，間隔 `SendTimeoutRetryInterval`（15 秒） |
| Rate limit（HTTP 429 或 rate-limit 訊息） | 註冊 30 分鐘 cooldown，於同一模型在 5 秒、10 秒、15 秒後重試；之後切換到下一個 fallback |
| 額度耗盡（HTTP 402 / 403 或額度 / 帳務訊息） | 註冊 30 分鐘 cooldown，立即切換到下一個 fallback |
| Context 長度超出 | v1.1.1 起先壓縮歷史（與超出 token 預算時相同的 `ExtractOldHistories` → `ToolHistory`）後重送；無法壓縮時才裁掉最舊的一組對話（`compact.TrimFallback`）重送；只有在無可裁剪時才中止 |
| 串流中無回應 | 每 30 秒 health probe（`UnresponsiveProbeInterval`），探測失敗後每 10 秒重試，失敗 3 次即切換模型 |
| 其他錯誤 | 切換到下一個 fallback 模型；無可用 fallback 時中止 |

Fallback 候選來自 `ResolveAgent` 排好的清單，自 v1.1.0 起不含 `pass` tier 模型。`nextAgent` 會略過與失敗模型同供應商的模型、以及 context window 小於目前輸入的模型，對每個候選做 health check（`HealthCheckTimeout`，10 秒），並最多重建清單 3 輪（`maxFallbackRounds`）。綁定特定模型（`auto` 以外）的 session 不會 fallback。

切換模型會清空 `ToolHistories`（或套用 `compact.RawToolFallback`）、重置去重表並歸零計數器——新模型不會繼承失敗模型的殘留狀態。

## Timeout 層級

| 層級 | 值 | 攔截的問題 |
|---|---|---|
| Provider HTTP client | 由 `go-llm-router` 各 provider 內部設定 | 傳輸層卡住 |
| `AgentSendTimeoutSec` | `config.json` 的 `limits.agent_send_timeout_seconds`，預設 `600` | exec 層以 `context.WithTimeout` 設下的上限 |
| 無回應 watchdog | 每 30 秒探測；探測失敗後每 10 秒重試；失敗 3 次即切換模型 | 串流卡住但未報錯 |
| Health check | 10 秒 | 各 fallback 候選的存活探測 |
