> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agnost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# LiveKit Agents

> Export native LiveKit Agents traces to Agnost AI over OpenTelemetry

## Choose a setup method

| Method              | Use it when                                                                    |
| ------------------- | ------------------------------------------------------------------------------ |
| **Agnost AI skill** | You want your coding agent to inspect the app, make the change, and verify it. |
| **Agnost AI SDK**   | You want explicit control over the interaction boundaries and fields.          |
| **OpenTelemetry**   | The framework already emits useful spans or you operate an OTel pipeline.      |

Start with one method for a call path. Combining SDK tracking with framework
OpenTelemetry on the same call can create duplicate interactions.

## Recommended: Agnost AI skill

<Note>
  **Recommended.** Choose this path when you want your coding agent to inspect the
  project, select a supported transport, make the smallest instrumentation change,
  send a test event, and verify that it reached Agnost AI.
</Note>

Install the skill once in your coding-agent environment:

```bash theme={null}
npx skills add AgnostAI/skills --skill agnost-ai
```

Run the framework-specific prompt below from the application root. Review the
resulting diff before deploying it.

```text theme={null}
Use the agnost-ai skill to add Agnost AI analytics to this LiveKit Agents application.
Org ID: your-org-id
Use LiveKit's native OpenTelemetry traces and verify one fresh agent session.
```

LiveKit Agents already instruments agent sessions, turns, model requests, tool calls,
speech, and transcription with OpenTelemetry. Point those native spans at Agnost AI;
no webhook or Agnost SDK wrapper is required.

## 1. Configure the OTLP destination

Set these variables in the agent worker environment:

```bash theme={null}
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://otel.agnost.ai/v1/traces
OTEL_EXPORTER_OTLP_HEADERS="X-Agnost-Org-ID=your-org-id"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

## 2. Register one batch exporter

Call LiveKit's tracer-provider hook before `AgentSession.start()`.

<CodeGroup>
  ```python Python theme={null}
  from livekit.agents.telemetry import set_tracer_provider
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
  from opentelemetry.sdk.trace import TracerProvider
  from opentelemetry.sdk.trace.export import BatchSpanProcessor

  provider = TracerProvider()
  provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
  set_tracer_provider(provider, metadata={
      "session.id": conversation_id,
      "enduser.id": user_id,
  })
  ```

  ```typescript TypeScript theme={null}
  import { telemetry } from '@livekit/agents';
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
  import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';

  const fanout = new telemetry.FanoutSpanProcessor();
  const provider = new NodeTracerProvider({
    spanProcessors: [
      new BatchSpanProcessor(new OTLPTraceExporter()),
      fanout,
    ],
  });
  provider.register();
  telemetry.setTracerProvider(provider, {
    metadata: {
      'session.id': conversationId,
      'enduser.id': userId,
    },
    registerSpanProcessor: (processor) => fanout.add(processor),
  });
  ```
</CodeGroup>

Install the OTel packages if the application does not already have them:

<CodeGroup>
  ```bash Python theme={null}
  pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
  ```

  ```bash TypeScript theme={null}
  npm install @opentelemetry/api @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-trace-node
  ```
</CodeGroup>

The TypeScript `FanoutSpanProcessor` is required by the current OTel 2.x API so
LiveKit can attach its metadata processor (and its Cloud exporter, when enabled)
after provider construction.

## 3. Add standard conversation context

Pass metadata to `set_tracer_provider`; LiveKit copies it to every native span.
Use standard attributes rather than Agnost-specific names:

```python theme={null}
set_tracer_provider(provider, metadata={
    "session.id": ctx.room.name,
    "enduser.id": customer_id,
    "user.plan": "enterprise",
    "user.locale": "en-US",
    "conversation.channel": "voice",
    "conversation.environment": "production",
})
```

| Attribute                             | Agnost behavior                          |
| ------------------------------------- | ---------------------------------------- |
| `session.id`                          | Groups every span into one conversation. |
| `enduser.id` or `user.id`             | Groups conversations into one user.      |
| `user.*` or `enduser.*`               | Promoted to user metadata.               |
| `conversation.*` or `session.*`       | Promoted to conversation metadata.       |
| Any other attribute, including `lk.*` | Preserved on the event.                  |

If `session.id` is absent, Agnost falls back to LiveKit's `lk.job_id`, then
`lk.room_name`. For user identity, explicit `enduser.id` or `user.id` is best;
the participant identity on a native user-turn span is a safe fallback.

## What appears in Agnost AI

* `agent_turn` spans become the ordered user/assistant conversation turns.
* `function_tool` spans become tool calls with native arguments, output, and error state.
* LLM request spans retain model, token, and `gen_ai.*` attributes.
* STT, TTS, speaking, evaluator, session, job, and activity spans remain visible in the trace.
* Native `lk.*`, standard `gen_ai.*`, and your custom attributes remain available on events.

LiveKit webhooks are useful for room and participant lifecycle events, but they do
not carry the complete agent turn, model, tool, and speech span tree. Use native
OTLP export for observability.

## Flush on shutdown

Batch export is asynchronous. Register a shutdown callback so the worker sends
the final turns before it exits:

<CodeGroup>
  ```python Python theme={null}
  async def flush_traces():
      provider.force_flush()

  ctx.add_shutdown_callback(flush_traces)
  ```

  ```typescript TypeScript theme={null}
  ctx.addShutdownCallback(async () => {
    await provider.shutdown();
  });
  ```
</CodeGroup>

## Verify

Run one real LiveKit agent session, then open **Users** in Agnost AI. Confirm:

1. The user appears under the value sent in `enduser.id`.
2. The conversation shows alternating user and assistant turns.
3. The trace contains the LiveKit agent, LLM, tool, STT, and TTS spans used by the session.
4. Tool details show their native input and output.

## Troubleshooting

* Register the provider before `AgentSession.start()`; earlier spans cannot be recovered.
* Use the traces URL ending in `/v1/traces` with the HTTP/protobuf exporter.
* Confirm `X-Agnost-Org-ID` is present in `OTEL_EXPORTER_OTLP_HEADERS`.
* Do not point a generic OTLP exporter at LiveKit's private observability URL.
* Flush the provider when a short-lived worker or test exits.

## References

* [LiveKit: Export traces](https://docs.livekit.io/deploy/observability/tracing/)
* [LiveKit Python OTel example](https://github.com/livekit/agents/blob/main/examples/voice_agents/otel_trace.py)
* [LiveKit TypeScript OTel example](https://github.com/livekit/agents-js/blob/main/examples/src/otel_trace.ts)
* [LiveKit TypeScript `setTracerProvider` reference](https://docs.livekit.io/reference/agents-js/functions/agents.telemetry.setTracerProvider.html)

## Next steps

* [Conversations](/using-conversations): review complete voice interactions.
* [Events](/using-events): inspect native LiveKit spans and attributes.
* [Tool calls](/using-tool-calls): analyze tool arguments, results, errors, and latency.
