Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Протокол "агент — агент" (A2A) позволяет стандартизированной связи между агентами, позволяя агентам, созданным с различными платформами и технологиями, легко взаимодействовать.
Что такое A2A?
A2A — это стандартизованный протокол, поддерживающий:
- Обнаружение агента с помощью карточек агента
- Обмен данными между агентами на основе сообщений
- Длительные агентические процессы с помощью задач
- Кроссплатформенная взаимодействие между разными платформами агентов
Дополнительные сведения см. в спецификации протокола A2A.
Библиотека Microsoft.Agents.AI.Hosting.A2A.AspNetCore предоставляет интеграцию ASP.NET Core для представления агентов через протокол A2A.
Пакеты NuGet:
Пример
В этом минимальном примере показано, как сделать агента доступным через A2A. Пример включает зависимости OpenAPI и Swagger для упрощения тестирования.
1. Создание проекта веб-API ASP.NET Core
Создайте проект веб-API ASP.NET Core или используйте существующий.
2. Установка необходимых зависимостей
Установите следующие пакеты:
Выполните следующие команды в каталоге проекта, чтобы установить необходимые пакеты NuGet:
# 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. Настройка подключения Microsoft Foundry
Приложению требуется подключение проекта Microsoft Foundry. Настройте конечную точку и имя развертывания, используя dotnet user-secrets или переменные окружения.
Вы также можете просто изменить appsettings.jsonфайл, но это не рекомендуется для приложений, развернутых в рабочей среде, так как некоторые данные могут считаться секретами.
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. Добавление кода в Program.cs
Замените содержимое Program.cs следующим кодом и запустите приложение:
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();
Предупреждение
DefaultAzureCredential удобно для разработки, но требует тщательного рассмотрения в рабочей среде. В рабочей среде рекомендуется использовать определенные учетные данные (например, ManagedIdentityCredential), чтобы избежать проблем с задержкой, непреднамеренной проверки данных аутентификации и потенциальных рисков безопасности из-за резервных механизмов.
Тестирование агента
После запуска приложения можно протестировать агент A2A с помощью следующего .http файла или пользовательского интерфейса Swagger.
Входной формат соответствует спецификации A2A. Можно указать значения для:
-
messageId— уникальный идентификатор для этого конкретного сообщения. Вы можете создать собственный идентификатор (например, GUID) или установитьnull, чтобы агент сгенерировал его автоматически. -
contextId— идентификатор беседы. Укажите собственный идентификатор, чтобы начать новую беседу или продолжить существующую, повторно выполнив предыдущийcontextId. Агент будет поддерживать журнал бесед для одного и того жеcontextId. Агент также создаст один для вас, если он не указан.
# 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"
}
}
Примечание. Замените {{baseAddress}} конечной точкой сервера.
Этот запрос возвращает следующий ответ JSON:
{
"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"
}
Ответ включает contextId (идентификатор беседы), messageId (идентификатор сообщения) и реальное содержимое пиратского агента.
Конфигурация AgentCard
Этот элемент AgentCard предоставляет метаданные агента для его обнаружения и интеграции.
app.MapA2A(agent, "/a2a/my-agent", agentCard: new()
{
Name = "My Agent",
Description = "A helpful agent that assists with tasks.",
Version = "1.0",
});
Вы можете получить доступ к карточке агента, отправив этот запрос:
# Send A2A request to the pirate agent
GET {{baseAddress}}/a2a/pirate/v1/card
Примечание. Замените {{baseAddress}} конечной точкой сервера.
Свойства AgentCard
- Имя: отображаемое имя агента
- Описание: краткое описание агента
- Версия: строка версии агента
- URL-адрес: URL-адрес конечной точки (автоматически назначается, если он не указан)
- Возможности: необязательные метаданные о потоковой передаче, push-уведомлениях и других функциях
Выявление нескольких агентов
Вы можете размещать несколько агентов в одном приложении, пока их конечные точки не конфликтуют. Приведем пример:
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");
Пакет agent-framework-a2a позволяет подключаться к внешним агентам, совместимым с A2A, и предоставлять агент Платформы агента через протокол A2A.
pip install agent-framework-a2a --pre
Подключение к агенту A2A
Упакуйте любую удаленную конечную точку A2A с помощью A2AAgent. Агент определяет возможности удаленного агента через AgentCard и обрабатывает все детали протокола.
import asyncio
import httpx
from a2a.client import A2ACardResolver
from agent_framework.a2a import A2AAgent
async def main():
a2a_host = "https://your-a2a-agent.example.com"
# 1. Discover the remote agent's capabilities
async with httpx.AsyncClient(timeout=60.0) as http_client:
resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_host)
agent_card = await resolver.get_agent_card()
print(f"Found agent: {agent_card.name}")
# 2. Create an A2AAgent and send a message
async with A2AAgent(
name=agent_card.name,
agent_card=agent_card,
url=a2a_host,
) as agent:
response = await agent.run("What are your capabilities?")
for message in response.messages:
print(message.text)
asyncio.run(main())
Ответы потоковые
A2A естественно поддерживает потоковую трансляцию через события, отправляемые сервером (Server-Sent Events) — обновления поступают в режиме реального времени по мере выполнения удалённым агентом задач.
async with A2AAgent(name="remote", url="https://a2a-agent.example.com") as agent:
stream = agent.run("Tell me about yourself", stream=True)
async for update in stream:
for content in update.contents:
if content.text:
print(content.text, end="", flush=True)
final = await stream.get_final_response()
print(f"\n({len(final.messages)} message(s))")
длительные задачи
По умолчанию A2AAgent ожидает завершения удаленного агента перед возвратом. Для длительно выполняющихся задач задайте background=True, чтобы получить токен продолжения, который можно использовать, чтобы позже выполнять опрос или подписаться:
async with A2AAgent(name="worker", url="https://a2a-agent.example.com") as agent:
# Start a long-running task
response = await agent.run("Process this large dataset", background=True)
if response.continuation_token:
# Poll for completion later
result = await agent.poll_task(response.continuation_token)
print(result)
Идентификатор беседы (context_id)
При вызове A2AAgent.run() с AgentSession, агент автоматически выводит A2A context_id из session.service_session_id, если исходящее сообщение еще не содержит его. Это позволяет поддерживать непрерывность бесед в нескольких вызовах A2A без настройки context_id вручную для каждого сообщения:
from agent_framework import AgentSession
from agent_framework.a2a import A2AAgent
async with A2AAgent(name="remote", url="https://a2a-agent.example.com") as agent:
session = AgentSession(service_session_id="my-conversation-1")
# context_id is automatically set to "my-conversation-1"
response = await agent.run("Hello!", session=session)
# Subsequent calls with the same session continue the conversation
response = await agent.run("Follow-up question", session=session)
Если в сообщении context_id есть явное значение additional_properties, это значение имеет приоритет над резервной заменой, производной от сеанса.
Authentication
Используйте AuthInterceptor для защищенных конечных точек A2A.
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!")
Размещение агента фреймворка через A2A
Модуль A2AExecutor адаптирует любой фреймворк агента Agent к серверному протоколу A2A. Его можно хостить с официальным a2a-sdk сервером Starlette/ASGI, чтобы прочие A2A-клиенты могли обнаруживать и обращаться к вашему агенту.
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),
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 передаёт обновления агента в потоковом режиме в виде артефактов A2A, когда базовый агент поддерживает потоковую передачу и передаёт A2A context_id в качестве session_id сеанса агента. Можно создать подкласс на основе A2AExecutor и переопределить метод handle_events, чтобы реализовать пользовательские преобразования из формата выходных данных агента в события протокола A2A.
Подсказка
Полный пример запуска см. в agent_framework_to_a2a.py примере .
Протокол A2A
Фреймворк Go Agent Framework поддерживает протокол Agent-to-Agent (A2A) как для размещения агентов Agent Framework, так и для использования удаленных агентов A2A. Интеграция Go использует пакет provider/a2aprovider как для адаптеров серверного хостинга, так и для клиентского доступа.
Установите пакеты Agent Framework и A2A в модуле Go:
go get github.com/microsoft/agent-framework-go
go get github.com/a2aproject/a2a-go/v2
Размещение агента через A2A
Создайте или используйте повторно агент в Agent Framework, опишите его с помощью карточки агента A2A и опубликуйте его через одну из транспортных привязок A2A. В этом примере hostAgent используется любая платформа *agent.Agentагента; сервер размещает конечную точку JSON-RPC и / обслуживает карточку агента по известному пути A2A.
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))
}
Оберните тот же обработчик запросов с помощью a2asrv.NewRESTHandler, если хотите предоставить привязку транспорта HTTP+JSON. Установите для ExecutorConfig.AllowBackgroundResponses значение true, если размещаемому агенту должно быть разрешено возвращать задачи A2A для длительно выполняемой работы.
Использование агента A2A
Определите карточку удалённого агента, создайте из неё клиент A2A и представьте этот клиент в виде стандартного агента Agent Framework:
import (
"context"
"github.com/a2aproject/a2a-go/v2/a2aclient"
"github.com/a2aproject/a2a-go/v2/a2aclient/agentcard"
"github.com/microsoft/agent-framework-go/agent"
"github.com/microsoft/agent-framework-go/provider/a2aprovider"
)
ctx := context.Background()
card, err := agentcard.DefaultResolver.Resolve(ctx, "http://localhost:5000")
if err != nil {
panic(err)
}
client, err := a2aclient.NewFromCard(ctx, card)
if err != nil {
panic(err)
}
a := a2aprovider.NewAgent(
client,
a2aprovider.AgentConfig{
Config: agent.Config{
Name: card.Name,
Description: card.Description,
},
},
)
resp, err := a.RunText(ctx, "Hello!").Collect()
Компонент a2aprovider сохраняет идентификаторы A2A context_id и идентификаторы задач в сеансе Agent Framework, чтобы последующие сообщения могли поддерживать непрерывность диалога.
Выбор протокола
Если удаленный агент объявляет несколько привязок транспорта, настройте предпочтительный транспорт при создании клиента A2A:
client, err := a2aclient.NewFromCard(
ctx,
card,
a2aclient.WithConfig(a2aclient.Config{
PreferredTransports: []a2a.TransportProtocol{a2a.TransportProtocolHTTPJSON},
}),
)
Используйте a2a.TransportProtocolJSONRPC , если вы хотите предпочесть вместо этого JSON-RPC.
Длительные задачи
Задачи A2A отображаются с помощью маркеров продолжения Agent Framework. Запустите выполнение с явно указанным сеансом и agent.AllowBackgroundResponses(true), затем выполняйте опрос, вызывая Run без новых сообщений и с токеном продолжения:
session, err := a.CreateSession(ctx)
if err != nil {
panic(err)
}
resp, err := a.RunText(
ctx,
"Process this large dataset.",
agent.WithSession(session),
agent.AllowBackgroundResponses(true),
).Collect()
if err != nil {
panic(err)
}
for resp.ContinuationToken != "" {
resp, err = a.Run(
ctx,
nil,
agent.WithSession(session),
agent.WithContinuationToken(resp.ContinuationToken),
).Collect()
if err != nil {
panic(err)
}
}
Для прерванных запусков потоковой передачи извлеките update.ContinuationToken из последнего полученного обновления и передайте его в последующий запуск потоковой передачи с agent.WithContinuationToken(token) и agent.Stream(true).
Использование удаленных агентов A2A в качестве инструментов
Вы также можете предоставить хост-агенту доступ к удалённым агентам A2A в виде инструментов-функций. Разрешите каждый удалённый агент, оберните его в a2aprovider.NewAgent, а затем преобразуйте в инструмент с помощью agenttool.New. В этом примере хост-агент использует развертывание модели на основе проекта Microsoft Foundry.
tools := make([]tool.Tool, 0, len(agentURLs))
for _, agentURL := range agentURLs {
card, err := agentcard.DefaultResolver.Resolve(ctx, agentURL)
if err != nil {
panic(err)
}
client, err := a2aclient.NewFromCard(ctx, card)
if err != nil {
panic(err)
}
remoteAgent := a2aprovider.NewAgent(client, a2aprovider.AgentConfig{
Config: agent.Config{
Name: card.Name,
Description: card.Description,
},
})
tools = append(tools, agenttool.New(remoteAgent, agenttool.Config{}))
}
host := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "Use your tools to delegate requests to specialized remote agents.",
Config: agent.Config{
Tools: tools,
},
})
Подсказка
Ознакомьтесь с примером клиентского сервера A2A, примером поставщика A2A и агентами A2A в качестве примеров средств для полных примеров запуска.