Step-by-step guide to building agents that call other agents
This guide walks you through building an orchestrator agent — an agent that calls other agents to complete complex workflows. By the end, you’ll have a working orchestrator that coordinates multiple leaf agents.
This is a hands-on tutorial. For reference documentation on orchestration concepts, see Orchestration. For SDK details, see SDK Reference.
The fastest way to scaffold an orchestrator is with orch init --template. Three orchestration patterns are available:
Copy
# Fan-out: call multiple agents in parallel, combine resultsorch init my-scanner --template fan-out# Pipeline: sequential processing — each step feeds into the nextorch init my-pipeline --template pipeline# Map-reduce: split input, process in parallel, aggregateorch init my-processor --template map-reduce
Each template generates a complete project with orchagent.json, main.py, schema.json, requirements.txt, and README.md. Update manifest.dependencies with your actual agents, edit the orchestration logic, and publish.All templates support JavaScript via --language javascript.
Templates are a great starting point. Read on to understand how orchestration works under the hood, or jump straight to Part 4: Publish and Run if you’ve already customized your template.
Before building the orchestrator, you need at least one agent to call. If you already have published agents, skip to Part 2.Here’s a minimal tool agent that scans code for leaked secrets:
{ "name": "leak-finder", "type": "tool", "description": "Scans code for leaked secrets and API keys", "supported_providers": ["any"], "runtime": { "command": "python main.py" }, "required_secrets": ["ANTHROPIC_API_KEY"]}
Agents are callable by default (callable: true). You only need to set "callable": false to explicitly opt out (e.g., for always-on services like Discord bots).
A managed loop orchestrator uses an LLM tool-use loop. The LLM reads your prompt, sees the available tools, and decides which dependencies to call and in what order. You don’t write code — just the prompt and tool definitions.Create a directory with three files:
Maximum LLM iterations before forcing a result (25 is generous for most tasks)
manifest.dependencies
Agents this orchestrator is allowed to call — anything not listed is blocked
manifest.orchestration_mode
"strict" (default) or "flexible". Strict removes bash and requires dependency calls. See Strict vs Flexible Mode.
manifest.max_hops
Maximum call chain depth (2 = this agent can call agents that don’t call other agents)
manifest.timeout_ms
Total timeout for the entire execution including all dependency calls
manifest.per_call_downstream_cap
Budget passed to each dependency call (limits child agent spending, not this agent’s own calls)
custom_tools
Tool definitions presented to the LLM — each command calls a dependency via the built-in orch_call.py helper
Strict mode is the default. Orchestrators with dependencies default to orchestration_mode: "strict" — bash is disabled and the agent must use its custom tools. To keep bash available, set "orchestration_mode": "flexible" in your manifest.
custom_tools is required for managed-loop orchestrators. If you declare manifest.dependencies but don’t add matching custom_tools, the LLM has no way to call your dependencies. It will waste all turns exploring the filesystem instead. Use orch scaffold orchestration to auto-generate the correct custom_tools from your dependencies.
The command field must use the exact format: python3 /home/user/helpers/orch_call.py org/agent@version. This helper script is pre-installed in every managed loop sandbox. It reads the tool input, calls the dependency through the gateway, and returns the result.
You are a security review agent. When given code or a repository, perform a thorough security audit.## Process1. Use scan_secrets to find any leaked credentials or API keys2. Use scan_dependencies to check for vulnerable dependencies3. Analyze the combined findings and assess overall risk## OutputReturn a JSON object with:- severity: "low", "medium", "high", or "critical"- findings: array of individual findings from both scans- summary: one-paragraph risk assessment- recommendations: array of specific actions to fix issues
A code runtime orchestrator runs your code directly. You control the execution flow — which agents to call, in what order, and how to process results. Use this when you need deterministic logic, custom data transformation, or parallel fan-out. Code runtime orchestrators can be written in Python or JavaScript.
orchagent-sdk must be in your requirements.txt. It is installed automatically inside the sandbox at runtime, but the install takes approximately 55 seconds — factor this into your timeout budget (see Timeout Budgeting).
orchagent-sdk must be in your package.json dependencies. It is installed via npm install inside the sandbox at runtime. The JS sandbox uses a dedicated Node.js template with npm pre-installed.
If your agent (or any agent in the chain) needs external API keys or credentials at runtime, use workspace secrets combined with the required_secrets field.
At runtime, these secrets are injected as environment variables into your agent’s sandbox. Secrets not listed in required_secrets will not be available, even if they exist in your workspace.
Python
JavaScript
Copy
import osapi_key = os.environ["EXTERNAL_API_KEY"] # Available because it's in required_secrets
Copy
const apiKey = process.env.EXTERNAL_API_KEY; // Available because it's in required_secrets
Do not add ORCHAGENT_SERVICE_KEY to required_secrets. The gateway automatically injects a temporary service key for agents that have manifest dependencies. Adding your own workspace secret named ORCHAGENT_SERVICE_KEY will override the auto-injected key and break orchestration with confusing billing errors.
Always publish bottom-up — leaf agents before orchestrators. The gateway validates that all declared dependencies exist at publish time.The easiest way is --all from the parent directory — it auto-detects the dependency graph and publishes in the correct order:
Copy
# Publish all agents in dependency order (recommended)orch publish --all# Preview the plan without publishingorch publish --all --dry-run
# Run on the cloud (default)orch run yourorg/security-review --data '{"code": "api_key = \"sk-abc123\""}'# Run with JSON outputorch run yourorg/security-review --json --data '{"code": "api_key = \"sk-abc123\""}'
Sandboxes need time to start up and install dependencies. Plan your timeouts accordingly:
Phase
Approximate Time
Sandbox startup
~5s
SDK installation (Python orchagent-sdk in requirements.txt)
~55s
npm install (JavaScript orchagent-sdk in package.json)
~15-30s
Your agent’s execution
varies
Each dependency call (includes its own startup + install)
~60-70s overhead + execution
Rules of thumb:
Managed loop orchestrators: Set timeout_seconds to at least 300 (5 minutes). The LLM loop, sandbox setup, and dependency calls all share this budget.
Code runtime orchestrators: Set timeout_seconds to at least 180 (3 minutes) to account for SDK installation + dependency call overhead.
manifest.timeout_ms should match or exceed timeout_seconds × 1000.
Leaf agents with no SDK dependency are fast (~5s startup). Set their timeouts lower.
The platform adds a 120-second buffer to your timeout for sandbox lifecycle management. A timeout_seconds: 300 agent gets a sandbox that lives for 420 seconds. You don’t need to account for this buffer yourself.
Agent runs but dependency calls return empty results
Agents are callable by default. If the dependency has "callable": false set explicitly, remove it or set it to true. The gateway blocks agent-to-agent calls to non-callable agents.
This usually means ORCHAGENT_SERVICE_KEY was overridden by a workspace secret. Remove ORCHAGENT_SERVICE_KEY from your required_secrets — the gateway injects it automatically.
Your agent must declare required_secrets in orchagent.json. Secrets added to the dashboard are only injected into sandboxes that explicitly request them.
The SDK install (~55s) plus sandbox startup (~5s) consumes budget before your code runs. Increase timeout_seconds to give your agent more time. For orchestrators, 300s is a safe starting point.
my-orchestrator/ orchagent.json # manifest + runtime.command + manifest.dependencies main.py # your Python code using orchagent-sdk requirements.txt # must include orchagent-sdk prompt.md # prompt (optional, used if agent also has direct LLM features) schema.json # input/output schemas
Orchestrators are hard to test because they depend on live sub-agents. Mocked fixtures solve this — they run the full LLM agent loop but return deterministic mock responses for sub-agent calls instead of hitting the network.
The mocks keys must match the custom tool name fields in your orchagent.json. Each value is the JSON response the LLM will see when it calls that tool.
The output shows the full agent loop — which tools the LLM chose to call, the mock responses injected, and whether the final output matches your expectations:
You can mix mocked and non-mocked fixtures in the same tests/ directory. orch test automatically routes each fixture to the correct runner based on whether it has a mocks field.
Mocked tests are ideal for CI/CD — they verify the LLM’s reasoning without requiring deployed sub-agents. See the CI/CD Guide for GitHub Actions setup.