Chapter DCapstone: support bot with RAG + toolsPage 5 of 8

Capstone: support bot with RAG + tools

Design retries, degradation, and recovery

Build a support API that retrieves versioned policy, reads live order state through a scoped tool, and returns a cited answer or human escalation as an operable release, not a slide-deck example.

~18 minFailure handling

1Try it yourself

Playground

Support bot: RAG + tool

Retrieve policy text, optionally fetch live order status, then answer with citations.

User: Can I refund order #8821 on my annual plan?

  1. Retrieve FAQ chunk
  2. Call order lookup tool
  3. Generate cited reply

Before you start

Why this matters

Before changing code, write the single production outcome this chapter must prove and the signal that would stop you. For this lab, the service boundary is SupportRequest(user_id, message) produces Answer(text, citations, order_facts, escalation) with every claim attributable to policy or tool output. Record one request identifier you can follow from ingress through the final decision. If you cannot name the owner of the stop decision, the rollout is not yet controlled.

The source architecture is retrieve policy, call a live-data tool, produce a cited reply, and escalate on no-match, anger, or fraud. This chapter turns that compact lesson into implementation evidence. The running scenario is a support API that retrieves versioned policy, reads live order state through a scoped tool, and returns a cited answer or human escalation. You will keep the same scenario across all eight chapters so setup decisions, tests, telemetry, and rollback controls accumulate into one coherent system rather than eight disconnected exercises.

2Learn the idea

Read

Classify before retrying

Map failures into invalid input, unauthorized action, transient dependency failure, permanent dependency rejection, internal defect, and ambiguous outcome. Retry only transient failures, with exponential backoff, jitter, a strict attempt cap, and an overall deadline shorter than the caller’s timeout. Invalid signatures, authorization failures, and schema errors are not retryable. An ambiguous side effect requires an idempotency lookup before another attempt.

The known hazards are missing policy, stale chunks, tool timeout, prompt injection in retrieved text, order ownership mismatch, and a model inventing an irreversible action. Pick three and inject them locally. For each, record expected HTTP behavior, durable state, user-facing response, alert, and operator action. A handled failure is not merely caught; it leaves the system in a state an operator can explain and recover.

Read

Add bounded degradation

Define a degraded mode that removes risky capability while preserving honest service. Never convert unavailable evidence into a confident answer. Return a typed temporarily_unavailable, serve a known-safe control, accept to a durable inbox, or create a human handoff as appropriate. Include a retry-after hint only when the service can support it.

Use a circuit breaker around a failing dependency to prevent retry storms. The breaker should open on a rolling failure threshold, permit a small number of probes after a cool-down, and emit state transitions. Bulkhead concurrency so one slow provider cannot consume every worker. Queue consumers need maximum attempts and a dead-letter destination with a replay procedure.

Read

Practice recovery

Execute the operational rollback: disable tool use and v2 retrieval independently, fall back to a policy-search-only response, and route unresolved conversations to the support queue. Measure time from the first injected breach to restored safe behavior. Confirm new requests use the safe path, in-flight requests are drained or completed according to contract, and no durable work is lost. Preserve the failed revision’s logs, configuration, and fixtures for diagnosis.

After rollback, reconcile ambiguous records. Compare ingress IDs with completion or outcome IDs, identify duplicates and gaps, and repair using an idempotent replay command. The replay command must support dry-run, bounded batches, and an operator-supplied reason. Never repair production by manually editing rows without an audit record.

Read

Exit criteria

This chapter passes when every classified failure has a deterministic disposition, retries are bounded, degradation is truthful, and rollback is rehearsed rather than merely documented. Save timestamps and commands from the drill. If recovery requires the original author’s memory, the system is not operable.

Read

Implement the safe failure branch

Make the disposition executable. This handler converts order_tool_timeout into a bounded response and never retries a policy or contract failure. The reason is safe for logs; detailed dependency text stays in a protected trace.

from dataclasses import dataclass

@dataclass(frozen=True)
class Recovery:
    action: str
    retryable: bool
    retry_after_seconds: int | None

def recover(kind: str, attempts: int) -> Recovery:
    if kind == "order_tool_timeout":
        return Recovery("tools.enabled=false retrieval.version=v1 escalation=required", False, None)
    if kind == "transient_dependency" and attempts < 3:
        return Recovery("retry_with_jitter", True, 2 ** attempts)
    return Recovery("hold_and_escalate", False, None)

Exercise the branch against the local service and capture both the control action and request identity. The expected result proves safe behavior without erasing evidence.

$ curl -sS -H 'X-Debug-Failure: order_tool_timeout' http://localhost:8000/support/answer
{"status":"degraded","action":"tools.enabled=false retrieval.version=v1 escalation=required","retryable":false}
$ curl -sS http://localhost:8000/support/answer
{"status":"ok","conversation_id":"fixture-001","revision":"stable"}

A generic 500 means classification is incomplete; do not add broad retries. If recovery says success but the second response still names the failed revision, routing or cache invalidation is incomplete. Preserve the identity, old configuration digest, and rollback timestamp before replaying ambiguous work.

Checking tutor…

Continue learning · glossary & guides