Documentation Index Fetch the complete documentation index at: https://docs.runflow.ai/llms.txt
Use this file to discover all available pages before exploring further.
A multi-agent system where a supervisor agent analyzes incoming messages and routes them to specialized agents — sales, support, or billing. Each specialist has its own tools, knowledge base, and personality. This pattern is ideal when a single agent can’t handle all scenarios well.
Project Structure
multi-agent/
├── main.ts
├── supervisor.ts
├── agents/
│ ├── sales.ts
│ ├── support.ts
│ └── billing.ts
├── tools/
│ ├── index.ts
│ ├── search-orders.ts
│ ├── create-ticket.ts
│ └── check-invoice.ts
├── prompts/
│ └── index.ts
├── .runflow/
│ └── rf.json
├── package.json
└── tsconfig.json
Step 1: Specialist Agents
Each specialist handles a specific domain with its own tools and instructions.
Sales Agent
import { Agent , openai } from '@runflow-ai/sdk' ;
export const salesAgentConfig = {
name: 'Sales Specialist' ,
instructions: `You are a sales specialist for ACME Corp.
## Behavior
- Help with pricing questions, plan comparisons, and purchasing
- Be consultative — understand needs before recommending plans
- For Enterprise inquiries, offer to schedule a demo
- Always provide clear pricing information
## Response Style
- Be enthusiastic but not pushy
- Use concrete numbers and comparisons
- End with a clear next step` ,
model: openai ( 'gpt-4o' ),
};
Support Agent
import { Agent , openai } from '@runflow-ai/sdk' ;
export const supportAgentConfig = {
name: 'Technical Support' ,
instructions: `You are a technical support specialist for ACME Corp.
## Behavior
- Search the knowledge base before answering technical questions
- Walk users through solutions step by step
- Create a ticket if the issue can't be resolved here
- Always confirm the user's problem before suggesting solutions
## Tools
- Use **search-orders** to look up order details
- Use **create-ticket** for issues needing human follow-up` ,
model: openai ( 'gpt-4o' ),
};
Billing Agent
import { Agent , openai } from '@runflow-ai/sdk' ;
export const billingAgentConfig = {
name: 'Billing Specialist' ,
instructions: `You are a billing specialist for ACME Corp.
## Behavior
- Help with invoices, payment issues, and subscription changes
- Always verify the account before making changes
- For refund requests, check the policy first, then proceed
## Tools
- Use **check-invoice** to look up invoice details
- Use **create-ticket** for complex billing issues that need manual review` ,
model: openai ( 'gpt-4o' ),
};
Step 2: Supervisor Agent
The supervisor uses the built-in agents config to automatically route to specialists:
import { Agent , openai } from '@runflow-ai/sdk' ;
import { salesAgentConfig } from './agents/sales' ;
import { supportAgentConfig } from './agents/support' ;
import { billingAgentConfig } from './agents/billing' ;
import { searchOrdersTool , createTicketTool , checkInvoiceTool } from './tools' ;
export const supervisorAgent = new Agent ({
name: 'Customer Service Supervisor' ,
instructions: `You route customer requests to the right specialist.
## Routing Rules
- **Sales**: pricing, plans, purchasing, upgrades, demos, discounts
- **Support**: technical issues, bugs, how-to questions, order status
- **Billing**: invoices, payments, refunds, subscription changes, charges
## Behavior
- Analyze the customer's message to determine intent
- Route to the most appropriate specialist
- If the intent is ambiguous, ask the customer to clarify
- If a conversation switches topics (e.g., support → billing), re-route
## Important
- You do NOT answer questions directly — you route to specialists
- Never make up information about pricing, policies, or account details` ,
model: openai ( 'gpt-4o-mini' ), // Cheap model for routing
// Specialist agents — Runflow handles routing automatically
agents: {
sales: salesAgentConfig ,
support: {
... supportAgentConfig ,
tools: {
searchOrders: searchOrdersTool ,
createTicket: createTicketTool ,
},
rag: {
vectorStore: 'support-docs' ,
k: 5 ,
threshold: 0.7 ,
},
},
billing: {
... billingAgentConfig ,
tools: {
checkInvoice: checkInvoiceTool ,
createTicket: createTicketTool ,
},
},
},
memory: {
maxTurns: 30 ,
summarizeAfter: 20 ,
summarizePrompt: 'Summarize: customer intent, which specialist handled it, actions taken, pending issues' ,
},
observability: 'full' ,
});
Shared tools used by multiple specialists:
import { createTool } from '@runflow-ai/sdk' ;
import { z } from 'zod' ;
export const searchOrdersTool = createTool ({
id: 'search-orders' ,
description: 'Search customer orders by order ID or email' ,
inputSchema: z . object ({
orderId: z . string (). optional (). describe ( 'Order ID (e.g., ORD-12345)' ),
email: z . string (). email (). optional (). describe ( 'Customer email' ),
}),
execute : async ( params ) => {
try {
const response = await fetch (
`https://api.yourcompany.com/orders?id= ${ params . orderId || '' } &email= ${ params . email || '' } ` ,
{ headers: { 'Authorization' : `Bearer ${ process . env . ORDERS_API_KEY } ` } }
);
const orders = await response . json ();
if ( ! orders . length ) {
return { found: false , query: params . orderId || params . email };
}
return {
found: true ,
orders: orders . map (( o : any ) => ({
id: o . id ,
status: o . status ,
total: o . total ,
createdAt: o . createdAt ,
})),
};
} catch {
return { found: false , error: 'Service unavailable' };
}
},
});
import { createTool } from '@runflow-ai/sdk' ;
import { z } from 'zod' ;
export const checkInvoiceTool = createTool ({
id: 'check-invoice' ,
description: 'Look up invoice details by invoice ID or customer email' ,
inputSchema: z . object ({
invoiceId: z . string (). optional (). describe ( 'Invoice ID' ),
email: z . string (). email (). optional (). describe ( 'Customer email' ),
}),
execute : async ( params ) => {
try {
const invoices = await fetchInvoices ( params . invoiceId , params . email );
if ( ! invoices . length ) {
return { found: false };
}
return {
found: true ,
invoices: invoices . map (( inv : any ) => ({
id: inv . id ,
amount: inv . amount ,
status: inv . status ,
dueDate: inv . dueDate ,
paidAt: inv . paidAt ,
})),
};
} catch {
return { found: false , error: 'Could not retrieve invoices' };
}
},
});
import { createTool } from '@runflow-ai/sdk' ;
import { track } from '@runflow-ai/sdk/observability' ;
import { z } from 'zod' ;
export const createTicketTool = createTool ({
id: 'create-ticket' ,
description: 'Create a support ticket for issues needing human follow-up' ,
inputSchema: z . object ({
subject: z . string (),
description: z . string (),
priority: z . enum ([ 'low' , 'medium' , 'high' ]),
department: z . enum ([ 'sales' , 'support' , 'billing' ]),
}),
execute : async ( params ) => {
try {
const ticket = { id: `TICKET- ${ Date . now () } ` , ... params };
track ( 'ticket_created' , {
department: params . department ,
priority: params . priority ,
});
return { success: true , ticketId: ticket . id };
} catch {
return { success: false , error: 'Failed to create ticket' };
}
},
});
export { searchOrdersTool } from './search-orders' ;
export { createTicketTool } from './create-ticket' ;
export { checkInvoiceTool } from './check-invoice' ;
Step 4: Main Entry Point
import { identify , track } from '@runflow-ai/sdk/observability' ;
import { supervisorAgent } from './supervisor' ;
export async function main ( input : any ) {
if ( ! input ?. message ) {
return { error: 'message is required' };
}
identify ( input . email || input . phone || input . userId || 'anonymous' );
try {
const result = await supervisorAgent . process ({
message: input . message ,
sessionId: input . sessionId ,
});
// Track routing metrics
track ( 'customer_request' , {
channel: input . channel || 'api' ,
routedTo: result . metadata ?. routedTo || 'unknown' ,
});
return {
message: result . message ,
metadata: result . metadata ,
};
} catch ( error ) {
console . error ( '[multi-agent] Error:' , error );
return { error: 'An error occurred. Please try again.' };
}
}
How It Works
Customer message arrives
↓
┌─────────────────────┐
│ Supervisor │ gpt-4o-mini analyzes intent
│ (routing only) │
└──────┬──────────────┘
↓
What intent?
╱ │ ╲
Sales Support Billing
↓ ↓ ↓
gpt-4o gpt-4o gpt-4o
+ no + RAG + invoice
tools + tools tools
↓
Response back
to customer
Key Patterns
Cheap Supervisor, Quality Specialists
The supervisor only needs to classify intent — use gpt-4o-mini (fast, cheap). Specialists do the real work — use gpt-4o for quality responses.
Built-in agents Config
Runflow’s agents config handles routing automatically. You define specialists inline and the supervisor routes based on its instructions:
const supervisor = new Agent ({
instructions: 'Route to sales, support, or billing...' ,
model: openai ( 'gpt-4o-mini' ),
agents: {
sales: { name: 'Sales' , instructions: '...' , model: openai ( 'gpt-4o' ) },
support: { name: 'Support' , instructions: '...' , model: openai ( 'gpt-4o' ) },
},
});
Some tools (like create-ticket) are used by multiple specialists. Define them once in tools/ and assign to each agent that needs them.
Conversation Continuity
Memory is shared across the supervisor session. If a customer starts with a support question and then asks about billing, the context carries over — the billing agent knows what was discussed before.
When to Use Multi-Agent
Scenario Use Single-purpose bot (FAQ, scheduling) Single agent Multiple domains with different tools/knowledge Multi-agent Complex routing with fallbacks Multi-agent Different response styles per department Multi-agent
Next Steps
Agents Supervisor pattern details
Customer Support Single-agent support example
Sales Automation Sales workflow example
Best Practices Tips for effective agents