diff --git a/python/samples/04-hosting/a2a/README.md b/python/samples/04-hosting/a2a/README.md index 0affc84e19..71cc9336c2 100644 --- a/python/samples/04-hosting/a2a/README.md +++ b/python/samples/04-hosting/a2a/README.md @@ -1,11 +1,12 @@ # A2A Agent Examples -This sample demonstrates how to host and consume agents using the [A2A (Agent2Agent) protocol](https://a2a-protocol.org/latest/) with the `agent_framework` package. There are two runnable entry points: +This sample demonstrates how to host and consume agents using the [A2A (Agent2Agent) protocol](https://a2a-protocol.org/latest/) with the `agent_framework` package. There are three runnable entry points: | Run this file | To... | |---------------|-------| | **[`a2a_server.py`](a2a_server.py)** | Host an Agent Framework agent as an A2A-compliant server. | | **[`agent_with_a2a.py`](agent_with_a2a.py)** | Connect to an A2A server and send requests (non-streaming and streaming). | +| **[`a2a_agent_as_function_tools.py`](a2a_agent_as_function_tools.py)** | Convert A2A agent skills into function tools for a host agent. | The remaining files are supporting modules used by the server: @@ -27,6 +28,11 @@ Make sure to set the following environment variables before running the examples ### Required (Client) - `A2A_AGENT_HOST` — URL of the A2A server (e.g. `http://localhost:5001/`) +### Required (Function Tools Sample) +- `A2A_AGENT_HOST` — URL of the A2A server (e.g. `http://localhost:5000/`) +- `AZURE_AI_PROJECT_ENDPOINT` — Your Azure AI Foundry project endpoint +- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME` — Model deployment name (e.g. `gpt-4o`) + ## Quick Start All commands below should be run from this directory: @@ -55,3 +61,14 @@ In a separate terminal (from the same directory), point the client at a running $env:A2A_AGENT_HOST = "http://localhost:5001/" uv run python agent_with_a2a.py ``` + +### 3. Run the Function Tools Sample + +This sample resolves the remote agent's skills and registers each one as a function tool +on a host OpenAI-powered agent. The host agent then autonomously selects the right skill +to handle the user's request. + +```powershell +$env:A2A_AGENT_HOST = "http://localhost:5000/" +uv run python a2a_agent_as_function_tools.py +``` diff --git a/python/samples/04-hosting/a2a/a2a_agent_as_function_tools.py b/python/samples/04-hosting/a2a/a2a_agent_as_function_tools.py new file mode 100644 index 0000000000..60d8317725 --- /dev/null +++ b/python/samples/04-hosting/a2a/a2a_agent_as_function_tools.py @@ -0,0 +1,152 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import os +import re + +import httpx +from a2a.client import A2ACardResolver +from agent_framework.a2a import A2AAgent +from agent_framework.azure import AzureOpenAIResponsesClient +from azure.identity import AzureCliCredential +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +""" +A2A Agent Skills as Function Tools + +This sample demonstrates how to represent an A2A agent's skills as individual +function tools and register them with a host agent. Each skill advertised in the +remote agent's AgentCard becomes a separate tool that the host agent can invoke. + +Key concepts demonstrated: +- Resolving an AgentCard from a remote A2A endpoint +- Converting each skill into a FunctionTool via as_tool() +- Registering those tools with a host agent +- Having the host agent autonomously select and invoke A2A skills + +Prerequisites: +- Set A2A_AGENT_HOST to the URL of a running A2A server +- Set AZURE_AI_PROJECT_ENDPOINT to your Azure AI Foundry project endpoint +- Set AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME to the model deployment name (e.g. gpt-4o) + +To run this sample: + cd python/samples/04-hosting/a2a + uv run python a2a_agent_as_function_tools.py +""" + + +async def main() -> None: + """Discover A2A agent skills and register them as tools on a host agent.""" + # 1. Read environment configuration. + a2a_agent_host = os.getenv("A2A_AGENT_HOST") + if not a2a_agent_host: + raise ValueError("A2A_AGENT_HOST environment variable is not set") + + project_endpoint = os.getenv("AZURE_AI_PROJECT_ENDPOINT") + deployment_name = os.getenv("AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME") + if not project_endpoint or not deployment_name: + raise ValueError( + "AZURE_AI_PROJECT_ENDPOINT and AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME must be set" + ) + + print(f"Connecting to A2A agent at: {a2a_agent_host}") + + # 2. Resolve the remote agent card to discover its skills. + async with httpx.AsyncClient(timeout=60.0) as http_client: + resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_agent_host) + agent_card = await resolver.get_agent_card() + + print(f"Found agent: {agent_card.name} ({len(agent_card.skills)} skill(s))") + for skill in agent_card.skills: + print(f" - {skill.name}: {skill.description}") + + # 3. Create the A2AAgent that wraps the remote endpoint. + async with A2AAgent( + name=agent_card.name, + description=agent_card.description, + agent_card=agent_card, + url=a2a_agent_host, + ) as a2a_agent: + # 4. Convert each A2A skill into a FunctionTool. + # Skill names may contain spaces or special characters, so we + # sanitize them into valid tool identifiers before passing to as_tool(). + skill_tools = [ + a2a_agent.as_tool( + name=re.sub(r"[^0-9A-Za-z]+", "_", skill.name), + description=skill.description or "", + ) + for skill in agent_card.skills + ] + + # 5. Create the host agent with the skill tools. + credential = AzureCliCredential() + client = AzureOpenAIResponsesClient( + project_endpoint=project_endpoint, + deployment_name=deployment_name, + credential=credential, + ) + host_agent = client.as_agent( + name="assistant", + instructions="You are a helpful assistant. Use your tools to answer questions.", + tools=skill_tools, + ) + + # 6. Run the host agent — it will select and invoke the appropriate A2A skill tools. + query = "Show me all invoices for Contoso" + print(f"\nUser: {query}\n") + response = await host_agent.run(query) + print(f"Agent: {response}") + + +if __name__ == "__main__": + asyncio.run(main()) + + +""" +Sample output: + +Connecting to A2A agent at: http://localhost:5000/ +Found agent: InvoiceAgent (1 skill(s)) + - InvoiceQuery: Handles requests relating to invoices. + +User: Show me all invoices for Contoso + +Agent: Here are the invoices for Contoso: + +1. **Invoice ID:** INV789 + - **Date:** 2026-02-15 + - **Products:** + - T-Shirts: 150 units @ $10.00 = $1,500.00 + - Hats: 200 units @ $15.00 = $3,000.00 + - Glasses: 300 units @ $5.00 = $1,500.00 + - **Total:** $6,000.00 + +2. **Invoice ID:** INV333 + - **Date:** 2026-03-14 + - **Products:** + - T-Shirts: 400 units @ $11.00 = $4,400.00 + - Hats: 600 units @ $15.00 = $9,000.00 + - Glasses: 700 units @ $5.00 = $3,500.00 + - **Total:** $16,900.00 + +3. **Invoice ID:** INV666 + - **Date:** 2026-02-06 + - **Products:** + - T-Shirts: 2,500 units @ $8.00 = $20,000.00 + - Hats: 1,200 units @ $10.00 = $12,000.00 + - Glasses: 1,000 units @ $6.00 = $6,000.00 + - **Total:** $38,000.00 + +4. **Invoice ID:** INV999 + - **Date:** 2026-03-19 + - **Products:** + - T-Shirts: 1,400 units @ $10.50 = $14,700.00 + - Hats: 1,100 units @ $9.00 = $9,900.00 + - Glasses: 950 units @ $12.00 = $11,400.00 + - **Total:** $36,000.00 + +If you need more details or a specific invoice, please let me know! +"""