로컬 복구와 에스컬레이션
일시적 장애는 재시도하고 복구할 수 없는 문제는 에스컬레이션합니다
로컬 복구와 에스컬레이션은(는) CoddyKit의 무료 Claude Architect 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 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.
AI 튜터와 함께 Python을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 26
- 레슨
- 104
자주 묻는 질문
“로컬 복구와 에스컬레이션” 강의는 무료인가요?
네 — “로컬 복구와 에스컬레이션” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Claude Architect 강의 전체를 잠금 해제할 수 있습니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.
“로컬 복구와 에스컬레이션”에서 뭘 배우나요?
일시적 장애는 재시도하고 복구할 수 없는 문제는 에스컬레이션합니다 브라우저에서 직접 실행하는 실습 코드로 Claude Architect을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Claude Architect을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Claude Architect은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“로컬 복구와 에스컬레이션” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Claude Architect 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Claude Architect 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 명확한 에스컬레이션 조건
- 안티 패턴: 감정 및 확신 점수
- 구조화된 오류 컨텍스트
- 로컬 복구와 에스컬레이션