A Google ADK implementation showing how an orchestrator delegates to specialized sub-agents that stream output and return control.
User → [Orchestrator] → delegates to → [Researcher/Writer] → streams output
↑ ↓
└──── returns control ─────────┘
Flow:
- Orchestrator talks to user, calls
delegate_research(topic) - Researcher streams output → saves to
state["researcher_result"] - Control returns to orchestrator with result
- Orchestrator calls
delegate_writing(data) - Writer streams output → saves to
state["writer_result"] - Orchestrator presents final result
# Define dummy tools (orchestrator sees these)
def delegate_research(topic: str) -> str: return "dummy"
def delegate_writing(raw_data: str) -> str: return "dummy"
# Manager intercepts and routes to real agents
if tool_name == "delegate_research":
worker = self.researcherresearcher = LlmAgent(
name="researcher",
output_key="researcher_result", # Auto-save output
include_contents='none', # Isolated context
instruction="Task: {task}\nProduce research..."
)async def _run_async_impl(self, ctx):
while keep_looping:
# 1. Run orchestrator until tool call
async for event in self.orchestrator.run_async(ctx):
yield event
if tool_call in managed_tools:
break
# 2. Run sub-agent (streams to user)
async for event in worker.run_async(ctx):
yield event
# 3. Inject synthetic tool response
# 4. Loop continues with orchestrator# Install
pip install google-adk litellm python-dotenv
# Configure
echo "OPENAI_API_KEY=your_key" > .env
# Run
adk web --reload_agentsTry: "Research quantum computing and write a summary"
Advantages:
- Only orchestrator talks to users (clean UX)
- Sub-agents stream in real-time
- Iterative refinement (orchestrator reacts to results)
- No AgentTool complexity
Use Cases:
- Multi-step workflows (research → write → review)
- Specialized experts (legal, medical, technical)
- Quality loops (draft → critique → revise)
- Virtual Tools: Orchestrator calls
delegate_research()but it's not a real function - Interception: Manager catches the call and routes to actual sub-agent
- Streaming: Sub-agent output streams to user immediately
- State: Results saved to
state["researcher_result"]for orchestrator to read - Synthetic Response: Manager injects fake tool response so orchestrator continues
| Issue | Fix |
|---|---|
| Tool not intercepted | Add to managed_tools set |
| No streaming | Check yield event in worker loop |
| State not updating | Set output_key on sub-agent |
| Orchestrator loops | Verify FunctionResponse.id matches tool_call.id |