Host-Agents mit A2A

Das Agent-to-Agent(A2A)-Protokoll ermöglicht eine standardisierte Kommunikation zwischen Agents, die mit verschiedenen Frameworks und Technologien erstellt wurden. Diese Seite behandelt das Verfügbarmachen von Agent Framework-Agents als A2A-Server.

Informationen zum Ermitteln und Aufrufen eines A2A-Remote-Agents finden Sie im A2A-Agentdienst.

Was ist A2A?

A2A ist ein standardisiertes Protokoll, das Folgendes unterstützt:

  • Agent-Ermittlung über Agentkarten
  • Nachrichtenbasierte Kommunikation zwischen Agents
  • Langfristige agentische Prozesse über Aufgaben
  • Plattformübergreifende Interoperabilität zwischen verschiedenen Agent-Frameworks

Weitere Informationen finden Sie in der A2A-Protokollspezifikation.

Die Microsoft.Agents.AI.Hosting.A2A.AspNetCore Bibliothek bietet ASP.NET Core-Integration zum Verfügbarmachen Ihrer Agents über das A2A-Protokoll.

NuGet-Pakete:

Example

Dieses minimale Beispiel zeigt, wie ein Agent über A2A verfügbar gemacht wird. Das Beispiel enthält OpenAPI- und Swagger-Abhängigkeiten, um tests zu vereinfachen.

1. Erstellen eines ASP.NET Core Web API-Projekts

Erstellen Sie ein neues ASP.NET Core Web API-Projekt, oder verwenden Sie ein vorhandenes.

2. Installieren erforderlicher Abhängigkeiten

Installieren Sie die folgenden Pakete:

Führen Sie die folgenden Befehle in Ihrem Projektverzeichnis aus, um die erforderlichen NuGet-Pakete zu installieren:

# Hosting.A2A.AspNetCore for A2A protocol integration
dotnet add package Microsoft.Agents.AI.Hosting.A2A.AspNetCore --prerelease

# Libraries to connect to Microsoft Foundry
dotnet add package Azure.AI.Projects --prerelease
dotnet add package Azure.Identity
dotnet add package Microsoft.Agents.AI.Foundry --prerelease

# Swagger to test app
dotnet add package Microsoft.AspNetCore.OpenApi
dotnet add package Swashbuckle.AspNetCore

3. Konfigurieren der Microsoft Foundry-Verbindung

Für die Anwendung ist eine Microsoft Foundry-Projektverbindung erforderlich. Konfigurieren Sie den Endpunkt und den Bereitstellungsnamen mithilfe von dotnet user-secrets oder Umgebungsvariablen. Sie können auch einfach die appsettings.json bearbeiten, jedoch wird dies für Apps, die in der Produktion bereitgestellt werden, nicht empfohlen, da einige der Daten als geheim eingestuft werden können.

dotnet user-secrets set "AZURE_OPENAI_ENDPOINT" "https://<your-openai-resource>.openai.azure.com/"
dotnet user-secrets set "AZURE_OPENAI_DEPLOYMENT_NAME" "gpt-4o-mini"

4. Hinzufügen des Codes zu Program.cs

Ersetzen Sie den Inhalt von Program.cs durch den folgenden Code, und führen Sie die Anwendung aus.

using A2A.AspNetCore;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting;
using Microsoft.Extensions.AI;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddSwaggerGen();

string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
    ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = builder.Configuration["AZURE_OPENAI_DEPLOYMENT_NAME"]
    ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set.");

// Register the chat client
IChatClient chatClient = new AIProjectClient(
        new Uri(endpoint),
        new DefaultAzureCredential())
        .GetProjectOpenAIClient()
        .GetProjectResponsesClient()
        .AsIChatClient(deploymentName);

builder.Services.AddSingleton(chatClient);

// Register an agent
var pirateAgent = builder.AddAIAgent("pirate", instructions: "You are a pirate. Speak like a pirate.");

var app = builder.Build();

app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI();

// Expose the agent via A2A protocol. You can also customize the agentCard
app.MapA2A(pirateAgent, path: "/a2a/pirate", agentCard: new()
{
    Name = "Pirate Agent",
    Description = "An agent that speaks like a pirate.",
    Version = "1.0"
});

app.Run();

Warning

DefaultAzureCredential ist praktisch für die Entwicklung, erfordert aber sorgfältige Überlegungen in der Produktion. Berücksichtigen Sie in der Produktion die Verwendung bestimmter Anmeldeinformationen (z. B. ManagedIdentityCredential), um Latenzprobleme, unbeabsichtigte Abfragen von Anmeldeinformationen und potenzielle Sicherheitsrisiken durch Ausweichmechanismen zu vermeiden.

Testen des Agents

Sobald die Anwendung ausgeführt wird, können Sie den A2A-Agent mit der folgenden .http Datei oder über die Swagger-Benutzeroberfläche testen.

Das Eingabeformat entspricht der A2A-Spezifikation. Sie können Werte für Folgendes angeben:

  • messageId – Ein eindeutiger Bezeichner für diese bestimmte Nachricht. Sie können Ihre eigene ID (z. B. eine GUID) erstellen oder sie auf null setzen, damit der Agent automatisch eine ID generiert.
  • contextId - Die Gesprächs-ID. Geben Sie Ihre eigene ID an, um eine neue Unterhaltung zu beginnen, oder um eine vorhandene Unterhaltung fortzusetzen, indem Sie eine vorige ID wieder contextId. Der Agent wird den Gesprächsverlauf für denselben contextId verwalten. Der Agent generiert auch eine für Sie, wenn keine bereitgestellt wird.
# Send A2A request to the pirate agent
POST {{baseAddress}}/a2a/pirate/v1/message:stream
Content-Type: application/json
{
  "message": {
    "kind": "message",
    "role": "user",
    "parts": [
      {
        "kind": "text",
        "text": "Hey pirate! Tell me where have you been",
        "metadata": {}
      }
    ],
	"messageId": null,
    "contextId": "foo"
  }
}

Hinweis: Ersetzen Sie den Serverendpunkt {{baseAddress}} durch Ihren Serverendpunkt.

Diese Anforderung gibt die folgende JSON-Antwort zurück:

{
	"kind": "message",
	"role": "agent",
	"parts": [
		{
			"kind": "text",
			"text": "Arrr, ye scallywag! Ye’ll have to tell me what yer after, or be I walkin’ the plank? 🏴‍☠️"
		}
	],
	"messageId": "chatcmpl-CXtJbisgIJCg36Z44U16etngjAKRk",
	"contextId": "foo"
}

Die Antwort enthält den contextId (Unterhaltungsbezeichner), messageId (Nachrichtenbezeichner) und den tatsächlichen Inhalt des Piraten-Agents.

AgentCard-Konfiguration

Dies AgentCard stellt Metadaten zu Ihrem Agent für die Ermittlung und Integration bereit:

app.MapA2A(agent, "/a2a/my-agent", agentCard: new()
{
    Name = "My Agent",
    Description = "A helpful agent that assists with tasks.",
    Version = "1.0",
});

Sie können auf die Agent-Karte zugreifen, indem Sie diese Anforderung senden:

# Send A2A request to the pirate agent
GET {{baseAddress}}/a2a/pirate/v1/card

Hinweis: Ersetzen Sie den Serverendpunkt {{baseAddress}} durch Ihren Serverendpunkt.

AgentCard-Eigenschaften

  • Name: Anzeigename des Agents
  • Beschreibung: Kurze Beschreibung des Agenten
  • Version: Versionszeichenfolge für den Agent
  • URL: Endpunkt-URL (automatisch zugewiesen, wenn nicht angegeben)
  • Funktionen: Optionale Metadaten zu Streaming, Pushbenachrichtigungen und anderen Features

Offenlegung mehrerer Agenten

Sie können mehrere Agents in einer einzigen Anwendung verfügbar machen, solange ihre Endpunkte nicht kollidieren. Im Folgenden finden Sie ein Beispiel:

var mathAgent = builder.AddAIAgent("math", instructions: "You are a math expert.");
var scienceAgent = builder.AddAIAgent("science", instructions: "You are a science expert.");

app.MapA2A(mathAgent, "/a2a/math");
app.MapA2A(scienceAgent, "/a2a/science");

Das agent-framework-a2a Paket macht einen Agent Framework-Agent über das A2A-Protokoll verfügbar.

pip install agent-framework-a2a --pre

Testen eines gesicherten Endpunkts

Verwenden Sie einen AuthInterceptor Testclient, um einen gesicherten A2A-Endpunkt zu überprüfen:

from a2a.client.auth.interceptor import AuthInterceptor

class BearerAuth(AuthInterceptor):
    def __init__(self, token: str):
        self.token = token

    async def intercept(self, request):
        request.headers["Authorization"] = f"Bearer {self.token}"
        return request

async with A2AAgent(
    name="secure-agent",
    url="https://secure-a2a-agent.example.com",
    auth_interceptor=BearerAuth("your-token"),
) as agent:
    response = await agent.run("Hello!")

Verfügbarmachen eines Agent Framework-Agents über A2A

Das agent-framework-a2a Paket stellt eine vorgefertigte A2AExecutor bereit, die jeden Agent-Framework-Agenten an das serverseitige A2A-Protokoll anpasst. Es führt den Agenten aus, ordnet unterstützte Ausgabeinhalte A2A-Ereignissen und Artefakten zu und verwaltet Statusaktualisierungen von Aufgaben über den offiziellen a2a-sdk.

Ihre Anwendung stellt den umgebenden A2A SDK-Server zusammen: die Agentkarte, DefaultRequestHandlerden Aufgabenspeicher, Routen oder Anwendungs-Generator, die Authentifizierung und die Bereitstellung. Einen Vergleich mit den App-eigenen Adaptern und eigenständigen Konvertierungshilfen in agent-framework-hosting-a2a finden Sie unter Selbstgehostete A2A-Agenten.

import uvicorn
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
from a2a.server.tasks import InMemoryTaskStore
from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill
from agent_framework import Agent
from agent_framework.a2a import A2AExecutor
from agent_framework.openai import OpenAIChatClient
from starlette.applications import Starlette

flight_skill = AgentSkill(
    id="Flight_Booking",
    name="Flight Booking",
    description="Search and book flights across Europe.",
    tags=["flights", "travel", "europe"],
    examples=[],
)

public_agent_card = AgentCard(
    name="Europe Travel Agent",
    description="Helps users search and book flights and hotels across Europe.",
    version="1.0.0",
    default_input_modes=["text"],
    default_output_modes=["text"],
    capabilities=AgentCapabilities(streaming=True),
    supported_interfaces=[
        AgentInterface(url="http://localhost:9999/", protocol_binding="JSONRPC"),
    ],
    skills=[flight_skill],
)

agent = Agent(
    client=OpenAIChatClient(),
    name="Europe Travel Agent",
    instructions="You are a helpful Europe Travel Agent.",
)

request_handler = DefaultRequestHandler(
    agent_executor=A2AExecutor(agent, stream=True),
    task_store=InMemoryTaskStore(),
    agent_card=public_agent_card,
)

server = Starlette(
    routes=[
        *create_agent_card_routes(public_agent_card),
        *create_jsonrpc_routes(request_handler, "/"),
    ]
)

uvicorn.run(server, host="0.0.0.0", port=9999)

A2AExecutor streamt Aktualisierungen des Agenten als A2A-Artefakte, wenn der zugrunde liegende Agent Streaming unterstützt, und gibt das A2A context_id als session_id der Agentensitzung weiter. Sie können von A2AExecutor ableiten und die Methode handle_events überschreiben, um benutzerdefinierte Transformationen vom Ausgabeformat Ihres Agents in A2A-Protokollereignisse zu implementieren.

A2A-Protokoll

Das Go Agent Framework unterstützt das Hosten von Agent Framework-Agents über das A2A-Protokoll (Agent-to-Agent) mit dem provider/a2aprovider Paket und den offiziellen A2A Go-Serverhandlern.

Installieren Sie die Agent Framework- und A2A-Pakete in Ihrem Go-Modul:

go get github.com/microsoft/agent-framework-go
go get github.com/a2aproject/a2a-go/v2

Einen Agenten über A2A bereitstellen

Erstellen oder wiederverwenden Sie einen Agent Framework-Agent, beschreiben Sie ihn mit einer A2A-Agent-Karte, und machen Sie ihn über eine der A2A-Transportbindungen verfügbar. In diesem Beispiel ist hostAgent ein beliebiges Agent-Framework *agent.Agent; der Server hostet einen JSON-RPC-Endpunkt unter / und stellt die Agentkarte auf dem Well-Known-A2A-Pfad bereit.

import (
    "fmt"
    "net/http"

    "github.com/a2aproject/a2a-go/v2/a2a"
    "github.com/a2aproject/a2a-go/v2/a2asrv"
    "github.com/microsoft/agent-framework-go/provider/a2aprovider"
)

url := "http://localhost:5000"

card := &a2a.AgentCard{
    Name:               "InvoiceAgent",
    Description:        "Handles requests relating to invoices.",
    Version:            "1.0.0",
    DefaultInputModes:  []string{"text"},
    DefaultOutputModes: []string{"text"},
    Capabilities: a2a.AgentCapabilities{
        Streaming: false,
    },
    SupportedInterfaces: []*a2a.AgentInterface{
        a2a.NewAgentInterface(url, a2a.TransportProtocolJSONRPC),
    },
}

mux := http.NewServeMux()
requestHandler := a2asrv.NewHandler(
    a2aprovider.NewExecutor(hostAgent, a2aprovider.ExecutorConfig{}),
    a2asrv.WithExtendedAgentCard(card),
)
mux.Handle("/", a2asrv.NewJSONRPCHandler(requestHandler))
mux.Handle(a2asrv.WellKnownAgentCardPath, a2asrv.NewStaticAgentCardHandler(card))

if err := http.ListenAndServe(":5000", mux); err != nil {
    panic(fmt.Errorf("A2A server failed: %w", err))
}

Umschließen Sie den gleichen Anforderungshandler, wenn a2asrv.NewRESTHandler Sie die HTTP+JSON-Transportbindung verfügbar machen möchten. Setzen Sie ExecutorConfig.AllowBackgroundResponses auf true, wenn der gehostete Agent A2A-Aufgaben für lang andauernde Vorgänge zurückgeben darf.

Siehe auch

Nächste Schritte