エージェント間 (A2A) プロトコルを使用すると、さまざまなフレームワークとテクノロジで構築されたエージェント間の標準化された通信が可能になります。 このページでは、エージェント フレームワーク エージェントを A2A サーバーとして公開する方法について説明します。
リモート A2A エージェントを検出して呼び出すには、 A2A エージェント サービスを参照してください。
A2A とは
A2A は、次をサポートする標準化されたプロトコルです。
- エージェント カードを使用したエージェントの検出
- エージェント間のメッセージ ベースの通信
- タスクを介した実行時間の長いエージェント プロセス
- 異なるエージェント フレームワーク間のクロスプラットフォーム相互運用性
詳細については、 A2A プロトコルの仕様を参照してください。
Microsoft.Agents.AI.Hosting.A2A.AspNetCore ライブラリは、A2A プロトコルを介してエージェントを公開するための ASP.NET Core 統合を提供します。
NuGet パッケージ:
Example
この最小限の例は、A2A を介してエージェントを公開する方法を示しています。 このサンプルには、テストを簡略化するための OpenAPI と Swagger の依存関係が含まれています。
1. ASP.NET Core Web API プロジェクトを作成する
新しい ASP.NET Core Web API プロジェクトを作成するか、既存のプロジェクトを使用します。
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();
Warning
DefaultAzureCredential は開発には便利ですが、運用環境では慎重に考慮する必要があります。 運用環境では、待機時間の問題、意図しない資格情報のプローブ、フォールバック メカニズムによる潜在的なセキュリティ リスクを回避するために、特定の資格情報 ( ManagedIdentityCredential など) を使用することを検討してください。
エージェントのテスト
アプリケーションが実行されたら、次の .http ファイルまたは Swagger UI を使用して、A2A エージェントをテストできます。
入力形式は A2A 仕様に準拠しています。 次の値を指定できます。
-
messageId- この特定のメッセージの一意の識別子。 独自の ID (GUID など) を作成することも、nullに設定してエージェントが自動的に生成できるようにすることもできます。 -
contextId- 会話識別子。 独自の ID を指定して新しい会話を開始するか、以前の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 (指定されていない場合は自動的に割り当てられます)
- 機能: ストリーミング、プッシュ通知、およびその他の機能に関する省略可能なメタデータ
複数のエージェントを公開する
エンドポイントが競合しない限り、1 つのアプリケーションで複数のエージェントを公開できます。 次に例を示します。
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 プロトコル経由で Agent Framework エージェントを公開します。
pip install agent-framework-a2a --pre
セキュリティで保護されたエンドポイントをテストする
テスト クライアントで 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 経由での Agent Framework エージェントの公開
agent-framework-a2a パッケージは、任意の Agent Framework エージェントを A2A サーバー側プロトコルに適合させる、オピニオンされたA2AExecutorを提供します。 エージェントを実行し、サポートされている出力コンテンツを A2A イベントと成果物にマップし、公式の a2a-sdkを介してタスクの状態の更新を管理します。
アプリケーションは、エージェント カード、 DefaultRequestHandler、タスク ストア、ルートまたはアプリケーション ビルダー、認証、デプロイなど、周囲の A2A SDK サーバーをアセンブルします。
agent-framework-hosting-a2aでのアプリ所有のアダプターとスタンドアロン変換ヘルパーとの比較については、「セルフホスト 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, 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 は、基になるエージェントがストリーミングをサポートし、A2A context_id をエージェント セッションの session_idとして伝達する場合に、エージェントの更新を A2A 成果物としてストリーミングします。
A2AExecutorをサブクラス化し、handle_events メソッドをオーバーライドして、エージェントの出力形式から A2A プロトコル イベントへのカスタム変換を実装できます。
A2A プロトコル
Go Agent Framework では、 provider/a2aprovider パッケージと公式の A2A Go サーバー ハンドラーを使用して、エージェント間 (A2A) プロトコルを介したエージェント フレームワーク エージェントのホストをサポートしています。
Go モジュールにエージェント フレームワークと A2A パッケージをインストールします。
go get github.com/microsoft/agent-framework-go
go get github.com/a2aproject/a2a-go/v2
A2A 経由でエージェントをホストする
エージェント フレームワーク エージェントを作成または再利用し、A2A エージェント カードで説明し、A2A トランスポート バインディングのいずれかを介して公開します。 この例では、 hostAgent は任意の Agent Framework *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))
}
HTTP+ JSON トランスポート バインドを公開する場合は、同じ要求ハンドラーを a2asrv.NewRESTHandler でラップします。
ExecutorConfig.AllowBackgroundResponsesをtrueに設定します。長時間の作業のために、ホストされるエージェントが A2A タスクを返すように許可する必要がある場合は、この設定を行います。