mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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>
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# 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,
|
||||
}
|
||||
Reference in New Issue
Block a user