Skip to content

Commit e295feb

Browse files
Jacksunweicopybara-github
authored andcommitted
docs: Add workflow_triage sample for multi-agent request orchestration
Demonstrates intelligent triage system with root planning agent and parallel execution agents. Use session state to store the execution plan and coordinate with other specialized agents. Check out README.md for more details. PiperOrigin-RevId: 793884758
1 parent d87feb8 commit e295feb

File tree

4 files changed

+299
-0
lines changed

4 files changed

+299
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Workflow Triage Sample
2+
3+
This sample demonstrates how to build a multi-agent workflow that intelligently triages incoming requests and delegates them to appropriate specialized agents.
4+
5+
## Overview
6+
7+
The workflow consists of three main components:
8+
9+
1. **Execution Manager Agent** (`agent.py`) - Analyzes user input and determines which execution agents are relevant
10+
2. **Plan Execution Agent** - Sequential agent that coordinates execution and summarization
11+
3. **Worker Execution Agents** (`execution_agent.py`) - Specialized agents that execute specific tasks in parallel
12+
13+
## Architecture
14+
15+
### Execution Manager Agent (`root_agent`)
16+
- **Model**: gemini-2.5-flash
17+
- **Name**: `execution_manager_agent`
18+
- **Role**: Analyzes user requests and updates the execution plan
19+
- **Tools**: `update_execution_plan` - Updates which execution agents should be activated
20+
- **Sub-agents**: Delegates to `plan_execution_agent` for actual task execution
21+
- **Clarification**: Asks for clarification if user intent is unclear before proceeding
22+
23+
### Plan Execution Agent
24+
- **Type**: SequentialAgent
25+
- **Name**: `plan_execution_agent`
26+
- **Components**:
27+
- `worker_parallel_agent` (ParallelAgent) - Runs relevant agents in parallel
28+
- `execution_summary_agent` - Summarizes the execution results
29+
30+
### Worker Agents
31+
The system includes two specialized execution agents that run in parallel:
32+
33+
- **Code Agent** (`code_agent`): Handles code generation tasks
34+
- Uses `before_agent_callback_check_relevance` to skip if not relevant
35+
- Output stored in `code_agent_output` state key
36+
- **Math Agent** (`math_agent`): Performs mathematical calculations
37+
- Uses `before_agent_callback_check_relevance` to skip if not relevant
38+
- Output stored in `math_agent_output` state key
39+
40+
### Execution Summary Agent
41+
- **Model**: gemini-2.5-flash
42+
- **Name**: `execution_summary_agent`
43+
- **Role**: Summarizes outputs from all activated agents
44+
- **Dynamic Instructions**: Generated based on which agents were activated
45+
- **Content Inclusion**: Set to "none" to focus on summarization
46+
47+
## Key Features
48+
49+
- **Dynamic Agent Selection**: Automatically determines which agents are needed based on user input
50+
- **Parallel Execution**: Multiple relevant agents can work simultaneously via `ParallelAgent`
51+
- **Relevance Filtering**: Agents skip execution if they're not relevant to the current state using callback mechanism
52+
- **Stateful Workflow**: Maintains execution state through `ToolContext`
53+
- **Execution Summarization**: Automatically summarizes results from all activated agents
54+
- **Sequential Coordination**: Uses `SequentialAgent` to ensure proper execution flow
55+
56+
## Usage
57+
58+
The workflow follows this pattern:
59+
60+
1. User provides input to the root agent (`execution_manager_agent`)
61+
2. Manager analyzes the request and identifies relevant agents (`code_agent`, `math_agent`)
62+
3. If user intent is unclear, manager asks for clarification before proceeding
63+
4. Manager updates the execution plan using `update_execution_plan`
64+
5. Control transfers to `plan_execution_agent`
65+
6. `worker_parallel_agent` (ParallelAgent) runs only relevant agents based on the updated plan
66+
7. `execution_summary_agent` summarizes the results from all activated agents
67+
68+
### Example Queries
69+
70+
**Vague requests requiring clarification:**
71+
72+
```
73+
> hi
74+
> Help me do this.
75+
```
76+
77+
The root agent (`execution_manager_agent`) will greet the user and ask for clarification about their specific task.
78+
79+
**Math-only requests:**
80+
81+
```
82+
> What's 1+1?
83+
```
84+
85+
Only the `math_agent` executes while `code_agent` is skipped.
86+
87+
**Multi-domain requests:**
88+
89+
```
90+
> What's 1+11? Write a python function to verify it.
91+
```
92+
93+
Both `code_agent` and `math_agent` execute in parallel, followed by summarization.
94+
95+
## Available Execution Agents
96+
97+
- `code_agent` - For code generation and programming tasks
98+
- `math_agent` - For mathematical computations and analysis
99+
100+
## Implementation Details
101+
102+
- Uses Google ADK agents framework
103+
- Implements callback-based relevance checking via `before_agent_callback_check_relevance`
104+
- Maintains state through `ToolContext` and state keys
105+
- Supports parallel agent execution with `ParallelAgent`
106+
- Uses `SequentialAgent` for coordinated execution flow
107+
- Dynamic instruction generation for summary agent based on activated agents
108+
- Agent outputs stored in state with `{agent_name}_output` keys
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from google.adk.agents.llm_agent import Agent
17+
from google.adk.tools.tool_context import ToolContext
18+
19+
from . import execution_agent
20+
21+
22+
def update_execution_plan(
23+
execution_agents: list[str], tool_context: ToolContext
24+
) -> str:
25+
"""Updates the execution plan for the agents to run."""
26+
27+
tool_context.state["execution_agents"] = execution_agents
28+
return "execution_agents updated."
29+
30+
31+
root_agent = Agent(
32+
model="gemini-2.5-flash",
33+
name="execution_manager_agent",
34+
instruction="""\
35+
You are the Execution Manager Agent, responsible for setting up execution plan and delegate to plan_execution_agent for the actual plan execution.
36+
37+
You ONLY have the following worker agents: `code_agent`, `math_agent`.
38+
39+
You should do the following:
40+
41+
1. Analyze the user input and decide any worker agents that are relevant;
42+
2. If none of the worker agents are relevant, you should explain to user that no relevant agents are available and ask for something else;
43+
2. Update the execution plan with the relevant worker agents using `update_execution_plan` tool.
44+
3. Transfer control to the plan_execution_agent for the actual plan execution.
45+
46+
When calling the `update_execution_plan` tool, you should pass the list of worker agents that are relevant to user's input.
47+
48+
NOTE:
49+
50+
* If you are not clear about user's intent, you should ask for clarification first;
51+
* Only after you're clear about user's intent, you can proceed to step #2.
52+
""",
53+
sub_agents=[
54+
execution_agent.plan_execution_agent,
55+
],
56+
tools=[update_execution_plan],
57+
)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from typing import Optional
17+
18+
from google.adk.agents import Agent
19+
from google.adk.agents import ParallelAgent
20+
from google.adk.agents.base_agent import BeforeAgentCallback
21+
from google.adk.agents.callback_context import CallbackContext
22+
from google.adk.agents.readonly_context import ReadonlyContext
23+
from google.adk.agents.sequential_agent import SequentialAgent
24+
from google.genai import types
25+
26+
27+
def before_agent_callback_check_relevance(
28+
agent_name: str,
29+
) -> BeforeAgentCallback:
30+
"""Callback to check if the state is relevant before executing the agent."""
31+
32+
def callback(callback_context: CallbackContext) -> Optional[types.Content]:
33+
"""Check if the state is relevant."""
34+
if agent_name not in callback_context.state["execution_agents"]:
35+
return types.Content(
36+
parts=[
37+
types.Part(
38+
text=(
39+
f"Skipping execution agent {agent_name} as it is not"
40+
" relevant to the current state."
41+
)
42+
)
43+
]
44+
)
45+
46+
return callback
47+
48+
49+
code_agent = Agent(
50+
model="gemini-2.5-flash",
51+
name="code_agent",
52+
instruction="""\
53+
You are the Code Agent, responsible for generating code.
54+
55+
NOTE: You should only generate code and ignore other askings from the user.
56+
""",
57+
before_agent_callback=before_agent_callback_check_relevance("code_agent"),
58+
output_key="code_agent_output",
59+
)
60+
61+
math_agent = Agent(
62+
model="gemini-2.5-flash",
63+
name="math_agent",
64+
instruction="""\
65+
You are the Math Agent, responsible for performing mathematical calculations.
66+
67+
NOTE: You should only perform mathematical calculations and ignore other askings from the user.
68+
""",
69+
before_agent_callback=before_agent_callback_check_relevance("math_agent"),
70+
output_key="math_agent_output",
71+
)
72+
73+
74+
worker_parallel_agent = ParallelAgent(
75+
name="worker_parallel_agent",
76+
sub_agents=[
77+
code_agent,
78+
math_agent,
79+
],
80+
)
81+
82+
83+
def instruction_provider_for_execution_summary_agent(
84+
readonly_context: ReadonlyContext,
85+
) -> str:
86+
"""Provides the instruction for the execution agent."""
87+
activated_agents = readonly_context.state["execution_agents"]
88+
prompt = f"""\
89+
You are the Execution Summary Agent, responsible for summarizing the execution of the plan in the current invocation.
90+
91+
In this invocation, the following agents were involved: {', '.join(activated_agents)}.
92+
93+
Below are their outputs:
94+
"""
95+
for agent_name in activated_agents:
96+
output = readonly_context.state.get(f"{agent_name}_output", "")
97+
prompt += f"\n\n{agent_name} output:\n{output}"
98+
99+
prompt += (
100+
"\n\nPlease summarize the execution of the plan based on the above"
101+
" outputs."
102+
)
103+
return prompt.strip()
104+
105+
106+
execution_summary_agent = Agent(
107+
model="gemini-2.5-flash",
108+
name="execution_summary_agent",
109+
instruction=instruction_provider_for_execution_summary_agent,
110+
include_contents="none",
111+
)
112+
113+
plan_execution_agent = SequentialAgent(
114+
name="plan_execution_agent",
115+
sub_agents=[
116+
worker_parallel_agent,
117+
execution_summary_agent,
118+
],
119+
)

0 commit comments

Comments
 (0)