Playbook for designing a cost-optimised model routing ladder: classifying request complexity, wiring the triage→escalate flow, measuring cost-per-resolved-task, and avoiding the common over-provisioning traps. Owned by claude-app-ops-engineer and claude-solution-architect.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-app-engineering:llm-routing-ladderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Designing the model-selection strategy for a new Claude app.
A routing ladder directs each request to the cheapest model that resolves it correctly. The metric is cost-per-resolved-task, not tokens or per-call cost.
Incoming request
│
▼
[Triage classifier — Haiku] ←─ cheap, fast, binary classification
│
├─ Simple ──────────────► [Haiku handler] → resolved? → done
│
├─ Medium ──────────────► [Sonnet handler] → resolved? → done
│ │ no
└─ Complex ──────────────► [Opus handler] ◄────────┘
| Tier | Characteristics | Default model |
|---|---|---|
| Simple | Single-turn, factual lookup, <500 token output, no tool use | Haiku |
| Medium | Multi-step, moderate reasoning, tool use with ≤3 calls | Sonnet |
| Complex | Long chain-of-thought, multi-tool orchestration, high-stakes output | Opus |
Write explicit definitions for your domain — "simple" for a customer support bot differs from "simple" for a code review agent.
The triage step is itself a Haiku call (or a heuristic) that returns a tier label:
TRIAGE_PROMPT = """Classify the following user request into one of three tiers:
- simple: factual lookup, one-step task, no reasoning chain needed
- medium: multi-step, moderate reasoning or tool use
- complex: requires deep reasoning, long output, or high-stakes judgment
Return ONLY the tier label. Request: {request}"""
def classify(request: str) -> str:
resp = client.messages.create(
model="claude-haiku-4-5",
max_tokens=10,
messages=[{"role": "user", "content": TRIAGE_PROMPT.format(request=request)}]
)
return resp.content[0].text.strip().lower()
Cache the triage result for identical requests (same hash) to avoid paying the classifier cost twice.
Simple and medium handlers should detect uncertainty and escalate:
UNCERTAINTY_SIGNALS = [
"I'm not sure", "I cannot determine", "this requires",
"outside my knowledge", "I would need more"
]
def should_escalate(response_text: str) -> bool:
return any(s in response_text for s in UNCERTAINTY_SIGNALS)
Escalation is one tier at a time — Haiku → Sonnet → Opus. Never skip tiers unless the triage classified complex from the start.
# Track per request:
# - model used
# - input_tokens, output_tokens, cache_read_input_tokens
# - resolved: bool (from your eval / user feedback)
# Then compute:
cost_per_resolved = total_cost / resolved_count
# Compare across ladder configs; the goal is lower cost_per_resolved, not lower per-call cost
A ladder that escalates 40 % of calls to Opus may be more expensive than running Sonnet everywhere if Haiku's resolution rate is low. Measure before assuming the ladder helps.
For async / offline workloads (nightly reports, bulk classification, eval runs):
| Model | Input cost | Cache-read cost | Batch discount |
|---|---|---|---|
| Haiku | low | 10 % of input | 50 % |
| Sonnet | medium | 10 % of input | 50 % |
| Opus | high | 10 % of input | 50 % |
[verify-at-build] — pricing changes; retrieve from knowledge/model-selection-and-2026-capability-map.md before quoting.
npx claudepluginhub mcorbett51090/ravenclaude --plugin claude-app-engineeringCreates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.