What Is Multi-Agent Orchestration?
When a single AI agent can't handle a complex task alone, multiple specialized agents can collaborate to get the job done. This coordination layer is called multi-agent orchestration. Google's Antigravity editor supports agent features for visual task splitting and delegation, but advanced use cases require a code-level orchestration layer.
Pattern 1: Router Pattern
The router pattern analyzes incoming requests and dispatches them to the most appropriate agent. It works well for customer support scenarios where different query types need different handlers.
// router-agent.ts
// Router pattern — classify input and delegate to the right agent
interface AgentRoute {
name: string;
description: string;
handler: (input: string) => Promise<string>;
}
class RouterAgent {
private routes: AgentRoute[] = [];
addRoute(route: AgentRoute) {
this.routes.push(route);
}
async classify(input: string): Promise<string> {
// Simple keyword classification (use LLM classification in production)
const keywords: Record<string, string[]> = {
"billing-agent": ["billing", "payment", "charge", "plan", "invoice"],
"technical-agent": ["error", "bug", "broken", "setup", "config"],
"general-agent": ["question", "how", "what", "help", "guide"],
};
for (const [agentName, words] of Object.entries(keywords)) {
if (words.some((w) => input.toLowerCase().includes(w))) {
return agentName;
}
}
return "general-agent";
}
async route(input: string): Promise<string> {
const targetName = await this.classify(input);
const target = this.routes.find((r) => r.name === targetName);
if (!target) {
return `No handler found for: ${targetName}`;
}
console.log(`[Router] Routing to: ${target.name}`);
return target.handler(input);
}
}
// Usage
const router = new RouterAgent();
router.addRoute({
name: "billing-agent",
description: "Handles billing and payment inquiries",
handler: async (input) => `[Billing] Processing: ${input}`,
});
router.addRoute({
name: "technical-agent",
description: "Resolves technical issues",
handler: async (input) => `[Tech] Investigating: ${input}`,
});
const result = await router.route("I need to change my payment method");
console.log(result);
// Expected output: [Router] Routing to: billing-agent
// [Billing] Processing: I need to change my payment methodThe key principle of the router pattern is clear separation of concerns. The router itself never executes tasks — it only dispatches.
Pattern 2: Pipeline Pattern
The pipeline pattern chains multiple agents sequentially, where each agent's output becomes the next agent's input. It's ideal for data transformation and content generation workflows.
// pipeline-agent.ts
// Pipeline pattern — chain agents in series for sequential processing
interface PipelineStage {
name: string;
process: (input: string, context: Record<string, unknown>) => Promise<string>;
}
class AgentPipeline {
private stages: PipelineStage[] = [];
addStage(stage: PipelineStage) {
this.stages.push(stage);
return this; // Enable method chaining
}
async execute(input: string): Promise<{ result: string; log: string[] }> {
let current = input;
const context: Record<string, unknown> = {};
const log: string[] = [];
for (const stage of this.stages) {
const start = Date.now();
try {
current = await stage.process(current, context);
const elapsed = Date.now() - start;
log.push(`✓ ${stage.name} (${elapsed}ms)`);
} catch (error) {
log.push(`✗ ${stage.name}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
return { result: current, log };
}
}
// Content generation pipeline example
const contentPipeline = new AgentPipeline();
contentPipeline
.addStage({
name: "research-agent",
process: async (topic, ctx) => {
ctx.sources = ["source1.com", "source2.com"];
return `Research findings for "${topic}": Key points A, B, C from 2 sources.`;
},
})
.addStage({
name: "writing-agent",
process: async (research, ctx) => {
ctx.wordCount = 500;
return `Draft article based on: ${research}\n\n[500 words of content...]`;
},
})
.addStage({
name: "review-agent",
process: async (draft, ctx) => {
ctx.reviewScore = 0.92;
return draft.replace("[500 words of content...]", "[Reviewed and polished content]");
},
});
const output = await contentPipeline.execute("Latest trends in AI agents");
console.log(output.log.join("\n"));
// Expected output:
// ✓ research-agent (150ms)
// ✓ writing-agent (320ms)
// ✓ review-agent (180ms)The shared context object allows metadata to flow between stages. Errors halt the pipeline immediately and are recorded in the execution log.
Pattern 3: Consensus Pattern
The consensus pattern runs the same task across multiple agents in parallel and selects the best result. Use this when answer accuracy is critical or when you want to compare different approaches.
// consensus-agent.ts
// Consensus pattern — run competing agents and pick the best result
interface CompetingAgent {
name: string;
solve: (task: string) => Promise<{ answer: string; confidence: number }>;
}
class ConsensusOrchestrator {
private agents: CompetingAgent[] = [];
addAgent(agent: CompetingAgent) {
this.agents.push(agent);
}
async resolveByConfidence(task: string): Promise<{
winner: string;
answer: string;
confidence: number;
allResults: { name: string; confidence: number }[];
}> {
const results = await Promise.allSettled(
this.agents.map(async (agent) => {
const result = await agent.solve(task);
return { name: agent.name, ...result };
})
);
const successful = results
.filter((r): r is PromiseFulfilledResult<{ name: string; answer: string; confidence: number }> =>
r.status === "fulfilled"
)
.map((r) => r.value);
if (successful.length === 0) throw new Error("All agents failed");
const best = successful.reduce((a, b) => (a.confidence > b.confidence ? a : b));
return {
winner: best.name,
answer: best.answer,
confidence: best.confidence,
allResults: successful.map((s) => ({ name: s.name, confidence: s.confidence })),
};
}
}
const orchestrator = new ConsensusOrchestrator();
orchestrator.addAgent({
name: "fast-agent",
solve: async (task) => ({ answer: `Quick: ${task}`, confidence: 0.75 }),
});
orchestrator.addAgent({
name: "thorough-agent",
solve: async (task) => ({ answer: `Detailed: ${task}`, confidence: 0.95 }),
});
const consensus = await orchestrator.resolveByConfidence("AI market forecast for 2026");
console.log(`Winner: ${consensus.winner} (confidence: ${consensus.confidence})`);
// Expected output: Winner: thorough-agent (confidence: 0.95)Integration with Antigravity Editor
Antigravity's agent features let you combine these patterns visually. You can write TypeScript in "Custom Code" blocks and connect agent data flows through the node-based interface.
For production deployments, pay attention to timeout management (use AbortSignal.timeout() with fallbacks), retry strategies (exponential backoff for transient errors), and observability (log execution time, success rates, and error rates for each agent to identify bottlenecks).
Best Practices
Single Responsibility: Each agent should have one clear role. "Do everything" agents produce lower quality output and are harder to debug.
Fail-Safe Design: No single agent failure should bring down the entire system. The pipeline pattern uses try-catch for error logging, and the consensus pattern uses Promise.allSettled to tolerate partial failures.
Minimize Context: Keep inter-agent data transfers as small as possible. Large context objects increase latency and token costs.
Looking back
This guide covered three fundamental multi-agent orchestration patterns: router, pipeline, and consensus. Combined with Antigravity editor's agent features, you can blend no-code visual design with code-level control for sophisticated AI workflows.
Start with a simple router pattern, then layer in pipelines and consensus as your task complexity grows. For more on agent development, check out Custom Agent Creation Guide and Web Scraping Agent.