agent.py: a LiveKit Agents worker with the concierge’s instructions and thecheck_availabilitytool, Dialog-RSN-1 as the model and ElevenLabs as the voice.- A text test your coding agent runs itself, and a voice session you run in your terminal with
lk agent console. - The agent in a room on a local LiveKit server, with a scripted caller that asks about the spa, talks over a long reply, and saves the agent’s side of the call to
call.wav.
- Your coding agent set up as in the quickstart’s Set up your agent section: skill installed,
POLY_API_KEYset, connection check passing. - The quickstart’s
voice_agent.py, with thecheck_availabilitytool, in the folder you build in. Continue in the same agent session if you can, so it already knows the code. - An ElevenLabs API key in
.envasELEVEN_API_KEY. LiveKit’s ElevenLabs plugin reads that name, notELEVENLABS_API_KEY. The free tier works while it has credits. - Python 3.10 or later with uv, as in the quickstart.
- The LiveKit CLI:
brew install livekit-cli. It gives youlk. - For Step 2, a LiveKit server. This page runs one on your machine with
brew install livekit, which needs no account. To use a LiveKit Cloud project instead, put its URL, API key and secret in.envin place of the local ones.
Step 1: make it a LiveKit agent
Prompt
pyproject.toml with livekit-agents[elevenlabs], python-dotenv (the worker reads .env with it), and livekit-plugins-polyai installed from GitHub (livekit-plugins-polyai @ git+https://github.com/PolyAI-LDN/livekit-plugins-polyai). In agent.py, an Agent subclass with the concierge’s instructions, and check_availability as a @function_tool method that returns its result as a JSON string. The session takes two arguments:
stt and no turn detector: Dialog-RSN-1 does both jobs. There’s no WebSocket code either. The plugin opens the Dialog-RSN-1 session, streams the room’s audio in, and hands each tool call to your function. It asks Dialog-RSN-1 for text, so ElevenLabs speaks every reply and the voice changes from the quickstart’s server voice to an ElevenLabs one. The greeting comes from the instructions and a plain session.generate_reply(), because the plugin drops per-reply instructions.
Your agent may also move the instructions and check_availability into a shared module, so voice_agent.py and agent.py stay in step. That’s fine.
Check it. lk agent console runs the agent on your machine and connects it to your microphone and speakers. It needs no LiveKit server. It does need a real terminal, which is why your coding agent tests with lk agent debugger, the same text mode driven from a script. Start in text mode, which turns ElevenLabs off:
check_availability call and its result, then the concierge offers the slots. Then run lk agent console agent.py with headphones on, and talk to it.
If it goes wrong:
Step 2: run it in a LiveKit room
Prompt
.env for the local server, which runs in dev mode with fixed credentials:
uv run agent.py dev, which registers it with the server and sends it into every new room. caller.py uses LiveKit’s Python SDK, not Dialog-RSN-1 directly. It signs a token with the same key and secret, joins a new room, and publishes a microphone track that streams generated speech in real time, with silence between lines. It records the agent’s audio track to call.wav and prints the transcript LiveKit publishes on the lk.transcription text stream. To know when to speak, it watches the agent’s lk.agent.state attribute, which goes from speaking to listening when a reply has played. For the barge-in, it starts the next line a second or two into the long reply instead of waiting.
Check it. To run the call yourself, start each command in its own terminal, in this order:
output
Caller lines are what Dialog-RSN-1 heard, sent with each reply. Play call.wav to hear the agent’s side, cut-off included.
Some log noise is harmless. The worker may log that dev mode is deprecated in favor of lk agent dev: it still works. Both programs may print an InsecureKeyLengthWarning, because the dev server’s secret is short. And caller.py may print AssertionError lines from FfiHandle.__del__ as it exits, after it has saved call.wav: that’s LiveKit’s Python SDK cleaning up.
If it goes wrong:
Troubleshooting
For key and connection errors, see the quickstart’s troubleshooting.
The LiveKit guide’s troubleshooting covers the plugin’s other errors, and why not the OpenAI plugin explains the difference between the two.

