Skip to content
Blog

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;
  1. Orchestrator receives the task and decomposes it into independent subtasks
  2. Sub-agents execute their subtasks in parallel, each with its own loop, tool access, and termination criteria
  3. 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

  1. Maker/Checker eliminates self-approval bias by using independent evaluation.
  2. Fan-out/fan-in orchestration enables parallel sub-agent execution with isolated worktrees.
  3. Background loops (/loop, cron, heartbeat) extend agents in time for monitoring and maintenance.
  4. Goal-driven loops (/goal) enable autonomous pursuit of high-level objectives with explicit guardrails.
  5. Safety is not optional at scale. Idempotency, budget limits, and audit trails are mandatory.