> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AG2

> Build agents in AG2 that communicate with StackOne's A2A agents using A2aRemoteAgent.

## Overview

[AG2](https://docs.ag2.ai/) (formerly AutoGen) is an open-source framework for building AI agents. AG2 has built-in support for the A2A protocol through `A2aRemoteAgent`, allowing your agents to communicate with remote A2A agents like StackOne's.

Use AG2 to build agents that:

* Consume StackOne's A2A agents as remote agents
* Orchestrate multi-agent conversations with local and remote agents
* Access StackOne platform actions without managing tool definitions

<Warning>
  `A2aRemoteAgent` supports only asynchronous methods - this is a limitation of the A2A client.
</Warning>

## Installation

Install AG2 with A2A support using `uv`. Include your model provider's extra (OpenAI here), and pin the `a2a-sdk` 0.3 line:

```bash theme={null}
uv add "ag2[a2a,openai]" "a2a-sdk[http-server]>=0.3,<0.4"
```

<Note>
  Pin `a2a-sdk` to the `0.3` line: StackOne's A2A server implements A2A 0.3, and the current `ag2[a2a]` otherwise resolves `a2a-sdk` 1.x (A2A v1.0), which is not compatible yet, so pinning `0.3` selects a compatible AG2 build. Also pass `max_turns` to `a_initiate_chat` (below) to bound the conversation, as each turn runs the remote StackOne agent end to end.
</Note>

## Quick Start

This example creates a local agent that delegates HR tasks to a StackOne A2A agent.

```python theme={null}
import asyncio
import base64
from autogen import ConversableAgent, LLMConfig
from autogen.a2a import A2aRemoteAgent, HttpxClientFactory

# StackOne A2A configuration
STACKONE_API_KEY = "<stackone_api_key>"
STACKONE_ACCOUNT_ID = "<account_id>"
BASE64_API_KEY = base64.b64encode(f"{STACKONE_API_KEY}:".encode()).decode()

# Create HTTP client with StackOne authentication headers
http_client = HttpxClientFactory(
    headers={
        "Authorization": f"Basic {BASE64_API_KEY}",
        "x-account-id": STACKONE_ACCOUNT_ID
    },
    timeout=120.0,  # StackOne agents run an LLM per call; the 5s default is too short
)

# Create a remote A2A agent pointing to StackOne
stackone_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="stackone_hr_agent",
    client=http_client
)

# Create a local agent to orchestrate conversations
llm_config = LLMConfig({"model": "gpt-4o-mini"})

orchestrator = ConversableAgent(
    name="hr_assistant",
    system_message="""
        You are an HR assistant that helps with employee management tasks.
        Delegate HR operations to the stackone_hr_agent.

        When the user asks about employees, time off, or other HR data,
        use the stackone_hr_agent to fetch or update the information.
    """,
    llm_config=llm_config,
)

# Start a conversation
async def main():
    await orchestrator.a_initiate_chat(
        recipient=stackone_agent,
        message={"role": "user", "content": "List the first 5 employees"},
        max_turns=1,
    )

asyncio.run(main())
```

See [Authentication Guide](/a2a/auth-security) for details on obtaining your API key and account ID.

## Architecture

AG2's `A2aRemoteAgent` allows your local agent to communicate with remote A2A agents:

```mermaid theme={null}
flowchart LR
    User[User] --> LocalAgent[Local Agent<br/>AG2]
    LocalAgent -->|A2A Protocol| StackOneA2A[StackOne A2A Agent]
    StackOneA2A --> Providers[(Connected SaaS<br/>HiBob, Workday, etc.)]
    LocalAgent --> LocalTools[Local Tools<br/>Optional]

    style StackOneA2A fill:#10b981,stroke:#059669,color:#fff
```

## Complete Example

Here's a complete example with a local tool and a remote StackOne agent:

```python theme={null}
import asyncio
import base64
from datetime import datetime
from autogen import ConversableAgent, LLMConfig
from autogen.a2a import A2aRemoteAgent, HttpxClientFactory

# Configuration
STACKONE_API_KEY = "<stackone_api_key>"
STACKONE_ACCOUNT_ID = "<account_id>"
BASE64_API_KEY = base64.b64encode(f"{STACKONE_API_KEY}:".encode()).decode()

# Local tool example
def get_current_date() -> str:
    """Get the current date."""
    return datetime.now().strftime("%Y-%m-%d")

# Create HTTP client with StackOne authentication headers
http_client = HttpxClientFactory(
    headers={
        "Authorization": f"Basic {BASE64_API_KEY}",
        "x-account-id": STACKONE_ACCOUNT_ID
    },
    timeout=120.0,  # StackOne agents run an LLM per call; the 5s default is too short
)

# Remote StackOne A2A agent
stackone_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="stackone_hr_agent",
    client=http_client
)

# Configure local agent with LLM
llm_config = LLMConfig({"model": "gpt-4o-mini"})

# Root orchestrator agent
orchestrator = ConversableAgent(
    name="hr_assistant",
    system_message="""
        You are an HR assistant that helps with employee management tasks.

        You have access to:
        1. get_current_date - Get today's date
        2. stackone_hr_agent - Delegate HR operations (employees, time off, etc.)

        When users ask about HR data, delegate to stackone_hr_agent.
        Always confirm actions before making changes.
    """,
    llm_config=llm_config,
)

# Register local tool
orchestrator.register_for_llm(name="get_current_date", description="Get the current date")(get_current_date)

async def main():
    # Start conversation with StackOne agent
    await orchestrator.a_initiate_chat(
        recipient=stackone_agent,
        message={"role": "user", "content": "List all employees"},
        max_turns=1,
    )

    # Print the conversation history
    messages = orchestrator.chat_messages[stackone_agent.name]
    for message in messages:
        print(f"{message['name']}: {message['content']}")

asyncio.run(main())
```

## Creating from AgentCard

If you already have an `AgentCard` (e.g., fetched from a discovery service), create an `A2aRemoteAgent` directly from it:

```python theme={null}
from a2a.types import AgentCard, AgentCapabilities
from autogen.a2a import A2aRemoteAgent

# Create or fetch an AgentCard
card = AgentCard(
    name="stackone_hr_agent",
    url="https://a2a.stackone.com",
    description="Agent that handles HR operations via StackOne integrations",
    version="0.1.0",
    default_input_modes=["text"],
    default_output_modes=["text"],
    capabilities=AgentCapabilities(streaming=True),
    skills=[],
    supports_authenticated_extended_card=False,
)

# Create the remote agent from the card
stackone_agent = A2aRemoteAgent.from_card(card)
```

## Human in the Loop Support

`A2aRemoteAgent` automatically handles Human in the Loop (HITL) interactions when the remote agent requests human input. This happens transparently - the client loops until the agent completes its task or the conversation terminates.

```python theme={null}
from autogen.a2a import A2aRemoteAgent

# Connect to a remote agent that may request human input
stackone_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="approval_agent",
    client=http_client
)

# The client automatically handles any human input requests
await orchestrator.a_initiate_chat(
    recipient=stackone_agent,
    message={"role": "user", "content": "Should I approve this budget proposal?"}
)
# If the remote agent requests input, the user will be prompted automatically
```

## Multiple StackOne Accounts

Connect to multiple StackOne accounts by creating separate HTTP clients and `A2aRemoteAgent` instances:

```python theme={null}
import base64
from autogen import ConversableAgent, LLMConfig
from autogen.a2a import A2aRemoteAgent, HttpxClientFactory

STACKONE_API_KEY = "<stackone_api_key>"
BASE64_API_KEY = base64.b64encode(f"{STACKONE_API_KEY}:".encode()).decode()

# HTTP client for HiBob account
hibob_http_client = HttpxClientFactory(
    headers={
        "Authorization": f"Basic {BASE64_API_KEY}",
        "x-account-id": "<hibob_account_id>"
    },
    timeout=120.0,
)

# HTTP client for BambooHR account
bamboo_http_client = HttpxClientFactory(
    headers={
        "Authorization": f"Basic {BASE64_API_KEY}",
        "x-account-id": "<bamboohr_account_id>"
    },
    timeout=120.0,
)

# Agent for HiBob integration
hibob_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="hibob_agent",
    client=hibob_http_client
)

# Agent for BambooHR integration
bamboo_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="bamboo_agent",
    client=bamboo_http_client
)

# Orchestrator with access to both agents
llm_config = LLMConfig({"model": "gpt-4o-mini"})

orchestrator = ConversableAgent(
    name="multi_hr_assistant",
    system_message="""
        You have access to multiple HR systems:
        - hibob_agent: For HiBob HR operations
        - bamboo_agent: For BambooHR operations

        Route requests to the appropriate system based on user context.
    """,
    llm_config=llm_config,
)
```

## Advanced Configuration

### Custom HTTP Client

Set custom options on the HTTP client such as headers and timeout:

```python theme={null}
from autogen.a2a import A2aRemoteAgent, HttpxClientFactory

# Create custom HTTP client factory
http_client = HttpxClientFactory(
    timeout=120.0,  # StackOne agents run an LLM per call; the 5s default is too short
    headers={
        "User-Agent": "MyApp/1.0",
        "Authorization": f"Basic {BASE64_API_KEY}",
        "x-account-id": STACKONE_ACCOUNT_ID
    }
)

stackone_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="stackone_hr_agent",
    client=http_client
)
```

### Client Configuration

Pass additional client configuration using `ClientConfig`:

```python theme={null}
from a2a.client import ClientConfig
from autogen.a2a import A2aRemoteAgent

stackone_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="stackone_hr_agent",
    client=http_client,
    client_config=ClientConfig(streaming=True),
)
```

### Testing with MockClient

Mock remote agent replies for testing:

```python theme={null}
from autogen.a2a import A2aRemoteAgent, MockClient

stackone_agent = A2aRemoteAgent(
    url="https://a2a.stackone.com",
    name="stackone_hr_agent",
    client=MockClient(response_message="Mock response for testing"),
)
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use Async Methods" icon="bolt">
    `A2aRemoteAgent` only supports asynchronous methods. Always use `a_initiate_chat` and other async methods:

    ```python theme={null}
    # Correct: Use async methods
    await orchestrator.a_initiate_chat(
        recipient=stackone_agent,
        message={"role": "user", "content": "List employees"}
    )

    # Incorrect: Sync methods won't work
    # orchestrator.initiate_chat(...)  # This will fail
    ```
  </Accordion>

  <Accordion title="Clear System Messages" icon="file-lines">
    Provide detailed system messages for your orchestrator agent. Include clear instructions about when to delegate to remote agents:

    ```python theme={null}
    orchestrator = ConversableAgent(
        name="hr_assistant",
        system_message="""
            You are an HR assistant. When users ask about:
            - Employee data -> delegate to stackone_hr_agent
            - Time off requests -> delegate to stackone_hr_agent
            - Local operations -> use local tools

            Always confirm before making changes to HR systems.
        """,
        llm_config=llm_config,
    )
    ```
  </Accordion>

  <Accordion title="Error Handling" icon="triangle-exclamation">
    Wrap A2A calls in try-except blocks to handle network or protocol errors:

    ```python theme={null}
    async def safe_hr_query(message: str):
        try:
            await orchestrator.a_initiate_chat(
                recipient=stackone_agent,
                message={"role": "user", "content": message}
            )
        except Exception as e:
            print(f"Error communicating with HR agent: {e}")
            # Handle fallback logic
    ```
  </Accordion>

  <Accordion title="Message Format" icon="message">
    Always provide messages in the correct format with `role` and `content`:

    ```python theme={null}
    # Correct format
    await orchestrator.a_initiate_chat(
        recipient=stackone_agent,
        message={"role": "user", "content": "List employees"}
    )

    # Also valid: just content string
    await orchestrator.a_initiate_chat(
        recipient=stackone_agent,
        message="List employees"
    )
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="A2A SDK" icon="code" href="/a2a/a2a-sdk">
    Build custom A2A tools with the Python or JavaScript SDK
  </Card>

  <Card title="Authentication" icon="shield" href="/a2a/auth-security">
    Learn about authentication and security
  </Card>

  <Card title="Quickstart" icon="rocket" href="/a2a/quickstart">
    Learn A2A basics with cURL
  </Card>
</CardGroup>
