Supervisor-Worker Agent Pattern for Cost Optimization
- Difficulty
- Advanced
- Time
- 3-4 hours
- Tested with
- Claude 4.6, Claude Haiku 4.5, GPT-5, GPT-4o-mini
Before you start
- Familiarity with the tool-use pattern
- API access to at least two model tiers (frontier + cheap)
- Python 3.10+, async experience helpful
Written in April 2026. Newer models have since shipped, so check current model names and prices before you copy them.
The economics
Frontier models (GPT-5, Claude Opus 4) cost many times more per token than cheap models (Haiku 4.5, Gemini Flash, GPT-4o-mini). Most agent steps don't need frontier quality -- summarization, extraction, formatting, classification all run fine on cheap models. If the frontier plans and dispatches, the cheap models execute, you pay frontier rates only for the hard thinking.
Step 1: Define the supervisor interface
Supervisor receives a goal, emits a list of sub-tasks with the right model for each.
from typing import Literal
from pydantic import BaseModel
class SubTask(BaseModel):
id: str
tool: Literal["summarize", "extract_entities", "classify", "draft_response", "final_synthesis"]
input: str
model_tier: Literal["cheap", "mid", "frontier"]
rationale: str
class Plan(BaseModel):
tasks: list[SubTask]
SUPERVISOR_PROMPT = """
You plan agent workflows. Given a user goal, emit a list of sub-tasks.
Available tools and their cost tiers:
- summarize (cheap): collapse text to N sentences
- extract_entities (cheap): pull structured facts
- classify (cheap): assign a label
- draft_response (mid): write a candidate response
- final_synthesis (frontier): combine, judge, polish
Constraints:
- Use the cheapest tier that plausibly works
- Reserve frontier for steps requiring judgment or high-stakes output
- Reserve mid for drafting
- Default to cheap for extraction/classification/summarization
"""
Step 2: Worker pool keyed by tier
WORKERS = {
"cheap": "claude-haiku-4-5",
"mid": "claude-sonnet-4-6",
"frontier": "claude-opus-4-6",
}
async def run_worker(task: SubTask) -> dict:
model = WORKERS[task.model_tier]
result = await call_llm(
model=model,
prompt=TOOL_PROMPTS[task.tool].format(input=task.input),
)
return {"task_id": task.id, "output": result, "tokens": result.usage}
Step 3: Cost accounting
PRICING = {...} # from the token-budget playbook
def cost(model: str, input_tokens: int, output_tokens: int) -> float:
p = PRICING[model]
return (input_tokens * p["input"] + output_tokens * p["output"]) / 1_000_000
class RunLedger:
def __init__(self):
self.entries = []
def log(self, step: str, model: str, usage):
self.entries.append({
"step": step,
"model": model,
"input_tokens": usage.input_tokens,
"output_tokens": usage.output_tokens,
"cost": cost(model, usage.input_tokens, usage.output_tokens),
})
def total(self) -> float:
return sum(e["cost"] for e in self.entries)
def report(self):
return {
"total_usd": self.total(),
"by_step": self.entries,
"frontier_cost": sum(e["cost"] for e in self.entries
if "opus" in e["model"].lower()),
"cheap_cost": sum(e["cost"] for e in self.entries
if "haiku" in e["model"].lower()),
}
Step 4: Orchestrate
import asyncio
async def orchestrate(goal: str) -> tuple[str, dict]:
ledger = RunLedger()
# Supervisor
plan_resp = await call_llm(
model=WORKERS["frontier"],
prompt=SUPERVISOR_PROMPT + f"\nGoal: {goal}",
response_format=Plan,
)
ledger.log("supervisor", WORKERS["frontier"], plan_resp.usage)
plan: Plan = plan_resp.parsed
# Workers (run parallelizable tasks concurrently)
worker_results = await asyncio.gather(*[run_worker(t) for t in plan.tasks])
for t, r in zip(plan.tasks, worker_results):
ledger.log(f"worker:{t.tool}", WORKERS[t.model_tier], r["tokens"])
# Final synthesis on frontier
synthesis = await call_llm(
model=WORKERS["frontier"],
prompt=f"Goal: {goal}\nSub-results: {worker_results}\nSynthesize a final answer.",
)
ledger.log("final_synthesis", WORKERS["frontier"], synthesis.usage)
return synthesis.text, ledger.report()
Step 5: Fallback logic
Workers on cheap models sometimes produce garbage on ambiguous inputs. Detect by validating worker output; on failure, retry on mid-tier once, then frontier.
async def run_worker_with_fallback(task: SubTask, validator) -> dict:
for tier in ["cheap", "mid", "frontier"]:
task.model_tier = tier
r = await run_worker(task)
if validator(r["output"]):
return r
raise RuntimeError(f"Task {task.id} failed on all tiers")
Expected savings
- Pure frontier baseline: $X per run
- Supervisor/worker with fallback: a fraction of X per run; measure your own baseline
- Latency: often FASTER despite more calls, because workers run concurrently
Failure modes
- Supervisor hallucinates tool names: use structured output (Pydantic schema) to enforce valid tools
- Worker output drift: cheap models drift on long instructions. Keep worker prompts short and narrow.
- Fallback cost spiral: if 50% of tasks need to fall back to frontier, you've picked wrong worker for the task. Reclassify.
Variations
- Model routing matrix: expand beyond 3 tiers to vendor-specific best picks per tool
- Cached supervisor plans: the same user goal often produces the same plan -- cache and replay
- Human review on frontier spend: flag any run where frontier cost > $X for human review
Rather have someone set it up for you?
That's a fair call. LTFI, the agentic business operating system from Kief Studio, can set up the work these guides describe and run it with you. No website yet? Kief Studio will build you one for free.

