Skip to main content

Overview

Multi-agent systems allow you to break down complex tasks into specialized sub-agents, each handling a specific domain or responsibility. This example demonstrates:
  • Creating specialized sub-agents with focused instructions
  • Coordinating agents through a root agent
  • Sharing state between agents
  • Transferring control between agents
Multi-agent systems are ideal for complex workflows like customer service, order processing, content moderation, or any task that benefits from specialized expertise.

Restaurant Order System Example

This example builds a restaurant ordering system with three specialized agents:
  1. Customer Analyzer: Extracts dietary restrictions, preferences, and budget
  2. Menu Validator: Checks item availability and suggests alternatives
  3. Order Finalizer: Confirms and processes the final order

Complete Example

1
Step 1: Create Specialized Sub-Agents
2
Each sub-agent focuses on one aspect of the workflow:
3
agents/customer-analyzer/agent.ts
import { LlmAgent } from "@iqai/adk";
import dedent from "dedent";

export function getCustomerAnalyzerAgent() {
	return new LlmAgent({
		name: "customer_analyzer",
		description: "Analyzes customer restaurant orders",
		instruction: dedent`
			Extract order items, dietary restrictions, special preferences, and budget constraints.
			Return the extracted information in a clear, structured format.
		`,
		outputKey: "customer_preferences",
		model: process.env.LLM_MODEL || "gemini-3-flash-preview",
	});
}
agents/menu-validator/agent.ts
import { LlmAgent } from "@iqai/adk";
import dedent from "dedent";

export function getMenuValidatorAgent() {
	return new LlmAgent({
		name: "menu_validator",
		description: "Validates items against menu",
		instruction: dedent`
			Based on customer preferences: {customer_preferences}

			Menu: Burgers (beef, chicken, veggie), Pizzas (margherita, pepperoni, veggie), 
			Salads (caesar, greek, quinoa), Pasta (spaghetti, penne, gluten-free), 
			Desserts (cheesecake, chocolate cake, fruit sorbet)

			Check availability and suggest alternatives if needed.
		`,
		outputKey: "menu_validation",
		model: process.env.LLM_MODEL || "gemini-3-flash-preview",
	});
}
agents/order-finalizer/agent.ts
import { LlmAgent } from "@iqai/adk";
import dedent from "dedent";

export function getOrderFinalizerAgent() {
	return new LlmAgent({
		name: "order_finalizer",
		description: "Finalizes and confirms orders",
		instruction: dedent`
			Based on:
			- Customer preferences: {customer_preferences}
			- Menu validation: {menu_validation}

			Provide a final order confirmation with prices and estimated prep time.
		`,
		model: process.env.LLM_MODEL || "gemini-3-flash-preview",
	});
}
4
Notice how each agent uses outputKey to store its results in the shared state. The next agent can reference this data using {outputKey} in its instructions.
5
Step 2: Create the Root Agent
6
The root agent coordinates all sub-agents:
7
import { AgentBuilder } from "@iqai/adk";
import { getCustomerAnalyzerAgent } from "./customer-analyzer/agent";
import { getMenuValidatorAgent } from "./menu-validator/agent";
import { getOrderFinalizerAgent } from "./order-finalizer/agent";

export function getRootAgent() {
	const customerAnalyzer = getCustomerAnalyzerAgent();
	const menuValidator = getMenuValidatorAgent();
	const orderFinalizer = getOrderFinalizerAgent();

	const initialState = {
		customer_preferences: "",
		menu_validation: "",
	};

	return AgentBuilder.create("restaurant_order_system")
		.withModel(process.env.LLM_MODEL || "gemini-3-flash-preview")
		.withSubAgents([customerAnalyzer, menuValidator, orderFinalizer])
		.withQuickSession({ state: initialState })
		.build();
}
8
Step 3: Use the Multi-Agent System
9
import { ask } from "../utils";
import { getRootAgent } from "./agents/agent";

async function main() {
	const { runner } = await getRootAgent();

	const questions = [
		"I'd like something vegetarian, not too spicy, around $20. Maybe a salad or pasta?",
		"Can I get a burger with fries?",
		"What desserts do you have?",
	];

	for (const question of questions) {
		await ask(runner, question);
	}
}

main().catch(console.error);
10
Step 4: Run the Example
11
node index.ts

Expected Output

👤 User: I'd like something vegetarian, not too spicy, around $20. Maybe a salad or pasta?

🤖 Agent: [Transfers to customer_analyzer]
Analyzing your preferences...
- Dietary: Vegetarian
- Spice level: Mild
- Budget: ~$20
- Interest: Salad or pasta

🤖 Agent: [Transfers to menu_validator]
Checking our menu... I recommend:
- Greek Salad ($12) - Fresh vegetables with feta
- Veggie Pizza ($16) - Loaded with vegetables
- Penne Pasta with marinara ($14) - Classic Italian

All options are vegetarian and mild.

🤖 Agent: [Transfers to order_finalizer]
Great choices! Would you like to order the Greek Salad ($12)? 
Estimated prep time: 10 minutes.

Key Concepts

Agent Coordination

The root agent automatically:
  • Determines which sub-agent should handle each request
  • Transfers control to the appropriate specialist
  • Manages shared state between agents
  • Returns responses to the user

State Sharing with Output Keys

Sub-agents share data through the outputKey mechanism:
// Agent 1 stores results
outputKey: "customer_preferences"

// Agent 2 references them
instruction: "Based on: {customer_preferences}"

Folder Structure for Multi-Agent Systems

project/
├── agents/
│   ├── agent.ts                    # Root agent
│   ├── customer-analyzer/
│   │   └── agent.ts
│   ├── menu-validator/
│   │   └── agent.ts
│   └── order-finalizer/
│       └── agent.ts
└── index.ts
Each sub-agent can have its own tools.ts, plugins.ts, and other files. This keeps complex systems organized and maintainable.

Advanced Patterns

Sequential vs Parallel Agents

This example shows sequential agent execution (one after another). For parallel execution:
import { AgentBuilder } from "@iqai/adk";

const parallelAgent = AgentBuilder.parallel([
	priceCheckAgent,
	inventoryAgent,
	shippingAgent,
]);

// All agents run simultaneously
const results = await parallelAgent.ask("Check product status");

Conditional Agent Routing

The root agent intelligently routes requests:
// Automatically routes to the right specialist
const { runner } = await getRootAgent();

// Goes to customer_analyzer
await runner.ask("I'm vegetarian and allergic to nuts");

// Goes to menu_validator
await runner.ask("Do you have gluten-free options?");

// Goes to order_finalizer
await runner.ask("I'll take the pasta, please");

Use Cases

Customer Support

Route to specialists: billing, technical, returns

Content Moderation

Analyze content safety, compliance, quality

Data Processing

Extract, validate, transform, and load data

Research Tasks

Gather, verify, synthesize information

Next Steps

Database Sessions

Persist multi-agent conversations across sessions

Memory Systems

Add long-term memory to your agents

Source Code

View the complete example in the repository: apps/examples/src/03-multi-agent-systems

Build docs developers (and LLMs) love