Loading...
Loading...
Build realtime voice / live audio agents with AG2's `ag2.live` module. Wrap a prompt + provider config in `LiveAgent` and open a bidirectional voice session with `agent.run()`, pumping mic audio in and playing synthesized speech out. Covers the two realtime providers — Gemini Live (`GeminiRealTimeConfig`) and OpenAI Realtime (`OpenAIRealTimeConfig`) with audio/text output modalities, voices, and user-speech transcription; audio I/O over the local sound card (`SoundDeviceRecorder` / `SoundDevicePlayer`, both sounddevice-backed); one-shot speech-to-text (`OpenAITranscriber`, `OpenAITranslationTranscriber`) and its `.pipe(agent)` voice pipeline; text-to-speech (`OpenAITTSConfig`); and `TTSObserver`, which speaks a regular text `Agent`'s streamed tokens aloud. Use when the user wants a talking agent, a phone/voice assistant, live transcription, or to add TTS playback to a text agent.
npx skill4agent add ag2ai/ag2-skills ag2-liveAgentAgentag2-multimodal-inputAudioInputHardware / keys caveat. A real session needs (a) a provider API key and (b) a working microphone + speaker. Neither is available headless. Everything in this skill constructs without hardware; the parts that actually open a socket or audio device are marked [needs keys + audio] below.
# OpenAI Realtime + OpenAI TTS/STT
pip install "ag2[openai-realtime]"
# Gemini Live
pip install "ag2[gemini-realtime]"
# Local microphone / speaker I/O (SoundDeviceRecorder / SoundDevicePlayer)
pip install "sounddevice[numpy]"pip install "ag2[openai-realtime]" "sounddevice[numpy]"Required. Run the relevant install before delivering code. Without the matching extra, the public symbol resolves to a placeholder that raises(ImportError/openai) or the additional-dependency error (gemini) the moment you use it.sounddevice[numpy]
ag2.live| Symbol | Role |
|---|---|
| Wraps a prompt + realtime config; |
| Gemini Live realtime provider config |
| OpenAI Realtime provider config |
| Text → speech (PCM bytes) |
| One-shot speech → text (transcription) |
| One-shot speech → English text (translation) |
| Mic capture → |
| Plays |
| Observer that speaks a text |
LiveAgentConversationContextmic ──SoundDeviceRecorder──▶ RecordedAudioEvent ──▶ provider session (Gemini/OpenAI)
│
SynthesizedAudioEvent (assistant audio)
▼
SoundDevicePlayer ──▶ speakerTranscriptionChunkEventTranscriptionCompletedEventModelMessageChunkToolCallEventToolResultEventUsageEventagent.run()ConversationContextimport asyncio
from ag2.live import (
LiveAgent,
OpenAIRealTimeConfig,
SoundDevicePlayer,
SoundDeviceRecorder,
)
from ag2.live.openai import AudioOutput, InputConfig
agent = LiveAgent(
"voice_bot",
"You are a friendly realtime voice assistant. Keep replies short and conversational.",
config=OpenAIRealTimeConfig(
"gpt-realtime",
output=AudioOutput(voice="marin"),
# Transcribe the user's speech so we can see it too.
input=InputConfig(transcription={"model": "gpt-4o-mini-transcribe"}),
),
)
async def main() -> None:
# run() yields the shared ConversationContext.
async with agent.run() as ctx:
# Recorder and player bind to the SAME context so audio flows on one bus.
async with (
SoundDeviceRecorder(context=ctx), # mic -> RecordedAudioEvent
SoundDevicePlayer(context=ctx), # SynthesizedAudioEvent -> speaker
):
print("Talk to the agent. Ctrl-C to stop.")
await asyncio.Event().wait() # keep the session open
if __name__ == "__main__":
asyncio.run(main())OPENAI_API_KEYclient=AsyncOpenAI(...)GEMINI_API_KEYGOOGLE_API_KEYclient=from ag2.live import GeminiRealTimeConfig
from ag2.live.gemini import AudioOutput, InputConfig
agent = LiveAgent(
"voice_bot",
"You are a friendly realtime voice assistant.",
config=GeminiRealTimeConfig(
"gemini-2.5-flash-native-audio-preview-12-2025",
output=AudioOutput(voice="Kore"),
input=InputConfig(transcribe=True), # user-speech transcription
),
)Gemini ( | OpenAI ( | |
|---|---|---|
| Import the knob types from | | |
| Output modes | | |
Voices ( | | |
| Example models (first positional arg) | | |
| User-speech transcription | | |
| Turn detection / VAD | | |
| Audio I/O sample rate | in 16 kHz / out 24 kHz (fixed by API) | configurable via |
| Generation knobs | | |
| Escape hatch for raw provider config | | |
| Bring your own client | | |
LiteralAudioOutputlanguage_code=speed=format=TextOutput()ModelMessageChunkLiveAgentLiveAgent(...)Agenttools=hitl_hook=middleware=observers=dependencies=variables=plugins=ToolCallEventrun()config=prompt=tools=observers=hitl_hook=Note:only supports function tools over realtime — provider server-side tool types raiseLiveAgentin both backends.NotImplementedError
SoundDeviceRecorderSoundDevicePlayerasync withConversationContextcontext=__aenter__from ag2.live import SoundDeviceRecorder, SoundDevicePlayer
# Recorder: mic -> RecordedAudioEvent on the stream.
recorder = SoundDeviceRecorder(context=ctx, sample_rate=16000, channels=1) # block_size optional
# Player: subscribes to SynthesizedAudioEvent, writes PCM to the speaker (24 kHz mono).
player = SoundDevicePlayer(context=ctx)SoundDeviceRecorderrecord(duration)VoiceInputvoice = recorder.record(duration=4.0) # blocks 4s, returns VoiceInput [needs audio]OpenAITranscriberOpenAITranslationTranscriberVoiceInput.pipe(agent)VoicePipelineAgentfrom ag2 import Agent
from ag2.config import OpenAIConfig
from ag2.live import OpenAITranscriber, SoundDeviceRecorder
from ag2.live.stt import VoiceInput
agent = Agent("assistant", "You answer questions.", config=OpenAIConfig(model="gpt-4o-mini"))
pipeline = OpenAITranscriber("gpt-4o-transcribe").pipe(agent)
# Record a clip, transcribe it, and ask the agent — in one call. [needs keys + audio]
voice: VoiceInput = SoundDeviceRecorder().record(4.0)
reply = await pipeline.ask(voice)
print(await reply.content())
# Continue the conversation with another clip:
followup = await reply.ask(SoundDeviceRecorder().record(4.0))VoiceInput(content: bytes, frame_rate: int, channels: int)OpenAITranslationTranscriberOpenAITTSConfigTTSObserverOpenAITTSConfigfrom ag2.live import OpenAITTSConfig
tts = OpenAITTSConfig("gpt-4o-mini-tts", voice="alloy", speed=1.0)
pcm: bytes = await tts.synthesize("Hello there!") # [needs keys]TTSObserverAgentModelMessageChunkSynthesizedAudioEventSoundDevicePlayerfrom ag2 import Agent, MemoryStream
from ag2.config import OpenAIConfig
from ag2.context import ConversationContext
from ag2.live import OpenAITTSConfig, SoundDevicePlayer, TTSObserver
agent = Agent(
"narrator",
"You are a helpful assistant.",
config=OpenAIConfig(model="gpt-4o-mini"),
observers=[TTSObserver(OpenAITTSConfig("gpt-4o-mini-tts", voice="nova"))],
)
# Agent and Player must share ONE stream: agent.ask(stream=...) and
# SoundDevicePlayer(context=...) whose .stream is that same stream.
stream = MemoryStream()
ctx = ConversationContext(stream=stream)
async with SoundDevicePlayer(context=ctx): # plays the synthesized speech [needs audio]
reply = await agent.ask("Tell me a one-line joke.", stream=stream)
print(await reply.content())TTSObserver(config)CompositeObserverModelMessageawait LiveAgent.usage_report(ctx)UsageReportreferences/test_samples.pyGeminiRealTimeConfigOpenAIRealTimeConfigAudioOutputTextOutputInputConfig_build_session(instructions=...)OpenAITTSConfigOpenAITranscriberOpenAITranslationTranscriberLiveAgenttools=observers=SoundDeviceRecorderSoundDevicePlayerTTSObserverCompositeObserverOpenAITranscriber(...).pipe(agent)VoicePipelineVoiceInputagent.run()async with SoundDeviceRecorder(...)SoundDevicePlayer(...)__aenter__SoundDeviceRecorder.record(duration)OpenAITTSConfig.synthesize(...)OpenAITranscriber.transcribe(...)VoicePipeline.ask(...)pip install "ag2[openai-realtime]""ag2[gemini-realtime]""sounddevice[numpy]"numpySoundDeviceRecorderSoundDevicePlayersounddevice[numpy]ImportErrorAudioOutputTextOutputInputConfigag2.live.geminiag2.live.openaiag2.liverun()ctxagent.run()SoundDeviceRecorder(context=ctx)SoundDevicePlayer(context=ctx)NotImplementedErroragent.run()await asyncio.Event().wait()ag2/live/{realtime.py,gemini.py,openai.py,protocols.py,observer.py,stt.py,sound_device.py}realtime.pyLiveAgentRealtimeConfigag2-multimodal-inputAudioInputag2-observers-and-alerts