> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# OpenTelemetry 追踪

ElevenLabs Agents 可将对话导出为编码为 **OTLP JSON**（`resourceSpans`）的 **OpenTelemetry 追踪数据**。可将其转发至 Datadog、Grafana Tempo、Honeycomb 或任何支持接收 OTLP 的后端。

> **Info**
>
> ElevenLabs 不会将追踪数据直接推送到 OTLP 收集器。你会通过 webhook、API 或监控 WebSocket 收到 OTLP 格式的 JSON，然后将其转发至后端。

## 概览

可从 3 个入口导出追踪数据。它们共用相同的**每个对话追踪 ID**和 `elevenlabs.*` 属性命名。通话后/GET（基于转写记录）与监控（基于事件）的 span 结构和时间信息有所不同。

### 导出入口

| 入口           | 获取数据的时间     | 最适合用于              |
| ------------ | ----------- | ------------------ |
| 通话后 webhook  | 对话结束且分析完成后  | 批处理管道、计费和 QA、持久化存储 |
| GET 对话 API   | 按需获取，在对话创建后 | 回填、调试、重新处理         |
| 监控 WebSocket | 实时对话期间      | 实时仪表板、告警、人工介入      |

### 选择入口

* **将所有已完成通话导入数据仓库**：通话后 webhook
* **一次性导出或修复**：使用 `format=opentelemetry` 的 GET 对话请求
* **实时主管界面或告警**：监控 WebSocket
* **事后获取完整保真时间线**：通话后 webhook 或 GET 对话请求
* **实时获取工具、MCP 或护栏事件**：监控 WebSocket

使用 `traceId` 或 `elevenlabs.conversation_id` 跨入口关联数据。结合监控处理实时运营、webhook 进行持久分析，以及 GET 用于回填。

每个入口都需要支持 OTLP 的收集器或可观测性服务商。通话后 webhook 需要工作区 webhook 端点。GET API 和监控 WebSocket 分别需要各自的 API 密钥范围和设置；请参阅下文相关章节。

## 通话后 webhook

对话结束后，如果配置了通话后 webhook、`events` 包含 `transcript`，且 `transcript_format` 为 `opentelemetry`，ElevenLabs 会发送 `POST` 请求。

webhook 的 `type` 为 `post_call_transcription_otel`（而非返回 JSON 转写记录的 `post_call_transcription`）。

### Webhook 负载

```json
{
  "type": "post_call_transcription_otel",
  "event_timestamp": 1700000000,
  "data": {
    "conversation_id": "conv_9001k1zph3fkeh5s8xg9z90swaqa",
    "agent_id": "agent_7101k5zvyjhmfg983brhmhkd98n6",
    "otlp_traces": {
      "resourceSpans": []
    }
  }
}
```

### 启用 OpenTelemetry 转写记录

#### 通过控制台配置

### 创建工作区 webhook

在 ElevenAgents 控制台中，使用 HTTPS URL 和身份验证创建工作区 webhook。

### 添加通话后 webhook

打开 [Agents 设置](https://elevenlabs.io/app/agents/settings)，将该 webhook 指定为通话后 webhook，启用 **Transcript** 事件，并开启 **OpenTelemetry transcript payloads**。

![通话后 webhook 设置](/docs/_fern-img/eb5d768612d6461a21bc3127611f60724be3e1a55005af43faf48f2d7bf23807.webp)

#### 通过 CLI 配置

> **Note**
>
> 工作区范围的通话后 webhook 可在控制台或 API 标签页中配置。使用 CLI 可覆盖特定智能体的 webhook 设置。

### 拉取智能体配置

```bash
elevenlabs agents pull --agent "<agent-name>"
```

### 编辑 `agent_configs/<agent-name>.json`

设置 `platform_settings.workspace_overrides.webhooks`：

```json
{
  "platform_settings": {
    "workspace_overrides": {
      "webhooks": {
        "post_call_webhook_id": "wh_01jqz7x8y9z0a1b2c3d4e5f6",
        "events": ["transcript"],
        "transcript_format": "opentelemetry"
      }
    }
  }
}
```

### 推送更改

```bash
elevenlabs agents push --agent "<agent-name>"
```

#### 通过 API 配置

**`Python`**

```python title="Python"
import os

from dotenv import load_dotenv
from elevenlabs import ElevenLabs

load_dotenv()
elevenlabs = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))

elevenlabs.conversational_ai.settings.update(
    webhooks={
        "post_call_webhook_id": "wh_01jqz7x8y9z0a1b2c3d4e5f6",
        "events": ["transcript"],
        "transcript_format": "opentelemetry",
    },
)
```

**`TypeScript`**

```typescript title="TypeScript"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

await elevenlabs.conversationalAi.settings.update({
  webhooks: {
    postCallWebhookId: "wh_01jqz7x8y9z0a1b2c3d4e5f6",
    events: ["transcript"],
    transcriptFormat: "opentelemetry",
  },
});
```

对于单个智能体，请在[更新智能体](/docs/zh/api-reference/agents/update)的 `platform_settings.workspace_overrides` 下传入相同的 `webhooks` 对象。

> **Warning**
>
> OpenTelemetry 转写记录 webhook 不包含音频。如需录音，请使用 `post_call_audio`。
>
> 返回 **2xx** 表示成功。**4xx** 和 **5xx** 均视为失败。
>
> 仅当工作区 webhook 开启 **Enable retries** 时，转写记录 webhook（包括 OpenTelemetry）才会重试。瞬时错误（**5xx**、**429**、**408**）最多重试 5 次；**4xx** 不会重试。音频 webhook 永不重试。反复失败可能会自动禁用 webhook。详见[通话后 webhook](/docs/zh/eleven-agents/workflows/post-call-webhooks)和 HIPAA 例外情况。

### 传送

| 主题   | 详情                                                                      |
| ---- | ----------------------------------------------------------------------- |
| 方法   | 使用 JSON 正文的 `POST`                                                      |
| 身份验证 | 对 `{timestamp}.{body}` 进行 `ElevenLabs-Signature: t={unix},v0={hmac}` 签名 |
| 重试   | 仅限转写记录 webhook；需在 webhook 上启用 **Enable retries**；见上方警告                  |
| 大小   | 较长的工具参数和结果会在每个 span 属性达到 4 KB 时截断                                       |

### 追踪结构

每次传送都是一条完整追踪数据：一个根 span 加上子 span。

```text
elevenlabs.conversation
├── elevenlabs.recv.user_transcript
├── elevenlabs.recv.agent_response
│   └── elevenlabs.tool.{name}
└── ...
```

如果传送内容包含[推理摘要](/docs/zh/eleven-agents/customization/llm#reasoning-summary)，智能体响应 span 会包含 `elevenlabs.reasoning_content`。

时间信息来自转写记录的 `time_in_call_secs` 和通话元数据。当通话并非以正常客户端断开结束时，根 span 将 `elevenlabs.source` 设为 `post_call_webhook`，状态设为 `ERROR`。

## GET 对话

在[获取对话](/docs/zh/api-reference/conversations/get)请求中指定 OpenTelemetry 格式，即可收到与通话后 OpenTelemetry webhook 相同的 `otlp_traces` 对象，以及完整对话模型。

```http
GET /v1/convai/conversations/{conversation_id}?format=opentelemetry
```

需要具备 `CONVAI_READ` 权限范围的 API 密钥。使用 `format=json`（默认值）时，会省略 `otlp_traces`。

```json
{
  "conversation_id": "conv_9001k1zph3fkeh5s8xg9z90swaqa",
  "agent_id": "agent_7101k5zvyjhmfg983brhmhkd98n6",
  "status": "done",
  "transcript": [],
  "otlp_traces": {
    "resourceSpans": []
  }
}
```

| 主题     | 详情                                    |
| ------ | ------------------------------------- |
| 时间信息   | 与通话后 webhook 使用相同的、基于转写记录的构建器         |
| 转写记录   | 仍会返回 `transcript`；`otlp_traces` 为附加内容 |
| 文件 URL | span 属性中的签名 URL 约 15 分钟后过期            |

**`Python`**

```python title="Python"
import os

from dotenv import load_dotenv
from elevenlabs import ElevenLabs

load_dotenv()
elevenlabs = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))

conversation = elevenlabs.conversational_ai.conversations.get(
    conversation_id="conv_9001k1zph3fkeh5s8xg9z90swaqa",
    format="opentelemetry",
)

otlp_traces = conversation.otlp_traces
```

**`TypeScript`**

```typescript title="TypeScript"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

const conversation = await elevenlabs.conversationalAi.conversations.get({
  conversationId: "conv_9001k1zph3fkeh5s8xg9z90swaqa",
  format: "opentelemetry",
});

const otlpTraces = conversation.otlpTraces;
```

**`cURL`**

```bash title="cURL"
curl -s "https://api.elevenlabs.io/v1/convai/conversations/conv_9001k1zph3fkeh5s8xg9z90swaqa?format=opentelemetry" \
  -H "xi-api-key: $ELEVENLABS_API_KEY" \
  | jq '.otlp_traces.resourceSpans[0].scopeSpans[0].spans[].name'
```

预期的 span 名称包括 `elevenlabs.conversation`、`elevenlabs.recv.user_transcript` 和 `elevenlabs.recv.agent_response`。

## 监控 WebSocket

> **Note**
>
> 实时监控需要企业版工作区或 `realtime-monitoring` 功能标志。有关配置、控制命令和访问要求，请参阅[实时监控](/docs/zh/eleven-agents/guides/realtime-monitoring)。

在对话进行时，以 OTLP JSON 流式传输 OpenTelemetry 追踪数据。每条消息都是一个小型 `resourceSpans` 批次，而非一条通话结束时的完整追踪数据。

```
wss://api.elevenlabs.io/v1/convai/conversations/{conversation_id}/monitor?events_format=opentelemetry
```

身份验证需要 `CONVAI_WRITE`、`xi-api-key`（或 `Authorization`），以及智能体工作区的 **EDITOR** 访问权限。请在对话开始后连接。

### 在智能体上启用监控

在通话前设置 `monitoring_enabled: true` 并配置 `monitoring_events`。请参阅[实时监控](/docs/zh/eleven-agents/guides/realtime-monitoring#configuration)。

### 使用 OpenTelemetry 格式连接

在监控 WebSocket URL 后附加 `events_format=opentelemetry`。

> **Warning**
>
> 配置自定义 `monitoring_events` 后，VAD、轮次概率和 ping 事件不可用。数据流仅包含文本和元数据，不包含原始音频。

### 会话协议

1. 使用身份验证请求头连接。
2. 接收 `{"type": "connected"}`。
3. 接收根 span 批次（`elevenlabs.conversation`，`elevenlabs.source` = `monitoring`）。
4. 接收缓存历史记录（约最近 100 个事件），随后接收 `{"type": "history_complete"}`。
5. 事件发生时接收实时 span 批次。

使用 `events_format=json`（默认值）时，WebSocket 返回原始客户端事件，而非 `resourceSpans`。控制命令与[实时监控](/docs/zh/eleven-agents/guides/realtime-monitoring#control-commands)一致。

### 追踪结构

```text
elevenlabs.conversation
├── elevenlabs.turn.0
│   ├── elevenlabs.event.user_transcript
│   └── elevenlabs.tool.{name}
└── elevenlabs.turn.1
```

| 方面      | 通话后和 GET             | 监控                        |
| ------- | -------------------- | ------------------------- |
| 粒度      | 每个 webhook 或请求一条追踪数据 | 每个对话多条消息                  |
| 事件 span | 转写记录轮次               | `elevenlabs.event.{type}` |
| 轮次分组    | 隐含于转写记录顺序中           | 明确使用 `elevenlabs.turn.N`  |
| 顺序      | 稳定的转写记录顺序            | 事件可能不会按严格的时间顺序到达          |

结构化事件会映射到专用属性（例如 `elevenlabs.user.text`、`elevenlabs.agent.text`）。未知事件使用包含截断 JSON 的 `elevenlabs.event.data`。

> **Info**
>
> 不要假设事件顺序与说话顺序一致。使用相同的 `traceId` 将实时 span 与通话后数据关联。

### 连接示例

**`TypeScript`**

```typescript title="TypeScript"
import WebSocket from "ws";

const ws = new WebSocket(
  "wss://api.elevenlabs.io/v1/convai/conversations/conv_9001k1zph3fkeh5s8xg9z90swaqa/monitor?events_format=opentelemetry",
  {
    headers: {
      "xi-api-key": process.env.ELEVENLABS_API_KEY!,
    },
  }
);

ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.type === "connected" || msg.type === "history_complete") return;

  if (msg.resourceSpans) {
    forwardToCollector({ resourceSpans: msg.resourceSpans });
  }
});
```

**`Python`**

```python title="Python"
import asyncio
import json
import os

import websockets
from dotenv import load_dotenv

load_dotenv()

async def monitor_opentelemetry():
    uri = (
        "wss://api.elevenlabs.io/v1/convai/conversations/conv_9001k1zph3fkeh5s8xg9z90swaqa/monitor"
        "?events_format=opentelemetry"
    )
    headers = {"xi-api-key": os.getenv("ELEVENLABS_API_KEY")}

    async with websockets.connect(uri, extra_headers=headers) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("type") in ("connected", "history_complete"):
                continue
            if msg.get("resourceSpans"):
                forward_to_collector({"resourceSpans": msg["resourceSpans"]})

asyncio.run(monitor_opentelemetry())
```

## OTLP JSON 结构

所有入口的 OpenTelemetry 追踪数据均使用相同的 OTLP JSON 批处理布局：

```json
{
  "resourceSpans": [
    {
      "resource": {
        "attributes": [
          { "key": "service.name", "value": { "stringValue": "elevenlabs-convai" } },
          {
            "key": "elevenlabs.conversation_id",
            "value": { "stringValue": "conv_9001k1zph3fkeh5s8xg9z90swaqa" }
          }
        ]
      },
      "scopeSpans": [
        {
          "scope": { "name": "elevenlabs.convai", "version": "1.0.0" },
          "spans": [
            {
              "traceId": "32_hex_chars",
              "spanId": "16_hex_chars",
              "name": "elevenlabs.recv.agent_response",
              "startTimeUnixNano": "1700000000000000000",
              "endTimeUnixNano": "1700000001000000000",
              "status": { "code": 1 }
            }
          ]
        }
      ]
    }
  ]
}
```

## 限制

* 不支持直接推送到 OTLP gRPC 端点。
* 负载是采用 OTLP 导出格式的 JSON，而非在线传输的原始 protobuf。

## 相关文档

* [通话后 webhook](/docs/zh/eleven-agents/workflows/post-call-webhooks)
* [实时监控](/docs/zh/eleven-agents/guides/realtime-monitoring)