Maker-Checker & Multi-Agent Orchestration Patterns
Learn advanced multi-agent patterns including Maker/Checker separation, fan-out/fan-in sub-agent orchestration, background scheduled loops, and autonomous goal-seeking systems.
Published on • 2026-07-30
AI Assistant

Single-agent loops run a single agent in a single loop. Many production workloads require more: multiple agents with specialized roles, agents that spawn sub-agents, agents that run on a schedule, and agents that pursue goals autonomously over extended periods. This article covers the advanced patterns that emerge when loop engineering scales beyond the single-agent case.
Maker/Checker Pattern
The Maker/Checker pattern splits responsibility across two agents: a Maker that produces output and a Checker that verifies it. This separation of duties, borrowed from financial operations and software deployment, prevents the blind spots that arise when a single agent both produces and evaluates its own work.
flowchart TD
User(["<b>User Request</b>"])
Maker["<b>Maker</b><br/><small>(LLM)</small>"]
Checker["<b>Checker</b><br/><small>(LLM, tests, linter)</small>"]
PassCond{"<b>PASS?</b>"}
ReturnSuccess["<b>Return Final Output</b>"]
User --> Maker
Maker -- "Output" --> Checker
Checker --> PassCond
PassCond -- "FAIL (Fix Request)" --> Maker
PassCond -- "PASS" --> ReturnSuccess
classDef user fill:#f3f4f6,stroke:#4b5563,stroke-width:2px,color:#111827;
classDef maker fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0f172a;
classDef checker fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#0f172a;
classDef passCond fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#0f172a;
classDef success fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#0f172a;
class User user;
class Maker maker;
class Checker checker;
class PassCond passCond;
class ReturnSuccess success;
How It Works
The Maker generates code, text, or structured output. The Checker evaluates it against criteria. The Checker may be a separate LLM call, a test suite, a linter, or a combination. If the Checker finds issues, it sends a fix request back to the Maker. This cycle repeats until the Checker passes or a limit is reached.
Why Two Agents?
A single-agent self-correction loop relies on the same model to both produce and critique. This works for surface-level issues but fails for deeper problems — the model tends to approve its own output even when it contains errors. The Maker/Checker pattern eliminates this blind spot by using independent evaluation.
In practice:
- Maker and Checker can use different models. A cheaper, faster model can serve as Maker, while a more capable model serves as Checker. This optimizes the cost-quality tradeoff.
- Checker can use non-LLM evaluation. Test suites, type checkers, linters, and formal verification tools are often more reliable than an LLM judge for well-defined criteria.
- Checker can be a multi-tool pipeline. For code, the Checker might run the linter, type checker, test suite, and security scanner, producing a consolidated report for the Maker.
Implementation
def maker_checker_loop(
spec: str,
max_iterations: int = 5
) -> str:
maker_model = "claude-3-haiku"
checker_model = "claude-3-opus"
for i in range(max_iterations):
output = generate(maker_model, f"Produce: {spec}")
check_result = evaluate(checker_model, spec, output)
if check_result["status"] == "PASS":
return output
spec = check_result["feedback"] # refine spec for next iteration
return output # best effort
When to Use
- High-stakes outputs: Production code, financial reports, legal documents, medical information
- Quality-sensitive tasks: Writing for public consumption, API design, database schemas
- Tasks with well-defined evaluation criteria: The Checker needs clear criteria to evaluate against
Spawning & Orchestrating Sub-agents
Not every problem should be solved by a single agent. When a task is complex enough to decompose into independent subtasks, the orchestrating agent can spawn sub-agents to work in parallel, each with its own loop, tools, and context.
Fan-Out / Fan-In
The most common orchestration pattern is fan-out / fan-in:
flowchart TD
A["<b>Orchestrator Agent</b><br/><small>(Decomposes task into subtasks)</small>"]
subgraph Workers ["Parallel Sub-Agents"]
direction TB
SA1["SA 1"]
SA2["SA 2"]
SA3["SA 3"]
SA4["SA 4"]
end
B["<b>Orchestrator Agent</b><br/><small>(Merges results)</small>"]
A --> SA1
A --> SA2
A --> SA3
A --> SA4
SA1 --> B
SA2 --> B
SA3 --> B
SA4 --> B
classDef orchestrator fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0f172a;
classDef worker fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#0f172a;
class A,B orchestrator;
class SA1,SA2,SA3,SA4 worker;
style Workers fill:#f9fafb,stroke:#e5e7eb,stroke-width:1px,stroke-dasharray: 4 4;
- Orchestrator receives the task and decomposes it into independent subtasks
- Sub-agents execute their subtasks in parallel, each with its own loop, tool access, and termination criteria
- Orchestrator collects results and merges them into a cohesive output
Sub-Agent Isolation
Each sub-agent should operate in an isolated environment. For coding agents, this means separate worktrees or temporary directories. For data processing, this means separate data copies. Isolation prevents side-effect conflicts, context interference, and resource contention.
Dynamic Spawning
The orchestrator may not know the number of subtasks in advance. Dynamic spawning creates sub-agents as needed based on the decomposition of the problem. This is essential for open-ended tasks like research and analysis across unknown domain sizes.
Sub-Agent Termination
A sub-agent terminates when it completes its task, fails irrecoverably, or is terminated by the orchestrator. The orchestrator should handle sub-agent timeouts independently — one slow sub-agent should not block the entire fan-in.
async def fan_out_orchestrate(
task: str, parallel_limit: int = 5
) -> dict:
subtasks = decompose_task(task)
results = {}
semaphore = asyncio.Semaphore(parallel_limit)
async def run_subtask(name, spec):
async with semaphore:
return name, await execute_agent(spec)
batch = [run_subtask(name, spec) for name, spec in subtasks.items()]
completed = await asyncio.gather(*batch, return_exceptions=True)
return merge_results(completed)
Scheduled & Background Loops
Not all loops are interactive. Many production agents run on schedules — polling for changes, performing maintenance, monitoring systems, and executing recurring tasks. These are background loops, also known as scheduled agents or cron agents.
The /loop Pattern
In CLI-based agent systems like AGY, the /loop command starts a background loop that periodically executes a task:
/loop "Check for new GitHub issues and triage them" --interval 5m
This is equivalent to a cron job with agentic intelligence — the agent does not just run a fixed command; it adapts its behavior based on what it finds each cycle.
Cron Triggers
Traditional cron schedules provide precise timing for background loops:
# Every hour, check system health
/loop "Run health checks on production services" --cron "0 * * * *"
# Every weekday at 9 AM, generate status report
/loop "Generate and email daily status report" --cron "0 9 * * 1-5"
Heartbeat Monitoring
A heartbeat loop periodically checks a system’s health and takes corrective action when anomalies are detected:
def heartbeat_loop(
check_fn, # returns health status
fix_fn, # attempts to fix issues
interval_sec: int = 60,
max_failures: int = 3
):
failures = 0
while True:
status = check_fn()
if status == "healthy":
failures = 0
else:
failures += 1
if failures >= max_failures:
escalate_to_human(status)
break
fix_fn(status)
time.sleep(interval_sec)
Goal-Driven Autonomous Loops
The most advanced loop pattern is the goal-driven autonomous loop: an agent receives a high-level goal and pursues it indefinitely, decomposing it into sub-goals, executing tasks, and replanning as needed, until the goal is achieved or the agent is terminated.
flowchart TD
A["<b>Define / Set Goal</b><br/><small><i>Reduce test failures to under 2%</i></small>"]
B["<b>Analyze Current State</b><br/><small>(Read CI dashboard, identify top<br/>failure categories, estimate effort)</small>"]
C["<b>Prioritize Next Action</b><br/><small>(Which failure gives the most<br/>improvement per unit of effort?)</small>"]
D["<b>Execute Action</b><br/><small>(Fix a failure, add tests, verify)</small>"]
E["<b>Measure Progress</b><br/><small>(Re-run test suite, measure rate)</small>"]
F{"<b>Goal Met?</b>"}
G["<b>Re-evaluate Strategy</b><br/><small>(Adjust approach based on results)</small>"]
H["<b>Return Success</b><br/><small>(Goal achieved)</small>"]
A --> B
B --> C
C --> D
D --> E
E --> F
F -- "Goal Not Met" --> G
G --> C
F -- "Goal Met" --> H
classDef goal fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0f172a;
classDef analyze fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#0f172a;
classDef prioritize fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#0f172a;
classDef execute fill:#e0e7ff,stroke:#4f46e5,stroke-width:2px,color:#0f172a;
classDef measure fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#0f172a;
classDef reevaluate fill:#ffedd5,stroke:#ea580c,stroke-width:2px,color:#0f172a;
classDef success fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#0f172a;
class A goal;
class B analyze;
class C prioritize;
class D execute;
class E,F measure;
class G reevaluate;
class H success;
Safety Considerations
Autonomous goal-seeking loops are the most powerful — and most dangerous — pattern. Safety measures are mandatory:
- Idempotency: Actions should be safe to retry.
- Budget limits: Hard token and cost limits prevent runaway spending.
- Audit trail: Every action should be logged for review.
- Slow start: New goal loops should start with tight constraints and expand as trust builds.
Key Takeaways
- Maker/Checker eliminates self-approval bias by using independent evaluation.
- Fan-out/fan-in orchestration enables parallel sub-agent execution with isolated worktrees.
- Background loops (
/loop, cron, heartbeat) extend agents in time for monitoring and maintenance. - Goal-driven loops (
/goal) enable autonomous pursuit of high-level objectives with explicit guardrails. - Safety is not optional at scale. Idempotency, budget limits, and audit trails are mandatory.