{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lesson 11 - Agent-to-Agent (A2A) Protocol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pip install agent-framework azure-ai-projects azure-identity python-dotenv" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import dotenv\n", "from agent_framework import tool, AgentResponseUpdate, WorkflowBuilder\n", "from agent_framework.foundry import FoundryChatClient\n", "from azure.identity import DefaultAzureCredential\n", "\n", "dotenv.load_dotenv()\n", "\n", "endpoint = os.getenv(\"AZURE_AI_PROJECT_ENDPOINT\")\n", "deployment_name = os.getenv(\"AZURE_AI_MODEL_DEPLOYMENT_NAME\")\n", "\n", "missing = [k for k, v in {\n", " \"AZURE_AI_PROJECT_ENDPOINT\": endpoint,\n", " \"AZURE_AI_MODEL_DEPLOYMENT_NAME\": deployment_name\n", "}.items() if not v]\n", "\n", "if missing:\n", " raise ValueError(\n", " f\"Missing required environment variables: {', '.join(missing)}. \"\n", " \"Please set them as environment variables (e.g., in your .env file or shell environment).\"\n", " )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create the Microsoft Foundry client\n", "client = FoundryChatClient(\n", " project_endpoint=endpoint,\n", " model=deployment_name,\n", " credential=DefaultAzureCredential()\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What is the A2A Protocol?\n", "\n", "The **Agent-to-Agent (A2A) protocol** is an open standard that enables AI agents to communicate,\n", "discover each other, and collaborate — even when they are built on different frameworks or hosted\n", "by different services.\n", "\n", "Key concepts:\n", "\n", "- **Discovery** – Agents publish an *Agent Card* that describes their capabilities, making it\n", " easy for other agents (or orchestrators) to find the right specialist for a task.\n", "- **Message Passing** – Agents exchange structured messages through a common protocol, so a\n", " request from one agent can be understood and fulfilled by another regardless of its internal\n", " implementation.\n", "- **Task Lifecycle** – A2A defines states such as *submitted*, *working*, *completed*, and\n", " *failed*, giving the orchestrator full visibility into how a delegated task is progressing.\n", "\n", "In this lesson we simulate A2A-style collaboration by wiring three specialized travel agents\n", "into a workflow where each agent contributes its expertise and passes results to the next." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating Specialized Travel Agents" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "currency_agent = client.as_agent(\n", " name=\"CurrencyExchangeAgent\",\n", " instructions=\"\"\"You are a currency exchange specialist. You help travelers understand:\n", "- Current exchange rates between currencies\n", "- Best times to exchange money\n", "- Tips for getting the best rates\n", "When asked about a destination, provide relevant currency information.\"\"\",\n", ")\n", "\n", "activity_agent = client.as_agent(\n", " name=\"ActivityPlannerAgent\",\n", " instructions=\"\"\"You are a local activities specialist. You recommend:\n", "- Must-see attractions and hidden gems\n", "- Local experiences and cultural activities\n", "- Restaurant and dining recommendations\n", "Tailor suggestions to the traveler's interests.\"\"\",\n", ")\n", "\n", "travel_manager = client.as_agent(\n", " name=\"TravelManagerAgent\",\n", " instructions=\"\"\"You are a travel manager who coordinates between specialist agents.\n", "When planning a trip:\n", "1. Gather currency information from the currency specialist\n", "2. Get activity recommendations from the activity planner\n", "3. Synthesize everything into a cohesive travel brief\n", "Present the final plan in an organized, easy-to-read format.\"\"\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multi-Agent Collaboration via Workflow\n", "\n", "We connect the three agents into a sequential workflow that mirrors A2A message passing:\n", "\n", "1. **CurrencyExchangeAgent** receives the user request and produces currency guidance.\n", "2. **ActivityPlannerAgent** receives the enriched context and adds activity recommendations.\n", "3. **TravelManagerAgent** synthesizes both inputs into a final travel brief." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "workflow = WorkflowBuilder(start_executor=currency_agent) \\\n", " .add_edge(currency_agent, activity_agent) \\\n", " .add_edge(activity_agent, travel_manager) \\\n", " .build()\n", "\n", "last_author = None\n", "events = workflow.run(\n", " \"Plan a week-long trip to Tokyo. I love food, temples, and technology.\",\n", " stream=True,\n", ")\n", "async for event in events:\n", " if event.type == \"output\" and isinstance(event.data, AgentResponseUpdate):\n", " update = event.data\n", " author = update.author_name\n", " if author != last_author:\n", " if last_author is not None:\n", " print()\n", " print(f\"\\n{'='*50}\")\n", " print(f\"🤖 {author}:\")\n", " print(f\"{'='*50}\")\n", " last_author = author\n", " print(update.text, end=\"\", flush=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Understanding A2A in Production\n", "\n", "In a production environment the A2A protocol unlocks powerful cross-service scenarios:\n", "\n", "| Capability | Description |\n", "|---|---|\n", "| **Cross-framework interop** | An agent built with one framework can delegate tasks to an agent built with any other A2A-compliant framework, enabling true cross-organization interoperability. |\n", "| **Service boundaries** | Agents can live in separate microservices, cloud regions, or even different organisations while still collaborating seamlessly. |\n", "| **Dynamic discovery** | An orchestrator can query an Agent Card registry at runtime to find the best-suited specialist for a given sub-task. |\n", "| **Streaming & push notifications** | A2A supports Server-Sent Events (SSE) for real-time progress updates and push notifications for long-running tasks. |\n", "\n", "The workflow we built above is a simplified, in-process version of this pattern. In a real\n", "deployment each agent would expose an HTTP endpoint, publish an Agent Card, and communicate\n", "via the A2A JSON-RPC protocol." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "In this lesson you learned:\n", "\n", "1. **What the A2A protocol is** — an open standard for agent-to-agent discovery, messaging,\n", " and task management.\n", "2. **How to create specialized agents** — a Currency Exchange agent, an Activity Planner agent,\n", " and a Travel Manager orchestrator.\n", "3. **How to wire agents into a workflow** — using `WorkflowBuilder` to model sequential\n", " message passing between agents.\n", "4. **How A2A works in production** — enabling cross-framework, cross-service collaboration\n", " with dynamic discovery and streaming updates." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.13" } }, "nbformat": 4, "nbformat_minor": 2 }