| name | agent-orchestration |
| version | 1.0 |
| description | Multi-agent orchestration patterns including coordinator agents, LlmAgent vs WorkflowAgent selection, agent-as-tool pattern, and inter-agent communication via session state. PROACTIVELY activate for: (1) multi-agent systems and agent coordination, (2) sub-agent delegation and agent-as-tool implementation, (3) workflow orchestration with LlmAgent and WorkflowAgent. Triggers: "multi-agent", "orchestration", "agent team"
|
| core-integration | {"techniques":{"primary":["structured_decomposition"],"secondary":[]},"contracts":{"input":"none","output":"none"},"patterns":"none","rubrics":"none"} |
Agent Orchestration: Multi-Agent System Patterns
Core Principles
Complex problems often require multiple specialized agents working together. ADK provides robust patterns for orchestrating agent teams, enabling modular, maintainable, and scalable agentic systems.
Key Insight: Single monolithic agents with overly complex prompts are harder to maintain and debug than teams of focused specialists coordinated by a simple orchestrator.
Agent Types: LlmAgent vs WorkflowAgent
LlmAgent (Dynamic Reasoning)
Purpose: For tasks where the next action depends on runtime reasoning and context.
Characteristics:
- LLM decides which tool to call and when
- Flexible, adaptive behavior
- Suitable for open-ended problems
- Can handle unexpected inputs
Use Cases:
- Conversational assistants
- Complex problem-solving requiring judgment
- Tasks with unpredictable user requests
- Research and analysis workflows
Example:
from google import genai
from google.genai import types
async def create_research_agent() -> types.Agent:
"""
Create LlmAgent for research tasks.
The agent dynamically decides whether to search, read, or summarize
based on user questions and intermediate findings.
"""
client = genai.Client(vertexai=True)
search_tool = types.Tool(function_declarations=[...])
read_tool = types.Tool(function_declarations=[...])
summarize_tool = types.Tool(function_declarations=[...])
agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
You are a research assistant. For each user query:
1. Use search_tool to find relevant sources
2. Use read_tool to examine source content
3. Use summarize_tool to synthesize findings
Adapt your approach based on the query complexity.
""",
tools=[search_tool, read_tool, summarize_tool]
)
return agent
WorkflowAgent (Deterministic Flow)
Purpose: For tasks with predictable, repeatable processes where the execution flow is known in advance.
Characteristics:
- Hardcoded execution sequence
- Predictable, reliable behavior
- No LLM reasoning overhead for flow control
- Ideal for automation pipelines
Types:
- SequentialAgent: Execute agents one after another
- ParallelAgent: Execute agents concurrently
- LoopAgent: Repeat agent execution with conditions
Use Cases:
- Data processing pipelines
- Validation workflows
- Multi-stage transformations
- Scheduled automation tasks
Example - SequentialAgent:
from google.genai import types
async def create_document_processor() -> types.SequentialAgent:
"""
Sequential workflow for document processing.
Flow: Upload -> Parse -> Validate -> Store (deterministic sequence)
"""
upload_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Validate and upload documents to storage.",
tools=[upload_tool]
)
parse_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Extract structured data from documents.",
tools=[parse_tool]
)
validate_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Validate extracted data against schema.",
tools=[validate_tool]
)
store_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Store validated data in database.",
tools=[store_tool]
)
workflow = types.SequentialAgent(
agents=[upload_agent, parse_agent, validate_agent, store_agent]
)
return workflow
Example - ParallelAgent:
async def create_parallel_analyzer() -> types.ParallelAgent:
"""
Parallel analysis workflow.
Run sentiment analysis, entity extraction, and summarization
simultaneously for speed.
"""
sentiment_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Analyze sentiment of the text.",
tools=[sentiment_tool]
)
entity_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Extract named entities from text.",
tools=[entity_tool]
)
summary_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="Generate concise summary of text.",
tools=[summary_tool]
)
parallel_workflow = types.ParallelAgent(
agents=[sentiment_agent, entity_agent, summary_agent]
)
return parallel_workflow
Decision Matrix: Which Agent Type?
| Scenario | Agent Type | Rationale |
|---|
| Answering unpredictable user questions | LlmAgent | Requires dynamic reasoning |
| Processing uploaded files through fixed steps | SequentialAgent | Deterministic pipeline |
| Running multiple independent analyses | ParallelAgent | No dependencies, gain speed |
| Customer support with varying needs | LlmAgent | Adaptive to user situation |
| Daily report generation | LoopAgent | Repeatable schedule |
| Code review (lint -> test -> analyze) | SequentialAgent | Fixed validation sequence |
Coordinator Pattern (Recommended Architecture)
Root Coordinator with Specialist Sub-Agents
Architecture: Single coordinator agent dispatches tasks to specialized agents based on request type.
Benefits:
- Clear separation of concerns
- Easy to add new specialists
- Simple routing logic
- Improved debuggability
Implementation:
from google import genai
from google.genai import types
from pydantic import BaseModel, ConfigDict, Field
async def create_code_specialist() -> types.LlmAgent:
"""Agent specialized in code generation and review."""
return types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
You are a senior software engineer specializing in Python.
Your responsibilities:
- Generate production-ready code with type hints
- Review code for bugs and improvements
- Suggest optimal algorithms and data structures
Always follow PEP 8 and include comprehensive docstrings.
""",
tools=[code_analyzer_tool, code_formatter_tool]
)
async def create_architecture_specialist() -> types.LlmAgent:
"""Agent specialized in system architecture."""
return types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
You are a principal architect with 15+ years experience.
Your responsibilities:
- Design scalable system architectures
- Evaluate trade-offs between approaches
- Create architecture decision records (ADRs)
Focus on maintainability, scalability, and security.
""",
tools=[diagram_tool, adr_tool]
)
async def create_test_specialist() -> types.LlmAgent:
"""Agent specialized in testing."""
return types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
You are a test automation engineer.
Your responsibilities:
- Generate comprehensive test suites
- Design test strategies (unit, integration, e2e)
- Achieve 80%+ code coverage
Use pytest patterns and AAA structure.
""",
tools=[test_generator_tool, coverage_tool]
)
async def create_coordinator() -> types.LlmAgent:
"""
Root coordinator that delegates to specialists.
Analyzes user requests and routes to appropriate specialist.
"""
code_agent = await create_code_specialist()
arch_agent = await create_architecture_specialist()
test_agent = await create_test_specialist()
code_tool = types.AgentTool(
agent=code_agent,
name="code_specialist",
description="For code generation, review, and optimization tasks"
)
arch_tool = types.AgentTool(
agent=arch_agent,
name="architecture_specialist",
description="For system design, architecture decisions, and scalability analysis"
)
test_tool = types.AgentTool(
agent=test_agent,
name="test_specialist",
description="For test generation, test strategy, and coverage analysis"
)
coordinator = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
You are a project coordinator managing specialist engineers.
Your role:
1. Analyze user requests
2. Determine which specialist(s) to consult
3. Delegate tasks using appropriate specialist tools
4. Synthesize results into cohesive response
Available specialists:
- code_specialist: For implementation and code review
- architecture_specialist: For system design
- test_specialist: For testing and quality assurance
Routing rules:
- "Create/implement/write code" -> code_specialist
- "Design/architect/plan system" -> architecture_specialist
- "Test/coverage/quality" -> test_specialist
- Complex tasks may require multiple specialists
""",
tools=[code_tool, arch_tool, test_tool]
)
return coordinator
Using the Coordinator
async def main() -> None:
"""Example usage of coordinator pattern."""
coordinator = await create_coordinator()
client = genai.Client(vertexai=True)
user_query = "Design and implement a user authentication system"
response = await client.aio.chats.create(
agent=coordinator
).send_message(user_query)
print(response.text)
Inter-Agent Communication via Session State
Sharing Data Between Agents
Pattern: Use session state to pass context and results between agents in a workflow.
Example:
from google.genai import types
async def data_extraction_agent(context: types.ToolContext) -> dict:
"""
Extract data from document and save to session state.
Args:
context: Tool execution context with session state
Returns:
Extracted data dictionary
"""
extracted_data = {
"entities": ["Alice", "Bob", "TechCorp"],
"dates": ["2025-01-15"],
"amounts": [150000]
}
context.state["extracted_data"] = extracted_data
return extracted_data
async def data_validation_agent(context: types.ToolContext) -> dict:
"""
Validate data extracted by previous agent.
Args:
context: Tool execution context with session state
Returns:
Validation results
"""
extracted_data = context.state.get("extracted_data", {})
validation_results = {
"valid": True,
"errors": [],
"warnings": ["Amount exceeds typical range"]
}
context.state["validation_results"] = validation_results
return validation_results
async def data_storage_agent(context: types.ToolContext) -> dict:
"""
Store validated data in database.
Args:
context: Tool execution context with session state
Returns:
Storage confirmation
"""
extracted_data = context.state.get("extracted_data")
validation_results = context.state.get("validation_results")
if validation_results.get("valid"):
return {"stored": True, "record_id": "12345"}
else:
return {"stored": False, "reason": "Validation failed"}
Best Practices:
- Use session state for lightweight data (< 10KB)
- Clear state after workflow completion
- Document state schema in agent instructions
- Validate state exists before accessing
Agent-as-Tool Pattern (AgentTool)
Wrapping Agents as Tools
Purpose: Make entire agents callable by other agents, enabling hierarchical delegation.
Pattern:
from google.genai import types
specialist_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="You are a database optimization specialist...",
tools=[query_analyzer_tool, index_advisor_tool]
)
db_specialist_tool = types.AgentTool(
agent=specialist_agent,
name="database_optimizer",
description="Optimize database queries and schema design. Use for slow queries, indexing strategy, or schema refactoring."
)
coordinator = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="You coordinate technical specialists...",
tools=[db_specialist_tool, other_tools...]
)
Benefits:
- Specialists maintain their own context and tools
- Coordinator stays simple (routing logic only)
- Easy to test specialists in isolation
- Specialists can be reused across coordinators
When to Use:
- Complex domain requiring specialized expertise
- Agent has multiple related tools
- Task requires extended reasoning in specific domain
- Want to isolate and test sub-functionality
Advanced Orchestration Patterns
Dynamic Agent Selection
async def create_dynamic_coordinator() -> types.LlmAgent:
"""
Coordinator that dynamically selects agents based on query analysis.
"""
specialists = {
"python": await create_python_specialist(),
"typescript": await create_typescript_specialist(),
"rust": await create_rust_specialist(),
"devops": await create_devops_specialist(),
"security": await create_security_specialist()
}
specialist_tools = [
types.AgentTool(
agent=agent,
name=name,
description=f"{name.title()} specialist for {name}-related tasks"
)
for name, agent in specialists.items()
]
coordinator = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
Analyze user queries and select appropriate specialist(s):
Query indicators:
- "Python"/"Django"/"Flask" -> python specialist
- "TypeScript"/"React"/"Node" -> typescript specialist
- "Rust"/"performance"/"systems" -> rust specialist
- "CI/CD"/"deployment"/"Docker" -> devops specialist
- "security"/"auth"/"encryption" -> security specialist
For cross-cutting concerns, consult multiple specialists.
""",
tools=specialist_tools
)
return coordinator
Multi-Stage Pipeline with Error Handling
async def create_robust_pipeline() -> types.SequentialAgent:
"""
Pipeline with error handling and rollback capabilities.
"""
validation_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
Validate input data. Save validation results to session state.
If validation fails, set state["pipeline_stop"] = True.
""",
tools=[validation_tool]
)
transform_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
Check state["pipeline_stop"]. If True, skip transformation.
Otherwise, transform data and save to state.
""",
tools=[transform_tool]
)
storage_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
Check state["pipeline_stop"]. If True, skip storage.
Otherwise, store data and record transaction ID.
If storage fails, set state["rollback_needed"] = True.
""",
tools=[storage_tool]
)
cleanup_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
If state["rollback_needed"], revert changes.
Clean up temporary resources.
Report final status.
""",
tools=[rollback_tool, cleanup_tool]
)
pipeline = types.SequentialAgent(
agents=[
validation_agent,
transform_agent,
storage_agent,
cleanup_agent
]
)
return pipeline
Anti-Patterns to Avoid
Monolithic Agent
giant_agent = types.LlmAgent(
model="gemini-2.0-flash-exp",
system_instruction="""
You are an expert in Python, TypeScript, Rust, DevOps, security,
databases, networking, machine learning, data science, mobile dev,
game dev, blockchain... [10 more domains]
[3000 line prompt trying to cover everything]
""",
tools=[100_different_tools]
)
Problem: Impossible to maintain, debug, or improve. Model gets confused.
Solution: Use coordinator pattern with focused specialists.
Circular Agent Dependencies
agent_a = types.LlmAgent(
tools=[types.AgentTool(agent=agent_b)]
)
agent_b = types.LlmAgent(
tools=[types.AgentTool(agent=agent_a)]
)
Problem: Infinite loops, stack overflow.
Solution: Design clear hierarchies (coordinator -> specialists, no loops).
Using WorkflowAgent for Dynamic Tasks
workflow = types.SequentialAgent([
search_agent,
analyze_agent,
summarize_agent
])
Problem: Wastes resources on unnecessary steps.
Solution: Use LlmAgent for dynamic tasks, WorkflowAgent only for predictable flows.
Overstuffing Session State
context.state["entire_dataset"] = load_1gb_file()
context.state["all_search_results"] = [...]
Problem: Performance degradation, memory issues.
Solution: Store references (IDs, paths) in session state, not full data.
When to Use This Skill
Activate this skill when:
- Designing multi-agent systems
- Choosing between LlmAgent and WorkflowAgent
- Implementing coordinator/dispatcher patterns
- Creating hierarchical agent teams
- Optimizing agent communication
- Debugging multi-agent workflows
Integration Points
This skill depends on:
adk-fundamentals: Single-agent basics
agentient-python-core/async-patterns: Async coordination
This skill enables:
- Complex production agent systems
- Modular, maintainable agent architectures
Related Resources
For deeper understanding: