Compose specialized agents into coordinated multi-agent systems
Multi-agent systems let you combine specialized agents—each with their own environment, tools, and model—into a coordinated workflow. A “conductor” agent orchestrates the specialists, dispatching tasks and synthesizing results.
The multi-agent pattern solves a common problem: as agent capabilities grow, a single agent with 50+ tools becomes unwieldy. By splitting responsibilities across specialized agents, each one stays focused and effective.The conductor sees only 2 tools—one per specialist. Each specialist has a focused toolset for its domain.
Prerequisites: You must deploy two hub environments before running this example:
Remote Browser: Go to hud-evals/hud-remote-browser → Fork to your GitHub → hud.ai → New → Environment → Import from your repo. Set required browser provider API keys (e.g., ANCHOR_API_KEY).
# Default task: research and save to markdownuv run python examples/07_multi_agent.py# Custom research taskuv run python examples/07_multi_agent.py \ --task "Find current prices of Bitcoin and Ethereum and save to crypto.md"# Verbose modeuv run python examples/07_multi_agent.py --verbose
Each sub-agent is an Environment with its own tools and scenario. Connect to HUD Hub environments or define local tools:
Copy
Ask AI
from hud import Environmentfrom hud.tools.agent import AgentTooldef create_browser_agent() -> AgentTool: """Create a browser sub-agent for web research.""" env = Environment("browser") env.connect_hub("hud-remote-browser-2") @env.scenario() async def web_research( task: str, start_url: str | None = None, expected_outcome: str | None = None, # Eval-only (hidden from conductor) ): """Research information on the web.""" prompt = f"""You are a web research agent with browser access.Research Task: {task}""" if start_url: prompt += f"\nStart URL: {start_url}" prompt += """Find relevant information, extract key data, and return structured findings.""" yield prompt yield 1.0 return AgentTool( env("web_research"), model="claude-sonnet-4-5", # Good at browser navigation name="web_research", description="Research information on the web. Use for finding articles, " "scraping data, comparing prices, and extracting structured information.", )
def create_coding_agent() -> AgentTool: """Create a coding sub-agent for file operations.""" env = Environment("coding") env.connect_hub("codex_environment_sandbox") @env.scenario() async def create_markdown( filename: str, content: str, expected_result: str | None = None, # Eval-only ): """Create a markdown file with the given content.""" prompt = f"""You are a file creation assistant.Task: Create a markdown file named '{filename}' with the following content:{content}IMPORTANT: Use the `apply_patch` tool to create the file.Steps:1. Use apply_patch to create '{filename}' with the content above2. Confirm it was created successfullyReturn a confirmation message.""" yield prompt yield 1.0 return AgentTool( env("create_markdown"), model="gpt-5.1", # Codex-capable for native shell/apply_patch name="create_markdown", description="Create a markdown file with specified content. Use for " "saving research findings, creating reports, and documenting results.", )
Create an Environment with sub-agents as tools, then run a conductor agent:
Copy
Ask AI
import hudfrom hud import Environmentfrom hud.agents import create_agentasync def run_research(task: str): # Create sub-agents as tools browser_agent = create_browser_agent() coding_agent = create_coding_agent() # Create coordinator environment with sub-agents as tools coordinator = Environment("coordinator") coordinator.add_tool(browser_agent) coordinator.add_tool(coding_agent) # Define the coordination scenario @coordinator.scenario() async def coordinate(prompt: str): yield prompt yield 1.0 # System prompt for the conductor system_prompt = """You are a research assistant coordinating specialized agents.Available sub-agents (call as tools):- web_research: Find information on the web- create_markdown: Create markdown filesCRITICAL: Sub-agents don't share context. When calling create_markdown, you MUST pass the content you want to save.Workflow:1. web_research: Gather data2. Format the data into markdown content3. create_markdown: Save the formatted content""" # Run with eval context async with hud.eval( coordinator("coordinate", prompt=task), name="multi-agent-research", ) as ctx: conductor = create_agent("gpt-4o", system_prompt=system_prompt) result = await conductor.run(ctx, max_steps=10) print(f"Reward: {ctx.reward}") print(f"Result: {result.content}")
AgentTool wraps an environment’s scenario as a callable tool:
Copy
Ask AI
from hud.tools.agent import AgentTooltool = AgentTool( env("scenario_name"), # Task from environment model="claude-sonnet-4-5", # Model for this sub-agent name="tool_name", # Name shown to conductor description="...", # Description for conductor agent=None, # Or provide custom agent class agent_params={}, # Params passed to agent trace=False, # Enable separate tracing)
Sub-agents don’t share context. Each sub-agent runs in its own isolated environment. The conductor must explicitly pass all necessary data when calling a sub-agent.
Copy
Ask AI
# ❌ Wrong: Assuming sub-agent knows about previous resultsresult = await ctx.call_tool(name="web_research", arguments={"task": "Find stock prices"})# The create_markdown agent won't know what web_research found!await ctx.call_tool(name="create_markdown", arguments={"filename": "report.md"})# ✅ Correct: Pass data explicitlyresult = await ctx.call_tool(name="web_research", arguments={"task": "Find stock prices"})await ctx.call_tool(name="create_markdown", arguments={ "filename": "report.md", "content": result.content # Pass the data!})
Your system prompt should remind the conductor about this:
Copy
Ask AI
system_prompt="""...CRITICAL: Sub-agents don't share context. When calling create_markdown, you MUST pass the content you want to save...."""
All sub-agent activity appears in a single trace on the HUD platform. When the conductor calls a sub-agent tool, the inference and tool calls are recorded under the parent trace—no separate URLs to track.
from hud.agents.claude import ClaudeAgent# Create and run with a custom agentasync with hud.eval(coordinator("coordinate", prompt=task)) as ctx: conductor = ClaudeAgent.create( checkpoint_name="claude-sonnet-4-5", system_prompt=system_prompt, max_tokens=8192, ) result = await conductor.run(ctx, max_steps=10)