LiveKit Agents
Use Confident AI for LLM observability and evals for LiveKit Agents
Overview
LiveKit Agents is a framework for building realtime voice and multimodal agents in Python and TypeScript.
The integration works through OpenTelemetry. LiveKit emits native spans for sessions, turns, model requests, tools, and speech timing; confident-trace, Confident AI's OpenTelemetry-native tracing SDK, exports those spans to Confident AI with their parent-child relationships intact.
Call init() at the top of your agent file. You don't need to wrap your agent or add a tracing callback to each session.
| Runtime | Requirements | Setup |
|---|---|---|
| Python | Python 3.10+, livekit-agents (tested with 1.8.3) | Call init() at module scope, before starting the agent server |
| TypeScript | Node.js 22+, @livekit/agents >=1.9.0 <2 | Call init() at module scope and start the worker with the preload |
Auto-Instrument
Install Dependencies
Add
confident-traceto your existing LiveKit agent project. Keep the model plugins your agent already uses.pip install confident-trace 'livekit-agents==1.8.3'npm install confident-trace '@livekit/agents@>=1.9.0 <2'yarn add confident-trace '@livekit/agents@>=1.9.0 <2'If you're starting a new agent, follow the LiveKit quickstart first, then add the tracing setup below.
Set Your API Key
Get your Confident AI Project API key and make it available to your agent server and its worker processes:
export CONFIDENT_API_KEY="<your-confident-project-key>"Keep your existing LiveKit credentials and model provider keys configured as usual.
Instrument Your Agent
Add
init()at module scope in the file LiveKit loads for each job, before starting the server. Keep your existing session, model, and tool configuration.agent.py from confident_trace import init from livekit.agents import AgentServer, JobContext, cli init() server = AgentServer() @server.rtc_session() async def entrypoint(ctx: JobContext): await ctx.connect() # Keep your existing AgentSession setup and agent logic here. if __name__ == "__main__": cli.run_app(server)agent.ts import { fileURLToPath } from "node:url"; import { init } from "confident-trace"; import { cli, defineAgent, ServerOptions } from "@livekit/agents"; import type { JobContext } from "@livekit/agents"; init(); export default defineAgent({ entry: async (ctx: JobContext) => { await ctx.connect(); // Keep your existing AgentSession setup and agent logic here. }, }); cli.runApp(new ServerOptions({ agent: fileURLToPath(import.meta.url) }));Run Your Agent
python agent.py devLaunch your compiled agent with the
confident-trace/registerpreload:node --import confident-trace/register dist/agent.js devAdjust the path to match your build output. If your project runs TypeScript source with
tsx, use:node --import confident-trace/register --import tsx agent.ts devThe preload installs automatic instrumentation;
init()configures export. Both are required. Keep the preload enabled in production and in worker processes.Use
startinstead ofdevfor production. Connect to your agent as usual, then open the Observatory in your Confident AI project to inspect its traces.Confident flushes pending spans after LiveKit finishes job cleanup, including spans completed by asynchronous shutdown callbacks. You don't need to add a separate flush callback. Allow graceful worker shutdown; a forced process kill can still lose pending spans.
What Gets Captured
The integration exports the native spans LiveKit emits, labelled LiveKit:
- Session and job lifecycle — the hierarchy and timing of work within a call.
- User and agent turns — conversational activity within the session.
- Model requests — model details and token usage when emitted by the model plugin.
- Tool calls — tool execution and the attributes LiveKit records for it.
- Speech timing — speaking, end-of-turn detection, and text-to-speech spans where available.
- Errors — error status recorded on native spans.
When LiveKit's native model spans are exported through Confident's provider, the integration suppresses overlapping provider spans so each model call is recorded once. Supported provider calls outside those native model spans can still be traced separately.
Call Transcripts
Confident AI reads the conversation from LiveKit's native lk.* attributes and maps it into the trace, so you can read a call without opening each span:
- Trace input and output — the trace output is the agent's last reply, and the trace input is the conversation before it.
- Turn spans — user turn spans show what the user said, agent turn spans show what the user said and how the agent answered, and text-to-speech spans show the text being spoken.
- Voice timing — when a call belongs to a thread, each turn shows how long the agent took to answer and whether the user interrupted it.
Calls often send spans in several pieces, so the transcript fills in as later spans arrive. Thread evaluations are held back while a call is still running, so online evals score the full conversation instead of a partial one.
Record Call Audio
When LiveKit records a call, Confident uploads the full call recording to the call's trace as the job ends, so you can listen to the call in the Observatory. To attach audio to individual spans on a per-turn basis instead, see Audio in Traces.
LiveKit only writes a recording when audio recording is on for the session. Pass record to session.start() so every call is recorded; without it, LiveKit falls back to your LiveKit Cloud project's recording setting.
@server.rtc_session()
async def entrypoint(ctx: JobContext):
await ctx.connect()
await session.start(room=ctx.room, agent=agent, record=True)export default defineAgent({
entry: async (ctx: JobContext) => {
await ctx.connect();
await session.start({ room: ctx.room, agent, record: true });
},
});Uploads follow a few limits:
- Size — recordings up to 18 MB, about 25 minutes of audio, are uploaded. Larger recordings are skipped, and Confident logs a warning.
- Content capture — setting
LIVEKIT_TELEMETRY_ALLOW_PII=0, orcapture_content=False/captureContent: falseininit(), also turns off recording uploads. - Export — recordings go to the same Confident AI endpoint and API key as your traces, so EU projects need no extra setup. They're only uploaded when traces are exported over OTLP HTTP, the default, not over gRPC or through your own exporter.
Set Trace Span Properties
Use a trace context to add tags, metadata, user ID, and customer ID inside your job entrypoint, before starting the session. It creates no extra span. LiveKit already has its job span active when it calls your entrypoint, so these properties fill unset fields on that span.
Keep init() at module scope as shown above. In the examples below, session and agent are your existing configured AgentSession and agent; replace the sample IDs with values from your application.
from confident_trace import trace_context
@server.rtc_session()
async def entrypoint(ctx: JobContext):
async with trace_context(
tags=["support"],
metadata={"release": "2026-09"},
user_id="user-42",
customer_id="customer-7",
):
await ctx.connect()
await session.start(room=ctx.room, agent=agent)import { traceContext } from "confident-trace";
export default defineAgent({
entry: async (ctx: JobContext) => {
await traceContext(
{
tags: ["support"],
metadata: { release: "2026-09" },
userId: "user-42",
customerId: "customer-7",
},
async () => {
await ctx.connect();
await session.start({ room: ctx.room, agent });
},
);
},
});See users and customers to set an optional display name alongside each ID, and trace context for every supported trace property and update behavior.
Content Privacy
LiveKit controls the content on its native spans. Set this before starting your worker to disable LiveKit's conversation-content capture:
export LIVEKIT_TELEMETRY_ALLOW_PII=0Confident's capture_content / captureContent and redaction options apply to Confident-generated spans; they do not scrub LiveKit's native span attributes. Configure both layers when using LiveKit alongside directly instrumented model clients.
Disable LiveKit Instrumentation
Pass init() a list of integration identifiers to enable only those integrations. The LiveKit identifier is "livekit" in both Python and TypeScript. Omit it to disable the adapter; an empty list disables all automatic instrumentation:
from confident_trace import init
init(instrumentations=())import { init } from "confident-trace";
init({ instrumentations: [] });This disables Confident's automatic adapter, including its LiveKit cleanup flush. LiveKit can still emit native OpenTelemetry spans through its configured provider. LiveKit is optional: applications that don't install it can use init() normally.
Next Steps
Online Evals
Evaluate supported trace and span inputs as they arrive in Confident AI.
Troubleshooting
Check startup configuration, export, and missing traces.
Last updated on