> ## Documentation Index
> Fetch the complete documentation index at: https://cndoc-langchain.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 OpenTelemetry 进行追踪

> 在 LangSmith 中配置 OpenTelemetry 追踪，包括 LANGSMITH_OTEL_ENABLED 和使用 OpenTelemetry Collector 进行 OTEL 扇出。

LangSmith 支持基于 OpenTelemetry 的追踪，允许您从任何兼容 OpenTelemetry 的应用程序发送追踪数据。本指南涵盖了 LangChain 应用程序的自动检测以及其他框架的手动检测。

了解如何使用 OpenTelemetry 与 LangSmith 追踪您的 LLM 应用程序。

<Note>
  对于自托管安装或区域 SaaS，请在下面的请求中相应地更新 LangSmith URL：欧盟 (GCP) 使用 `eu.api.smith.langchain.com`；AWS 托管的美国 SaaS 使用 `aws.api.smith.langchain.com`。
</Note>

## 追踪 LangChain 应用程序

如果您使用 LangChain 或 LangGraph，请使用内置集成来追踪您的应用程序：

1. 安装支持 OpenTelemetry 的 LangSmith 包：

   <CodeGroup>
     ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
     pip install "langsmith[otel]"
     pip install langchain
     ```
   </CodeGroup>

   <Info>
     需要 Python SDK 版本 `langsmith>=0.3.18`。我们推荐 `langsmith>=0.4.25` 以获得重要的 OpenTelemetry 修复。
   </Info>

2. 在您的 LangChain/LangGraph 应用程序中，通过设置 `LANGSMITH_OTEL_ENABLED` 环境变量启用 OpenTelemetry 集成：

   <CodeGroup>
     ```bash Shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
     LANGSMITH_OTEL_ENABLED=true
     LANGSMITH_TRACING=true
     LANGSMITH_ENDPOINT=https://api.smith.langchain.com
     LANGSMITH_API_KEY=<your_langsmith_api_key>
     # 对于链接到多个工作区的 LangSmith API 密钥，请设置 LANGSMITH_WORKSPACE_ID 环境变量以指定使用哪个工作区。
     ```
   </CodeGroup>

3. 创建一个带有追踪功能的 LangChain 应用程序。例如：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   import os
   from langchain_openai import ChatOpenAI
   from langchain_core.prompts import ChatPromptTemplate

   # 创建一个链
   prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
   model = ChatOpenAI()
   chain = prompt | model

   # 运行链
   result = chain.invoke({"topic": "programming"})
   print(result.content)
   ```

4. 应用程序运行后，在 LangSmith 仪表板中查看追踪数据（[示例](https://smith.langchain.com/public/a762af6c-b67d-4f22-90a0-728df16baeba/r)）。

## 追踪非 LangChain 应用程序

对于非 LangChain 应用程序或自定义检测，您可以使用标准的 OpenTelemetry 客户端在 LangSmith 中追踪您的应用程序。（我们推荐 **langsmith ≥ 0.4.25**。）

1. 安装 OpenTelemetry SDK、OpenTelemetry 导出器包以及 OpenAI 包：

   <CodeGroup>
     ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
     pip install openai
     pip install opentelemetry-sdk
     pip install opentelemetry-exporter-otlp
     ```
   </CodeGroup>

2. 设置端点的环境变量，替换为您特定的值：

   <CodeGroup>
     ```bash Shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
     OTEL_EXPORTER_OTLP_ENDPOINT=https://api.smith.langchain.com/otel
     OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your langsmith api key>"
     ```
   </CodeGroup>

   <Note>
     根据您的 otel 导出器的配置方式，如果您只发送追踪数据，可能需要将 `/v1/traces` 附加到端点。
   </Note>

   <Note>
     如果您是自托管 LangSmith，请将基础端点替换为您的 LangSmith API 端点并附加 `/api/v1`。例如：`OTEL_EXPORTER_OTLP_ENDPOINT=https://ai-company.com/api/v1/otel`
   </Note>

   可选：指定一个不同于 "default" 的自定义项目名称：

   <CodeGroup>
     ```bash Shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
     OTEL_EXPORTER_OTLP_ENDPOINT=https://api.smith.langchain.com/otel
     OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your langsmith api key>,Langsmith-Project=<project name>"
     ```
   </CodeGroup>

3. 记录一个追踪。

   此代码设置一个 OTEL 追踪器和导出器，用于将追踪数据发送到 LangSmith。然后调用 OpenAI 并发送所需的 OpenTelemetry 属性。

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   from openai import OpenAI
   from opentelemetry import trace
   from opentelemetry.sdk.trace import TracerProvider
   from opentelemetry.sdk.trace.export import (
       BatchSpanProcessor,
   )
   from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

   client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

   otlp_exporter = OTLPSpanExporter(
       timeout=10,
   )

   trace.set_tracer_provider(TracerProvider())
   trace.get_tracer_provider().add_span_processor(
       BatchSpanProcessor(otlp_exporter)
   )

   tracer = trace.get_tracer(__name__)

   def call_openai():
       model = "gpt-5.4-mini"
       with tracer.start_as_current_span("call_open_ai") as span:
           span.set_attribute("langsmith.span.kind", "LLM")
           span.set_attribute("langsmith.metadata.user_id", "user_123")
           span.set_attribute("gen_ai.system", "OpenAI")
           span.set_attribute("gen_ai.request.model", model)
           span.set_attribute("llm.request.type", "chat")

           messages = [
               {"role": "system", "content": "You are a helpful assistant."},
               {
                   "role": "user",
                   "content": "Write a haiku about recursion in programming."
               }
           ]

           for i, message in enumerate(messages):
               span.set_attribute(f"gen_ai.prompt.{i}.content", str(message["content"]))
               span.set_attribute(f"gen_ai.prompt.{i}.role", str(message["role"]))

           completion = client.chat.completions.create(
               model=model,
               messages=messages
           )

           span.set_attribute("gen_ai.response.model", completion.model)
           span.set_attribute("gen_ai.completion.0.content", str(completion.choices[0].message.content))
           span.set_attribute("gen_ai.completion.0.role", "assistant")
           span.set_attribute("gen_ai.usage.prompt_tokens", completion.usage.prompt_tokens)
           span.set_attribute("gen_ai.usage.completion_tokens", completion.usage.completion_tokens)
           span.set_attribute("gen_ai.usage.total_tokens", completion.usage.total_tokens)

           return completion.choices[0].message

   if __name__ == "__main__":
       call_openai()
   ```

4. 在 LangSmith 仪表板中查看追踪数据（[示例](https://smith.langchain.com/public/4f2890b1-f105-44aa-a6cf-c777dcc27a37/r)）。

## 将追踪发送到备用提供商

虽然 LangSmith 是 OpenTelemetry 追踪的默认目标，但您也可以配置 OpenTelemetry 将追踪发送到其他可观测性平台。

<Info>
  在 LangSmith Python SDK **≥ 0.4.1** 中可用。我们推荐 **≥ 0.4.25**，其中包含改进 OTEL 导出和混合扇出稳定性的修复。
</Info>

### 使用环境变量进行全局配置

默认情况下，LangSmith OpenTelemetry 导出器会将数据发送到 LangSmith API OTEL 端点，但可以通过设置标准 OTEL 环境变量来自定义：

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
OTEL_EXPORTER_OTLP_ENDPOINT: 覆盖端点 URL
OTEL_EXPORTER_OTLP_HEADERS: 添加自定义头（LangSmith API 密钥和项目会自动添加）
OTEL_SERVICE_NAME: 设置自定义服务名称（默认为 "langsmith"）
```

LangSmith 默认使用 HTTP 追踪导出器。如果您想使用自己的追踪提供程序，可以：

1. 如上所示设置 OTEL 环境变量，或者
2. 在初始化 LangChain 组件之前设置全局追踪提供程序，LangSmith 将检测并使用它，而不是创建自己的。

### 配置备用 OTLP 端点

要将追踪发送到不同的提供程序，请使用提供程序的端点配置 OTLP 导出器：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import os

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# 为 LangChain 设置环境变量
os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_TRACING"] = "true"

# 为您的自定义端点配置 OTLP 导出器
provider = TracerProvider()
otlp_exporter = OTLPSpanExporter(
    # 更改为您的提供程序端点
    endpoint="https://otel.your-provider.com/v1/traces",
    # 添加任何所需的认证头
    headers={"api-key": "your-api-key"},
)
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# 创建并运行 LangChain 应用程序
prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
model = ChatOpenAI()
chain = prompt | model
result = chain.invoke({"topic": "programming"})
print(result.content)
```

<Info>
  混合追踪在版本 **≥ 0.4.1** 中可用。要**仅**将追踪发送到您的 OTEL 端点，请设置：

  `LANGSMITH_OTEL_ONLY="true"`
  （推荐：使用 **langsmith ≥ 0.4.25**。）
</Info>

## 支持的 OpenTelemetry 属性和事件映射

通过 OpenTelemetry 将追踪发送到 LangSmith 时，以下属性会映射到 LangSmith 字段：

### 核心 LangSmith 属性

| OpenTelemetry 属性               | LangSmith 字段                                 | 备注                                                               |
| ------------------------------ | -------------------------------------------- | ---------------------------------------------------------------- |
| `langsmith.trace.name`         | 运行名称                                         | 覆盖运行的 span 名称                                                    |
| `langsmith.span.kind`          | [运行类型](/langsmith/run-data-format#run-types) | 值：`llm`、`chain`、`tool`、`retriever`、`embedding`、`prompt`、`parser` |
| `langsmith.trace.session_id`   | 会话 ID                                        | 相关追踪的会话标识符                                                       |
| `langsmith.trace.session_name` | 会话名称                                         | 会话的名称                                                            |
| `langsmith.span.tags`          | 标签                                           | 附加到 span 的自定义标签（逗号分隔）                                            |
| `langsmith.metadata.{key}`     | `metadata.{key}`                             | 带有 langsmith 前缀的自定义元数据                                           |

### GenAI 标准属性

| OpenTelemetry 属性                        | LangSmith 字段                  | 备注                                                          |
| --------------------------------------- | ----------------------------- | ----------------------------------------------------------- |
| `gen_ai.system`                         | `metadata.ls_provider`        | GenAI 系统（例如 "openai"、"anthropic"）                           |
| `gen_ai.operation.name`                 | 运行类型                          | 将 "chat"/"completion" 映射为 "llm"，"embedding" 映射为 "embedding" |
| `gen_ai.prompt`                         | `inputs`                      | 发送给模型的输入提示                                                  |
| `gen_ai.completion`                     | `outputs`                     | 模型生成的输出                                                     |
| `gen_ai.prompt.{n}.role`                | `inputs.messages[n].role`     | 第 n 个输入消息的角色                                                |
| `gen_ai.prompt.{n}.content`             | `inputs.messages[n].content`  | 第 n 个输入消息的内容                                                |
| `gen_ai.prompt.{n}.message.role`        | `inputs.messages[n].role`     | 角色的替代格式                                                     |
| `gen_ai.prompt.{n}.message.content`     | `inputs.messages[n].content`  | 内容的替代格式                                                     |
| `gen_ai.completion.{n}.role`            | `outputs.messages[n].role`    | 第 n 个输出消息的角色                                                |
| `gen_ai.completion.{n}.content`         | `outputs.messages[n].content` | 第 n 个输出消息的内容                                                |
| `gen_ai.completion.{n}.message.role`    | `outputs.messages[n].role`    | 角色的替代格式                                                     |
| `gen_ai.completion.{n}.message.content` | `outputs.messages[n].content` | 内容的替代格式                                                     |
| `gen_ai.input.messages`                 | `inputs.messages`             | 输入消息数组                                                      |
| `gen_ai.output.messages`                | `outputs.messages`            | 输出消息数组                                                      |
| `gen_ai.tool.name`                      | `invocation_params.tool_name` | 工具名称，同时将运行类型设置为 "tool"                                      |

### GenAI 请求参数

| OpenTelemetry 属性                   | LangSmith 字段                          | 备注           |
| ---------------------------------- | ------------------------------------- | ------------ |
| `gen_ai.request.model`             | `invocation_params.model`             | 用于请求的模型名称    |
| `gen_ai.response.model`            | `invocation_params.model`             | 响应中返回的模型名称   |
| `gen_ai.request.temperature`       | `invocation_params.temperature`       | 温度设置         |
| `gen_ai.request.top_p`             | `invocation_params.top_p`             | Top-p 采样设置   |
| `gen_ai.request.max_tokens`        | `invocation_params.max_tokens`        | 最大 token 数设置 |
| `gen_ai.request.frequency_penalty` | `invocation_params.frequency_penalty` | 频率惩罚设置       |
| `gen_ai.request.presence_penalty`  | `invocation_params.presence_penalty`  | 存在惩罚设置       |
| `gen_ai.request.seed`              | `invocation_params.seed`              | 用于生成的随机种子    |
| `gen_ai.request.stop_sequences`    | `invocation_params.stop`              | 停止生成的序列      |
| `gen_ai.request.top_k`             | `invocation_params.top_k`             | Top-k 采样参数   |
| `gen_ai.request.encoding_formats`  | `invocation_params.encoding_formats`  | 输出编码格式       |

### GenAI 使用指标

| OpenTelemetry 属性                        | LangSmith 字段                      | 备注                 |
| --------------------------------------- | --------------------------------- | ------------------ |
| `gen_ai.usage.input_tokens`             | `usage_metadata.input_tokens`     | 使用的输入 token 数      |
| `gen_ai.usage.output_tokens`            | `usage_metadata.output_tokens`    | 使用的输出 token 数      |
| `gen_ai.usage.total_tokens`             | `usage_metadata.total_tokens`     | 使用的总 token 数       |
| `gen_ai.usage.prompt_tokens`            | `usage_metadata.input_tokens`     | 使用的输入 token 数（已弃用） |
| `gen_ai.usage.completion_tokens`        | `usage_metadata.output_tokens`    | 使用的输出 token 数（已弃用） |
| `gen_ai.usage.details.reasoning_tokens` | `usage_metadata.reasoning_tokens` | 使用的推理 token 数      |

### TraceLoop 属性

| OpenTelemetry 属性                         | LangSmith 字段     | 备注                                      |
| ---------------------------------------- | ---------------- | --------------------------------------- |
| `traceloop.entity.input`                 | `inputs`         | 来自 TraceLoop 的完整输入值                     |
| `traceloop.entity.output`                | `outputs`        | 来自 TraceLoop 的完整输出值                     |
| `traceloop.entity.name`                  | 运行名称             | 来自 TraceLoop 的实体名称                      |
| `traceloop.span.kind`                    | 运行类型             | 映射到 LangSmith 运行类型                      |
| `traceloop.llm.request.type`             | 运行类型             | "embedding" 映射为 "embedding"，其他映射为 "llm" |
| `traceloop.association.properties.{key}` | `metadata.{key}` | 带有 traceloop 前缀的自定义元数据                  |

### OpenInference 属性

| OpenTelemetry 属性          | LangSmith 字段             | 备注                       |
| ------------------------- | ------------------------ | ------------------------ |
| `input.value`             | `inputs`                 | 完整输入值，可以是字符串或 JSON       |
| `output.value`            | `outputs`                | 完整输出值，可以是字符串或 JSON       |
| `openinference.span.kind` | 运行类型                     | 将各种类型映射到 LangSmith 运行类型  |
| `llm.system`              | `metadata.ls_provider`   | LLM 系统提供程序               |
| `llm.model_name`          | `metadata.ls_model_name` | 来自 OpenInference 的模型名称   |
| `tool.name`               | 运行名称                     | 当 span 类型为 "TOOL" 时的工具名称 |
| `metadata`                | `metadata.*`             | 要合并的元数据的 JSON 字符串        |

### LLM 属性

| OpenTelemetry 属性             | LangSmith 字段                          | 备注             |
| ---------------------------- | ------------------------------------- | -------------- |
| `llm.input_messages`         | `inputs.messages`                     | 输入消息           |
| `llm.output_messages`        | `outputs.messages`                    | 输出消息           |
| `llm.token_count.prompt`     | `usage_metadata.input_tokens`         | 提示 token 计数    |
| `llm.token_count.completion` | `usage_metadata.output_tokens`        | 完成 token 计数    |
| `llm.token_count.total`      | `usage_metadata.total_tokens`         | 总 token 计数     |
| `llm.usage.total_tokens`     | `usage_metadata.total_tokens`         | 替代的总 token 计数  |
| `llm.invocation_parameters`  | `invocation_params.*`                 | 调用参数的 JSON 字符串 |
| `llm.presence_penalty`       | `invocation_params.presence_penalty`  | 存在惩罚           |
| `llm.frequency_penalty`      | `invocation_params.frequency_penalty` | 频率惩罚           |
| `llm.request.functions`      | `invocation_params.functions`         | 函数定义           |

### 提示模板属性

| OpenTelemetry 属性                | LangSmith 字段 | 备注                                   |
| ------------------------------- | ------------ | ------------------------------------ |
| `llm.prompt_template.variables` | 运行类型         | 将运行类型设置为 "prompt"，与 input.value 一起使用 |

### 检索器属性

| OpenTelemetry 属性                            | LangSmith 字段                        | 备注                  |
| ------------------------------------------- | ----------------------------------- | ------------------- |
| `retrieval.documents.{n}.document.content`  | `outputs.documents[n].page_content` | 第 n 个检索文档的内容        |
| `retrieval.documents.{n}.document.metadata` | `outputs.documents[n].metadata`     | 第 n 个检索文档的元数据（JSON） |

### 工具属性

| OpenTelemetry 属性 | LangSmith 字段                       | 备注                |
| ---------------- | ---------------------------------- | ----------------- |
| `tools`          | `invocation_params.tools`          | 工具定义数组            |
| `tool_arguments` | `invocation_params.tool_arguments` | 工具参数，作为 JSON 或键值对 |

### Logfire 属性

| OpenTelemetry 属性      | LangSmith 字段       | 备注                     |
| --------------------- | ------------------ | ---------------------- |
| `prompt`              | `inputs`           | Logfire 提示输入           |
| `all_messages_events` | `outputs`          | Logfire 消息事件输出         |
| `events`              | `inputs`/`outputs` | Logfire 事件数组，分割输入/选择事件 |

### OpenTelemetry 事件映射

| 事件名称                        | LangSmith 字段         | 备注                          |
| --------------------------- | -------------------- | --------------------------- |
| `gen_ai.content.prompt`     | `inputs`             | 从事件属性中提取提示内容                |
| `gen_ai.content.completion` | `outputs`            | 从事件属性中提取完成内容                |
| `gen_ai.system.message`     | `inputs.messages[]`  | 对话中的系统消息                    |
| `gen_ai.user.message`       | `inputs.messages[]`  | 对话中的用户消息                    |
| `gen_ai.assistant.message`  | `outputs.messages[]` | 对话中的助手消息                    |
| `gen_ai.tool.message`       | `outputs.messages[]` | 工具响应消息                      |
| `gen_ai.choice`             | `outputs`            | 带有完成原因的模型选择/响应              |
| `exception`                 | `status`, `error`    | 将状态设置为 "error" 并提取异常消息/堆栈跟踪 |

#### 事件属性提取

对于消息事件，提取以下属性：

* `content` → 消息内容
* `role` → 消息角色
* `id` → tool\_call\_id（用于工具消息）
* `gen_ai.event.content` → 完整消息 JSON

对于选择事件：

* `finish_reason` → 选择完成原因
* `message.content` → 选择消息内容
* `message.role` → 选择消息角色
* `tool_calls.{n}.id` → 工具调用 ID
* `tool_calls.{n}.function.name` → 工具函数名称
* `tool_calls.{n}.function.arguments` → 工具函数参数
* `tool_calls.{n}.type` → 工具调用类型

对于异常事件：

* `exception.message` → 错误消息
* `exception.stacktrace` → 错误堆栈跟踪（附加到消息）

## 实现示例

### 使用 LangSmith SDK 进行追踪

使用 LangSmith SDK 的 OpenTelemetry 辅助工具配置导出。以下示例[追踪一个 Google ADK 代理](/langsmith/trace-with-google-adk)：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import asyncio
from langsmith.integrations.otel import configure
from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService
from google.genai import types

# 配置 LangSmith OpenTelemetry 导出（无需 OTEL 环境变量或头）
configure(project_name="adk-otel-demo")


async def main():
    agent = LlmAgent(
        name="travel_assistant",
        model="gemini-2.5-flash-lite",
        instruction="You are a helpful travel assistant.",
    )

    session_service = InMemorySessionService()
    runner = Runner(app_name="travel_app", agent=agent, session_service=session_service)

    user_id = "user_123"
    session_id = "session_abc"
    await session_service.create_session(app_name="travel_app", user_id=user_id, session_id=session_id)

    new_message = types.Content(parts=[types.Part(text="Hi! Recommend a weekend trip to Paris.")], role="user")

    for event in runner.run(user_id=user_id, session_id=session_id, new_message=new_message):
        print(event)


if __name__ == "__main__":
    asyncio.run(main())
```

<Note>
  您无需设置 OTEL 环境变量或导出器。`configure()` 会自动为 LangSmith 配置它们；检测器（如 `GoogleADKInstrumentor`）创建 span。
</Note>

这是在 LangSmith 中生成的追踪数据的[示例](https://smith.langchain.com/public/d6d47eeb-511e-4fda-ad17-2caa7bd7150b/r)。

### 向追踪添加附件

LangSmith 支持[向追踪附加文件](/langsmith/upload-files-with-traces)。这在构建具有多模态输入或输出的代理时非常有用。使用 OpenTelemetry 追踪时也支持附件。

下面的示例[追踪一个 Google ADK 代理](/langsmith/trace-with-google-adk)并向追踪添加附件。它结合使用了 LangSmith 的 `OtelSpanProcessor` 和一个自定义的 `AttachmentSpanProcessor`，后者使用 [`on_end()`](https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.export.html#opentelemetry.sdk.trace.export.SimpleSpanProcessor.on_end) 向父 span 添加图像附件。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import asyncio
import base64
import json
from pathlib import Path
from dotenv import load_dotenv
from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService
from google.genai import types
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
from langsmith.integrations.otel import OtelSpanProcessor


class AttachmentSpanProcessor(SpanProcessor):
    """自定义 SpanProcessor，用于向调用 span 添加附件。"""

    def __init__(self):
        self.attachment_data = None

    def set_attachment(self, attachment_data):
        self.attachment_data = attachment_data

    def on_end(self, span):
        if span.name == "invocation" and self.attachment_data:
            attachments_json = json.dumps([self.attachment_data])
            span._attributes["langsmith.attachments"] = attachments_json

load_dotenv()

# 手动设置 TracerProvider
provider = TracerProvider()
trace.set_tracer_provider(provider)

# 首先添加附件处理器（在 LangSmith 处理器之前运行）
attachment_processor = AttachmentSpanProcessor()
provider.add_span_processor(attachment_processor)

# 其次添加 LangSmith 处理器（接收已修改的 span）
langsmith_processor = OtelSpanProcessor(project="travel-assistant")
provider.add_span_processor(langsmith_processor)

def get_flight_info(destination: str, departure_date: str) -> dict:
    """获取目的地的航班信息。"""
    return {
        "destination": destination,
        "departure_date": departure_date,
        "price": "$450",
        "duration": "5h 30m",
        "airline": "Example Airways"
    }

def get_hotel_recommendations(city: str, check_in: str) -> dict:
    """获取城市的酒店推荐。"""
    return {
        "city": city,
        "check_in": check_in,
        "hotels": [
            {"name": "Grand Plaza Hotel", "rating": 4.5, "price": "$120/night"},
            {"name": "City Center Inn", "rating": 4.2, "price": "$95/night"}
        ]
    }

async def main():
    # 准备附件
    receipt_path = Path("receipt-template-example.png")
    with open(receipt_path, "rb") as img_file:
        image_bytes = img_file.read()
        image_base64 = base64.b64encode(image_bytes).decode("ascii")

    attachment_data = {
        "name": "receipt-template-example",
        "content": image_base64,
        "mime_type": "image/jpeg",
    }

    attachment_processor.set_attachment(attachment_data)

    # 创建 ADK 代理
    agent = LlmAgent(
        name="travel_assistant",
        tools=[get_flight_info, get_hotel_recommendations],
        model="gemini-2.0-flash-exp",
        instruction="You are a helpful travel assistant that can help with flights and hotels.",
    )

    # 设置会话和运行器
    session_service = InMemorySessionService()
    runner = Runner(
        app_name="travel_app",
        agent=agent,
        session_service=session_service
    )

    await session_service.create_session(
        app_name="travel_app",
        user_id="traveler_456",
        session_id="session_789"
    )

    # 向代理发送消息
    new_message = types.Content(
        parts=[types.Part(text="I need to book a flight to Paris for March 15th and find a good hotel.")],
        role="user",
    )

    # 运行代理并处理事件
    events = runner.run(
        user_id="traveler_456",
        session_id="session_789",
        new_message=new_message,
    )

    for event in events:
        print(event)

if __name__ == "__main__":
    asyncio.run(main())
```

这是在 LangSmith 中生成的追踪数据的[示例](https://smith.langchain.com/public/9574f70a-b893-49fe-8c62-691bd114bf14/r)。

## 高级配置

### 使用 OpenTelemetry collector 进行扇出

当您需要 OTEL 扇出时，请使用 `LANGSMITH_OTEL_ENABLED=true`。配置您的应用程序发出一次 OTEL span，然后使用 OpenTelemetry Collector 将它们路由到 LangSmith 和任何其他可观测性后端。

当您正在追踪应用程序并希望进行多目标路由时，请使用此方法。如果您正在操作 LangSmith 平台基础设施遥测（来自 Kubernetes 上自托管 LangSmith 服务的日志、指标、追踪），请改用[为 LangSmith 遥测配置您的 collector](/langsmith/langsmith-collector) 指南。

对于更高级的场景，您可以使用 OpenTelemetry Collector 将您的遥测数据扇出到多个目标。这比在应用程序代码中配置多个导出器更具可扩展性。

1. 为您的环境[安装 OpenTelemetry Collector](https://opentelemetry.io/docs/collector/getting-started/)。

2. 创建一个配置文件（例如 `otel-collector-config.yaml`），导出到多个目标：

   ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   receivers:
     otlp:
       protocols:
         grpc:
           endpoint: 0.0.0.0:4317
         http:
           endpoint: 0.0.0.0:4318

   processors:
     batch:

   exporters:
     otlphttp/langsmith:
       endpoint: https://api.smith.langchain.com/otel/v1/traces
       headers:
         x-api-key: ${env:LANGSMITH_API_KEY}
         Langsmith-Project: my_project
     otlphttp/other_provider:
       endpoint: https://otel.your-provider.com/v1/traces
       headers:
         api-key: ${env:OTHER_PROVIDER_API_KEY}

   service:
     pipelines:
       traces:
         receivers: [otlp]
         processors: [batch]
         exporters: [otlphttp/langsmith, otlphttp/other_provider]
   ```

3. 配置您的应用程序发送到 collector：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   import os
   from opentelemetry import trace
   from opentelemetry.sdk.trace import TracerProvider
   from opentelemetry.sdk.trace.export import BatchSpanProcessor
   from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
   from langchain_openai import ChatOpenAI
   from langchain_core.prompts import ChatPromptTemplate

   # 指向您的本地 OpenTelemetry Collector
   otlp_exporter = OTLPSpanExporter(
       endpoint="http://localhost:4318/v1/traces"
   )
   provider = TracerProvider()
   processor = BatchSpanProcessor(otlp_exporter)
   provider.add_span_processor(processor)
   trace.set_tracer_provider(provider)

   # 为 LangChain 设置环境变量
   os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
   os.environ["LANGSMITH_TRACING"] = "true"

   # 创建并运行 LangChain 应用程序
   prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
   model = ChatOpenAI()
   chain = prompt | model
   result = chain.invoke({"topic": "programming"})
   print(result.content)
   ```

此方法具有以下几个优点：

* 所有遥测目标的集中配置
* 减少应用程序代码中的开销
* 更好的可扩展性和弹性
* 无需更改应用程序代码即可添加或删除目标

### 使用 LangChain 和 OpenTelemetry 进行分布式追踪

当您的 LLM 应用程序跨越多个服务或进程时，分布式追踪至关重要。OpenTelemetry 的上下文传播功能确保追踪在服务边界之间保持连接。

#### 分布式追踪中的上下文传播

在分布式系统中，上下文传播在服务之间传递追踪元数据，以便将相关的 span 链接到同一追踪：

* **Trace ID**：整个追踪的唯一标识符
* **Span ID**：当前 span 的唯一标识符
* **采样决策**：指示是否应采样此追踪

#### 使用 LangChain 设置分布式追踪

要启用跨多个服务的分布式追踪：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import os
from opentelemetry import trace
from opentelemetry.propagate import inject, extract
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
import requests
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# 设置 OpenTelemetry 追踪提供程序
provider = TracerProvider()
otlp_exporter = OTLPSpanExporter(
    endpoint="https://api.smith.langchain.com/otel/v1/traces",
    headers={"x-api-key": os.getenv("LANGSMITH_API_KEY"), "Langsmith-Project": "my_project"}
)
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)

# 服务 A：创建一个 span 并将上下文传播到服务 B
def service_a():
    with tracer.start_as_current_span("service_a_operation") as span:
        # 创建一个链
        prompt = ChatPromptTemplate.from_template("Summarize: {text}")
        model = ChatOpenAI()
        chain = prompt | model

        # 运行链
        result = chain.invoke({"text": "OpenTelemetry is an observability framework"})

        # 将上下文传播到服务 B
        headers = {}
        inject(headers)  # 将追踪上下文注入到头中

        # 使用追踪上下文调用服务 B
        response = requests.post(
            "http://service-b.example.com/process",
            headers=headers,
            json={"summary": result.content}
        )
        return response.json()

# 服务 B：提取上下文并继续追踪
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/process", methods=["POST"])
def service_b_endpoint():
    # 从请求头中提取追踪上下文
    context = extract(request.headers)
    with tracer.start_as_current_span("service_b_operation", context=context) as span:
        data = request.json
        summary = data.get("summary", "")

        # 使用另一个 LLM 链处理摘要
        prompt = ChatPromptTemplate.from_template("Analyze the sentiment of: {text}")
        model = ChatOpenAI()
        chain = prompt | model
        result = chain.invoke({"text": summary})

        return jsonify({"analysis": result.content})

if __name__ == "__main__":
    app.run(port=5000)
```

***

<div className="source-links">
  <Callout icon="terminal-2">
    [将这些文档连接](/use-these-docs)到 Claude、VSCode 等，通过 MCP 获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/langsmith/trace-with-opentelemetry.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
