Capstone: support bot with RAG + tools
Constrain access and rehearse rollback
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.
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?
- → Retrieve FAQ chunk
- ○ Call order lookup tool
- ○ 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
Draw the trust boundaries
Mark user input, application runtime, configuration service, external provider, durable store, and operator console. At each crossing, specify authentication, authorization, validation, encryption, and audit evidence. The system’s concrete policy is: authenticate the user, authorize each order lookup, minimize transcript PII, treat retrieved text as data rather than instructions, and require humans for refunds. Test that policy with a negative request, not only a configuration screenshot.
Secrets enter at runtime through a secrets manager or workload identity. They never appear in source, fixtures, process arguments, telemetry, or exception bodies. Scope credentials to the smallest resource and operation. Production configuration writes require role-based access, review for high-risk changes, and an immutable record with actor and reason.
Read
Defend the data path
Set body and field-size limits before parsing expensive input. Normalize only after signature or identity checks where raw bytes are security-relevant. Treat retrieved documents, webhook payloads, and tool responses as untrusted data. Apply output schemas and explicit allowlists before any side effect. For customer data, minimize collection, define retention, and verify deletion reaches logs or indexes where promised.
Build abuse tests for replay, cross-tenant identifiers, prompt injection, forged configuration, stale credentials, and denial-of-service through expensive requests. Assert both response and absence of side effects. A 403 that still performed a provider call is a security and cost bug.
Read
Rehearse rollback safely
Execute: disable tool use and v2 retrieval independently, fall back to a policy-search-only response, and route unresolved conversations to the support queue. Use a two-person checklist for production-like drills: announce start, snapshot current revision and metrics, apply the control, verify safe behavior, reconcile in-flight work, and announce completion. Ensure rollback permissions are available to on-call staff without granting broad routine admin access.
Distinguish rollback from evidence destruction. Keep the failed artifact, configuration digest, audit events, and redacted traces under retention policy. If compromise is possible, rotate credentials and isolate the revision before analysis. Never reactivate a known-compromised secret merely because it restores service.
Read
Operational handoff
The runbook must name owner, escalation path, safe-mode behavior, exact control, verification query, and conditions for re-enabling. Run it with someone who did not write the code. Measure completion time and note any undocumented access. The chapter passes only when least privilege, negative tests, audited changes, and a timed rollback drill all produce evidence.
Read
Test least privilege and denial
Separate runtime and operator permissions. The application receives one capability, while the operator role owns the audited recovery action. Security invariant: A user cannot read another user order or issue a refund.
roles:
runtime:
principal: support-bot-runtime
allow:
- orders:read-owned
deny:
- admin:*
break_glass_operator:
allow:
- orders:read-owned
conditions:
reason_required: true
approval_count: 2
ttl_minutes: 30
Run a negative probe before the positive path. The exact status may be 401 or 403 by contract, but no downstream call or durable mutation may occur. Audit output names the decision, never credentials or payload contents.
$ curl -si -H 'Authorization: Bearer invalid-fixture' http://localhost:8000/support/answer
HTTP/1.1 401 Unauthorized
$ printf '%s\n' '{"decision":"deny","principal":"unknown","side_effects":0}'
{"decision":"deny","principal":"unknown","side_effects":0}
If side_effects is nonzero, move authorization ahead of retrieval, queue publication, or provider calls; changing only the status code is not a fix. If runtime unexpectedly has admin rights, inspect role inheritance and workload identity binding. Record actor, reason, approvals, prior support-bot-v4, and expiry during rollback. A break-glass token still valid after the drill is a failed security test.