· 7 min read
Matching the model to the task: Haiku, Sonnet or Opus
A small model for mechanical edits, a mid model for normal features, the big model for architecture and review. The cost and latency maths behind the split, and how to encode it so agents actually follow it.

A Claude Code subagent can inherit the main session's model, and my main session runs on the biggest one. That works, and it wastes money in a way that is easy to miss. A subagent renaming a CSS class across twelve files does not produce a better rename because the model behind it could also design a database schema.
So my global instruction file carries a rule: haiku for mechanical work, sonnet for normal work, the big model for hard work, with the main session planning, reviewing and merging. This post is the reasoning behind that split, the cost and latency maths, and how to make the rule stick, both for coding agents and for LLM features inside a product.
Three kinds of work
Mechanical work has its answer fully written down before the agent starts. Rename this, move these files, copy this text into that component, take screenshots at these widths, collect these numbers. What it needs is accurate instruction-following over many small steps. Small models do that well, and quickly.
Normal work has a clear goal and some decisions along the way: build this component, add validation to this endpoint, fix this bug that has a repro, write tests for this function. That is most of the work in any codebase, and it is where the mid model earns its place.
Hard work is where a wrong decision is expensive or invisible: the shape of a data model, auth and sessions, billing, merging two branches that both changed the same logic, or reviewing a diff for what is missing rather than what is there. There I want the best reasoning available, spent on few tokens. Planning and reviewing, not typing.
Deciding quickly
I ask two questions, in order. Is the exact change already written down? If so, it goes to haiku. If not, does it touch money, auth, security or the architecture? If so, the big model. Everything else goes to sonnet.
When I cannot decide between two tiers, I take the bigger model and turn its effort setting down. A cheap wrong answer costs more than it saves once my review time is counted, and a stronger model at low effort is often cheaper than it looks. The reverse mistake, giving hard work to a small model, tends to fail quietly: the code compiles, the tests the agent wrote pass, and the flaw shows up later.
The cost maths
List prices per million tokens at the time of writing: Haiku 5.5 is $0.10 in and $0.50 out (for prompts up to 100K tokens), Sonnet 5.5 is $2 in and $10 out, and Opus 5.5 is $4 in and $20 out. Agent runs are dominated by input tokens, because every turn resends the conversation plus every file read so far.
To see the shape, take a made-up but plausible day: thirty mechanical tasks at about 60K input and 5K output tokens each, ten features at 400K in and 30K out, and two reviews at 300K in and 15K out. These are assumptions for illustration, not measurements from my sessions.
const PRICE = { // $ per million tokens: [input, output]
haiku: [0.1, 0.5],
sonnet: [2, 10],
opus: [4, 20],
} as const
type Tier = keyof typeof PRICE
export const cost = (tier: Tier, inTok: number, outTok: number) =>
(inTok * PRICE[tier][0] + outTok * PRICE[tier][1]) / 1e6
cost('opus', 60_000, 5_000) // 0.34 per mechanical task
cost('haiku', 60_000, 5_000) // 0.0085
cost('opus', 400_000, 30_000) // 2.20 per feature
cost('sonnet', 400_000, 30_000) // 1.10All on Opus, that day costs $35.20. Routed, it costs $14.26. What surprised me is where the saving comes from. Moving mechanical tasks to Haiku looks dramatic per task, forty times cheaper, but it saves about $10 in total. Moving features to Sonnet saves $11, because that is where the token volume sits. If you only get one tier right, make it the middle one.
Prompt caching changes the absolute numbers a lot, since cache reads cost a fraction of normal input, but it applies to every tier, so the ranking holds.
Cost per finished task, not per try
The table assumes every run succeeds. They do not, and the failure rate differs by tier. The number that matters is cost per try divided by pass rate, plus the review time each try costs me. With made-up pass rates, Haiku on a feature task is still cheaper in dollars: about $0.17 per success against $1.22. But it takes three reviews to get there, and ten minutes of my attention is worth more than both.
That is the real reason the haiku rule says mechanical work only. On mechanical work the pass rate stays high, so the cheap model is cheap per finished task as well as per token. On anything with judgement in it, the cheap model moves the cost from the API bill to me.
Latency
Smaller models return tokens faster, and that matters more in an agent loop than in chat, because a loop is many round trips in a row. But with parallel agents the clock that counts is not any single agent's. It is the time until I have reviewed everything. Haiku tasks tend to finish while I am still reading the first Sonnet diff, so their speed rarely ends up on the critical path. The serial parts are planning and review on the big model, which is why I keep both short: the plan is a list of tasks with file ownership, not an essay.
Making agents follow the rule
A rule in prose gets followed most of the time. To make it stick, I put it in three places.
First, the global CLAUDE.md rule tells the main session how to choose. I quote it in full, along with how I test it, in the post on agent instructions. Second, subagents that always do the same kind of work carry the model in their definition, so nobody has to choose at all:
---
name: mechanical-editor
description: Applies fully specified edits such as renames, moves and copy changes across many files. Use when the exact change is already written down.
model: haiku
isolation: worktree
---
Apply exactly the change described. Do not refactor, rename anything else or improve nearby code.
When done, run npm run lint and npm run typecheck and report their output with the list of changed files.Third, for general-purpose subagents, the Agent tool takes a model parameter on every call, and the rule says to always set it. A probe run that inspects the tool calls checks that it actually happens.
The same rule inside a product
The split carries over when the model is a feature rather than a tool. In product code I keep one table from the kind of call to its model and effort, so the choice lives in one reviewed place instead of in model strings scattered across the codebase.
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
// One reviewed table decides which model each kind of call uses.
const MODELS = {
classify: { model: 'claude-haiku-5-5', effort: 'low', max_tokens: 1024 },
draft: { model: 'claude-sonnet-5-5', effort: 'medium', max_tokens: 16000 },
review: { model: 'claude-opus-5-5', effort: 'high', max_tokens: 16000 },
} as const
export async function run(kind: keyof typeof MODELS, prompt: string) {
const { model, effort, max_tokens } = MODELS[kind]
const res = await client.messages.create({
model,
max_tokens,
output_config: { effort },
messages: [{ role: 'user', content: prompt }],
})
if (res.stop_reason === 'refusal') throw new Error(`${kind}: model declined`)
return res.content.flatMap((b) => (b.type === 'text' ? [b.text] : [])).join('')
}Two things make the table worth having. Changing a tier is a one-line diff that shows up in review, with the reason in the commit message. And because each call site names a kind rather than a model, I can run an eval for one kind against a cheaper tier and move just that kind if quality holds.
Sort the work
Mechanical if the change is written down, hard if it touches money, auth, security or architecture, normal otherwise.
Look where the tokens are
The middle tier usually holds most of the volume, so it holds most of the saving.
Price the finished task
Divide by the pass rate and count your review time. Cheap retries are not cheap.
Encode it
A rule in CLAUDE.md,
model:in agent definitions, and a lookup table in product code.Re-check when models change
Prices and abilities move. A table makes the re-run a one-line change.