NTT DATA TECH
🤖

A2A×LangGraphの公式サンプルを動かしてみた

に公開

はじめに

こんにちは!
MEKIKI X AIハッカソンもぐもぐ勉強会 Advent Calendar 2025の1日目を担当する鈴木です。
生成 AI/LLM の急速な発展とともに、AI エージェントという技術が非常に注目を浴びています。
それに伴って AI エージェントの標準化の動きが進んでおり、中でもMCPという AI エージェントとツールの通信を標準化するプロトコルは、聞いたこともある方が多いと思います。
その MCP を補完するような技術として、2025 年 4 月に Google が発表したのが、Agent2Agent (A2A) Protocolです。こちらはマルチエージェントを前提とした、エージェント同士の通信を標準化するプロトコルです。
今回はそんな A2A の公式サンプルを触ってみました。

サンプル概要

今回はこちらのサンプルを触っていきます。

アプリケーションのシーケンス図です。

またフォルダ構成は以下の通りです。

.
├── .vscode/
│   └── launch.json
├── app/
│   ├── __init__.py
│   ├── __main__.py
│   ├── agent.py
│   ├── agent_executor.py
│   └── test_client.py
├── .python-version
├── Containerfile
├── README.md
├── pyproject.toml
└── uv.lock

環境構築編

README.md の手順に沿って進めていきます。なお、README.md に記載の以下条件に加え、今回の記事では WSL 2 に Docker Engine をインストールしてあることを前提とします。

  • Python 3.12 or higher
  • uv
  • Access to an LLM and API Key

また Zscaler などのセキュリティソフトが入っている場合は証明書周りの設定も必要ですが、記事が煩雑になるのでここでは割愛します。

リポジトリのクローン

まずはローカルリポジトリに資材をクローンします。
対象となるリポジトリは/a2a-samples/tree/main/samples/python/agents/langgraphであり、このディレクトリのみをクローンしたいため、今回はsparse-checkoutを使用した以下の手順でクローンを行います。

  1. 履歴のみを取得
    $ git clone --filter=blob:none --no-checkout https://github.com/a2aproject/a2a-samples.git
    
  2. ディレクトリを移動
    $ cd a2a-samples
    
  3. sparse-checkoutを有効化
    $ git sparse-checkout init --cone
    
  4. checkout するディレクトリの選択
    $ git sparse-checkout set samples/python/agents/langgraph
    
  5. 実際に checkout
    $ git checkout
    
  6. 資材をクローンしたディレクトリに移動
    $ cd samples/python/agents/langgraph
    

これで関連する資材のみをクローンすることができます。

環境変数の設定

Containerfileと同階層に.envファイルを作成し、必要な情報を追加します。今回私は OpenAI の API キーを用いました。

  .
  ├── .vscode/
  │   └── launch.json
  ├── app/
  │   ├── __init__.py
  │   ├── __main__.py
  │   ├── agent.py
  │   ├── agent_executor.py
  │   └── test_client.py
  ├── .python-version
  ├── Containerfile
  ├── README.md
  ├── pyproject.toml
  ├── uv.lock
+ └── .env
.env
API_KEY=sk-proj-xxx
TOOL_LLM_URL=https://api.openai.com/v1/
TOOL_LLM_NAME=gpt-4-turbo
model_source=openai

コンテナの起動

docker build を行います。-f をつけて Docker に明示的に Containerfile というファイル名を指定します。

$ docker build -f Containerfile . -t langgraph-a2a-server

docker buildが完了したら、docker runしていきます。

$ docker run --rm -p 10000:10000 langgraph-a2a-server

test_client.py の実行

ではいよいよ test_client.py を実行してみましょう。なお、uvがインストールされていない場合はあらかじめインストールしておきましょう。

$ uv run app/test_client.py

実行したところ、エラーとなってしまいました。
どうやらサーバ側の処理が返ってくる前に、クライアントの httpx の規定タイムアウトに引っ掛かったようです。LLM に処理を投げるため、長めのタイムアウトを設けてあげる必要があります。

そこでtest_client.pyを以下のように書き換えます。

test_client.py
+ timeout = httpx.Timeout(connect=30.0, read=180.0, write=30.0, pool=30.0)
+ async with httpx.AsyncClient(timeout=timeout) as httpx_client:
- async with httpx.AsyncClient() as httpx_client:
    resolver = A2ACardResolver(
        ...

改めてtest_client.pyを実行すると、処理が正常に終了し、いい感じのログが出力されます。これで動かすという目標は達成なのですが、一つ気になる点がありました。

改良編

Agent Card の URL について

色々とコマンドを実行している中、ある結果が気になりました。

$ curl -i http://localhost:10000/.well-known/agent-card.json

HTTP/1.1 200 OK
date: Tue, 28 Oct 2025 08:34:32 GMT
server: uvicorn
content-length: 585
content-type: application/json

{
    "capabilities": {
        "pushNotifications": true,
        "streaming": true
    },
    "defaultInputModes": [
        "text",
        "text/plain"
    ],
    "defaultOutputModes": [
        "text",
        "text/plain"
    ],
    "description": "Helps with exchange rates for currencies",
    "name": "Currency Agent",
    "preferredTransport": "JSONRPC",
    "protocolVersion": "0.3.0",
    "skills": [
        {
            "description": "Helps with exchange values between various currencies",
            "examples": [
                "What is exchange rate between USD and GBP?"
            ],
            "id": "convert_currency",
            "name": "Currency Exchange Rates Tool",
            "tags": [
                "currency exchange"
            ]
        }
    ],
    "url": "http://0.0.0.0:10000/",
    "version": "1.0.0"
}

Curl コマンドによって参照しているのはAgent Cardと呼ばれるものです。公式の説明を見てみます。

https://a2a-protocol.org/latest/specification/#5-agent-discovery-the-agent-card

A2A Servers MUST make an Agent Card available. The Agent Card is a JSON document that describes the server's identity, capabilities, skills, service endpoint URL, and how clients should authenticate and interact with it. Clients use this information for discovering suitable agents and for configuring their interactions.

この通り、A2A サーバは必ずこの Agent Card というものを作成・公開する必要があり、その正体は ID、機能、スキル、サービスエンドポイント URL などが記述された JSON ドキュメントです。

さて、今回取得した Agent Card の url にはクライアントのアクセス先となる AI エージェントのサービスエンドポイント URL が表示されます。今回の場合では、http://0.0.0.0:10000/となっており、RFC 1122で仕様上送信先として用いないことと定められている0.0.0.0が宛先として表示されている状態です。

そこで__main__.pyの実装を確認してみると、サーバのバインド先(uvicorn の host)と Agent Card に書き込むサービスエンドポイント URL を同じ host で兼用してる実装になっています。

__main__.py
@click.command()
@click.option('--host', 'host', default='localhost')
@click.option('--port', 'port', default=10000)
def main(host, port):
    ...
    agent_card = AgentCard(
        name='Currency Agent',
        description='Helps with exchange rates for currencies',
        url=f'http://{host}:{port}/',
        version='1.0.0',
        default_input_modes=CurrencyAgent.SUPPORTED_CONTENT_TYPES,
        default_output_modes=CurrencyAgent.SUPPORTED_CONTENT_TYPES,
        capabilities=capabilities,
        skills=[skill],
    )
    ...
    uvicorn.run(server.build(), host=host, port=port)

ここでAgent Discovery in A2Aを参照してみましょう。Agent の Discovery の手法として以下 3 つが紹介されています。

  1. Well-Known URI

    • 使いどころ
      公開エージェントや、特定ドメイン内で広く自動発見させたい場合。
    • 仕組み
      A2A サーバーがドメイン配下の標準パスにエージェントカードを配置。
      標準パス:https://{agent-server-domain}/.well-known/agent-card.json(RFC 8615 の考え方に準拠)
    • 流れ
    • 利点
      実装が簡単/標準準拠/自動発見しやすい。
    • 留意点
      公開・ドメイン管理前提のシナリオに最適。機密情報を含む場合はエンドポイント側で認証が必要。
  2. キュレート型レジストリ(カタログベースの発見)

    • 使いどころ
      企業内やパブリックなマーケットプレイスで、中央管理したい場合。
    • 仕組み
      中間サービス(レジストリ)がエージェントカードを集約し、スキル/タグ/提供者名/機能などで検索可能。
    • 流れ
      • A2A サーバーが自分のエージェントカードをレジストリに公開。
      • クライアントはレジストリ API にクエリして条件(例:「特定スキル」)で検索。
      • 一致するエージェントカードや参照が返る。
    • 利点
      中央管理・ガバナンス/能力(スキル)ベースの検索/アクセス制御や信頼枠組みを適用しやすい/私設・公設どちらでも有効。
    • 留意点
      レジストリの運用・保守が必要。現行の A2A 仕様にはレジストリ API の標準規定がない。
  3. 直接設定/プライベート検出

    • 使いどころ
      密結合なシステム、非公開エージェント、開発・検証用途。
    • 仕組み
      クライアントがハードコード/設定ファイル/環境変数/独自 API などで発見情報を保持。
    • 流れ
      デプロイや運用ポリシーに依存(個別実装)。
    • 利点
      既知で固定的な関係では手っ取り早く確実。
    • 留意点
      動的発見には不向き。エージェントカードが変わるたびクライアント再設定が必要。独自 API も標準化はされていない。

サンプルの実装は1番を想定しているものの、Agent CardのURLとして0.0.0.0が入っているために、ややちぐはぐな印象を受けます。
現状のままでも動きはするのですが、せっかくなのでより実践的な形で、最も一般的なディスカバリ方法であると思われる 1 番を想定した実装を行い、正しく動作するか試してみましょう。

ソースコードの修正

修正すべき点は上で述べた通り、__main__.pyで、uvicorn のバインド先のホストと Agent Card の URL のホスト部分が同一のものとなっているという点です。

そこで現状の実装をベースに、問題点を解決するようにソースコードを修正してみます。今回は分かりやすくするため、Agent Card を取得するためのホスト名をdiscover.localhostとし、取得した Agent Card の URL に記載されているホスト名をa2a.localhostとします。またこれらのURLがループバックに解決されるように、設定をホストファイルに記述します。

127.0.0.1  a2a.localhost discovery.localhost
::1        a2a.localhost discovery.localhost

こうすることで、既存の実装を維持しつつ 2 つのホスト名を分離することができます。

__main__.pyの修正

まず、__main__.pyを修正します。Agent Card の url として設定するホスト名を、PUBLIC_BASE_URLという環境変数で設定するようにします。

__main__.py
def main(host, port):
    ...
+    public_base = os.getenv("PUBLIC_BASE_URL", f"http://{host}:{port}/")
    agent_card = AgentCard(
        ...
-        url=f'http://{host}:{port}/',
+        url=public_base,

また、.envファイルにPUBLIC_BASE_URLを追加します。

.env
PUBLIC_BASE_URL=http://a2a.localhost:10000/

test_client.pyの修正

次にtest_client.pyを修正します。Agent Card を取得するための URL であるbase_url に、discover.localhostを指定します。timeout設定は上記と同様です。

test_client.py
async def main() -> None:
    ...
+    base_url = "http://discovery.localhost:10000"
+    timeout = httpx.Timeout(connect=30.0, read=180.0, write=30.0, pool=30.0)
+    async with httpx.AsyncClient(timeout=timeout) as httpx_client:
-    async with httpx.AsyncClient() as httpx_client:

動作確認

再ビルドと再実行を行い、コンテナを再作成します。

$ docker build -f Containerfile . -t langgraph-a2a-server
$ docker run --rm -p 10000:10000 langgraph-a2a-server

まず、あらためて curl コマンドで Agent Card を確認してみます。

$ curl -i http://discovery.localhost:10000/.well-known/agent-card.json

HTTP/1.1 200 OK
date: Thu, 30 Oct 2025 11:02:15 GMT
server: uvicorn
content-length: 591
content-type: application/json

{
    "capabilities": {
        "pushNotifications": true,
        "streaming": true
    },
    "defaultInputModes": [
        "text",
        "text/plain"
    ],
    "defaultOutputModes": [
        "text",
        "text/plain"
    ],
    "description": "Helps with exchange rates for currencies",
    "name": "Currency Agent",
    "preferredTransport": "JSONRPC",
    "protocolVersion": "0.3.0",
    "skills": [
        {
            "description": "Helps with exchange values between various currencies",
            "examples": [
                "What is exchange rate between USD and GBP?"
            ],
            "id": "convert_currency",
            "name": "Currency Exchange Rates Tool",
            "tags": [
                "currency conversion",
                "currency exchange"
            ]
        }
    ],
    "url": "http://a2a.localhost:10000/",
    "version": "1.0.0"
}

URL が想定通り、http://a2a.localhost:10000/となっていることが確認できます。次に修正後のtest_client.pyを改めて実行し、ログを見ていきます。

ログ
INFO:__main__:Attempting to fetch public agent card from: http://discovery.localhost:10000/.well-known/agent-card.json
INFO:httpx:HTTP Request: GET http://discovery.localhost:10000/.well-known/agent-card.json "HTTP/1.1 200 OK"
INFO:a2a.client.card_resolver:Successfully fetched agent card data from http://discovery.localhost:10000/.well-known/agent-card.json: {'capabilities': {'pushNotifications': True, 'streaming': True}, 'defaultInputModes': ['text', 'text/plain'], 'defaultOutputModes': ['text', 'text/plain'], 'description': 'Helps with exchange rates for currencies', 'name': 'Currency Agent', 'preferredTransport': 'JSONRPC', 'protocolVersion': '0.3.0', 'skills': [{'description': 'Helps with exchange values between various currencies', 'examples': ['What is exchange rate between USD and GBP?'], 'id': 'convert_currency', 'name': 'Currency Exchange Rates Tool', 'tags': ['currency conversion', 'currency exchange']}], 'url': 'http://a2a.localhost:10000/', 'version': '1.0.0'}
INFO:__main__:Successfully fetched public agent card:
INFO:__main__:{
  "capabilities": {
    "pushNotifications": true,
    "streaming": true
  },
  "defaultInputModes": [
    "text",
    "text/plain"
  ],
  "defaultOutputModes": [
    "text",
    "text/plain"
  ],
  "description": "Helps with exchange rates for currencies",
  "name": "Currency Agent",
  "preferredTransport": "JSONRPC",
  "protocolVersion": "0.3.0",
  "skills": [
    {
      "description": "Helps with exchange values between various currencies",
      "examples": [
        "What is exchange rate between USD and GBP?"
      ],
      "id": "convert_currency",
      "name": "Currency Exchange Rates Tool",
      "tags": [
        "currency conversion",
        "currency exchange"
      ]
    }
  ],
  "url": "http://a2a.localhost:10000/",
  "version": "1.0.0"
}
INFO:__main__:
Using PUBLIC agent card for client initialization (default).
INFO:__main__:
Public card does not indicate support for an extended card. Using public card.
INFO:__main__:Service base URL resolved from AgentCard: http://a2a.localhost:10000
/home/suzukiyuzzzg/a2a-langgraph-sample-test/a2a-samples/samples/python/agents/langgraph/app/ex_test_client.py:121: DeprecationWarning: A2AClient is deprecated and will be removed in a future version. Use ClientFactory to create a client with a JSON-RPC transport.
  client = A2AClient(httpx_client=api_client, agent_card=final_agent_card_to_use)
INFO:__main__:A2AClient initialized with AgentCard-derived base URL.
INFO:httpx:HTTP Request: POST http://a2a.localhost:10000/ "HTTP/1.1 200 OK"
{'id': 'f04b9088-be6a-49c3-8fb9-cc55e4daaf47', 'jsonrpc': '2.0', 'result': {'artifacts': [{'artifactId': '20533a84-e752-48fb-b5d6-88c462d86743', 'name': 'conversion_result', 'parts': [{'kind': 'text', 'text': 'As of the latest available data, 1 USD is equivalent to 88.25 INR. Therefore, 10 USD would be approximately 882.5 INR.'}]}], 'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'history': [{'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'kind': 'message', 'messageId': '218776590a694e2cae8a5f716b9d16c8', 'parts': [{'kind': 'text', 'text': 'how much is 10 USD in INR?'}], 'role': 'user', 'taskId': 'e1f3c220-94d1-4362-b7c3-a097833cb339'}, {'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'kind': 'message', 'messageId': 'f11a537a-9202-4354-97a5-ee399cb723f1', 'parts': [{'kind': 'text', 'text': 'Looking up the exchange rates...'}], 'role': 'agent', 'taskId': 'e1f3c220-94d1-4362-b7c3-a097833cb339'}, {'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'kind': 'message', 'messageId': 'ffe4c4ec-e376-4c3a-9246-cfee04e5c86b', 'parts': [{'kind': 'text', 'text': 'Processing the exchange rates..'}], 'role': 'agent', 'taskId': 'e1f3c220-94d1-4362-b7c3-a097833cb339'}], 'id': 'e1f3c220-94d1-4362-b7c3-a097833cb339', 'kind': 'task', 'status': {'state': 'completed', 'timestamp': '2025-10-28T11:58:35.408314+00:00'}}}
INFO:httpx:HTTP Request: POST http://a2a.localhost:10000/ "HTTP/1.1 200 OK"
{'id': '69806ca4-30c1-4cd7-8746-6513ff18bf1a', 'jsonrpc': '2.0', 'result': {'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'history': [{'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': '5fb1a151f8ba4043bb830d4fa3a4b989', 'parts': [{'kind': 'text', 'text': 'How much is the exchange rate for 1 USD?'}], 'role': 'user', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}], 'id': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32', 'kind': 'task', 'status': {'message': {'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': 'e904a326-d9bb-4e9d-bc1e-48d0de7d7ad0', 'parts': [{'kind': 'text', 'text': 'Could you please specify the currency you want to convert USD to?'}], 'role': 'agent', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}, 'state': 'input-required', 'timestamp': '2025-10-28T11:58:37.724726+00:00'}}}
INFO:httpx:HTTP Request: POST http://a2a.localhost:10000/ "HTTP/1.1 200 OK"
{'id': '7f1b822b-618e-46bb-aeed-80ec19b8aa8f', 'jsonrpc': '2.0', 'result': {'artifacts': [{'artifactId': 'b3f4ecc1-0409-493a-b9a4-7af3c544f299', 'name': 'conversion_result', 'parts': [{'kind': 'text', 'text': 'The exchange rate for 1 USD to CAD is 1.3983 as of the latest available data.'}]}], 'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'history': [{'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': '5fb1a151f8ba4043bb830d4fa3a4b989', 'parts': [{'kind': 'text', 'text': 'How much is the exchange rate for 1 USD?'}], 'role': 'user', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}, {'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': 'e904a326-d9bb-4e9d-bc1e-48d0de7d7ad0', 'parts': [{'kind': 'text', 'text': 'Could you please specify the currency you want to convert USD to?'}], 'role': 'agent', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}, {'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': 'd7681b4ed20c416dac5e5c3c4928c02c', 'parts': [{'kind': 'text', 'text': 'CAD'}], 'role': 'user', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}, {'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': '5757e105-73de-4255-8c44-74f4b0d6e8e5', 'parts': [{'kind': 'text', 'text': 'Looking up the exchange rates...'}], 'role': 'agent', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}, {'contextId': '7ccae868-b9b8-47cb-ada2-9dd138303d41', 'kind': 'message', 'messageId': '4ec6ba06-4166-4cdb-8316-bb7dc0e93f8e', 'parts': [{'kind': 'text', 'text': 'Processing the exchange rates..'}], 'role': 'agent', 'taskId': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32'}], 'id': 'd090b17f-b5ac-4cc8-bfe8-f0bdfe796a32', 'kind': 'task', 'status': {'state': 'completed', 'timestamp': '2025-10-28T11:58:44.917248+00:00'}}}
INFO:httpx:HTTP Request: POST http://a2a.localhost:10000/ "HTTP/1.1 200 OK"
{'id': '8bafef6b-c7b0-4ea6-b46e-806574ce11da', 'jsonrpc': '2.0', 'result': {'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'history': [{'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'kind': 'message', 'messageId': '218776590a694e2cae8a5f716b9d16c8', 'parts': [{'kind': 'text', 'text': 'how much is 10 USD in INR?'}], 'role': 'user', 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}], 'id': '94cf0ea6-a53c-4d30-b411-25a4be4e3657', 'kind': 'task', 'status': {'state': 'submitted'}}}
{'id': '8bafef6b-c7b0-4ea6-b46e-806574ce11da', 'jsonrpc': '2.0', 'result': {'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'final': False, 'kind': 'status-update', 'status': {'message': {'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'kind': 'message', 'messageId': '818104a8-38c6-4afd-b80d-ee7df8bcb29f', 'parts': [{'kind': 'text', 'text': 'Looking up the exchange rates...'}], 'role': 'agent', 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}, 'state': 'working', 'timestamp': '2025-10-28T11:58:46.278761+00:00'}, 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}}
{'id': '8bafef6b-c7b0-4ea6-b46e-806574ce11da', 'jsonrpc': '2.0', 'result': {'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'final': False, 'kind': 'status-update', 'status': {'message': {'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'kind': 'message', 'messageId': '2ec4bebe-e160-4add-b55e-1d19b0bf7780', 'parts': [{'kind': 'text', 'text': 'Processing the exchange rates..'}], 'role': 'agent', 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}, 'state': 'working', 'timestamp': '2025-10-28T11:58:46.490695+00:00'}, 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}}
{'id': '8bafef6b-c7b0-4ea6-b46e-806574ce11da', 'jsonrpc': '2.0', 'result': {'artifact': {'artifactId': 'c61d37f6-ea95-47bb-9b04-9c0a9f323ecf', 'name': 'conversion_result', 'parts': [{'kind': 'text', 'text': 'As of the latest available data, 10 USD is equivalent to approximately 882.5 INR.'}]}, 'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'kind': 'artifact-update', 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}}
{'id': '8bafef6b-c7b0-4ea6-b46e-806574ce11da', 'jsonrpc': '2.0', 'result': {'contextId': 'efc746e4-5af3-4271-9fe0-703e836bed7e', 'final': True, 'kind': 'status-update', 'status': {'state': 'completed', 'timestamp': '2025-10-28T11:58:49.777504+00:00'}, 'taskId': '94cf0ea6-a53c-4d30-b411-25a4be4e3657'}}

無事に成功していそうですね!ではログを詳しく見ていきましょう。
まず以下のログから Agent Card の取得にhttp://discovery.localhost:10000をホスト先として取得していることが分かります。

INFO:__main__:Attempting to fetch public agent card from: http://discovery.localhost:10000/.well-known/agent-card.json
INFO:httpx:HTTP Request: GET http://discovery.localhost:10000/.well-known/agent-card.json "HTTP/1.1 200 OK"

また以下のログから、AI エージェントへのリクエストがhttp://a2a.localhost:10000/向けに投げられていることが分かります。

INFO:httpx:HTTP Request: POST http://a2a.localhost:10000/ "HTTP/1.1 200 OK"
{'id': 'f04b9088-be6a-49c3-8fb9-cc55e4daaf47', 'jsonrpc': '2.0', 'result': {'artifacts': [{'artifactId': '20533a84-e752-48fb-b5d6-88c462d86743', 'name': 'conversion_result', 'parts': [{'kind': 'text', 'text': 'As of the latest available data, 1 USD is equivalent to 88.25 INR. Therefore, 10 USD would be approximately 882.5 INR.'}]}], 'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'history': [{'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'kind': 'message', 'messageId': '218776590a694e2cae8a5f716b9d16c8', 'parts': [{'kind': 'text', 'text': 'how much is 10 USD in INR?'}], 'role': 'user', 'taskId': 'e1f3c220-94d1-4362-b7c3-a097833cb339'}, {'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'kind': 'message', 'messageId': 'f11a537a-9202-4354-97a5-ee399cb723f1', 'parts': [{'kind': 'text', 'text': 'Looking up the exchange rates...'}], 'role': 'agent', 'taskId': 'e1f3c220-94d1-4362-b7c3-a097833cb339'}, {'contextId': '2f1e844c-4dd4-4f15-847c-c0548ec53ab4', 'kind': 'message', 'messageId': 'ffe4c4ec-e376-4c3a-9246-cfee04e5c86b', 'parts': [{'kind': 'text', 'text': 'Processing the exchange rates..'}], 'role': 'agent', 'taskId': 'e1f3c220-94d1-4362-b7c3-a097833cb339'}], 'id': 'e1f3c220-94d1-4362-b7c3-a097833cb339', 'kind': 'task', 'status': {'state': 'completed', 'timestamp': '2025-10-28T11:58:35.408314+00:00'}}}

またさらに中身を確認すると、以下のような形できちんと質問に対する応答が返されていることも確認ができます。

  • 質問(user)
    「how much is 10 USD in INR?」
  • 進捗メッセージ(agent / history)
    「Looking up the exchange rates...」
    「Processing the exchange rates..」
  • 回答(agent / artifacts.conversion_result)
    「As of the latest available data, 1 USD is equivalent to 88.25 INR. Therefore, 10 USD would be approximately 882.5 INR.」

ということで、無事想定通り修正できたことが確認できました!

まとめ

今回は A2A×LangGraph の公式サンプルを動かしてみました。いろいろと試行する過程で、Agent Card や Discovery の仕組みなどについての理解が深められたと思います。

参考文献

GitHub
Agent Card
Agent Discovery in A2A

NTT DATA TECH
NTT DATA TECH
設定によりコメント欄が無効化されています