The pattern

Meta's engineering team published a write-up on its "thin orchestrator" agent architecture, built for data column onboarding across Meta's recommendation systems: a pipeline that spans three repositories, three languages and up to six stages. Their fix for an unreliable agent was to take decisions away from the router. The orchestrator is "a router that owns sequencing and reliability checks, and leaves every domain decision to specialists", and the authors say it "performs zero domain work itself".

Meta reports that configuration prep for a new column took three to four days by hand, about a day with its earlier config-driven system, and under an hour with the thin orchestrator. About two-thirds of the system's changes land without significant human edits. Their earlier single-agent design reached 70–80% success only with manual prompt engineering per task, and 10–20% without it.

If you read our post on routing agent threads by their first message, keep the two apart. That router classifies a request and chooses which model runs the thread. Meta's orchestrator makes no model decisions and no domain decisions: it runs a fixed checklist in code and hands exactly one step to a specialist. The two can coexist, since a specialist could itself sit behind a model router.

When to use it and when not

Use it when:

  • The work has a repeatable shape (client onboarding, publishing, migrations) and you know the order of phases in advance.

  • You have distinct domains whose conventions leak into each other when one prompt holds them all. Meta saw its single agent "cross-contaminate concepts", such as applying one subsystem's field naming to another.

  • Mistakes are expensive and you need hard checks before anything ships.

Skip it when:

  • The task is open-ended research or conversation where the next step depends on judgement.

  • You have one domain and one prompt that already works. A router adds plumbing for nothing.

  • You can't write the phase order down. If you can't draw the state machine, you aren't ready to code it.

How it works

Input: a request, such as "onboard client Acme on the Growth package". The router then runs Meta's fixed seven-step protocol:

  1. Read the relevant subagent's instruction file.

  2. Check whether the target already exists (prevent duplicates).

  3. Gather required fields from the user.

  4. Auto-discover what can be inferred (in Meta's case, repository type and existing configurations).

  5. Run a reliability gate check for active warnings or required reviewers.

  6. Delegate to the subagent with full context.

  7. Handle results: surface errors, confirm success.

In Meta's words, "Steps 1–5 and 7 are deterministic. Only step 6 involves large language model (LLM) reasoning", and that reasoning happens inside an isolated subagent. Their rationale: "'Which subagent handles this?' is a routing decision, not a reasoning task. Don't use an LLM for it."

Output: a confirmed result or a surfaced error, plus a record of what happened at each step.

Components

  • Workflow templates. Meta calls these "deterministic state machines that constrain the LLM's decision space". Column Onboarding runs Schema → Validation → Population → Testing; Pipeline Migration runs Assessment → Planning → Population → Validation → Monitoring. New use cases reuse the same specialists in a different order.

  • Specialist subagents. Meta runs fourteen specialised subagents, "each a complete domain expert with isolated context". Each follows a standard structure: knowledge sources, a pre-gathering checklist, a step-by-step workflow, discovery patterns and troubleshooting.

  • Knowledge loaded at runtime. "Knowledge files are loaded on demand at subagent execution time, not embedded in the orchestrator." Update a file and the next session uses it.

  • Reliability gate. A check for warnings or required reviewers before anything is delegated.

  • The router. Plain code: a lookup table, a loop and a log.

Minimal implementation

Python-ish pseudo-code for a solo builder's version:

TEMPLATES = {
  "client_onboarding": ["intake", "proposal_recap", "workspace", "welcome_email"],
  "content_publish":   ["brief", "draft", "seo_check", "schedule"],
}
SPECIALISTS = {  # phase -> (instruction file, required fields)
  "intake":         ("agents/intake.md",   ["client_name", "email", "package"]),
  "proposal_recap": ("agents/recap.md",    ["client_name", "package", "start_date"]),
  # ...
}

def run(template, request, state):
    for phase in TEMPLATES[template]:
        path, required = SPECIALISTS[phase]
        instructions = read_file(path)                     # 1. read instructions
        if state.already_done(request.id, phase):          # 2. duplicate check
            continue
        fields = {k: request.get(k) for k in required}     # 3. gather fields
        fields |= auto_discover(request, missing(fields))  # 4. CRM / Drive lookups
        if missing(fields):
            return ask_user(phase, missing(fields))        # stop; never guess
        if gate_blocks(phase, fields, state):              # 5. reliability gate
            return escalate(phase, state.warnings)
        result = call_specialist(instructions, fields)     # 6. the only LLM call
        if not validate(phase, result):                    # 7. handle results
            return surface_error(phase, result)
        state.record(request.id, phase, result)
    return confirm(request.id)

What the router never does: ask a model which phase comes next, pick a specialist from free text, or rewrite a field. call_specialist starts a fresh context containing only that specialist's instructions and the fields it needs.

The n8n variant

  1. A Webhook or Form trigger receives the request, including a template field.

  2. A Code node expands the template into one item per phase, in order.

  3. A data table or Google Sheets lookup checks whether this request_id + phase already succeeded; if so, skip.

  4. An IF node checks required fields. Missing? Send an email or Slack message asking for them and stop.

  5. A Switch node routes on the phase name using plain comparisons. No AI node sits here.

  6. Each Switch output calls an Execute Sub-workflow node, one sub-workflow per specialist, each with its own AI Agent node and system prompt.

  7. An IF node validates the output against a fixed shape. Fail: alert and stop. Pass: write a log row and continue.

Failure modes and evals

  • Router creep. Someone adds "just one" LLM call to classify ambiguous requests. Meta's warning applies: when a heavyweight orchestrator hallucinates, "it routes to the wrong agent, gathers the wrong fields, or skips reliability checks". Handle ambiguity by asking the user.

  • Guessed fields. A specialist invents a start date. The router must refuse to delegate until required fields exist, and validate every output.

  • Duplicate runs. A webhook retry creates two welcome emails. Key the step-2 check on request ID plus phase.

  • Stale knowledge. Instructions pasted into a prompt drift from reality. Load them from files on every call.

  • Overlapping specialists. Two agents both edit the client record. Give every field one owner.

Evals in three layers:

  • Router tests are ordinary unit tests: given template X and state Y, assert phase order, assert the gate blocks, assert duplicates skip. No model is involved, so expect 100%.

  • Per-specialist evals: 15–30 real cases each, scored in isolation. Our post on automated evals and prompt tuning with a held-out set shows how to improve a specialist without overfitting.

  • End-to-end: copy Meta's metric, the share of runs that land without significant human edits, plus time per run against your manual baseline. Track both weekly per template.

Build it this weekend: a client-onboarding orchestrator

Saturday morning: Write the template: intake → proposal recap → workspace setup → welcome email. For each phase, write a one-page instruction file using Meta's five parts: knowledge sources, pre-gathering checklist, steps, discovery patterns, troubleshooting.

Saturday afternoon: Build the router in Python or n8n as above. Hard-code the phase list. Create a log table with request_id, phase, status, output and timestamp.

Sunday morning : Build the specialists. Only proposal recap and welcome email need a model; workspace setup can be plain API calls that create a Drive folder and a Notion page. Some phases need no LLM at all, which is the whole lesson in miniature.

Sunday afternoon : Run 10 past clients through it. Check: does a rerun skip completed phases? Does it stop when the package field is blank? How many welcome emails would you send unedited? Meta's two-thirds is a reasonable bar.

Stretch. Add a content_publish template that reuses two existing specialists. If they slot in without edits, your boundaries are right.

Comments

Avatar

or to participate