diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/README.md b/packages/uipath-google-adk/samples/multi-agent-remote/README.md new file mode 100644 index 00000000..bd194d51 --- /dev/null +++ b/packages/uipath-google-adk/samples/multi-agent-remote/README.md @@ -0,0 +1,68 @@ +# Multi-Agent Remote (A2A) Sample + +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. + +## Architecture + +``` +SequentialAgent (pipeline) + +-- Agent (coordinator) ........... local, delegates to remote sub-agents + | +-- RemoteA2aAgent (research_agent) ... UiPath Studio Web agent + | +-- RemoteA2aAgent (code_agent) ....... UiPath Studio Web agent + +-- Agent (formatter) ............. local, structures output as JSON +``` + +## Prerequisites + +- [UiPath CLI](https://docs.uipath.com/cli) installed and configured +- Access to [UiPath Studio Web](https://cloud.uipath.com/) +- Python 3.10+ + +## Step 1: Create the Agents in UiPath Studio Web + +Go to **UiPath Studio Web** and create a new solution (e.g. `MultiAgentSolution 1`) with two agents: + +![UiPath Studio Web - ResearcherAgent](studio_web_screenshot.png) + +### ResearcherAgent (Conversational Agent) + +- **Model:** `anthropic.claude-sonnet-4-5-20250929-v1:0` (or any supported model) +- **System prompt:** + +``` +You are a research specialist. Use the search_web tool to find information about the given topic. Provide a thorough summary of your findings. +``` + +### PythonCoderAgent (Conversational Agent) + +- **Model:** any supported model +- **System prompt:** + +``` +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. +``` + +## Step 2: Deploy and Configure + +1. **Publish** the solution from Studio Web +2. **Deploy** the solution to a folder in Orchestrator +3. Note down the **folder key** (from the folder URL) and the **release IDs** for each agent +4. Update [main.py](main.py) with your values: + +```python +ORG_NAME = "YourOrgName" +TENANT_NAME = "YourTenantName" +RESEARCH_AGENT_FOLDER_KEY = "" +RESEARCH_AGENT_RELEASE_ID = "" +CODE_AGENT_FOLDER_KEY = "" +CODE_AGENT_RELEASE_ID = "" +``` + +## Step 3: Run + +```bash +uipath auth +uipath dev web +``` + +This authenticates with UiPath Cloud and starts the local dev server with the ADK agent pipeline. diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/agent.mermaid b/packages/uipath-google-adk/samples/multi-agent-remote/agent.mermaid new file mode 100644 index 00000000..8292928e --- /dev/null +++ b/packages/uipath-google-adk/samples/multi-agent-remote/agent.mermaid @@ -0,0 +1,18 @@ +flowchart TB + __start__(__start__) + pipeline(pipeline) + coordinator(coordinator) + research_agent(research_agent) + code_agent(code_agent) + formatter(formatter) + __end__(__end__) + coordinator --> research_agent + research_agent --> coordinator + coordinator --> code_agent + code_agent --> coordinator + pipeline --> coordinator + coordinator --> pipeline + pipeline --> formatter + formatter --> pipeline + __start__ --> |input|pipeline + pipeline --> |output|__end__ diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/google_adk.json b/packages/uipath-google-adk/samples/multi-agent-remote/google_adk.json new file mode 100644 index 00000000..61974aef --- /dev/null +++ b/packages/uipath-google-adk/samples/multi-agent-remote/google_adk.json @@ -0,0 +1,5 @@ +{ + "agents": { + "agent": "main.py:agent" + } +} diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/main.py b/packages/uipath-google-adk/samples/multi-agent-remote/main.py new file mode 100644 index 00000000..ff1159fb --- /dev/null +++ b/packages/uipath-google-adk/samples/multi-agent-remote/main.py @@ -0,0 +1,127 @@ +"""Google ADK multi-agent-remote example: same pipeline as multi-agent but with sub-agents hosted remotely via A2A. + +Demonstrates how to mix local orchestration with remote agent implementations: +- Coordinator and formatter run locally (they hold the orchestration logic) +- Specialist sub-agents (research, code) are RemoteA2aAgent instances hosted elsewhere +- The remote services don't need to know about each other — coordination stays local + +Compare with the multi-agent sample: + multi-agent: Agent(tools=[search_web]) Agent(tools=[run_python]) + multi-agent-remote: RemoteA2aAgent(agent_card=...) for each specialist + +The key insight: RemoteA2aAgent cannot have sub_agents (it's not an LlmAgent), +but a local Agent CAN have RemoteA2aAgent instances as its sub_agents. This lets +you keep orchestration logic local while moving implementations to remote services. +""" + +import os + +import httpx +from a2a.client.client import ClientConfig as A2AClientConfig +from a2a.client.client_factory import ClientFactory as A2AClientFactory +from a2a.types import TransportProtocol as A2ATransport +from google.adk.agents import Agent, SequentialAgent +from google.adk.agents.remote_a2a_agent import RemoteA2aAgent +from pydantic import BaseModel, Field + + +class ReportInput(BaseModel): + """Structured input for the report generation pipeline.""" + + topic: str = Field( + default="Natural Language Processing fundamentals", + description="The topic to research and analyze", + ) + depth: str = Field( + default="standard", + description="How deep the analysis should be: 'brief', 'standard', or 'detailed'", + ) + + +class ReportOutput(BaseModel): + """Structured output from the report generation pipeline.""" + + title: str = Field(description="Report title") + summary: str = Field(description="Executive summary of findings") + key_findings: list[str] = Field(description="Key findings as bullet points") + code_snippet: str = Field(description="A relevant Python code example") + + +# UIPATH_ACCESS_TOKEN is set automatically by `uipath auth` +_access_token = os.environ.get("UIPATH_ACCESS_TOKEN", "") + +_http_client = httpx.AsyncClient( + headers={"Authorization": f"Bearer {_access_token}"}, + timeout=httpx.Timeout(300.0), +) + +_a2a_client_factory = A2AClientFactory( + config=A2AClientConfig( + httpx_client=_http_client, + supported_transports=[A2ATransport.jsonrpc], + streaming=False, + polling=False, + accepted_output_modes=["text"], + ), +) + +# --- Remote Sub-agents --- +# Replace the URLs with your actual deployed agent endpoints. +ORG_NAME = "YourOrgName" +TENANT_NAME = "YourTenantName" +RESEARCH_AGENT_FOLDER_KEY = "a11f72b1-90fd-4b30-b733-f0285cbf4a19" +RESEARCH_AGENT_RELEASE_ID = "1234" +CODE_AGENT_FOLDER_KEY = "b22f83c2-91fe-5c41-c844-g1396dcg5b2a" +CODE_AGENT_RELEASE_ID = "5678" + +research_agent = RemoteA2aAgent( + name="research_agent", + 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", + description="Remote research specialist that searches the web and summarizes findings", + a2a_client_factory=_a2a_client_factory, +) + +code_agent = RemoteA2aAgent( + name="code_agent", + 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", + description="Remote Python developer that writes and executes code examples", + a2a_client_factory=_a2a_client_factory, +) + + +# --- Coordinator (local Agent, sub_agents are remote) --- +coordinator = Agent( + name="coordinator", + model="gemini-2.5-flash", + instruction=( + "You are a report coordinator. Given a topic:\n" + "1. Delegate research to research_agent to gather information\n" + "2. Delegate to code_agent to write a relevant Python code example\n" + "3. Compile all findings into a comprehensive text report\n" + "Include the research findings and the code example in your response." + ), + sub_agents=[research_agent, code_agent], + input_schema=ReportInput, + output_key="research_results", +) + + +# --- Formatter (local Agent with output_schema) --- +formatter = Agent( + name="formatter", + model="gemini-2.5-flash", + instruction=( + "You are a report formatter. Take the research results from the previous " + "step and format them into a structured report with a title, summary, " + "key findings, and a code snippet. Output valid JSON matching the schema." + ), + output_schema=ReportOutput, + output_key="report", +) + + +# --- Root: SequentialAgent pipeline --- +agent = SequentialAgent( + name="pipeline", + sub_agents=[coordinator, formatter], +) diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/pyproject.toml b/packages/uipath-google-adk/samples/multi-agent-remote/pyproject.toml new file mode 100644 index 00000000..ec17361d --- /dev/null +++ b/packages/uipath-google-adk/samples/multi-agent-remote/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "multi-agent-remote" +version = "0.0.1" +description = "Google ADK multi-agent example with RemoteA2aAgent sub-agents via A2A protocol" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "uipath-google-adk", + "google-adk[a2a]>=1.25.0", + "uipath>=2.8.18, <2.9.0", +] + +[dependency-groups] +dev = [ + "uipath-dev", +] + +[tool.uv] +override-dependencies = ["opentelemetry-sdk>=1.39.0,<1.40.0"] diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/studio_web_screenshot.png b/packages/uipath-google-adk/samples/multi-agent-remote/studio_web_screenshot.png new file mode 100644 index 00000000..b88d0277 Binary files /dev/null and b/packages/uipath-google-adk/samples/multi-agent-remote/studio_web_screenshot.png differ diff --git a/packages/uipath-google-adk/samples/multi-agent-remote/uipath.json b/packages/uipath-google-adk/samples/multi-agent-remote/uipath.json new file mode 100644 index 00000000..52263d59 --- /dev/null +++ b/packages/uipath-google-adk/samples/multi-agent-remote/uipath.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://cloud.uipath.com/draft/2024-12/uipath", + "runtimeOptions": { + "isConversational": false + }, + "packOptions": { + "fileExtensionsIncluded": [], + "filesIncluded": [], + "filesExcluded": [], + "directoriesExcluded": [], + "includeUvLock": true + }, + "functions": {} +}