Travel Booking Multi-Agent Assistant (LiveKit ADK)
An enterprise-grade multi-agent voice orchestrator system built on Google's Agent Development Kit (ADK) and LiveKit. This application establishes a low-latency WebRTC-to-Gemini-Live bridge allowing users to naturally converse with specialized travel agents to search, book, and manage flights and hotels in a single session.
1. System Architecture & Flow
The system coordinates high-performance WebRTC audio streams, translates them into real-time bidirectional ADK session events, and communicates with Gemini Live API models.
┌───────────────────────────────────────┐
│ Client Browser │
└───────────────────────────────────────┘
▲ │
WebRTC │ (Render Audio / │ (User Audio /
DataChannel │ Data Transcription) │ WebRTC Media)
│ ▼
┌──────────────────────┼───────────────────────────────────────────────────────┐
│ │ LiveKit Server │
│ │ │
│ └──────────────┬───────────────▲────────────────────────┘
│ │ │
│ Audio Frames │ │ Audio Frames
│ (Gemini -> LK) │ │ (LK -> Gemini)
│ ▼ │
│ ┌────────────────────────────────────────┴────────────┐ │
│ │ FastAPI Application Server │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ app/livekit_bridge.py │ │ │
│ │ │ - Converts 48kHz LK Audio -> 16kHz PCM │ │ │
│ │ │ - Feeds LiveRequestQueue │ │ │
│ │ └──────────────────────┬──────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ LiveRequestQueue │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ ADK Runner │ │ │
│ │ └──────────┬──────────┘ │ │
│ └──────────────────────────┼──────────────────────────┘ │
└───────────────────────────────────────┼──────────────────────────────────────┘
│
│ Bidirectional WebSockets (v1alpha)
▼
┌───────────────────────────────────────┐
│ Gemini Live API │
│ (gemini-live-2.5-flash) │
└───────────────────────────────────────┘
Protocol Lifecycle:
- Session Establishment: Client requests a token from
/token, triggering the background instantiation of theLiveKitGeminiBridge. - WebRTC Join: The client browser joins the LiveKit room. The
LiveKitGeminiBridgealso connects a virtual audio participant into the room. - Upstream Pipeline (User -> gemini):
- Client streams user audio to the LiveKit Room using WebRTC.
LiveKitGeminiBridgesubscribes to the track, extracts the 48kHz frames, downsamples to 16kHz mono PCM, and pushes them to the ADKLiveRequestQueue.- The ADK
Runnerstreams the queue content to Gemini Live API via an underlying secure WebSocket connection (bidiGenerateContentprotocol).
- Downstream Pipeline (Gemini -> User):
- Gemini responds in real-time with audio buffers and textual transcriptions via the secure WebSocket.
- ADK
Runnerintercepts the events and passes them to the bridge. - The bridge pushes the audio (resampled to 24kHz) back into LiveKit via the LocalAudioTrack, and publishes transcription text through WebRTC DataChannels.
2. The Booking Agents (Multi-Agent Orchestrator)
Inside the app/travel_booking/ package, we implement a three-tier hierarchical multi-agent orchestrator:
┌──────────────────────┐
│ session_orchestrator │
│ (Central Router) │
└──────────┬───────────┘
│
┌──────────────────┴──────────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ FlightBookingAgent │◀─────────────▶│ HotelBookingAgent │
│ (Flight search/book)│ A2A Transfer│ (Hotel search/book) │
└─────────────────────┘ └─────────────────────┘
- Session Orchestrator (
app/travel_booking/agent.py):- The root agent. Listens to the user's intent and delegates control to the specialist sub-agents (
FlightBookingAgentorHotelBookingAgent) via ADK's native agent-routing tool calls.
- The root agent. Listens to the user's intent and delegates control to the specialist sub-agents (
- FlightBookingAgent (
app/travel_booking/agents/flight_booking.py):- Specialized in travel search and reservations.
- Equipped with 3 mock tools:
search_flights,book_flight,cancel_flight. - Configured with
HotelBookingAgentas a sub-agent to enable a direct, silent handoff when the user switches context to hotel bookings.
- HotelBookingAgent (
app/travel_booking/agents/hotel_booking.py):- Specialized in lodging discovery and booking.
- Equipped with 3 mock tools:
search_hotels,book_hotel,cancel_hotel.
3. LiveKit Bridge & Configuration Overview
app/livekit_bridge.py Overview
This class acts as the low-latency audio converter and routing engine.
- Audio Buffering & Downsampling: Standardizes LiveKit's high-fidelity audio stream (downsamples 48kHz stereo to 16kHz mono PCM) and sends it in small, optimized 20ms buffers (640 bytes) to reduce WebSocket latency.
- Realtime Event Mapping: Listens to
runner.run_liveevent generators, translates model text outputs and prints them to the server console, and publishes data events onto WebRTC DataChannels.
Runner Configuration (app/main.py)
- The
Runneris configured with standardInMemorySessionServiceorDatabaseSessionServiceto persist dialogue histories. - Integrated with a custom
SessionResumptionIsolationPlugin: Clears the transparency resumption handles when transferring between sub-agents, preventing key-based Gemini API errors during sub-agent handoffs.
runner = Runner(
app_name="livekit-adk",
agent=agent.root_agent,
session_service=session_service,
auto_create_session=True,
plugins=[SessionResumptionIsolationPlugin()]
)
Run Configuration (app/livekit_bridge.py)
Configured specifically to handle high-performance native audio models over WebRTC:
run_config = RunConfig(
streaming_mode=StreamingMode.BIDI,
response_modalities=["AUDIO"],
input_audio_transcription=types.AudioTranscriptionConfig(),
output_audio_transcription=types.AudioTranscriptionConfig(),
session_resumption=types.SessionResumptionConfig(),
enable_affective_dialog=True
)
response_modalities=["AUDIO"]: Crucial for native audio Gemini models; enforces direct-to-audio responses to ensure natural voice cadence.output_audio_transcription: Tells Gemini to send text transcripts along with the audio stream so the bridge can display them on the UI.
4. Configuration & Run Instructions
1. Environment Setup (app/.env)
Create a file named app/.env inside the project. Configure the variables:
# Model selection
DEMO_AGENT_MODEL="gemini-live-2.5-flash-native-audio"
# LiveKit Settings (Local development values)
USE_LIVEKIT=true
LIVEKIT_URL=ws://localhost:7880
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=secret
2. Installing & Running LiveKit (macOS)
For local development, you need a running LiveKit server instance. On macOS, install and run it using Homebrew:
# Install LiveKit Server
brew install livekit
# Start the server in development mode
livekit-server --dev
The --dev flag automatically starts the server on localhost:7880 using the default credentials:
- API Key:
devkey - API Secret:
secret
(These credentials match the default .env template above.)
3. Launching the Application
Ensure you are running commands using the project's virtual environment.
Step 1: Sync dependencies
uv sync
Step 2: Start the Dev Server
Navigate into the app folder and run:
uv run --project .. python3 -m uvicorn main:app --reload
The application starts a FastAPI server listening at http://127.0.0.1:8000/static/livekit. Open this URL in your browser to interact with the voice assistant!