Raw LiveKit
Instrument a worker that drives LiveKit Agents directly. init_observability emits the tag bundle the server expects; run_judges_on_report runs judges at end-of-call.
agent-transport's AudioStreamServer wires observability internally. When you
drive LiveKit Agents directly — your own AgentServer with
@server.rtc_session(...) — two helpers replace the hand-rolled plumbing:
init_observability(ctx.tagger, …)— validates the upload URL (raises if unset) and emits the full tag bundle (agent_id,account_id,agent.name,transport) the server's ingest path expects. One call instead of ~20 lines oftagger.add(...).run_judges_on_report(report, judges=…)— wraps theJudgeGroupconstruction, exception handling, structured logging, andllm.aclose()cleanup. Mix LiveKit built-ins with SDK judges freely.
Minimal worker
from agent_observability.livekit import Goal, init_observability, run_judges_on_report
from agent_observability.livekit.judges import default_judges
from livekit.agents import AgentServer, JobContext
from livekit.agents.evals import accuracy_judge
server = AgentServer()
async def on_session_end(ctx: JobContext) -> None:
report = ctx.make_session_report()
await run_judges_on_report(
report,
judges=[
accuracy_judge(), # LiveKit built-in
*default_judges(), # 4 ground-truth-free SDK judges
],
)
@server.rtc_session(agent_name="support-bot", on_session_end=on_session_end)
async def entrypoint(ctx: JobContext) -> None:
init_observability(
ctx.tagger,
agent_id="9c2f7e3d-…", # stable opaque UUID
agent_name="support-bot",
account_id="acct-7",
transport="text",
goals=[
Goal("identity-check", "Confirm the caller's identity before account changes"),
Goal("order-resolution", "Resolve the order issue or open a support ticket"),
],
)
# …your usual AgentSession.start(...) setupThat's the whole observability surface for a raw-LiveKit worker — no hand-rolled
tagger.add(...), no JudgeGroup boilerplate, no llm.aclose() cleanup.
Conversation goals
The goals parameter declares task outcomes the server's
goal analyzer judges after each session — no judging
code in the agent, just data. Each entry is a Goal(name, description) —
both fields required:
from agent_observability.livekit import Goal
init_observability(
ctx.tagger,
agent_id="9c2f7e3d-…",
goals=[
# name + description — the description is what the LLM judge evaluates
Goal("identity-check", "Confirm the caller's identity before account changes"),
# descriptions may contain colons; names may not
Goal("escalation-policy", "Escalate only after: two failed resolution attempts"),
],
)A Goal validates itself at construction (and duplicate names raise before
any tag is emitted):
- names are the goal's stable identity — non-empty, unique within the
call, and colon-free (the wire format
goal:<name>:<description>splits at the first colon). - descriptions are required free text. Phrase them as observable conversation behavior — the judge only sees the transcript (guidance).
Verdicts (met/unmet, reasoning, what went wrong) land on the agent's
Conversation Goals tab once the server has OPENAI_API_KEY set.
Running on agent-transport's AudioStreamServer (which wires the
observability bootstrap internally)? Call add_goal_tags(ctx.tagger, goals)
instead — same goal validation, no second bootstrap, no upload-URL check.
URL resolution
init_observability raises if neither LIVEKIT_OBSERVABILITY_URL nor the
AGENT_OBSERVABILITY_URL fallback is set — there's no point continuing if the
report has nowhere to go. For a non-fatal, warn-only contract, call
ensure_observability_url() yourself instead.
Runnable example
A complete text-only worker (tool call, recording options, judges at
end-of-call) lives in the repo at
plugins/examples/python/text_only_livekit_worker.py:
export LIVEKIT_OBSERVABILITY_URL=https://obs.example.com
export AGENT_OBSERVABILITY_AGENT_ID=9c2f7e3d-4b8a-4d2e-9f1b-textonly
export OPENAI_API_KEY=sk-...
uv run plugins/examples/python/text_only_livekit_worker.py console --text --recordSee the judge reference for the full catalogue.
Quickstart
Install agent-observability-sdk and point your Python agent at the dashboard. Covers the agent-transport path (observability auto-emits) and where to go for raw LiveKit workers.
Judges
The nine LiveKit-compatible judges shipped by agent-observability-sdk, plus the default_judges() composition helper. Drop them into a JudgeGroup or an agent-transport EvaluationConfig.