Sub-Agent Layer
Once the router decides which domains to activate, it hands work off to sub-agents — specialized agents that each manage a single area of responsibility.
Each sub-agent is built with the Agent class, giving it its own tools, system prompt, and loop control.
Think of sub-agents as mini-experts:
The Orders Agent knows sales data.
The Inventory Agent knows stock.
The Analytics Agent knows KPIs.
Why Use the Agent Class for Sub-Agents
The Agent class lets you define domain-specific logic once and reuse it anywhere.
This keeps your system consistent and easy to scale.
Benefits:
- Encapsulation – each domain’s model, tools, and prompts stay self-contained
- Consistency – shared behavior across environments (API, dashboard, chat)
- Type safety – tool inputs and outputs are fully typed with Zod
- Modularity – you can add or remove domains without touching the router
Creating a Sub-Agent
Define each sub-agent with the Agent class, giving it:
- a clear system role,
- a small toolset (3–7 tools), and
- loop control to manage reasoning.
Example: Orders Agent
import { Experimental_Agent as Agent, stepCountIs, tool } from "ai"
import { z } from "zod"
// Orders Agent — handles sales and revenue queries
export const ordersAgent = new Agent({
model: "openai/gpt-4o",
system: `You are an ecommerce orders specialist.
You can fetch, summarize, and analyze order data.`,
tools: {
getSalesData: tool({
description: "Fetch total sales and revenue for a date range",
parameters: z.object({
startDate: z.string(),
endDate: z.string(),
}),
execute: async ({ startDate, endDate }) => {
const revenue = 124_000 // fetched from API or DB
const totalOrders = 3800
return { startDate, endDate, revenue, totalOrders }
},
}),
summarizePerformance: tool({
description: "Summarize performance given sales data",
parameters: z.object({
revenue: z.number(),
totalOrders: z.number(),
}),
execute: async ({ revenue, totalOrders }) => ({
summary: `Revenue: $${revenue}, Orders: ${totalOrders}`,
avgOrderValue: Math.round(revenue / totalOrders),
}),
}),
},
stopWhen: stepCountIs(10), // allow up to 10 reasoning steps
})The agent runs its own loop:
it can call getSalesData, then summarizePerformance, then produce text — all automatically.
Example: Inventory Agent
Another sub-agent owns the Inventory domain.
import { Experimental_Agent as Agent, tool } from "ai"
import { z } from "zod"
export const inventoryAgent = new Agent({
model: "openai/gpt-4o",
system: `You manage stock data for an ecommerce store.
You know how to check product quantities and restock needs.`,
tools: {
getInventoryLevels: tool({
description: "Get current stock levels for products",
parameters: z.object({
category: z.string().optional(),
}),
execute: async ({ category }) => {
const data = [
{ product: "Wireless Mouse", stock: 83 },
{ product: "Laptop Stand", stock: 45 },
]
return { category, data }
},
}),
},
})Parallel Execution
When the router detects multiple intents, it can run sub-agents in parallel.
import { inventoryAgent } from "./inventory-agent"
import { ordersAgent } from "./orders-agent"
async function handleMultiIntent() {
const [orders, inventory] = await Promise.all([
ordersAgent.generate({ prompt: "Get sales this week" }),
inventoryAgent.generate({ prompt: "Get stock for top sellers" }),
])
return {
orders: orders.text,
inventory: inventory.text,
}
}Parallel runs make responses fast and independent — perfect for queries like:
“Show me this week’s revenue and current stock.”
Sequential Execution
Sometimes agents must work in order — for example:
“Find products with low stock, then estimate lost revenue.”
Here the router triggers agents sequentially:
const inventory = await inventoryAgent.generate({
prompt: "Find products with low stock",
})
const orders = await ordersAgent.generate({
prompt: `Estimate revenue impact from low-stock products: ${inventory.text}`,
})Design Principles
Keep sub-agents tight and deterministic:
- 3–7 tools max — small, static catalogs are predictable
- Validated inputs (Zod schemas) — guardrail for safety
- Stable schemas — keep data structures consistent across runs
- Small system prompts — clarity beats cleverness
Scaling with Ease
Adding a new domain is simple:
- Create a new sub-agent (e.g.,
analyticsAgent.ts) - Register it in the router’s domain map
- Done — your router now supports a new capability
The whole system stays modular and reliable.
ai sdk patterns.