ローカル復旧とエスカレーション
一時的な障害は再試行し、復旧不能な障害はエスカレーションします
「ローカル復旧とエスカレーション」はCoddyKit上の無料Claude Architectレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはClaude Architect学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Claude Architectコースには全4レッスンが含まれています。
このレッスンの一部はまだ翻訳されておらず、英語で表示されています。
Two Ways a Step Can Fail
Inside an agentic system, a single tool call or subagent step can fail for very different reasons. The architect's job is to classify the failure before reacting.
- Transient fault — a momentary, self-correcting problem: a network blip, a rate limit, a brief timeout. Retrying the exact same call may just work.
- Non-recoverable failure — a structural problem: invalid credentials, a missing permission, a malformed request, or a business rule violation. Retrying changes nothing.
The core rule of this lesson: recover transient faults locally, escalate the non-recoverable.
Local Recovery: Keep It in the Subagent
In a hub-and-spoke multi-agent system, the coordinator delegates work to subagents. When a subagent hits a transient fault, it should try to fix it where it happened — without bubbling noise up to the coordinator.
This keeps the coordinator focused on orchestration instead of low-level retries, and it preserves the coordinator's context budget. Local recovery is the first line of defense.
def run_tool_with_local_recovery(tool, args, max_attempts=3):
for attempt in range(max_attempts):
result = tool(**args)
if not result.get("isError"):
return result
# Only retry faults the result says are retryable
if result.get("isRetryable") and result.get("errorCategory") == "transient":
continue
break # validation / business / permission -> stop, escalate
return result # hand the structured error upwardLet the Error Tell You What to Do
You can only route intelligently if the failure is structured. A generic "Operation failed" blocks recovery — the agent can't tell a rate limit from a permission denial.
A well-designed MCP tool returns an error envelope:
isError: trueerrorCategory:transient/validation/business/permissionisRetryable: booleanmessage,attempted_query,partial_results
The errorCategory drives the decision: transient is a retry candidate; validation, business, and permission are not.
{
"isError": true,
"errorCategory": "transient",
"isRetryable": true,
"message": "Upstream timeout after 5s",
"attempted_query": "SELECT * FROM orders WHERE customer_id = 4821",
"partial_results": []
}Failure vs Empty Result
A subtle but exam-critical distinction: an access failure is not the same as a valid empty result.
- Failure — the query never completed (timeout, auth error). The data is unknown. This might be retryable.
- Empty — the query ran successfully and found nothing.
0 rowsis a correct, final answer. Retrying is pointless and misleading.
Conflating the two leads agents to retry forever on legitimate "no matches found" cases, or to report a real outage as "no data."
if result.get("isError"):
handle_failure(result) # access failure: maybe retry / escalate
elif len(result["rows"]) == 0:
return "No matching records found." # valid empty result, DONE
else:
return result["rows"]When to Escalate
Escalation means handing the problem up — to the coordinator, or ultimately to a human. Good escalation triggers are objective:
- An explicit human request — escalate immediately, no further attempts.
- A policy gap — the situation isn't covered by the rules the agent has.
- No progress after attempts — local recovery exhausted.
- A threshold violation — e.g. a refund exceeds an allowed limit.
Notice these are all things you can detect deterministically — not guesses about the user's mood.
Bad Escalation Triggers
Just as important: know what not to escalate on. These triggers feel reasonable but are unreliable and are classic exam distractors:
- Sentiment analysis — escalating because the message "sounds angry."
- Model self-rated confidence — "I'm only 4/10 confident, so escalate." Self-ratings are not calibrated.
- Untrained classifiers bolted on as gatekeepers.
Instead, follow the proven pattern: acknowledge the emotion, propose a concrete solution, and escalate only if the customer reiterates the request. Behavior — a repeated explicit ask — is a far better signal than inferred feeling.
Escalate WITH Context, Not Just a Shrug
When a subagent escalates, it must propagate structured context so the coordinator (or human) can act without re-doing the work:
- the failure type (the errorCategory),
- the attempted query or action,
- any partial results already gathered,
- and viable alternatives.
An escalation that says only "it failed" forces the coordinator to start from zero. An escalation carrying partial results lets the rest of the workflow continue and the human resolve faster.
def escalate(coordinator, failure):
coordinator.report(
failure_type=failure["errorCategory"],
attempted_query=failure["attempted_query"],
partial_results=failure.get("partial_results", []),
alternatives=["retry via read-replica", "ask user for order ID"],
)Don't Abort the Whole Workflow
One failed branch should not collapse the entire job. In a multi-agent research system, if one source is unreachable, the coordinator should still aggregate the successful branches and clearly annotate the gap in coverage.
Two failure modes to avoid:
- Silent suppression — swallowing the error so the final answer looks complete but isn't. This destroys trust and provenance.
- Whole-workflow abort — killing every other branch because one failed.
The middle path: continue, deliver partial results, and be explicit about what's missing.
Caps Are a Safety Net, Not the Plan
A retry loop needs a bound, but the bound is a safety net — never the primary control mechanism. The same principle governs the whole agentic loop: you terminate on stop_reason reaching end_turn, and iteration caps merely prevent runaway loops.
For retries specifically: stop because the structured error says isRetryable: false, or because progress has been made — not merely because you hit attempt #3. The cap exists so a transient-looking-but-permanent fault can't spin forever.
# Cap = backstop. The REAL stop signal is the error category.
for attempt in range(MAX_ATTEMPTS): # safety net only
res = call_tool(args)
if not res["isError"]:
return res
if not res["isRetryable"]: # primary, decision-driven stop
return escalate(res)
sleep(backoff(attempt))
return escalate(res) # exhausted -> escalate, never silentDeterministic Guards for Hard Limits
Some escalations protect against financial, legal, or safety consequences — for example, a refund above a policy threshold. Here, prompt guidance (~90% reliable) is not enough.
Use a hook for 100% deterministic enforcement. A PostToolUse or outgoing-call hook can block a policy-violating action before it ever executes, forcing escalation to a human. Prompts persuade; hooks guarantee.
# .claude hook: block refunds over $500 -> force escalation
def on_outgoing_call(call):
if call.tool == "process_refund" and call.args["amount"] > 500:
return {
"block": True,
"reason": "Refund exceeds $500 policy limit; escalate to human.",
}
return {"block": False}Putting It Together: The Decision Flow
For any failed step, walk this flow:
- 1. Empty, not failed? Return the valid empty result. Done.
- 2. Transient + retryable? Recover locally with bounded retries and backoff.
- 3. Recovered? Continue the workflow.
- 4. Non-recoverable (validation / business / permission), explicit human request, policy gap, threshold violation, or retries exhausted? Escalate with structured context and partial results.
Never silently suppress, never abort the whole workflow, and never escalate on sentiment or self-rated confidence.
Quick Check
A subagent's database tool returns isError: true, errorCategory: "permission", isRetryable: false, with the attempted query and empty partial results. What should the subagent do?
Recap: Recover Local, Escalate the Rest
Key takeaways:
- Classify first: transient (retryable) vs non-recoverable (validation/business/permission).
- Recover transient faults locally in the subagent with bounded retries; the cap is a safety net, the structured error is the real stop signal.
- Distinguish access failure from a valid empty result — 0 rows is a final answer, not a retry trigger.
- Escalate the non-recoverable with structured context: failure type, attempted query, partial results, alternatives.
- Escalate on objective triggers (explicit human request, policy gap, no progress, threshold violation) — never on sentiment or self-rated confidence.
- Enforce financial/legal/safety limits with hooks, not prompts. Never silently suppress; never abort the whole workflow on one failure.
よくある質問
「ローカル復旧とエスカレーション」レッスンは無料ですか?
はい。「ローカル復旧とエスカレーション」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Claude Architectコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Claude Architectコースには全4レッスンが含まれています。
「ローカル復旧とエスカレーション」で何を学びますか?
一時的な障害は再試行し、復旧不能な障害はエスカレーションします ブラウザで直接実行するハンズオンコードでClaude Architectを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Claude Architectを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのClaude Architectは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「ローカル復旧とエスカレーション」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このClaude Architectレッスンでコードを書いて実行できますか?
はい。すべてのClaude Architectレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 明確なエスカレーション条件
- アンチパターン:感情と信頼度のスコア
- 構造化されたエラーコンテキスト
- ローカル復旧とエスカレーション