back to artifacts
Building Sub-Agents: How to Design Multi-Agent AI Systems
Learn how to architect and build sub-agent systems — breaking complex AI tasks into specialized agents that collaborate, delegate, and deliver better results.
Learn how to architect and build sub-agent systems — breaking complex AI tasks into specialized agents that collaborate, delegate, and deliver better results.
As AI agents grow more capable, the tasks we assign them grow more complex. A single monolithic agent often struggles with multi-step workflows that require different expertise at each stage. The solution? Sub-agents — specialized agents that each handle one part of the problem, coordinated by a parent agent.
This guide covers the architecture, patterns, and practical implementation of sub-agent systems.
A sub-agent is a specialized AI agent that is invoked by a parent (orchestrator) agent to handle a specific subtask. Think of it like a team lead delegating work:
Each sub-agent can have its own system prompt, tools, context window, and even a different model.
| Single Agent | Multi-Agent | | ----------------------------------- | --------------------------------------------- | | One system prompt for everything | Specialized prompts per task | | Single context window fills up fast | Each agent gets a fresh context | | One model for all tasks | Use cheaper models for simple tasks | | Hard to debug failures | Isolate failures to specific agents | | Difficult to extend | Add new agents without changing existing ones |
Agents execute in a fixed order, each passing output to the next.
User Request
↓
[Research Agent]
↓
[Writer Agent]
↓
[Editor Agent]
↓
Final Outputasync function sequentialPipeline(userRequest: string) {
// Step 1: Research
const research = await researchAgent.execute({
task: `Research the following topic: ${userRequest}`,
tools: ['web_search', 'read_url'],
});
// Step 2: Write
const draft = await writerAgent.execute({
task: `Write an article based on this research: ${research.output}`,
tools: ['file_write'],
});
// Step 3: Edit
const final = await editorAgent.execute({
task: `Review and improve this draft: ${draft.output}`,
tools: ['file_edit'],
});
return final.output;
}The orchestrator dispatches independent subtasks simultaneously.
User Request
↓
[Orchestrator]
├─→ [Frontend Agent] ─┐
├─→ [Backend Agent] ──┤
└─→ [Test Agent] ─────┤
↓
[Merge Agent]
↓
Outputasync function parallelFanOut(userRequest: string) {
const plan = await orchestrator.plan(userRequest);
// Execute independent tasks in parallel
const results = await Promise.all([
frontendAgent.execute(plan.frontendTask),
backendAgent.execute(plan.backendTask),
testAgent.execute(plan.testTask),
]);
// Merge results
const merged = await mergeAgent.execute({
task: 'Combine these outputs into a cohesive result',
context: results.map(r => r.output),
});
return merged.output;
}The orchestrator analyzes the request and routes to the appropriate agent.
async function routerPattern(userRequest: string) {
// Classify the request
const classification = await orchestrator.classify(userRequest);
// Route to the right specialist
switch (classification.type) {
case 'code_generation':
return codeAgent.execute(userRequest);
case 'data_analysis':
return dataAgent.execute(userRequest);
case 'documentation':
return docAgent.execute(userRequest);
default:
return generalAgent.execute(userRequest);
}
}The orchestrator iteratively delegates and reviews until quality criteria are met.
async function supervisorLoop(userRequest: string) {
let result = await workerAgent.execute(userRequest);
let approved = false;
let iterations = 0;
const maxIterations = 3;
while (!approved && iterations < maxIterations) {
const review = await reviewerAgent.evaluate(result.output);
if (review.score >= 0.9) {
approved = true;
} else {
result = await workerAgent.execute({
task: userRequest,
feedback: review.feedback,
previousAttempt: result.output,
});
}
iterations++;
}
return result.output;
}Here is a minimal but practical implementation using TypeScript:
interface AgentConfig {
name: string;
model: string;
systemPrompt: string;
tools: Tool[];
maxTokens?: number;
}
interface AgentResult {
output: string;
tokensUsed: number;
toolCalls: ToolCall[];
}
class SubAgent {
private config: AgentConfig;
constructor(config: AgentConfig) {
this.config = config;
}
async execute(input: string): Promise<AgentResult> {
const response = await llm.chat({
model: this.config.model,
system: this.config.systemPrompt,
messages: [{ role: 'user', content: input }],
tools: this.config.tools,
maxTokens: this.config.maxTokens ?? 4096,
});
return {
output: response.content,
tokensUsed: response.usage.totalTokens,
toolCalls: response.toolCalls,
};
}
}const researchAgent = new SubAgent({
name: 'researcher',
model: 'claude-4-5-sonnet-20260101',
systemPrompt: `You are a research specialist. Your job is to gather
accurate, relevant information on a given topic. Always cite sources
and distinguish facts from opinions.`,
tools: [webSearchTool, urlReaderTool],
});
const codeAgent = new SubAgent({
name: 'coder',
model: 'claude-4-5-sonnet-20260101',
systemPrompt: `You are a senior software engineer. Write clean,
well-tested, production-ready code. Follow best practices and
include error handling.`,
tools: [fileReadTool, fileWriteTool, terminalTool],
});
const reviewAgent = new SubAgent({
name: 'reviewer',
model: 'claude-4-5-haiku-20260101', // Cheaper model for review
systemPrompt: `You are a code reviewer. Check for bugs, security
issues, and style violations. Be concise and actionable.`,
tools: [fileReadTool],
});class Orchestrator {
private agents: Map<string, SubAgent>;
constructor() {
this.agents = new Map();
}
registerAgent(name: string, agent: SubAgent) {
this.agents.set(name, agent);
}
async run(task: string): Promise<string> {
// Step 1: Plan
const plan = await this.planTask(task);
// Step 2: Execute sub-tasks
const results: string[] = [];
for (const step of plan.steps) {
const agent = this.agents.get(step.agentName);
if (!agent) throw new Error(`Agent ${step.agentName} not found`);
const result = await agent.execute(step.instruction);
results.push(`[${step.agentName}]: ${result.output}`);
}
// Step 3: Synthesize
return results.join('\n\n');
}
private async planTask(task: string) {
// Use LLM to decompose the task
const response = await llm.chat({
model: 'claude-4-5-haiku-20260101',
system: 'Decompose this task into steps. Return JSON.',
messages: [{ role: 'user', content: task }],
});
return JSON.parse(response.content);
}
}Each sub-agent should do one thing well. If an agent's system prompt exceeds a paragraph, consider splitting it.
Not every subtask needs the most powerful model:
Only pass relevant output between agents. Sending entire conversation histories wastes tokens and dilutes focus.
async function safeExecute(agent: SubAgent, input: string) {
try {
return await agent.execute(input);
} catch (error) {
console.error(`Agent ${agent.name} failed:`, error);
return { output: `Error: ${error.message}`, tokensUsed: 0, toolCalls: [] };
}
}Track which agent handled what, how many tokens were used, and what tools were called. This is essential for debugging and cost optimization.
Sub-agent architectures let you decompose complex AI tasks into manageable, specialized units. Whether you use a sequential pipeline, parallel fan-out, router, or supervisor loop depends on your specific use case.
Start simple with two agents (worker + reviewer), measure the results, and scale up as needed. The key is to treat each agent as a composable building block — focused, testable, and replaceable.

Developer ในยุค AI ในมุมของผม: งานไม่ได้หายไป แค่ย้ายจาก "พิมพ์โค้ด" ไป "ตัดสินใจและรับผิดชอบ"
16 Nov 2026 · 2 min read

สรุป: เปลี่ยน Grokbot ให้เป็นระบบ Multi-Agent 300 ตัวด้วย Kimi K3
15 Nov 2026 · 3 min read

Gemini 4 Hydrogen: โมเดลที่ทำงานเร็วและราคาถูกสำหรับ agent workflow และการสลับโมเดลกลางคัน
13 Nov 2026 · 4 min read