Understanding the Qwen Code SubAgent Architecture: How to Create Custom Sub-Agents

The Qwen Code SubAgent architecture enables a primary AI agent to delegate specialized tasks to isolated sub-agents defined via Markdown files or programmatic APIs, each with distinct system prompts, tool permissions, and model configurations.

Qwen Code implements a sophisticated delegation system that treats specialized agents as first-class citizens. The SubAgent architecture allows the primary AI to offload domain-specific work to isolated agents with custom capabilities, configured through YAML-fronted Markdown files or TypeScript APIs. This design separates static agent definitions from dynamic execution scopes, enabling users to extend Qwen Code's functionality without modifying core engine code.

What Is the SubAgent Architecture?

The SubAgent architecture is a delegation framework where a primary AI agent distributes work to specialized, isolated agents. Each sub-agent operates as an independent entity with its own system prompt, tool allowances, and model configuration, ensuring clean separation of concerns and configurable isolation.

According to the Qwen Code source code, the architecture distinguishes between definition (static configuration) and execution (dynamic runtime scope). Definitions are stored as Markdown files with YAML front-matter or managed programmatically through the SubagentManager class. The system supports five storage levels—builtin, project, user, extension, and session—with precedence resolving from session (highest) down to builtin (lowest). The SubagentLevel type definition in packages/sdk-typescript/src/types/types.ts (lines 10-16) formalizes these persistence layers.

Core Components of the SubAgent System

SubagentConfig and Storage Levels

Every sub-agent conforms to the SubagentConfig interface defined in packages/sdk-typescript/src/types/types.ts (lines 30-48). This interface specifies:

  • name: Unique identifier for the agent
  • description: Human-readable purpose statement
  • tools: Array of permitted tool identifiers (e.g., read_file, edit)
  • modelConfig: LLM parameters including model name and temperature
  • runConfig: Execution constraints like max_time_minutes and max_turns
  • systemPrompt: Behavioral instructions templated at runtime

Storage levels determine where configurations persist. Builtin agents reside in BuiltinAgentRegistry (packages/core/src/subagents/builtin-agents.ts) and are immutable. Project and user level agents serialize to .qwen/agents/ directories, while session agents exist only in memory for the current runtime.

SubagentManager

The SubagentManager class in packages/core/src/subagents/subagent-manager.ts orchestrates the sub-agent lifecycle. It handles discovery, validation, caching, and CRUD operations.

Key methods include:

  • loadSubagent(name): Resolves configurations respecting level precedence (session > project > user > builtin)
  • createSubagentScope: Converts stored configurations into runtime objects at lines 86-95, producing PromptConfig, ModelConfig, RunConfig, and ToolConfig instances
  • convertToRuntimeConfig: Transforms static definitions into execution-ready parameters

The manager maintains an in-memory subagentsCache to avoid redundant disk I/O during lookups.

SubAgentScope and Execution

The SubAgentScope class in packages/core/src/subagents/subagent.ts implements the actual execution engine. When invoked via runNonInteractive, the scope:

  1. Builds a chat context templating ${…} placeholders with current ContextState
  2. Streams model responses through the chat interface
  3. Dispatches tool calls via CoreToolScheduler
  4. Aggregates tool results into subsequent context rounds
  5. Returns finalText alongside a termination mode (GOAL, TIMEOUT, or ERROR)

Execution emits lifecycle events through SubAgentEventEmitter, including START, ROUND_START, TOOL_CALL, TOOL_RESULT, and FINISH, enabling real-time UI feedback and telemetry collection.

Task Tool Invocation

Primary agents invoke sub-agents through the Task tool implemented in packages/core/src/tools/task.ts. The tool accepts a subagent_type parameter specifying the target agent name. Internally, TaskTool delegates to SubagentManager to load the configuration, instantiate a SubAgentScope, and execute the non-interactive run loop.

How to Create Custom Sub-Agents

Method 1: Markdown Definition Files

The simplest approach creates a Markdown file with YAML front-matter. Save the following to ~/.qwen/agents/code-reviewer.md (user-level) or PROJECT_ROOT/.qwen/agents/code-reviewer.md (project-level):

---
name: code-reviewer
description: |
  Use this agent to review a recently-added function and suggest improvements.
tools:
  - read_file
  - write_file
  - edit
modelConfig:
  model: qwen3-coder-plus
  temp: 0.2
runConfig:
  max_time_minutes: 5
  max_turns: 10
color: auto
---

You are a code-review specialist. The user will provide a snippet of TypeScript code.
Your task is to:
1. Analyse the code for correctness, style and performance.
2. Suggest concrete improvements and, if possible, emit the updated source file using the **edit** tool.
Only respond with the revised code block and a short rationale.

The fields map directly to the SubagentConfig interface. The SubagentManager automatically discovers these files on startup via the discovery mechanism that populates subagentsCache.

Method 2: Programmatic Creation with SubagentManager

For dynamic agent creation, instantiate SubagentManager and call createSubagent:

import { Config } from 'qwen-code/packages/core/src/config/config.js';
import { SubagentManager } from 'qwen-code/packages/core/src/subagents/subagent-manager.js';
import type { SubagentConfig } from 'qwen-code/packages/core/src/subagents/types.js';

async function addCustomAgent(globalConfig: Config) {
  const manager = new SubagentManager(globalConfig);

  const myAgent: SubagentConfig = {
    name: 'code-reviewer',
    description: 'Reviews a newly added TypeScript function and suggests improvements.',
    tools: ['read_file', 'write_file', 'edit'],
    systemPrompt: `
You are a code-review specialist. The user will provide a snippet of TypeScript code.
Your task is to:
1. Analyse the code for correctness, style and performance.
2. Suggest concrete improvements and, if possible, emit the updated source file using the **edit** tool.
Only respond with the revised code block and a short rationale.
`.trim(),
    level: 'project',
    modelConfig: { model: 'qwen3-coder-plus', temp: 0.2 },
    runConfig: { max_time_minutes: 5, max_turns: 10 },
    color: 'auto',
  };

  await manager.createSubagent(myAgent, { level: 'project', overwrite: true });
}

The manager validates the configuration using SubagentValidator and persists the definition using serializeSubagent, which handles YAML front-matter generation via the utility in packages/core/src/utils/yaml-parser.ts.

Method 3: LLM-Generated Agents

Qwen Code provides the subagentGenerator utility in packages/core/src/utils/subagentGenerator.ts to generate configurations from natural language descriptions:

import { subagentGenerator } from 'qwen-code/packages/core/src/utils/subagentGenerator.js';
import { Config } from 'qwen-code/packages/core/src/config/config.js';

const userText = 'I need an agent that can automatically document my public functions.';
const generated = await subagentGenerator(userText, config, AbortSignal.timeout(30000));

console.log(generated);
/* => {
   name: 'auto-docs',
   description: 'Creates Markdown documentation for all exported functions in a module.',
   systemPrompt: 'You are a documentation generator …'
} */

Pass the returned object to SubagentManager.createSubagent to persist the generated configuration.

Invoking Custom Sub-Agents from Primary Sessions

Once defined, invoke custom sub-agents through the Task tool:

import { TaskTool } from 'qwen-code/packages/core/src/tools/task.js';
import { Config } from 'qwen-code/packages/core/src/config/config.js';

const task = new TaskTool(config, config.getSubagentManager());

await task.run({
  subagent_type: 'code-reviewer',
  description: 'Review the newly added `add` function in src/utils/math.ts',
  prompt: 'Please read the file and suggest improvements.',
});

The TaskTool loads the code-reviewer definition via SubagentManager, creates a SubAgentScope, and executes runNonInteractive. The method streams the model response, handles permitted tool calls, and returns the final text output along with execution statistics.

Summary

  • SubAgent architecture separates static definitions (Markdown/YAML or programmatic configs) from dynamic execution scopes, enabling specialized agent delegation without core code modifications.
  • Five storage levels (session, project, user, extension, builtin) provide flexible persistence with session-level configurations overriding project-level defaults.
  • SubagentManager in packages/core/src/subagents/subagent-manager.ts handles CRUD operations, caching, and runtime configuration conversion via createSubagentScope.
  • SubAgentScope in packages/core/src/subagents/subagent.ts executes agents non-interactively, emitting lifecycle events and returning termination states (GOAL, TIMEOUT, ERROR).
  • Task tool serves as the invocation interface, accepting subagent_type parameters to route work to specific agents.
  • Creation methods include Markdown files in .qwen/agents/, programmatic SubagentManager.createSubagent() calls, or LLM-assisted generation via subagentGenerator.

Frequently Asked Questions

What is the difference between builtin and project-level sub-agents?

Builtin agents are hard-coded in BuiltinAgentRegistry (packages/core/src/subagents/builtin-agents.ts) and provide default capabilities like general-purpose assistance. They are immutable and always available. Project-level agents are stored as Markdown files in PROJECT_ROOT/.qwen/agents/ and are specific to the current workspace, allowing teams to version-control custom agents alongside their codebase.

How does the SubAgent system handle tool permissions?

Tool permissions are explicitly declared in the tools array within the SubagentConfig interface. When SubAgentScope executes via runNonInteractive, it restricts the agent to only those tools listed in its configuration. The CoreToolScheduler dispatches calls exclusively for permitted tools, ensuring isolation between different sub-agents' capabilities.

What happens when a sub-agent exceeds its execution limits?

The runConfig parameters max_time_minutes and max_turns define hard boundaries. When exceeded, SubAgentScope terminates execution and returns the termination mode TIMEOUT alongside any accumulated finalText. Similarly, unhandled exceptions during tool execution result in the ERROR termination mode, allowing the primary agent to handle failures gracefully.

Can sub-agent definitions be modified during an active session?

Yes. While SubagentManager maintains an in-memory subagentsCache for performance, you can update configurations by overwriting the Markdown files in .qwen/agents/ or calling manager.createSubagent() with overwrite: true. The cache respects the storage level precedence, so session-level changes take immediate effect, while file-based changes are reloaded on the next loadSubagent call if the cache entry has expired or been invalidated.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →