Skip to content

Commit d8efb8d

Browse files
committed
feat(samples): add remote a2a samples
1 parent 60eadd5 commit d8efb8d

8 files changed

Lines changed: 4022 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Multi-Agent Remote (A2A) Sample
2+
3+
This sample demonstrates how to orchestrate **remote UiPath agents** via the [A2A protocol](https://google.github.io/A2A/) using Google ADK. A local coordinator agent delegates tasks to remote specialist agents hosted in UiPath, combining local orchestration with remote execution.
4+
5+
## Architecture
6+
7+
```
8+
SequentialAgent (pipeline)
9+
+-- Agent (coordinator) ........... local, delegates to remote sub-agents
10+
| +-- RemoteA2aAgent (research_agent) ... UiPath Studio Web agent
11+
| +-- RemoteA2aAgent (code_agent) ....... UiPath Studio Web agent
12+
+-- Agent (formatter) ............. local, structures output as JSON
13+
```
14+
15+
## Prerequisites
16+
17+
- [UiPath CLI](https://docs.uipath.com/cli) installed and configured
18+
- Access to [UiPath Studio Web](https://cloud.uipath.com/)
19+
- Python 3.10+
20+
21+
## Step 1: Create the Agents in UiPath Studio Web
22+
23+
Go to **UiPath Studio Web** and create a new solution (e.g. `MultiAgentSolution 1`) with two agents:
24+
25+
![UiPath Studio Web - ResearcherAgent](studio_web_screenshot.png)
26+
27+
### ResearcherAgent (Conversational Agent)
28+
29+
- **Model:** `anthropic.claude-sonnet-4-5-20250929-v1:0` (or any supported model)
30+
- **System prompt:**
31+
32+
```
33+
You are a research specialist. Use the search_web tool to find information about the given topic. Provide a thorough summary of your findings.
34+
```
35+
36+
### PythonCoderAgent (Conversational Agent)
37+
38+
- **Model:** any supported model
39+
- **System prompt:**
40+
41+
```
42+
You are a Python developer. Given a topic, write a short, practical Python code example that demonstrates or relates to the topic. Use the run_python tool to execute your code and verify it works. Return both the code and its output.
43+
```
44+
45+
## Step 2: Deploy and Configure
46+
47+
1. **Publish** the solution from Studio Web
48+
2. **Deploy** the solution to a folder in Orchestrator
49+
3. Note down the **folder key** (from the folder URL) and the **release IDs** for each agent
50+
4. Update [main.py](main.py) with your values:
51+
52+
```python
53+
ORG_NAME = "YourOrgName"
54+
TENANT_NAME = "YourTenantName"
55+
RESEARCH_AGENT_FOLDER_KEY = "<your-folder-key>"
56+
RESEARCH_AGENT_RELEASE_ID = "<your-release-id>"
57+
CODE_AGENT_FOLDER_KEY = "<your-folder-key>"
58+
CODE_AGENT_RELEASE_ID = "<your-release-id>"
59+
```
60+
61+
## Step 3: Run
62+
63+
```bash
64+
uipath auth
65+
uipath dev web
66+
```
67+
68+
This authenticates with UiPath Cloud and starts the local dev server with the ADK agent pipeline.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
flowchart TB
2+
__start__(__start__)
3+
pipeline(pipeline)
4+
coordinator(coordinator)
5+
research_agent(research_agent)
6+
code_agent(code_agent)
7+
formatter(formatter)
8+
__end__(__end__)
9+
coordinator --> research_agent
10+
research_agent --> coordinator
11+
coordinator --> code_agent
12+
code_agent --> coordinator
13+
pipeline --> coordinator
14+
coordinator --> pipeline
15+
pipeline --> formatter
16+
formatter --> pipeline
17+
__start__ --> |input|pipeline
18+
pipeline --> |output|__end__
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"agents": {
3+
"agent": "main.py:agent"
4+
}
5+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""Google ADK multi-agent-remote example: same pipeline as multi-agent but with sub-agents hosted remotely via A2A.
2+
3+
Demonstrates how to mix local orchestration with remote agent implementations:
4+
- Coordinator and formatter run locally (they hold the orchestration logic)
5+
- Specialist sub-agents (research, code) are RemoteA2aAgent instances hosted elsewhere
6+
- The remote services don't need to know about each other — coordination stays local
7+
8+
Compare with the multi-agent sample:
9+
multi-agent: Agent(tools=[search_web]) Agent(tools=[run_python])
10+
multi-agent-remote: RemoteA2aAgent(agent_card=...) for each specialist
11+
12+
The key insight: RemoteA2aAgent cannot have sub_agents (it's not an LlmAgent),
13+
but a local Agent CAN have RemoteA2aAgent instances as its sub_agents. This lets
14+
you keep orchestration logic local while moving implementations to remote services.
15+
16+
Graph structure:
17+
__start__ → pipeline → coordinator → research_agent (RemoteA2aAgent)
18+
→ code_agent (RemoteA2aAgent)
19+
→ formatter (output_schema=ReportOutput)
20+
→ __end__
21+
22+
Schema resolution (handled by the runtime recursively):
23+
- input_schema: from FIRST sub_agent chain → coordinator.input_schema (ReportInput)
24+
- output_schema: from LAST sub_agent chain → formatter.output_schema (ReportOutput)
25+
- output_key: from LAST sub_agent chain → formatter.output_key ("report")
26+
"""
27+
28+
import os
29+
30+
import httpx
31+
from a2a.client.client import ClientConfig as A2AClientConfig
32+
from a2a.client.client_factory import ClientFactory as A2AClientFactory
33+
from a2a.types import TransportProtocol as A2ATransport
34+
from google.adk.agents import Agent, SequentialAgent
35+
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
36+
from pydantic import BaseModel, Field
37+
38+
39+
class ReportInput(BaseModel):
40+
"""Structured input for the report generation pipeline."""
41+
42+
topic: str = Field(
43+
default="Natural Language Processing fundamentals",
44+
description="The topic to research and analyze",
45+
)
46+
depth: str = Field(
47+
default="standard",
48+
description="How deep the analysis should be: 'brief', 'standard', or 'detailed'",
49+
)
50+
51+
52+
class ReportOutput(BaseModel):
53+
"""Structured output from the report generation pipeline."""
54+
55+
title: str = Field(description="Report title")
56+
summary: str = Field(description="Executive summary of findings")
57+
key_findings: list[str] = Field(description="Key findings as bullet points")
58+
code_snippet: str = Field(description="A relevant Python code example")
59+
60+
61+
# UIPATH_ACCESS_TOKEN is set automatically by `uipath auth`
62+
_access_token = os.environ.get("UIPATH_ACCESS_TOKEN", "")
63+
64+
_http_client = httpx.AsyncClient(
65+
headers={"Authorization": f"Bearer {_access_token}"},
66+
timeout=httpx.Timeout(300.0),
67+
)
68+
69+
_a2a_client_factory = A2AClientFactory(
70+
config=A2AClientConfig(
71+
httpx_client=_http_client,
72+
supported_transports=[A2ATransport.jsonrpc],
73+
streaming=False,
74+
polling=False,
75+
accepted_output_modes=["text"],
76+
),
77+
)
78+
79+
# --- Remote Sub-agents ---
80+
# Replace the URLs with your actual deployed agent endpoints.
81+
ORG_NAME = "YourOrgName"
82+
TENANT_NAME = "YourTenantName"
83+
RESEARCH_AGENT_FOLDER_KEY = "a11f72b1-90fd-4b30-b733-f0285cbf4a19"
84+
RESEARCH_AGENT_RELEASE_ID = "1234"
85+
CODE_AGENT_FOLDER_KEY = "b22f83c2-91fe-5c41-c844-g1396dcg5b2a"
86+
CODE_AGENT_RELEASE_ID = "5678"
87+
88+
research_agent = RemoteA2aAgent(
89+
name="research_agent",
90+
agent_card=f"https://cloud.uipath.com/{ORG_NAME}/{TENANT_NAME}/agenthub_/a2a/{RESEARCH_AGENT_FOLDER_KEY}/{RESEARCH_AGENT_RELEASE_ID}/.well-known/agent-card.json",
91+
description="Remote research specialist that searches the web and summarizes findings",
92+
a2a_client_factory=_a2a_client_factory,
93+
)
94+
95+
code_agent = RemoteA2aAgent(
96+
name="code_agent",
97+
agent_card=f"https://cloud.uipath.com/{ORG_NAME}/{TENANT_NAME}/agenthub_/a2a/{CODE_AGENT_FOLDER_KEY}/{CODE_AGENT_RELEASE_ID}/.well-known/agent-card.json",
98+
description="Remote Python developer that writes and executes code examples",
99+
a2a_client_factory=_a2a_client_factory,
100+
)
101+
102+
103+
# --- Coordinator (local Agent, sub_agents are remote) ---
104+
coordinator = Agent(
105+
name="coordinator",
106+
model="gemini-2.5-flash",
107+
instruction=(
108+
"You are a report coordinator. Given a topic:\n"
109+
"1. Delegate research to research_agent to gather information\n"
110+
"2. Delegate to code_agent to write a relevant Python code example\n"
111+
"3. Compile all findings into a comprehensive text report\n"
112+
"Include the research findings and the code example in your response."
113+
),
114+
sub_agents=[research_agent, code_agent],
115+
input_schema=ReportInput,
116+
output_key="research_results",
117+
)
118+
119+
120+
# --- Formatter (local Agent with output_schema) ---
121+
formatter = Agent(
122+
name="formatter",
123+
model="gemini-2.5-flash",
124+
instruction=(
125+
"You are a report formatter. Take the research results from the previous "
126+
"step and format them into a structured report with a title, summary, "
127+
"key findings, and a code snippet. Output valid JSON matching the schema."
128+
),
129+
output_schema=ReportOutput,
130+
output_key="report",
131+
)
132+
133+
134+
# --- Root: SequentialAgent pipeline ---
135+
agent = SequentialAgent(
136+
name="pipeline",
137+
sub_agents=[coordinator, formatter],
138+
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[project]
2+
name = "multi-agent-remote"
3+
version = "0.0.1"
4+
description = "Google ADK multi-agent example with RemoteA2aAgent sub-agents via A2A protocol"
5+
readme = "README.md"
6+
requires-python = ">=3.11"
7+
dependencies = [
8+
"uipath-google-adk",
9+
"google-adk[a2a]>=1.25.0",
10+
"uipath>=2.8.18, <2.9.0",
11+
]
12+
13+
[dependency-groups]
14+
dev = [
15+
"uipath-dev",
16+
]
17+
18+
[tool.uv]
19+
override-dependencies = ["opentelemetry-sdk>=1.39.0,<1.40.0"]
198 KB
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://cloud.uipath.com/draft/2024-12/uipath",
3+
"runtimeOptions": {
4+
"isConversational": false
5+
},
6+
"packOptions": {
7+
"fileExtensionsIncluded": [],
8+
"filesIncluded": [],
9+
"filesExcluded": [],
10+
"directoriesExcluded": [],
11+
"includeUvLock": true
12+
},
13+
"functions": {}
14+
}

0 commit comments

Comments
 (0)