# LiveAgent

`LiveAgent` is a full-duplex voice agent backed by a provider's realtime API. Unlike the [turn-by-turn STT/TTS pipeline](https://docs.ag2.ai/latest/docs/beta/live/stt_tts/), it opens a single bidirectional session for the entire conversation — audio flows in and out continuously, with built-in voice activity detection and barge-in.

## Quick start

A `LiveAgent` holds a `RealtimeConfig` and is opened via `agent.run()`, which yields a `ConversationContext`. Peers (player, recorder, observers) share that context so they all read from and write to the same event stream.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>``` | ```<br>import asyncio<br>from autogen.beta.live import (<br>    LiveAgent,<br>    SoundDevicePlayer,<br>    SoundDeviceRecorder,<br>    openai,<br>)<br>agent = LiveAgent(<br>    name="assistant",<br>    prompt="You are a helpful voice assistant.",<br>    config=openai.RealTimeConfig(<br>        "gpt-realtime-2",<br>        output=openai.AudioOutput(voice="ballad", speed=1.2),<br>    ),<br>)<br>async def main() -> None:<br>    async with (<br>        agent.run() as context,<br>        SoundDevicePlayer(context=context),<br>        SoundDeviceRecorder(context=context),<br>    ):<br>        print("Starting...")<br>        await asyncio.Future()  # run until cancelled<br>if __name__ == "__main__":<br>    asyncio.run(main())<br>``` |

Note

The three context managers must share the **same**`context` so the recorder's `RecordedAudioEvent`s reach the provider session and the provider's `SynthesizedAudioEvent`s reach the player.

## Watching the transcript

The realtime provider streams both audio and a text transcript. Subscribe to `ModelMessageChunk` to receive the assistant's transcript token-by-token.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>``` | ```<br>import asyncio<br>from autogen.beta.events import ModelMessageChunk<br>from autogen.beta.live import (<br>    LiveAgent,<br>    OpenAIRealTimeConfig,<br>    SoundDevicePlayer,<br>    SoundDeviceRecorder,<br>)<br>agent = LiveAgent(<br>    name="assistant",<br>    prompt="You are a helpful voice assistant.",<br>    config=OpenAIRealTimeConfig("gpt-realtime-2"),<br>)<br>async def main() -> None:<br>    async with (<br>        agent.run() as context,<br>        SoundDevicePlayer(context=context),<br>        SoundDeviceRecorder(context=context),<br>    ):<br>        print("Starting...")<br>        with context.stream.where(ModelMessageChunk).join() as events:<br>            async for event in events:<br>                print(event)<br>if __name__ == "__main__":<br>    asyncio.run(main())<br>``` |

Tip

`stream.where(EventType).join()` gives you an async iterator that yields filtered events. It's the idiomatic way to consume a single event type from the live session without writing a subscriber.

## Text-only output

To keep the realtime session for its low-latency turn detection but disable audio output entirely, swap `AudioOutput` for `TextOutput`. The model returns raw text via `ModelMessageChunk` and never produces synthesized audio.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>``` | ```<br>import asyncio<br>from autogen.beta.events import ModelMessageChunk<br>from autogen.beta.live import (<br>    LiveAgent,<br>    SoundDeviceRecorder,<br>    openai,<br>)<br>agent = LiveAgent(<br>    name="assistant",<br>    prompt="You are a helpful voice assistant.",<br>    config=openai.RealTimeConfig(<br>        "gpt-realtime-2",<br>        output=openai.TextOutput(),<br>    ),<br>)<br>async def main() -> None:<br>    async with (<br>        agent.run() as context,<br>        SoundDeviceRecorder(context=context),<br>    ):<br>        print("Starting...")<br>        with context.stream.where(ModelMessageChunk).join() as events:<br>            async for event in events:<br>                print(event)<br>if __name__ == "__main__":<br>    asyncio.run(main())<br>``` |

## Tools in a realtime session

`LiveAgent` supports the same `@agent.tool` decorator as a regular `Agent`. Tool calls are routed through AG2's normal tool executor, and results are sent back to the provider's realtime session automatically.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>``` | ```<br>import asyncio<br>from autogen.beta.live import (<br>    LiveAgent,<br>    OpenAIRealTimeConfig,<br>    SoundDevicePlayer,<br>    SoundDeviceRecorder,<br>)<br>agent = LiveAgent(<br>    name="assistant",<br>    prompt="You are a helpful voice assistant.",<br>    config=OpenAIRealTimeConfig("gpt-realtime-2"),<br>)<br>@agent.tool<br>async def sum_numbers(a: int, b: int) -> int:<br>    """You can use this tool to sum two numbers."""<br>    print(f"Summing {a} and {b}")<br>    return a + b<br>async def main() -> None:<br>    async with (<br>        agent.run() as context,<br>        SoundDevicePlayer(context=context),<br>        SoundDeviceRecorder(context=context),<br>    ):<br>        print("Starting...")<br>        await asyncio.Future()<br>if __name__ == "__main__":<br>    asyncio.run(main())<br>``` |

## Providers

`LiveAgent` is provider-neutral — it accepts any `RealtimeConfig`. AG2 Beta ships with two implementations.

[OpenAI](https://docs.ag2.ai/latest/docs/beta/live/live_agent/#__tabbed_1_1)[Gemini](https://docs.ag2.ai/latest/docs/beta/live/live_agent/#__tabbed_1_2)

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>``` | ```<br>from autogen.beta.live import openai<br>config = openai.RealTimeConfig(<br>    "gpt-realtime-2",<br>    output=openai.AudioOutput(voice="ballad", speed=1.2),<br>    input=openai.InputConfig(<br>        # semantic VAD with interruption is the default<br>        turn_detection={<br>            "type": "semantic_vad",<br>            "create_response": True,<br>            "interrupt_response": True,<br>        },<br>    ),<br>)<br>``` |

Available voices: `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, `cedar`.

|     |     |
| --- | --- |
| ```<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>``` | ```<br>from autogen.beta.live import gemini<br>config = gemini.RealTimeConfig(<br>    "gemini-3.1-flash-live-preview",<br>    output=gemini.AudioOutput(voice="Puck", language_code="en-US"),<br>    input=gemini.InputConfig(transcribe=True),<br>)<br>``` |

Available voices: `Aoede`, `Charon`, `Fenrir`, `Kore`, `Leda`, `Orus`, `Puck`, `Zephyr`.

Warning

Gemini Live's audio I/O is fixed by the API: **16 kHz mono PCM** input, **24 kHz mono PCM** output. Configure the recorder accordingly:

```
SoundDeviceRecorder(context=context, sample_rate=16000)
```

**Full Gemini example with a tool**

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>``` | ```<br>import asyncio<br>from autogen.beta.events import ModelMessageChunk, TranscriptionChunkEvent<br>from autogen.beta.live import (<br>    LiveAgent,<br>    SoundDevicePlayer,<br>    SoundDeviceRecorder,<br>    gemini,<br>)<br>agent = LiveAgent(<br>    name="assistant",<br>    prompt="You are a helpful voice assistant. Always respond in English.",<br>    config=gemini.RealTimeConfig(<br>        "gemini-3.1-flash-live-preview",<br>        output=gemini.AudioOutput(voice="Puck", language_code="en-US"),<br>        input=gemini.InputConfig(transcribe=True),<br>    ),<br>)<br>async def main() -> None:<br>    async with (<br>        agent.run() as context,<br>        SoundDevicePlayer(context=context),<br>        # Gemini Live requires 16 kHz mono PCM input<br>        SoundDeviceRecorder(context=context, sample_rate=16000),<br>    ):<br>        print("Starting...")<br>        with context.stream.where(ModelMessageChunk | TranscriptionChunkEvent).join() as events:<br>            async for event in events:<br>                print(event)<br>if __name__ == "__main__":<br>    asyncio.run(main())<br>``` |

## LiveAgent vs Agent

`LiveAgent` mirrors `Agent`'s constructor surface — `name`, `prompt`, `tools`, `middleware`, `observers`, `dependencies`, `variables`, `plugins`, `hitl_hook` — so most agent-level concepts carry over. The differences:

| Feature | `Agent` | `LiveAgent` |
| --- | --- | --- |
| Entry point | `await agent.ask(input)` | `async with agent.run() as context` |
| History | Returned via `AgentReply` | Lives on the session's stream |
| Turn detection | Application-driven (you call `ask`) | Provider-driven (VAD) |
| Structured output | Supported | Not supported |
| `tasks` / `run_subtask` | Supported | Not supported |

If you need both — for example, a realtime voice front-end that hands off to a tasking agent — drive the handoff through a tool on the `LiveAgent` that delegates to a separate `Agent` using `Agent.as_tool()`.

## What's next

- **[STT & TTS](https://docs.ag2.ai/latest/docs/beta/live/stt_tts/)** — the lower-latency turn-by-turn alternative.
- **[Tools](https://docs.ag2.ai/latest/docs/beta/tools/tools/)** — tool authoring, middleware, and approval flows that all work inside a `LiveAgent`.
