Install
openclaw skills install @antreasantoniou/agent-orchestraDesign and compose multi-agent graphs for correctness, coverage, or creativity. Use when a task benefits from isolated proposals, explicit arbitration, adversarial verification, committees, recursive review, cross-modal checks, or saturation loops. Applies across agent runtimes; the included JavaScript workflow is one adapter.
openclaw skills install @antreasantoniou/agent-orchestraA catalog of multi-agent coordination structures and how to map them onto an orchestration runtime. The goal: be creative and deliberate about topology — pick (or invent) the structure that fits the task, not reflexively reach for one shape.
Token-efficient variant: when budget matters, apply
token-efficient-orchestraon top of any graph here — economical models for redundant workers and stronger models for supervision chokepoints (arbiter/adversary/integrator/gate). The rule of thumb: redundancy downgrades, chokepoints upgrade.
git clone --no-hardlinks <live> /tmp/<proj>-source) and point agents at that. Assume an
agent can escape its intended working directory. The disposable source limits the resulting
damage to a throwaway copy rather than the live repo.git add -A && git diff --cached →
git apply --recount <patch> on the live tree (recount tolerates wrong hunk counts, a common
model error). Verify .git is intact and run the suite before committing.The graph grammar is runtime-neutral. Preserve isolation, typed outputs, dependency edges,
concurrency barriers, and filesystem boundaries when porting it. The code skeletons below use a
JavaScript workflow adapter exposing agent, parallel, and pipeline; that API is one adapter,
not a requirement of the pattern.
: string[], interfaces, generics fail to parse.\...` brief closes the string early and breaks parsing. Use plain quotes inside briefs; escape backticks (\``) only
in the prompt-builder functions.agent(prompt, {schema, label, phase, model, effort, agentType, isolation}) — with schema
(a JSON Schema), the agent is forced to return a validated object. Always use a schema for
anything you'll machine-process (diffs, verdicts, scores).parallel([thunks]) — barrier; awaits all; a thrown thunk becomes null → .filter(Boolean).pipeline(items, stage1, stage2, …) — the default. No barrier between stages; each item
flows independently. Use parallel inside a stage for the fan-out, and pass phase: on each
agent() to avoid races on the global phase() state.min(16, cores−2); pass up to 4096 items and they queue.isolation: 'worktree' only works if the session cwd is itself a git repo. When it isn't (common),
use the disposable-/tmp-source pattern instead (directive 7).budget / loop-until-dry / loop-until-count patterns exist for unknown-size work.Each entry: shape · when · adapter skeleton. Compose freely; these are primitives, not a menu.
The default move is to COMPOSE a custom graph, not to pick one row. The named structures below (pyramid, committee, Hecate, cross-review…) are nodes and motifs, not the unit of work. The real unit is the graph you wire for this task — sequential where there are dependencies, parallel where there aren't, multi-agent where one witness isn't enough, multi-type where different roles (builder / researcher / visual / adversary / integrator) belong on different nodes. A bespoke topology is expected, never exotic — you should reach for "what graph does this task want?" before reaching for any single skill or command. The worked example
examples/saturating-review-engine.workflow.jsis a custom graph for any reviewable artifact (paper, deck, README, landing page, design doc, dataset report) — read it as a template for how to compose, not as a paper tool.
Think in nodes and edges, then build it with the substrate from §1.
agent(...)) with a role. Role types: DRAFTER, SYNTHESIZER,
VERIFIER, ADVERSARY, VISUAL, INTEGRATOR, COORDINATOR. Pick the type per node — a graph is
multi-type when nodes play different roles, which is the common case.async function
that runs its internal parallel/pipeline and returns the validated object (see §2.11). This
is how you get arbitrarily deep graphs without arbitrarily complex top-level code.pipeline stage or
await it after the producer. Parallel (no edge) → same parallel([...]) batch. A
modality-conditioned edge feeds one node's output into a node working in a different
modality — e.g. a visual node that receives the text node's verdict and checks pixels against
it (see §2.12). That edge is where contradictions surface.Shape: 2 isolated workers → 1 arbiter. When: any single correctness-critical step.
const drafts = await parallel([
() => agent(spec, {label:'work:A', schema:S}),
() => agent(spec, {label:'work:B', schema:S}), // identical spec, different label only
])
const verdict = await agent(arbitratePrompt(drafts.filter(Boolean)), {schema:VERDICT})
Arbiter decision matrix: full agreement → apply (conf 90+); minor diffs → pick cleaner (80+); structural → analyze+merge (60–85); fundamental → escalate (<40).
Shape: 2 independent researchers (zero shared context) → 1 cross-reference verifier that manually investigates disagreements. When: facts/predictions where only triple-verified data should survive. Scale 3 → 6–9 → 12+ by widening the researcher pool.
Shape: N drafters → comparator/synthesizer → proportional verifiers → adversary → integrator.
When: hallucination-prone or high-stakes one-shots. Verifiers per cluster ≈ floor(N/2).
Label outputs ✅ VERIFIED (unanimous) / ⚠️ CONTESTED (resolved disagreement) / ❌ STRIPPED
(confirmed fabrication). See the hardened build pattern in §4.
Shape: N reviewers × M rounds until a quality bar is met, each committee with distinct criteria. When: quality iteration (papers, grants, designs). Loop:
let work = seed, round = 0
while (round++ < M) {
const reviews = await parallel(CRITERIA.map(c => () => agent(reviewPrompt(work, c), {schema:R})))
const verdict = await agent(judgePrompt(work, reviews), {schema:J})
if (verdict.meets_bar) break
work = await agent(rewritePrompt(work, reviews, verdict), {schema:W})
}
Shape: N experts → an arbiter with decision authority and a verb menu each round: conclude · another round · ask Expert X a question · stage a debate between X and Y on topic Z · other intervention. When: the path to clarity isn't fixed — let the arbiter steer dynamically.
Shape: N reviewers produce independent reviews → each sees ALL peer reviews → grades peers → rewrites its own. When: peer-informed revision without losing initial independence (the first pass is blind; exposure comes after).
Shape: like 2.6 but each reviewer sees only M random peers. When: you want to preserve minority views — partial exposure stops premature consensus; a reviewer can hold firm.
Shape: dimensions defined by negative constraints (what each CANNOT do, not a positive role) → independent passes → synthesis that adjudicates. When: you want emergent findings (claims no single dimension produced) and provably diverse coverage.
Shape: 1 coordinator holding state + N workers over extended time, workers may depend on prior
workers' output. When: multi-day projects with task dependencies. Encode as sequential
pipeline waves where later items consume earlier artifacts.
Triggers: arbiter confidence < 40, fundamental disagreement, or both workers wrong.
Output: an escalation object — disagreement summary, both positions, analysis, tentative
recommendation — surfaced to the human instead of a forced merge. Always give structures an escape
hatch; a confident-wrong merge is worse than an honest escalation.
Shape: a single logical node — e.g. "Reviewer Aglaia" — expands into its OWN subgraph and returns one validated review. When: the unit you're fanning out over deserves more than one witness internally — you want N independent reviewers, but each reviewer should itself be robust, not a single agent's hot take. The inner subgraph is your choice — Byzantine-2, a Hecate lens spread, a persona panel, or a single pass for cheap nodes; the recursion is the point, not the filling.
async function runReviewer(rev, input, round) { // <- the node IS a function
const lenses = await parallel(LENSES.map(L => async () => { // inner structure: pick whatever fits
const drafts = await parallel([ // (here Byzantine-2 per lens; swappable)
() => agent(lensPrompt(rev,L,input,'A'), {phase:'Review', schema:LR}),
() => agent(lensPrompt(rev,L,input,'B'), {phase:'Review', schema:LR}),
])
return agent(lensSynth(rev,L,drafts.filter(Boolean)), {phase:'Review', schema:LR})
}))
return agent(reviewerSynth(rev, lenses.filter(Boolean)), {phase:'Review', schema:REVIEW}) // one node out
}
const reviews = (await parallel(REVIEWERS.map(r => () => runReviewer(r, input, 1)))).filter(Boolean)
The top level stays legible (a parallel over reviewers) while each "reviewer" hides a full
subgraph. Compose this to any depth; just return one schema-validated object per level.
Shape: a node working in modality B receives a node's verdict from modality A and is tasked to
confirm or refute it against B's evidence. The canonical case: a VISUAL committee that reads
the rendered page images AND the textual review of the same reviewer, hunting where the words
and the pixels disagree. When: text alone is blind to surface truth — a title at the bottom of
the page, an [insert figure here] placeholder, a figure whose bars contradict its caption, an
unrendered equation. The text review can score 68% while the page is visibly unfinished; only the
cross-modal edge catches it.
const text = await agent(reviewerSynth(...), {phase:'Review', schema:TEXTREVIEW})
const visual = await parallel(VISUAL_LENSES.map(L => () =>
agent(visualPrompt(L, manifest.image_paths, text), {phase:'Visual', schema:VISUAL}))) // sees text + pixels
const adv = await agent(visualAdversary(visual), {phase:'Visual', schema:ADVERSARY}) // inverted bias: obvious defects default HOLD
return agent(reconcile(text, visual, adv), {schema:RECONCILED}) // pixels win on contradiction
Key inversion: the visual adversary defaults holds=true for obvious surface defects (humans DO penalise them) and holds=false for taste. Same byzantine machinery, flipped prior.
Shape: a PRODUCE pyramid and an IMPROVE pyramid wired in a feedback loop — produce a verdict →
improve the artifact to clear it → re-ingest the improved artifact → repeat. Stop on
saturation: K consecutive rounds with no new findings (dedup against a seen set), no gate
fired, and the improver's adversary holding. When: "review and harden X until it stops getting
better" — and X is any artifact, not just a paper.
const seen = new Set(); let dry = 0, manifest = await agent(ingest(ARTIFACT), {schema:MANIFEST})
for (let round = 1; round <= MAX; round++) {
const reviews = (await parallel(REVIEWERS.map(r => () => runReviewer(r, manifest, round)))).filter(Boolean)
const panel = await agent(panelSynth(reviews), {schema:PANEL}) // anti-averaging integrate
const fresh = panel.must_fix.filter(m => { const k = m.page+'|'+m.issue; return seen.has(k)?false:(seen.add(k),true) })
if (!fresh.length && !panel.gate_applied && !panel.must_fix.length) { if (++dry >= 1) break } else dry = 0
const fix = await parallel([() => agent(rewrite(manifest,panel,'A'),{schema:REWRITE}),
() => agent(rewrite(manifest,panel,'B'),{schema:REWRITE})])
const chosen = await agent(rewriteArbiter(panel, fix.filter(Boolean)), {schema:VERDICT})
manifest = await agent(ingest(chosen.new_artifact_path), {schema:MANIFEST}) // <- feedback edge
}
Full runnable version (recursive reviewers + modality-conditioned visual QA + this loop, all three
composed): examples/saturating-review-engine.workflow.js.
model: on agent()). Two calls to the same model are one witness counted twice.Real human reviewers are annoyed, anchored, and biased — and that bias catches things a polite "expert reviewer" waves through. Two moves make a review graph behave like real humans:
[insert figure here] as near-fatal."
Stated bias is a feature: it makes the reviewer score the way a human actually would.[insert figure here], a missing/broken figure, a title in the wrong place, or unrendered
\ref/?? getting 68% because strong substance bought back a soft deduction. A human doesn't
do that — an obvious disqualifying defect caps the score (e.g. ≤40) and attaches the flag "at
best the author forgot to include material; at worst this was produced by an automated process that
never inspected its own output." Make the gate a non-negotiable instruction in the synthesis +
panel prompts, and seed it with a deterministic pre-scan (grep for placeholder patterns) so the
defect's existence is ground truth and only its weight is judged. Substance must not buy back
the cap. See GATE_DOCTRINE in the worked example.Use this pattern for an "implement or migrate N items" task where independent implementation and adversarial review justify the overhead.
const SOURCE = '/tmp/proj-source' // disposable clone of the live repo (directive 7)
// ITEMS: [{id, title, files /* single-owner scope */, brief /* NO raw backticks */}]
const results = await pipeline(ITEMS,
// Stage 1 — two isolated implementers race the same item in their own /tmp clones.
(item) => parallel([
() => agent(implPrompt(item,'A'), {label:`impl:${item.id}:A`, phase:'Build', schema:IMPL, agentType:'general-purpose'}),
() => agent(implPrompt(item,'B'), {label:`impl:${item.id}:B`, phase:'Build', schema:IMPL, agentType:'general-purpose'}),
]).then(impls => ({item, impls: impls.filter(Boolean)})),
// Stage 2 — Oracle reads both diffs, picks the authoritative one, does gap analysis.
(p) => agent(verifyPrompt(p.item, p.impls), {label:`verify:${p.item.id}`, phase:'Verify', schema:VERDICT})
.then(v => ({item:p.item, verdict:v})),
// Stage 3 — Cassandra tries to REFUTE the chosen diff (default holds=false if unsure).
(p) => agent(refutePrompt(p.item, p.verdict), {label:`adversary:${p.item.id}`, phase:'Verify', schema:ADVERSARY})
.then(a => ({id:p.item.id, diff:p.verdict.diff, gaps:p.verdict.gaps, adversary:a})),
)
return results.filter(Boolean)
Implementer brief must: rm -rf $WORKDIR && git clone -q $SOURCE $WORKDIR && cd $WORKDIR; a
SANDBOX RULE forbidding any path outside $WORKDIR; touch only item.files; set up a venv + run
the full suite; return git add -A && git diff --cached. Schemas: IMPL {variant,summary,diff, tests_passed,test_tail,files_touched}; VERDICT {chosen_variant,diff,rationale,gaps,confidence};
ADVERSARY {holds,problems,severity}.
Integration (you, on the live tree): move strays aside → git apply --recount each chosen diff
→ run suite → read the adversary verdicts and fix any holds=false before committing → dogfood
any linters → commit per-item
provenance → push.
| Structure | Agents | Best for | Overhead |
|---|---|---|---|
| Single agent | 1 | trivial / deterministic | none |
| Byzantine 2+1 | 3 | correctness-critical step | low |
| Triple-verified | 3→12+ | research, predictions | low |
| Pyramid Byzantine | 5–15+ | hallucination-prone one-shots, builds | medium |
| Committee N×M | N×M | quality iteration | medium |
| Arbiter-directed | N+1 | open-ended path to clarity | medium |
| Cross-review | N×2 | peer-informed revision | medium |
| Random cross-review | N×2 | preserving minority views | medium |
| Hecate dimensional | N+Σ | emergence, orthogonality | high |
| Recursive node (§2.11) | node×subgraph | robust per-witness (reviewer = pyramid) | high |
| Modality-conditioned (§2.12) | +visual | text-vs-pixel contradiction hunting | medium |
| Saturation loop (§2.13) | two pyramids ×R | harden any artifact until dry | high |
| Grinding | N+1 | multi-day, dependent tasks | high |
| N×M aggregation | N×M | maximum diversity | very high |
Scale verification to bits-at-risk, not by reflex: a zero-entropy step gets one agent; a high-stakes one gets the full pyramid. Spend tracks uncertainty.
/tmp source clone made; live path redacted; recoverable backup verified--recount), verify (.git + suite), and act on adversary holds