如何用 ZizkaDB 配合 LiveKit 審核語音智能體
How to Use ZizkaDB with LiveKit to Audit Your Voice Agents
ZizkaDB 的 LiveKit 整合可將每通語音通話記錄為單一 session,集中呈現逐字稿、工具呼叫及其因果鏈。教學涵蓋 observer 接入、記錄級別選擇和故障測試,並指出 ZizkaDB 不儲存音訊,逐字稿會在通話結束時從 LiveKit session report 複製。verbose 會額外記錄原始 payload,文中建議按環境和私隱要求選擇記錄級別。
This is the next how-to guide in the series. The earlier guides covered text agents, LangChain, and Cursor. This one is for teams building voice agents on LiveKit: support lines, appointment booking, collections, banking call handling. Voice adds a problem text does not have. When a customer says the agent told them something wrong on a call, nobody can scroll back through the conversation, and the transcript, the tool calls, and the decisions behind them live in different places.
ZizkaDB’s LiveKit integration maps one call to one session, so you can open a call and see the transcript, the tool calls, and the causal chain behind what the agent said. Every command and code snippet below comes from the repository’s LiveKit documentation.
A note on scope: this guide covers the open source self-hosted setup. Package versions were current when I checked, but the docs list slightly different patch numbers (0.2.0 and 0.2.1), so confirm the latest on PyPI. Plan details, compliance questions, and recording consent rules are things to confirm with your own legal counsel.
Before you start: what you get
Voice pipelines are noisy. A single call produces speech turns, interruptions, timeouts, state changes, tool calls, and token usage. The integration organizes this into one session per LiveKit call, with these event types:
session_started and session_ended user_message and assistant_response user_transcript, the final speech-to-text result tool_call and tool_result agent_handoff for multi-agent workflows State, interruption, and timeout events usage_updated for token usage error
Each event links to the one that caused it through a parent_id, which is what makes the Why? view and db.why() work for voice the same way they do for text.
Two design facts matter for production. First, no audio is stored in ZizkaDB. Transcript text is copied from LiveKit’s session report at the end of the call. Second, ZizkaDB writes go onto a background queue, so nothing on the logging path blocks the audio path, and a ZizkaDB outage degrades observability, not the call.
Step 1: Start ZizkaDB
You need Docker. This starts the stack without cloning the repo, and the first image pull can take a while:
bash curl -fsSL https://raw.githubusercontent.com/Zizka-ai/ZizkaDB/main/scripts/quickstart-remote.sh | bash
The API runs at http://localhost:8000 and the dashboard at http://localhost:3001. On localhost there is no signup and no API key.
Step 2: Install the integration
One command:
bash pip install zizkadb-livekit
Per the docs, this pulls in zizkadb-sdk>=0.3.0 and livekit-agents>=1.3.0 automatically, so there is no separate SDK install. If you already have a LiveKit agent, check that the version it pins is compatible.
Step 3: Set the environment Variable What it does ZIZKADB_HOST Self-host API URL, default http://localhost:8000 ZIZKADB_AGENT Agent name, which must match the dashboard ZIZKADB_API_KEY Cloud key (zizkadb_live_...) used instead of a host ZIZKADB_EVENT_LEVEL Verbosity: transcript, standard (default), or verbose
You also need your usual LiveKit variables, which are LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET.
Step 4: Wire the observer into your agent
This is the full wiring from the docs:
python import os from livekit.agents import AgentServer, JobContext, AgentSession, Agent from zizkadb import ZizkaDB from zizkadb_livekit import ZizkaDBLiveKitObserver, pop_observer, register_observer
server = AgentServer()
async def on_session_end(ctx: JobContext) -> None: observer = pop_observer(ctx) if observer is None: return try: await observer.ingest_session_report(ctx) finally: await observer.aclose() # flushes queued events, closes the connection
@server.rtc_session(agent_name="support-voice", on_session_end=on_session_end) async def entrypoint(ctx: JobContext): db = ZizkaDB(host=os.getenv("ZIZKADB_HOST", "http://localhost:8000")) observer = ZizkaDBLiveKitObserver( db, agent=os.getenv("ZIZKADB_AGENT", "support-voice"), session_id=ctx.room.name, ) register_observer(ctx, observer)
session = AgentSession(...) # your STT / LLM / TTS
Attach before session.start() so no turn is missed.
observer.attach(session, job_ctx=ctx)
observer.queue_session_started(room=ctx.room.name, job_id=ctx.job.id)
await session.start(agent=Agent(instructions="..."), room=ctx.room)
await ctx.connect()
Three details are worth understanding, because they are where voice integrations usually go wrong:
One observer per call. The observer is registered and looked up per call using the job context. A worker serves many calls at once, so a shared module-level observer would let calls overwrite each other’s transcripts. Keep it per call as shown.
Attach before starting the session. Call observer.attach(...) before session.start(), or early turns can be missed.
Close it at the end. In the session end handler, ingest_session_report back-fills the full transcript, and aclose() flushes queued writes and releases the connection. Do not skip the try and finally, or the last events of a call may not be written.
If you want the quickest route, the repository includes a runnable voice worker with a local browser interface at http://localhost:8080 in examples/livekit-agent/. Run that first to see the whole flow before adapting your own agent.
Step 5: Make a test call and verify
Place a short test call. Then open the dashboard, go to Activity, choose your agent (for example support-voice), and open Sessions. You should see one session for the call, with the same event types as a text agent.
If nothing appears, check these in order: the stack is running, the host URL is reachable from where your worker runs, the agent name matches exactly, the observer is attached before the session starts, and the end of call handler actually runs and closes the observer.
Step 6: Pick the right verbosity level
Voice pipelines emit many more events than text agents, so events are tiered. A level logs its own tier and everything quieter. Errors and session start and end are always logged.
transcript: turns, tool calls and results, handoffs, errors, and session boundaries. Lightest, good for cost and privacy sensitive setups. standard (default): adds pipeline detail such as state changes, interruptions, timeouts, and token usage. verbose: adds partial transcripts, turn detector predictions, speech events, and a bounded raw copy of each LiveKit payload. Use it for debugging, not as the default.
You can set the level per observer with level="verbose" or globally through ZIZKADB_EVENT_LEVEL. A sensible pattern is standard in production, verbose in staging, and transcript where privacy rules require minimizing what you keep. Because verbose stores raw payloads, treat it as more sensitive.
Step 7: Investigate a call end to end
When a customer disputes what the agent said, use the same workflow as any other incident:
Open the call’s session and read the sequence, from the first user turn to the last assistant response. Find the first step where the call went wrong. In voice, look closely at user_transcript events, since many failures start with a misheard word, not a bad decision. Open the Why? (causal) tab on that step, or run zizkadb why , to see what led to it. Check tool_call and tool_result events for a failed or slow tool that the agent routed around. Look for interruption and timeout events near the failure, which often explain a clipped or repeated answer.
The speech-to-text layer deserves special attention. If the transcript says the customer said a different amount than they did, the agent’s reasoning may have been sound on bad input. Recording that distinction is the difference between fixing the model and fixing the transcription setup.
Step 8: Add manual events for what LiveKit cannot see
The observer captures what flows through LiveKit. For things outside it, such as a database write, a payment authorization, or a human approval, use the manual helpers log_tool_call, log_tool_result, and log_error, and link them into the chain with the appropriate parent. Without that link, why() stops at the edge of what LiveKit knew.
Step 9: Handle privacy and consent for voice
Voice calls raise questions text does not. Decide these before production:
Audio. ZizkaDB does not store audio, only transcript text. If your own stack records audio, that is separate and has its own consent and retention rules. Transcripts are personal data. Callers say names, account details, and health or financial information aloud. Redact or limit what your agent logs where you can, and use the transcript level where minimizing helps. Consent and notice. Rules on informing callers about recording and AI use vary by country and sector. Confirm yours with counsel before launch. Erasure. Plan for deletion requests. ZizkaDB has db.forget() for erasure by metadata filter, so tag sessions with a stable subject identifier from the start, such as a hashed customer ID, so you can find everything belonging to one person. Step 10: Test the failure modes
Before relying on the trail, test these on purpose:
Stop ZizkaDB mid call and confirm the call continues normally. The docs say failures are swallowed with a warning, so verify that on your setup. Run several concurrent calls and confirm each gets its own correct transcript. End a call abruptly, for example by dropping the connection, and confirm the session still closes and the transcript is back-filled. Check a call with a tool failure and confirm the error shows in the session.
Each of these is a case where silent gaps would otherwise only show up during a real dispute.
What the business case looks like
These figures are modeled assumptions, not measured results. Replace them with your own.
Assume a voice support line handles 300 disputed or escalated calls a month, with a reviewer at 50 euro per hour. Without a connected record, reviewing a call means pulling the recording, listening through, and matching it against tool logs, about 30 minutes each, costing 7,500 euro a month. With the transcript, tool calls, and causal chain in one session, review takes about 8 minutes, costing about 2,000 euro a month. That saves roughly 5,500 euro a month.
Add engineering time. If your team handles 10 voice incidents a month at 100 euro per hour, and the average falls from 5 hours to 1.5 hours with a connected trail, that is about 3,500 euro a month more.
The setup cost is small. A single LiveKit agent integration is plausibly a few hours of work, since it is one package and a short wiring block, so payback depends mostly on how often calls get questioned.
The larger value is in regulated settings. If your voice agent gives information about money, health, or contracts, the question you will eventually face is what exactly did the agent say, and why. A recorded chain answers that. Without one, you rely on recordings and memory.
Common mistakes to avoid
Using one shared observer across calls. Transcripts overwrite each other. Register one per call.
Attaching after the session starts. Early turns are missed.
Skipping aclose. Queued events at the end of a call may never be written.
Blaming the model for a transcription error. Check the user_transcript events before changing prompts.
Running verbose by default. It stores raw payloads, which adds volume and sensitivity.
Ignoring consent and retention. Voice data has rules of its own, so settle them before launch.
Never testing an outage. Prove that a ZizkaDB failure does not affect calls.
Where to go from here
Run the example worker in examples/livekit-agent/, place a test call, and open its session in the dashboard. Then wire the observer into your own agent, test the failure cases, and decide your verbosity and privacy settings. After that, the earlier guides on drift monitoring, point-in-time reconstruction, and audit preparation all apply to voice, since they build on the same record.
If you tell me your LiveKit setup, your STT and LLM providers, and what the agent is allowed to do on a call, I can help adapt these steps, including the privacy settings and the test plan.
Want to test ZizkaDB on your voice agent? Try our open source version here: https://github.com/ZIZKA-AI-SL/ZizkaDB
Interested in a design partnership? Fill in the form on our site or reach me directly at founder@zizka.ai
Sources: ZizkaDB LiveKit integration docs · ZizkaDB connection guide
來源:DEV Community · dev.to