---
title: "AG2 Tracing"
description: Auto-instrument your AG2 multi-agent application for seamless observability
---

import RegisterTracerPython from "../../../../snippets/register-tracer-python.mdx";

[AG2](https://github.com/ag2ai/ag2) (formerly AutoGen) is an open-source Python framework for
building multi-agent LLM applications. It centers on the `ConversableAgent`, which agents use to
chat with one another, call tools, and coordinate through group chats and sequential
conversations.

Phoenix instruments AG2 through the `openinference-instrumentation-ag2` package. Calling
`AG2Instrumentor().instrument()` patches `ConversableAgent` and emits spans for chats, replies,
and tool executions, nesting them correctly through group chat orchestration.

<Note>
This instrumentor supports AG2 0.14, which is imported as `autogen`. AG2 1.0 uses a new
middleware architecture that is not covered yet.
</Note>

## Install

```bash
pip install openinference-instrumentation-ag2 openinference-instrumentation-openai "ag2[openai]" arize-phoenix-otel arize-phoenix
```

AG2 delegates its LLM calls to the underlying model client. Pair the AG2 instrumentor with the
instrumentor for that provider — `openinference-instrumentation-openai` in the examples below —
so the LLM spans appear nested under the agent spans. If your agents call a different provider,
install and register that provider's OpenInference instrumentor instead.

## Setup

Use the `register` function to connect your application to Phoenix. Because AG2 relies on a
separate model instrumentor for LLM visibility, keep `auto_instrument=True` so both the AG2 and
model instrumentors are activated from your installed dependencies.

<RegisterTracerPython projectName="ag2-tracing" />

## Run AG2

From here you can use AG2 as normal, and Phoenix will trace each agent chat, reply, and tool
call. The example below runs a single agent with the quickstart `run()` API:

```python
import os
from autogen import ConversableAgent, LLMConfig

llm_config = LLMConfig(
    {"api_type": "openai", "model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}
)

agent = ConversableAgent(
    name="helpful_agent",
    system_message="You are a helpful assistant.",
    llm_config=llm_config,
)

response = agent.run(message="What is the capital of France?", max_turns=1, user_input=False)
response.process()
```

## What gets traced

The instrumentor patches `ConversableAgent` and produces three span kinds:

| AG2 method | Span name | Span kind |
| --- | --- | --- |
| `initiate_chat` / `a_initiate_chat` (also used by `run` and `initiate_chats`) | `<agent>.initiate_chat` | `AGENT` |
| `generate_reply` / `a_generate_reply` | `<agent>.generate_reply` | `AGENT` |
| `execute_function` / `a_execute_function` | `<tool>` | `TOOL` |

Tool spans carry `tool.name`, `tool_call.id`, `tool_call.function.arguments`, and
`tool.parameters` with resolved parameter types. The instrumentor also supports suppressing
tracing, propagating context attributes (`using_session`, `using_user`, `using_attributes`), and
masking sensitive data with a `TraceConfig`.

## Examples

### Tool calling

An LLM-driven tool call, split across an agent that decides to call the tool and a user proxy
that executes it — the registration split AG2 uses throughout its tools guide.

```python expandable
import os
from typing import Annotated

from autogen import ConversableAgent, LLMConfig
from phoenix.otel import register

# auto_instrument activates the installed AG2 and OpenAI OpenInference instrumentors
register(project_name="ag2-tool-calling", auto_instrument=True)

llm_config = LLMConfig(
    {"api_type": "openai", "model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}
)

RATES = {("USD", "EUR"): 0.92, ("EUR", "USD"): 1.09, ("USD", "JPY"): 157.0}

assistant = ConversableAgent(
    name="assistant",
    system_message=(
        "You convert currencies using the provided tool. Once you have the answer, "
        "state it and reply TERMINATE."
    ),
    llm_config=llm_config,
)
user_proxy = ConversableAgent(
    name="user_proxy",
    human_input_mode="NEVER",
    is_termination_msg=lambda message: "TERMINATE" in (message.get("content") or ""),
)


@user_proxy.register_for_execution()
@assistant.register_for_llm(description="Convert an amount between two currencies.")
def get_exchange_rate(
    amount: Annotated[float, "The amount to convert"],
    base: Annotated[str, "The currency code to convert from, e.g. USD"],
    quote: Annotated[str, "The currency code to convert to, e.g. EUR"],
) -> str:
    rate = RATES.get((base.upper(), quote.upper()))
    if rate is None:
        return f"No exchange rate available for {base} to {quote}."
    return f"{amount} {base.upper()} is {amount * rate:.2f} {quote.upper()}."


user_proxy.initiate_chat(assistant, message="How much is 250 USD in EUR?", max_turns=4)
```

### Group chat

An `AutoPattern` group chat where a manager routes between specialist agents. The trace shows the
manager's speaker-selection decisions interleaved with each specialist's reply:

```
_User.initiate_chat [AGENT]
  chat_manager.generate_reply [AGENT]
    finance_bot.generate_reply [AGENT]
      ChatCompletion [LLM]
    checking_agent.initiate_chat [AGENT]
      speaker_selection_agent.generate_reply [AGENT]
        ChatCompletion [LLM]
    summary_bot.generate_reply [AGENT]
      ChatCompletion [LLM]
```

```python expandable
import os
from typing import Any

from autogen import ConversableAgent, LLMConfig
from autogen.agentchat import initiate_group_chat
from autogen.agentchat.group.patterns import AutoPattern
from phoenix.otel import register

# auto_instrument activates the installed AG2 and OpenAI OpenInference instrumentors
register(project_name="ag2-group-chat", auto_instrument=True)

llm_config = LLMConfig(
    {"api_type": "openai", "model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}
)

TRANSACTIONS = [
    "Transaction: $500 to Staples. Memo: Quarterly supplies.",
    "Transaction: $23,000 to CyberSins Ltd. Memo: Confidential.",
    "Transaction: $1,500 to Initech. Memo: Routine payment.",
]

FINANCE_SYSTEM_MESSAGE = """
You are a financial compliance assistant reviewing transactions.
Flag a transaction as suspicious when the amount is over $10,000 or the memo is vague.
Approve the rest. Review every transaction in one reply, then hand off to summary_bot.
"""

SUMMARY_SYSTEM_MESSAGE = """
You are a financial summary assistant. Summarize the reviewed transactions as a markdown
table with Vendor, Memo, Amount, and Status columns, followed by the approved and
rejected counts. End your reply with "==== SUMMARY GENERATED ====".
"""


def is_termination_msg(message: dict[str, Any]) -> bool:
    return "==== SUMMARY GENERATED ====" in (message.get("content") or "")


finance_bot = ConversableAgent(
    name="finance_bot", system_message=FINANCE_SYSTEM_MESSAGE, llm_config=llm_config
)
summary_bot = ConversableAgent(
    name="summary_bot", system_message=SUMMARY_SYSTEM_MESSAGE, llm_config=llm_config
)

pattern = AutoPattern(
    initial_agent=finance_bot,
    agents=[finance_bot, summary_bot],
    group_manager_args={"llm_config": llm_config, "is_termination_msg": is_termination_msg},
)

result, _, _ = initiate_group_chat(
    pattern=pattern,
    messages="Please review these transactions:\n" + "\n".join(TRANSACTIONS),
    max_rounds=6,
)
```

### Sequential chats

`initiate_chats` runs a queue of chats in order, passing each chat's summary into the next as
carryover. Each chat in the queue gets its own `AGENT` span, so the trace shows the whole
pipeline:

```python expandable
import os

from autogen import ConversableAgent, LLMConfig
from phoenix.otel import register

# auto_instrument activates the installed AG2 and OpenAI OpenInference instrumentors
register(project_name="ag2-sequential-chats", auto_instrument=True)

llm_config = LLMConfig(
    {"api_type": "openai", "model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}
)

researcher = ConversableAgent(
    name="researcher",
    system_message="List the key facts about the topic in three short bullets.",
    llm_config=llm_config,
)
writer = ConversableAgent(
    name="writer",
    system_message="Turn the research you are given into a two-sentence summary.",
    llm_config=llm_config,
)
editor = ConversableAgent(
    name="editor",
    system_message="Tighten the summary you are given into a single sentence.",
    llm_config=llm_config,
)
coordinator = ConversableAgent(name="coordinator", human_input_mode="NEVER")

# Each chat's summary is carried into the next chat in the queue.
results = coordinator.initiate_chats(
    [
        {
            "recipient": researcher,
            "message": "Research the benefits of tracing LLM applications.",
            "max_turns": 1,
            "summary_method": "last_msg",
        },
        {
            "recipient": writer,
            "message": "Write the summary.",
            "max_turns": 1,
            "summary_method": "last_msg",
        },
        {
            "recipient": editor,
            "message": "Edit it down.",
            "max_turns": 1,
            "summary_method": "last_msg",
        },
    ]
)
```

### Structured outputs

Passing a pydantic model as `response_format` on `LLMConfig` makes the agent reply with JSON
matching that schema. The agent span's output value is the serialized model, so the trace shows
exactly what downstream code will parse:

```python expandable
import json
import os

from autogen import ConversableAgent, LLMConfig
from phoenix.otel import register
from pydantic import BaseModel

# auto_instrument activates the installed AG2 and OpenAI OpenInference instrumentors
register(project_name="ag2-structured-output", auto_instrument=True)


class TransactionAuditEntry(BaseModel):
    vendor: str
    amount: float
    memo: str
    status: str
    reason: str


class AuditLogSummary(BaseModel):
    total_transactions: int
    approved_count: int
    rejected_count: int
    transactions: list[TransactionAuditEntry]


llm_config = LLMConfig(
    {"api_type": "openai", "model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]},
    response_format=AuditLogSummary,
)

TRANSACTIONS = """
Transaction: $500 to Staples. Memo: Quarterly supplies.
Transaction: $23,000 to CyberSins Ltd. Memo: Confidential.
Transaction: $1,500 to Initech. Memo: Routine payment.
"""

summary_bot = ConversableAgent(
    name="summary_bot",
    system_message=(
        "You are a financial summary assistant that generates audit logs. Reject "
        "transactions over $10,000 or with a vague memo, and approve the rest."
    ),
    llm_config=llm_config,
)

response = summary_bot.run(
    message=f"Produce the audit log for these transactions:\n{TRANSACTIONS}",
    max_turns=1,
    user_input=False,
)
response.process()

audit_log = AuditLogSummary.model_validate_json(response.messages[-1]["content"])
print(json.dumps(audit_log.model_dump(), indent=2))
```

## Observe

Now that you have tracing set up, all AG2 agent chats, replies, and tool calls are streamed to
Phoenix for observability and evaluation. Each `ConversableAgent` chat and reply appears as an
`AGENT` span, with tool executions nested underneath as `TOOL` spans.

<Frame caption="An AG2 trace in Phoenix">
  <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-docs-images/ag2-example-trace.png" />
</Frame>

## Migrating from `openinference-instrumentation-autogen`

`openinference-instrumentation-ag2` replaces `openinference-instrumentation-autogen`. The
`autogen` package is now a thin, deprecated compatibility facade that delegates to
`AG2Instrumentor`. Move to `openinference-instrumentation-ag2` and use `AG2Instrumentor` directly.

## Resources

* [OpenInference package](https://pypi.org/project/openinference-instrumentation-ag2/)

* [Example scripts](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-ag2/examples)
