Twelve patterns for combining model calls, tools and code, and when each one earns its cost.
Drawn from the Agentic Orchestration Playground · github.com/javiator/agentic-orchestration-play
Code decides the steps. Predictable, cheap, easy to test.
The same strict writing task two ways: one big prompt, or small steps with code checks between them.
spec + topic ──▶ model ──▶ text (1 call)
topic ──▶ ① outline (JSON) ──▶ [gate: valid JSON, 3 sections?] ── no → retry once
│
▼ ② write each section — in PARALLEL, one call each
┌─────┼─────┐
▼ ▼ ▼
[code: assemble title + sections + takeaway]
│
▼
[gate: check the full spec] ── fails → ③ targeted fix call
│ (only the failed checks)
▼
text (~5–7 calls)
A cheap model reads each request and picks the path — and the price — for it.
request ──▶ ROUTER (small model, one short call → JSON)
│ route + confidence + reason
├── simple ──▶ small model, one call (cheap, fast)
├── reasoning ──▶ strong model, one call (math, code, analysis)
├── research ──▶ strong model + web tools (fresh facts)
└── decline ──▶ canned reply, no model call (out of scope)
│
└─ confidence < ROUTER_MIN_CONFIDENCE? ESCALATE to the
safer, more capable path — a wrong cheap answer usually
costs more than the price difference.
One question → several models answer independently → a judge merges them and flags disagreements.
┌──▶ agent 1 (model A) ──┐
question ──┼──▶ agent 2 (model B) ──┼──▶ JUDGE (its own model) ──▶ merged answer
└──▶ agent 3 (model C) ──┘ │ + disagreements
in parallel, isolated │
optional: web tools (ReAct) ▼
disagreements found?
│ yes (rebuttal on)
▼
REBUTTAL: each agent sees the others' claims,
defends or revises ──▶ judge again (round 2)
memory: every verdict is stored (SQLite); when on, related past
findings are recalled into the shared prompt — "verify before relying".
Draft, critique, revise — until it's good enough, stops improving, or runs out of budget.
task ──▶ WRITER ──▶ draft v1
▲ │
│ ▼
│ CRITIC: score 1–10 + concrete issues (JSON)
│ │
│ stop? ├─ score ≥ target → good enough
│ ├─ no better than before → plateau
│ └─ rounds used up → budget
│ │ no
└── revise ◀┘ (writer gets the draft + the issues)
result = the BEST-scoring version, not necessarily the last —
a revision can make things worse.
A model decides the steps. Flexible, and you pay a model call for every decision.
A supervisor model decides, turn by turn, which specialist acts next — and when the job is done.
┌──────────────────────────────────┐
goal ─────▶ │ SUPERVISOR (a tool-calling LLM) │ ◀──────────────────┐
└──────┬───────────────────┬───────┘ │
delegate(specialist, task) finish(final_answer) ──▶ done │
▼ │
┌──────────────┐ appends ┌────────────┐ │
│ SPECIALIST │ ─────────▶ │ SCRATCHPAD │ shared state │
│ (own model) │ ◀───reads─ └────────────┘ │
└──────┬───────┘ │
└── output returned as the tool result ──────────┘
Plan the whole job as a graph up front, run independent steps in parallel, replan when a step fails.
goal ──▶ PLANNER (one call) ──▶ plan = steps + dependencies (a DAG)
│ validated by code: ids, deps, no cycles
▼
wave 1: [s1 research] [s2 research] ← no deps: run in PARALLEL
wave 2: [s3 think: needs s1, s2]
wave 3: [s4 think: needs s3]
│
a step FAILED? ──▶ REPLANNER: keeps finished work,
│ plans only what's left (≤ MAX_REPLANS)
▼
SYNTHESIZER ──▶ final answer
Same question, model and documents: one fixed search, or an agent that searches like a researcher.
question ──▶ search(question) ──▶ top-k chunks ──▶ model answers, citing chunks
question ──▶ ┌──────────────── agent loop (≤ RAG_MAX_STEPS) ───────────────┐
│ model: what do I still need? │
│ ├─ search_docs(query) ← its own query: one fact at a │
│ │ time, rephrased when it misses │
│ ├─ read_doc(doc_id) ← whole document when a snippet │
│ │ isn't enough │
│ └─ answer (text) │
│ └─▶ GROUNDING CHECK: is every claim supported by │
│ what was retrieved? no → feedback, search more │
└────────────────────────────────────────────────────────────┘
The model writes Python, runs it, reads the output or traceback — and fixes its code.
question ──▶ ┌──────────────────── loop (≤ CODE_MAX_STEPS) ───────────────────┐
│ model ── run_python(code) ──▶ sandbox: fresh process │
│ ▲ │ │
│ └── exit code + stdout/stderr ─┘ error? the model reads │
│ the traceback and fixes │
│ model ── text answer ──▶ done │
└───────────────────────────────────────────────────────────────┘
The controls that make any of the eight safe to run and worth their cost.
The agent works on its own — until an action is risky. Then it pauses for you.
request ──▶ agent loop (tools over a fake shop)
│ tool call
▼
POLICY CHECK (code, see world.needs_approval)
├─ read / internal write / small refund ──▶ run it now
└─ big refund / external email ──▶ PAUSE ⏸
│ the run's state (the conversation so far + the
│ proposed action) is held while a human decides:
├─ approve → run as proposed
├─ edit + approve → run with the human's arguments
└─ reject + feedback → NOT run; the agent is told why
▼
loop continues ──▶ final summary for the operator
The same support bot with and without layered input and output checks — against real attacks.
user input ──▶ [1 PII redaction · code] ──▶ [2 injection patterns · code]
│ hit → BLOCK (no model call at all)
▼
[3 input classifier · small model] injection / harmful / off-topic → BLOCK
▼
BOT (same model + prompt as the unguarded bot)
▼
[4 secret-leak check · code] → BLOCK
[5 PII in output · code] → REDACT
[6 output policy · small model] → BLOCK
▼
answer
One long conversation replayed four ways — then quizzed on what each strategy still remembers.
FULL HISTORY send everything. Best recall; cost grows every
turn, and long contexts dilute attention.
SLIDING WINDOW send the last N messages. Cheap and flat; forgets anything
older than the window, completely.
SUMMARY every K messages, a model Middle ground; summaries keep the
call folds older messages gist but drop specifics (numbers,
into a running summary; names) unless told to keep them.
keep the last few raw.
NOTES every K messages, a model Precise on facts it decided to keep
call updates a list of (and can overwrite changed ones);
durable facts; keep the misses details nobody flagged as
last few raw. important. The idea behind "memory" features.
Score single models and orchestration patterns on the same questions — quality, latency and cost.
dataset (questions + expected answers / rubrics)
│
▼ for every (target, question) pair, limited concurrency
┌───────────┐ ┌─────────────────────────────────────────────────────┐
│ TARGETS │──▶│ run target → answer (calls + tokens counted) │
│ single │ │ exact-match check (when `expected` given) │
│ models, │ │ LLM grader → 1–5 + reason (rubric / reference) │
│ exercises │ └─────────────────────────────────────────────────────┘
└───────────┘ │
▼
per-target summary: accuracy · grade · latency · cost
Start with a workflow. Add agency where the steps cannot be known in advance. Wrap it in approvals, guardrails and context discipline. Measure before you believe it.
Run the exercises: github.com/javiator/agentic-orchestration-play