Files
agent-framework/python/samples/04-hosting/a2a/agent_definitions.py
T
Giles Odigwe ded32f3ff8 Python: Add A2A server sample (#4528)
* Python: Add A2A server sample and fix client streaming bug

Add a pure Python A2A server sample so testing the A2A client no longer
requires running the .NET server. The server uses the a2a-sdk's
A2AStarletteApplication with uvicorn and supports three agent types
(invoice, policy, logistics) backed by AzureOpenAIResponsesClient.

New files:
- a2a_server.py: Main server entry point with CLI args
- agent_executor.py: Bridges a2a-sdk AgentExecutor to Agent Framework
- agent_definitions.py: Agent and AgentCard factory definitions
- invoice_data.py: Mock invoice data and query tool functions
- a2a_server.http: REST Client requests for testing

Also fixes a streaming bug in agent_with_a2a.py where async with was
used on ResponseStream which does not support the async context manager
protocol. Changed to async for to match all other samples.

Closes #4045

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: handle CancelledError and fix end_date filtering

- Re-raise asyncio.CancelledError before the broad exception handler
  so cooperative cancellation is not swallowed.
- Make end_date filter inclusive of the full day by comparing with
  < end + timedelta(days=1) instead of <= midnight.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-10 00:00:49 +00:00

170 lines
5.5 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
"""Agent definitions and AgentCard factories for the A2A server sample.
Provides factory functions to create Agent Framework agents and A2A
AgentCards for the invoice, policy, and logistics agent types.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
from invoice_data import query_by_invoice_id, query_by_transaction_id, query_invoices
if TYPE_CHECKING:
from agent_framework import Agent
from agent_framework.azure import AzureOpenAIResponsesClient
# ---------------------------------------------------------------------------
# Agent instructions
# ---------------------------------------------------------------------------
INVOICE_INSTRUCTIONS = "You specialize in handling queries related to invoices."
POLICY_INSTRUCTIONS = """\
You specialize in handling queries related to policies and customer communications.
Always reply with exactly this text:
Policy: Short Shipment Dispute Handling Policy V2.1
Summary: "For short shipments reported by customers, first verify internal shipment records
(SAP) and physical logistics scan data (BigQuery). If discrepancy is confirmed and logistics data
shows fewer items packed than invoiced, issue a credit for the missing items. Document the
resolution in SAP CRM and notify the customer via email within 2 business days, referencing the
original invoice and the credit memo number. Use the 'Formal Credit Notification' email
template."
"""
LOGISTICS_INSTRUCTIONS = """\
You specialize in handling queries related to logistics.
Always reply with exactly:
Shipment number: SHPMT-SAP-001
Item: TSHIRT-RED-L
Quantity: 900
"""
# ---------------------------------------------------------------------------
# Agent factories
# ---------------------------------------------------------------------------
def create_invoice_agent(client: AzureOpenAIResponsesClient) -> Agent:
"""Create an invoice agent backed by the given client with query tools."""
return client.as_agent(
name="InvoiceAgent",
instructions=INVOICE_INSTRUCTIONS,
tools=[query_invoices, query_by_transaction_id, query_by_invoice_id],
)
def create_policy_agent(client: AzureOpenAIResponsesClient) -> Agent:
"""Create a policy agent backed by the given client."""
return client.as_agent(
name="PolicyAgent",
instructions=POLICY_INSTRUCTIONS,
)
def create_logistics_agent(client: AzureOpenAIResponsesClient) -> Agent:
"""Create a logistics agent backed by the given client."""
return client.as_agent(
name="LogisticsAgent",
instructions=LOGISTICS_INSTRUCTIONS,
)
# ---------------------------------------------------------------------------
# AgentCard factories
# ---------------------------------------------------------------------------
_CAPABILITIES = AgentCapabilities(streaming=True, push_notifications=False)
def get_invoice_agent_card(url: str) -> AgentCard:
"""Return an A2A AgentCard for the invoice agent."""
return AgentCard(
name="InvoiceAgent",
description="Handles requests relating to invoices.",
url=url,
version="1.0.0",
default_input_modes=["text"],
default_output_modes=["text"],
capabilities=_CAPABILITIES,
skills=[
AgentSkill(
id="id_invoice_agent",
name="InvoiceQuery",
description="Handles requests relating to invoices.",
tags=["invoice", "agent-framework"],
examples=["List the latest invoices for Contoso."],
),
],
)
def get_policy_agent_card(url: str) -> AgentCard:
"""Return an A2A AgentCard for the policy agent."""
return AgentCard(
name="PolicyAgent",
description="Handles requests relating to policies and customer communications.",
url=url,
version="1.0.0",
default_input_modes=["text"],
default_output_modes=["text"],
capabilities=_CAPABILITIES,
skills=[
AgentSkill(
id="id_policy_agent",
name="PolicyAgent",
description="Handles requests relating to policies and customer communications.",
tags=["policy", "agent-framework"],
examples=["What is the policy for short shipments?"],
),
],
)
def get_logistics_agent_card(url: str) -> AgentCard:
"""Return an A2A AgentCard for the logistics agent."""
return AgentCard(
name="LogisticsAgent",
description="Handles requests relating to logistics.",
url=url,
version="1.0.0",
default_input_modes=["text"],
default_output_modes=["text"],
capabilities=_CAPABILITIES,
skills=[
AgentSkill(
id="id_logistics_agent",
name="LogisticsQuery",
description="Handles requests relating to logistics.",
tags=["logistics", "agent-framework"],
examples=["What is the status for SHPMT-SAP-001"],
),
],
)
# ---------------------------------------------------------------------------
# Lookup helpers
# ---------------------------------------------------------------------------
AGENT_FACTORIES = {
"invoice": create_invoice_agent,
"policy": create_policy_agent,
"logistics": create_logistics_agent,
}
AGENT_CARD_FACTORIES = {
"invoice": get_invoice_agent_card,
"policy": get_policy_agent_card,
"logistics": get_logistics_agent_card,
}