Chapter 4 ยท AGENTS

Sub-Agent Execution System

๐Ÿ“„ 04_sub_agent_execution_system.md ๐Ÿท Agents

Chapter 4: Sub-Agent Execution System

In Chapter 3: Skills & Capabilities System, we gave our Lead Agent "hands" (Skills) to interact with the world. It can now run SQL queries or read files.

However, some tasks are too big for one pair of hands.

Imagine asking the Lead Agent: "Create a fully functional Snake game in Python." If the Lead Agent tries to do this in the main chat, it will be messy. It has to write code, run it, see an error, rewrite it, run it again... The chat window would be flooded with 50 messages of trial-and-error code.

In this chapter, we introduce the Sub-Agent Execution System. This allows the Lead Agent to hire "Subcontractors" to handle complex jobs in the background.

The Motivation: The General Contractor

Think of the Lead Agent as a General Contractor building a house.

When it's time to wire the electricity:

  1. Bad Way: The General Contractor stops talking to the Homeowner, grabs wire cutters, and spends 3 hours struggling with wires in the living room while the Homeowner watches every mistake.
  2. deer-flow Way: The General Contractor hires a Sub-Agent (Specialist Electrician).

Benefits:

Central Use Case: "Build a Snake Game"

User: "Write a Snake game in Python."

To solve this, the Lead Agent will:

  1. Delegate: Call the task tool to hire a "Coder" Sub-Agent.
  2. Wait: The User sees a "Working..." card (created in Chapter 1).
  3. Execute: The Sub-Agent writes code, runs it, fixes bugs, and verifies it works.
  4. Report: The Sub-Agent returns the final code to the Lead Agent.

Key Concept: The task Tool

The interface between the Lead Agent and the Sub-Agent is a specialized tool called task.

Unlike the "Skills" from Chapter 3 which run a specific Python script, the task tool spins up another AI Brain.

The Tool Definition

Let's look at how the Lead Agent calls for help. This is defined in backend/src/tools/builtins/task_tool.py.

Simplified Tool Logic:

@tool("task")
def task_tool(description: str, prompt: str, subagent_type: str):
    """
    Delegate a complex task to a subagent.
    args:
        description: Short label for the UI (e.g., "Writing Snake Game").
        prompt: Detailed instructions for the subagent.
        subagent_type: Who to hire (e.g., "general-purpose", "bash").
    """
    # 1. Configure the sub-agent
    config = get_subagent_config(subagent_type)
    
    # 2. Start the agent in the background
    executor = SubagentExecutor(config=config, ...)
    task_id = executor.execute_async(prompt)
    
    # 3. Wait for results (Polling)
    return wait_for_result(task_id)

Explanation:


Internal Implementation: The Execution Loop

How does this run without freezing the whole application? We use Background Threads.

When the Lead Agent calls the tool, we don't block the server. We spin off a separate thread where the Sub-Agent has its own conversation with the LLM.

Sequence Diagram

sequenceDiagram participant Lead as Lead Agent participant Tool as Task Tool participant Exec as Subagent Executor participant Back as Background Thread Lead->>Tool: Call task("Build Snake Game") Tool->>Exec: Create Executor Exec->>Back: Start Async Task Note over Back: Sub-Agent "Thinking" Loop:<br/>1. Write Code<br/>2. Run Code<br/>3. Fix Errors loop Polling Tool->>Back: Is it done yet? Back-->>Tool: No, still coding... end Back->>Tool: Done! Here is the code. Tool-->>Lead: Task Complete. Result: [Code...]

1. The Executor (executor.py)

The SubagentExecutor is responsible for setting up the new agent. It gives the Sub-Agent a fresh start but shares important data (like the file sandbox) from the parent.

Simplified Code (src/subagents/executor.py):

class SubagentExecutor:
    def _create_agent(self):
        # 1. Create a new independent AI model
        model = create_chat_model(self.config.model)

        # 2. Give it tools (But NOT the task tool - no recursive hiring!)
        tools = get_available_tools(subagent_enabled=False)

        # 3. Create the agent graph
        return create_agent(model=model, tools=tools, ...)

Explanation: Notice step 2. We usually prevent Sub-Agents from hiring their own Sub-Agents to strictly control costs and complexity.

2. Async Execution

To keep the application responsive, the actual work happens in a ThreadPoolExecutor.

Simplified Code (src/subagents/executor.py):

def execute_async(self, task: str):
    # 1. Generate a unique ID for this job
    task_id = str(uuid.uuid4())

    # 2. Run the heavy AI work in a separate thread
    _execution_pool.submit(self.execute, task)

    # 3. Return the ID immediately so we can track it
    return task_id

3. The Polling Loop (task_tool.py)

The Lead Agent's tool needs to wait for the Sub-Agent to finish. While it waits, it sends updates to the Frontend.

Simplified Code (src/tools/builtins/task_tool.py):

# Inside task_tool function
task_id = executor.execute_async(prompt)

while True:
    # 1. Check status
    result = get_background_task_result(task_id)

    # 2. Send live updates to Frontend (See Chapter 1)
    if new_messages_found(result):
        send_frontend_update("task_running", result.last_message)

    # 3. Check if finished
    if result.status == "completed":
        return f"Task Succeeded. Result: {result.result}"
    
    time.sleep(5) # Wait 5 seconds before checking again

Explanation: This loop is why the user sees "Running..." updates in the UI. The task_tool is acting as a bridge, relaying messages from the hidden Sub-Agent thread to the visible Frontend stream.


Configuration: Defining Types of Workers

We define what types of Sub-Agents exist in our config.yaml or app_config.py.

Simplified Config:

subagents:
  types:
    - name: "general-purpose"
      system_prompt: "You are a problem solver. Solve the user's request."
      timeout_seconds: 600
    
    - name: "bash"
      system_prompt: "You are a DevOps engineer. Use the terminal."
      allowed_tools: ["terminal_tool"]

This allows us to create specialized workers. A bash sub-agent might only be allowed to use the terminal, making it safer and more focused than a general agent.

Summary

In this chapter, we scaled our AI's ability to work:

  1. The Concept: A General Contractor (Lead Agent) hiring Specialists (Sub-Agents).
  2. The Tool: The task tool acts as the hiring phone call.
  3. The Isolation: Sub-Agents run in background threads, keeping the main chat clean.
  4. The Loop: The task tool polls for completion, streaming progress to the user.

Now our agent can plan (Chapter 2), use tools (Chapter 3), and execute complex multi-step projects (Chapter 4).

However, whether it's the Lead Agent or a Sub-Agent running code, they are both executing commands on your computer. How do we ensure they don't accidentally delete your hard drive?

We need a safe place to play.

Next Chapter: Sandboxed Environment


Generated by Code IQ