Skip to content

Commit e4c39f4

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

8 files changed

Lines changed: 4043 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: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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(default="Natural Language Processing fundamentals", description="The topic to research and analyze")
43+
depth: str = Field(default="standard", description="How deep the analysis should be: 'brief', 'standard', or 'detailed'")
44+
45+
46+
class ReportOutput(BaseModel):
47+
"""Structured output from the report generation pipeline."""
48+
49+
title: str = Field(description="Report title")
50+
summary: str = Field(description="Executive summary of findings")
51+
key_findings: list[str] = Field(description="Key findings as bullet points")
52+
code_snippet: str = Field(description="A relevant Python code example")
53+
54+
55+
# --- Shared HTTP client with authorization ---
56+
#
57+
# UIPATH_ACCESS_TOKEN is used to authenticate requests to the remote A2A endpoints.
58+
# Set it in your .env file before running.
59+
_access_token = os.environ.get("UIPATH_ACCESS_TOKEN", "")
60+
61+
async def _log_request(request: httpx.Request):
62+
body = (request.content or b"")[:4000]
63+
print(f">>> {request.method} {request.url}\n body={body.decode(errors='replace')}", flush=True)
64+
65+
66+
async def _log_response(response: httpx.Response):
67+
await response.aread()
68+
print(f"<<< {response.status_code} {response.request.url}\n body={response.text[:4000]}", flush=True)
69+
70+
71+
_http_client = httpx.AsyncClient(
72+
headers={"Authorization": f"Bearer {_access_token}"},
73+
timeout=httpx.Timeout(120.0),
74+
event_hooks={"request": [_log_request], "response": [_log_response]},
75+
)
76+
77+
_a2a_client_factory = A2AClientFactory(
78+
config=A2AClientConfig(
79+
httpx_client=_http_client,
80+
supported_transports=[A2ATransport.jsonrpc],
81+
streaming=False,
82+
polling=False,
83+
accepted_output_modes=["text"],
84+
),
85+
)
86+
87+
# --- Remote Sub-agents ---
88+
#
89+
# These replace the local Agent(tools=[...]) definitions from the multi-agent sample.
90+
# The coordinator delegates to them exactly the same way — the only difference is
91+
# that their implementation runs remotely via the A2A protocol.
92+
#
93+
# Replace the URLs with your actual deployed agent endpoints.
94+
ORG_NAME = "YourOrgName"
95+
TENANT_NAME = "YourTenantName"
96+
RESEARCH_AGENT_FOLDER_KEY = "a11f72b1-90fd-4b30-b733-f0285cbf4a19"
97+
RESEARCH_AGENT_RELEASE_ID = "1234"
98+
99+
CODE_AGENT_FOLDER_KEY = "b22f83c2-91fe-5c41-c844-g1396dcg5b2a"
100+
CODE_AGENT_RELEASE_ID = "5678"
101+
102+
research_agent = RemoteA2aAgent(
103+
name="research_agent",
104+
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",
105+
description="Remote research specialist that searches the web and summarizes findings",
106+
a2a_client_factory=_a2a_client_factory,
107+
)
108+
109+
code_agent = RemoteA2aAgent(
110+
name="code_agent",
111+
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",
112+
description="Remote Python developer that writes and executes code examples",
113+
a2a_client_factory=_a2a_client_factory,
114+
)
115+
116+
117+
# --- Coordinator (local Agent, sub_agents are remote) ---
118+
#
119+
# Stays local so it can reference the remote sub_agents above.
120+
# RemoteA2aAgent cannot have sub_agents — only local Agents can.
121+
# The coordinator LLM decides when to call each remote sub-agent;
122+
# the remote services themselves don't need to know about each other.
123+
coordinator = Agent(
124+
name="coordinator",
125+
model="gemini-2.5-flash",
126+
instruction=(
127+
"You are a report coordinator. Given a topic:\n"
128+
"1. Delegate research to research_agent to gather information\n"
129+
"2. Delegate to code_agent to write a relevant Python code example\n"
130+
"3. Compile all findings into a comprehensive text report\n"
131+
"Include the research findings and the code example in your response."
132+
),
133+
sub_agents=[research_agent, code_agent],
134+
input_schema=ReportInput,
135+
output_key="research_results",
136+
)
137+
138+
139+
# --- Formatter (local Agent with output_schema) ---
140+
#
141+
# Stays local — output_schema + structured JSON formatting is a local concern.
142+
formatter = Agent(
143+
name="formatter",
144+
model="gemini-2.5-flash",
145+
instruction=(
146+
"You are a report formatter. Take the research results from the previous "
147+
"step and format them into a structured report with a title, summary, "
148+
"key findings, and a code snippet. Output valid JSON matching the schema."
149+
),
150+
output_schema=ReportOutput,
151+
output_key="report",
152+
)
153+
154+
155+
# --- Root: SequentialAgent pipeline ---
156+
agent = SequentialAgent(
157+
name="pipeline",
158+
sub_agents=[coordinator, formatter],
159+
)
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)