前一天我們談 System Prompt Assembly。
核心觀念是:
System Prompt 不是一份固定文件,而是 Harness 根據目前狀態動態組裝出的執行介面。
當 Agent 開始真的執行多輪任務之後,下一個一定會遇到的問題是:
出錯了,現在怎麼辦?
最直覺的答案通常是:
Retry。
Tool 失敗,Retry。
模型輸出格式錯誤,Retry。
API Timeout,Retry。
測試失敗,Retry。
Agent 卡住,還是 Retry。
但如果所有錯誤都用 Retry 解決,系統最後很容易變成:
失敗
↓
再試一次
↓
又失敗
↓
換個 Prompt 再試
↓
還是失敗
↓
增加 Retry 次數
看起來很有韌性。
但系統沒有真正理解失敗,只是把同一個錯誤多執行幾次。
今天我們要把 Error Recovery 拆成四個問題:
先建立最重要的區分:
Retry 是 Recovery 的一種,不是全部。
真正的 Error Recovery 至少可能包含:
不同 Failure Mode 應該對應不同策略。
例如:
API 429
→ Retry
Tool Arguments 格式錯誤
→ Repair
主要搜尋 API 掛掉
→ Fallback
原本假設不存在
→ Replan
高階功能不可用
→ Degrade
需求缺少必要資訊
→ Ask User
高風險操作無法驗證
→ Human Handoff
Permission Denied
→ Stop 或改走其他路徑
所以 Recovery 的第一步不是執行。
而是分類。
Transient Failure 是暫時性錯誤。
例如:
這類錯誤的特性是:
同樣的操作稍後再做一次,成功機率會提高。
這才是 Retry 最適合的地方。
如果 API 已經 Rate Limit,立即連續 Retry 通常只會讓問題更嚴重。
更合理的策略是:
第一次失敗
↓
等待短時間
↓
Retry
第二次失敗
↓
等待更久
↓
Retry
再次失敗
↓
Fallback 或 Stop
這就是 Backoff。
常見方式包括:
例如:
delays = [1, 2, 4, 8]
Jitter 則是在等待時間中加入隨機量,避免大量 Agent 同時重試,造成 Thundering Herd。
如果沒有 Retry Budget,Agent 可以把一個小錯誤變成成本黑洞。
每個 Recovery Policy 最少要定義:
例如:
search_api
max_retries: 3
timeout: 10s
backoff: exponential
fallback: browser_search
Retry 的限制應該由 Harness 控制。
不要只在 Prompt 裡寫:
如果一直失敗,就不要再試了。
這和 Day 2 的 Turn Budget 是同一個原則。
可確定的限制,應該存在程式碼。
這是 Production Agent 很容易忽略的問題。
假設工具是:
read_file
Retry 通常沒有問題。
但如果工具是:
send_email
charge_card
create_order
deploy_service
第一次操作可能其實已經成功。
只是回應在網路途中丟失。
如果 Harness 看到 Timeout 就直接 Retry,可能造成:
所以 Side Effect Tool 不能把 Retry 當成預設安全行為。
對可重複呼叫的 Side Effect,可以加入 Idempotency Key。
例如:
operation_id = order_123_refund_v1
即使 Runtime 重送相同請求,外部系統也能辨認:
這是同一個操作,不應該執行兩次。
如果外部 API 不支援 Idempotency,也可以由 Harness 保存 Execution Record。
例如:
operation_id
tool_name
arguments_hash
status
external_reference
Retry 前先查:
這個操作是不是其實已經成功?
這比單純重送安全很多。
有些錯誤不會因為等一下再試就消失。
例如:
這類失敗適合:
Repair,而不是 Retry。
例如模型產生:
{
"tool": "read_file",
"arguments": {
"file": "config.yaml"
}
}
但 Tool Schema 要的是:
path
如果 Harness 完全不改資訊,只要求:
再試一次。
模型可能再次產生相同錯誤。
更好的方式是回傳結構化 Observation:
{
"ok": false,
"error_type": "validation_error",
"message": "Missing required field: path",
"expected_schema": {
"path": "string"
}
}
模型得到新的資訊後,再修正 Tool Call。
這是:
Failure
↓
Useful Observation
↓
Repair
↓
Retry corrected action
而不是盲目 Retry。
Tool Failure 不只是系統例外。
對 Agent 來說,它是新的環境資訊。
例如:
FileNotFound
可能代表:
所以 Error Recovery 的一個重要原則是:
把失敗轉換成模型可以理解、但不會洩漏不必要資訊的 Observation。
好的 Error Result 可以包含:
例如:
{
"ok": false,
"error_type": "file_not_found",
"retryable": false,
"message": "src/config.py does not exist",
"suggestion": "Search for files named config.py before retrying."
}
這會比完整 Stack Trace 更有用。
有時候工具本身沒有壞。
但整個策略錯了。
例如 Agent 想 Debug 一個問題:
假設:
問題來自 Redis
執行:
查 Redis Log
結果:
完全沒有相關錯誤
再次:
查更多 Redis Log
再次:
查更久的 Redis Log
每一次 Tool Call 都成功。
但 Strategy 沒有進展。
這時再 Retry 同類行動沒有意義。
應該:
Replan
Strategy Failure 的訊號可能包括:
這些都是:
Loop 正在移動,但任務沒有前進。
Error Recovery 不只要偵測 Exception。
也要偵測:
No Progress。
可以建立簡單訊號:
如果 Progress 太低,可以觸發:
Replan
Fallback
Escalate
Stop
這也是 Loop Engineering 很重要的一部分。
假設主要 Tool 不可用。
不一定要直接失敗。
例如:
Primary:
Internal Search API
Fallback:
Public Search
或者:
Primary:
Structured Parser
Fallback:
Plain-text extraction
Fallback 的目的不是追求完全一樣的品質。
而是:
主要能力失敗時,能否用較弱但可接受的方法繼續?
可以把 Recovery Level 想成:
Preferred Path
↓
Fallback Path
↓
Degraded Path
↓
Human Handoff
Fallback 最大的風險是:
系統悄悄變差,但使用者不知道。
例如原本:
Grounded Retrieval
失敗後偷偷改成:
模型憑記憶回答
表面上任務完成了。
但可靠度完全不同。
所以 Fallback 應該標記:
例如:
Primary source unavailable.
Continuing with cached data from 2 hours ago.
Freshness cannot be guaranteed.
這叫 Graceful Degradation。
不是 Silent Degradation。
假設 Agent 的原目標是:
產生完整 PDF 報告。
PDF Renderer 壞掉。
但 Agent 還可以:
這可能比整個 Task 直接 Fail 更好。
所以 Recovery 不一定要:
恢復成完整能力。
有時候合理目標是:
保留最大可用價值。
這就是 Degradation。
不是所有問題都應該讓 Agent 自己修。
例如:
請幫我寄信給 Alex。
系統發現 Contact 中有三個 Alex。
這不是 Tool Failure。
也不是模型能力不足。
它是:
缺少必要資訊。
如果 Agent 自己猜,風險很高。
這時最好的 Recovery 是:
Ask User。
適合 Ask User 的情況包括:
這不是失敗。
這是一種正確的控制流程。
有些 Failure Mode 已經超出 Agent 應該自行處理的範圍。
例如:
這時應該把目前狀態整理好,交給人類。
好的 Handoff 不應該只說:
Something went wrong.
而應該包含:
Goal
目前要完成什麼
Progress
已完成什麼
Failure
哪裡失敗
Attempts
已經試過什麼
Current State
目前環境狀態
Risk
有哪些不確定性
Recommended Action
建議人類下一步怎麼做
這樣人類才能直接接手,而不是重新調查。
Agent 系統常有一個錯誤偏見:
一定要繼續做,直到完成。
但有時候最正確的行為是停止。
例如:
停止不是 Agent 不夠聰明。
而是 Harness 正確限制了風險。
如果所有錯誤最後都只是:
Exception
Recovery 很難做。
可以先建立基本 Taxonomy。
例如:
不同 Error Type 對應不同 Recovery Policy。
如果每個 Tool 自己決定:
整個 Agent 很難有一致策略。
更好的方式是:
Tool
回傳結構化 Failure
Recovery Policy
決定下一步
Agent Loop
執行 Recovery Decision
例如:
Tool Result:
type = rate_limit
retryable = true
Recovery Policy:
max retries = 3
backoff = exponential
fallback = alternate provider
把 Error Detection 和 Recovery Decision 分開,更容易測試。
例如:
| Error Type | Default Recovery |
|---|---|
| Rate Limit | Backoff + Retry |
| Timeout | Retry once, then Fallback |
| Invalid Arguments | Repair |
| Unknown Tool | Replan |
| Permission Denied | Alternative Path or Stop |
| Verification Failed | Replan |
| Missing User Input | Ask User |
| Budget Exhausted | Degrade or Stop |
| Side Effect Unknown | Verify State before Retry |
這張表本身就可以變成 Harness Policy。
每個 Tool 最好標記:
read_only
reversible
irreversible
idempotent
external_side_effect
因為 Recovery Strategy 會不同。
例如:
Tool Runtime 如果完全不知道 Tool 的 Side Effect 類型,就很難做安全 Recovery。
當 Agent 最後失敗時,我們應該能回答:
Recovery Trace 對 Debug 和 Evaluation 都非常重要。
否則你只會看到:
Agent failed after 12 turns.
但不知道那 12 輪到底在做什麼。
Recovery 不是免費的。
假設原始任務:
1 次模型呼叫
2 次 Tool Calls
但 Recovery 後變成:
5 次 Retry
3 次 Replan
2 次 Fallback
1 次 Judge
即使最後成功,成本可能已經高很多。
所以 Evaluation 不應該只看:
最後有沒有成功?
也要看:
有時候一個成功率只提升 1% 的 Recovery Mechanism,卻讓平均成本增加 50%。
不一定值得。
可以建立專門的 Failure Dataset。
例如:
然後測:
這比只測 Happy Path 更接近 Production。
模型很適合判斷:
但一些 Recovery 邊界應該由程式控制:
Retry Count
Timeout
Backoff
Allowed Fallback
Permission
Budget
Side Effect Safety
Maximum Replans
可以分成:
Model
決定如何調整策略
Harness
決定允許調整多少次、哪些操作可以做
這和整個系列的主題一致。
可以整理成:
Failure
↓
Classify
↓
Is retryable?
├── Yes → Retry with Budget + Backoff
└── No
↓
Can repair?
├── Yes → Repair
└── No
↓
Alternative path?
├── Yes → Fallback or Replan
└── No
↓
Can degrade?
├── Yes → Degrade with disclosure
└── No
↓
Need user or human?
├── Yes → Ask / Handoff
└── No → Stop
這比:
except Exception:
retry()
可靠很多。
Temporary Failure 和 Logic Failure 被混在一起。
一個小錯誤變成無限成本。
可能產生重複付款、重複 Email 或重複修改。
模型很難使用,也可能洩漏敏感資訊。
Agent 一直移動,但任務沒有真正前進。
使用者不知道可靠度已經下降。
不會因為多試幾次就突然有權限。
最後只知道任務失敗,不知道在哪個策略失敗。
可以先定義八種 Error Type:
transient
validation
permission
dependency
strategy
verification
budget
ambiguity
然後為每種 Error 定義:
再為所有有 Side Effect 的 Tool 加入:
最後加入兩個 Global Limit:
這已經能避免很多「一直 Retry」的問題。
Day 11 我們建立 Prompt Assembly。
今天加入:
因此,Agent 遇到失敗時,不再只有:
再試一次。
而是可以先回答:
這是哪一種 Failure?最安全、最便宜的下一步是什麼?
Error Recovery 的核心不是增加 Retry 次數。
而是把不同失敗映射到不同處理策略。
真正可靠的 Recovery 至少要知道:
最重要的原則是:
Retry 解決暫時失敗,Recovery 解決的是「下一步應該怎麼改」。
下一篇會進入 Task System:
當 Agent 任務不再是一次 Session 就能完成,我們要怎麼保存 State、等待 Approval、暫停、恢復,甚至在數小時後繼續執行?
完整系列與範例收錄於:https://github.com/hardness1020/awesome-agent-architecture