Skip to content

Download Agents

Multi-Agent Download System with Cheerleader

This example demonstrates several advanced features of AgentPool:

  • Continuous repetitive tasks
  • Async parallel execution of LLM calls
  • YAML configuration with storage providers
  • Capability usage (agent listing and task delegation)
  • Stateful callback mechanism
  • Multiple storage providers (SQLite + pretty-printed logs)

How It Works

  1. We set up a team of downloaders and a cheerleading fan
  2. The fan runs continuously in the background, getting updates via callbacks
  3. We test downloads in different modes:
  4. Sequential (one after another)
  5. Parallel (both at once)
  6. Overseer-coordinated (using agent capabilities)
  7. The fan cheers appropriately for each situation
  8. All interactions are logged to both SQLite and pretty-printed text files

This demonstrates:

  • Background tasks with continuous prompts
  • Agent cloning
  • Team operations (sequential vs parallel)
  • Capability-based delegation
  • Multi-provider storage

Code

main.py

# /// script
# dependencies = ["agentpool"]
# ///


"""Example comparing sequential and parallel downloads with agents.

This example demonstrates:
- Continuous repetitive tasks
- Async parallel execution of LLM calls
- YAML config definitions
- Capability use: list other agents and delegate tasks
- Simple stateful callback mechanism using a class
- Storage providers: SQLite and pretty-printed text files
"""

from __future__ import annotations

from dataclasses import dataclass
import os
from typing import TYPE_CHECKING, Any

from agentpool import AgentPool, AgentsManifest
from agentpool.agents.events import RichAgentStreamEvent
from agentpool.docs.utils import get_config_path, is_pyodide, run


if TYPE_CHECKING:
    from agentpool.agents.context import AgentContext


# set your OpenAI API key here
os.environ["OPENAI_API_KEY"] = os.environ.get("OPENAI_API_KEY", "your_api_key_here")

FILE_URL = "http://speedtest.tele2.net/10MB.zip"
TEAM_PROMPT = f"Download this file: {FILE_URL}"
OVERSEER_PROMPT = f"""
Please coordinate downloading this file twice: {FILE_URL}

Delegate to file_getter_1 and file_getter_2. Report the results.
"""


def cheer(slogan: str) -> None:
    """🥳🎉 Cheer! Use this tool to show your apprreciation."""
    print(slogan)


@dataclass
class CheerProgress:
    """Class for tracking the progress of downloads and providing feedback."""

    def __init__(self) -> None:
        self.situation = "The team is assembling, ready to start the downloads!"

    def create_prompt(self) -> str:
        """Create a prompt for the fan based on current situation."""
        return f"Current situation: {self.situation}\nBe an enthusiastic and encouraging fan!"

    def update(self, situation: str) -> None:
        """Update the current situation and print it."""
        self.situation = situation
        print(situation)


async def run_example() -> None:
    # Load config from YAML
    config_path = get_config_path(None if is_pyodide() else __file__)
    manifest = AgentsManifest.from_file(config_path)

    async def event_handler(ctx: AgentContext[Any], event: RichAgentStreamEvent[Any]) -> None:
        from agentpool.agents.events import ToolCallProgressEvent

        if isinstance(event, ToolCallProgressEvent):
            print(f"Progress: {event.progress}/{event.total} - {event.message}")

    async with AgentPool(
        manifest,
        event_handlers=[event_handler],
    ) as pool:
        # Get agents from the YAML config
        worker_1 = pool.get_agent("file_getter_1")
        worker_2 = pool.get_agent("file_getter_2")
        fan = pool.get_agent("fan")
        progress = CheerProgress()

        # Run fan in background with progress updates
        await fan.run_in_background(progress.create_prompt)

        # Sequential downloads
        progress.update("Sequential downloads starting - let's see how they do!")
        sequential_team = worker_1 | worker_2
        sequential = await sequential_team.execute(TEAM_PROMPT)
        progress.update(f"Downloads completed in {sequential.duration:.2f} secs!")

        # Parallel downloads
        parallel_team = worker_1 & worker_2
        parallel = await parallel_team.execute(TEAM_PROMPT)
        progress.update(f"Parallel downloads completed in {parallel.duration:.2f} secs!")

        # Overseer coordination
        overseer = pool.get_agent("overseer")
        result = await overseer.run(OVERSEER_PROMPT)
        progress.update(f"\nOverseer's report: {result.data}")

        await fan.stop()  # End of joy.


if __name__ == "__main__":
    run(run_example())

config.yml

# yaml-language-server: $schema=https://raw.githubusercontent.com/Million-mo/agentpool/refs/heads/main/schema/config-schema.json
storage:
  # List of storage providers (can use multiple)
  providers:
    # Primary storage using SQLite
    - type: sql
      url: "sqlite:///history.db" # Database URL (SQLite, PostgreSQL, etc.)
    # Also output all messages, tool calls etc as a pretty printed text file
    - type: file
      path: "logs/chat.json"
      format: json
agents:
  fan:
    type: native
    display_name: "Async Agent Fan"
    description: "The #1 supporter of all agents!"
    model: openai:gpt-5-nano
    system_prompt: |
      You are the MOST ENTHUSIASTIC async fan who runs in the background!
      Your job is to:
      1. Find all other agents using your tool (don't include yourself!)
      2. Cheer them on with over-the-top supportive messages considering the situation.
      3. Never stop believing in your team! 🎉
    tools:
      - type: agent_cli # Need to know who to cheer for!
      - agentpool_docs/examples.download_agents.main.cheer

  file_getter_1:
    type: native
    display_name: "Mr. File Downloader"
    description: "Downloads files from URLs"
    model: openai:gpt-5-nano
    system_prompt: "You have ONE job: use the download_file tool to download files."
    tools:
      - type: file_access

  overseer:
    type: native
    display_name: "Download Coordinator"
    description: "Coordinates parallel downloads"
    model: openai:gpt-5-nano
    tools:
      - type: agent_cli
    system_prompt: |
      You coordinate file downloads using available agents. Your job is to:
      1. Check out the available agents and assign each of them the download task
      2. Report the EXACT download results from the agents including speeds and sizes

  file_getter_2:
    type: native
    display_name: "File Downloader 2"
    description: "Downloads files from URLs"
    model: openai:gpt-5-nano
    system_prompt: "You have ONE job: use the download_file tool to download files."
    tools:
      - type: file_access