> ## 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.

# 将追踪记录到特定项目

> 使用环境变量或 SDK 将 LangSmith 追踪路由到指定项目，而非默认项目。

本页介绍如何控制 LangSmith 将您的追踪发送到何处：

* [静态设置目标项目](#静态设置目标项目)
* [动态设置目标项目](#动态设置目标项目)
* [动态设置目标工作区](#动态设置目标工作区)
* [使用副本将追踪写入多个目标](#使用副本将追踪写入多个目标)

## 静态设置目标项目

LangSmith 使用 [*项目*](/langsmith/observability-concepts#projects) 的概念来分组追踪。如果未指定，项目将设置为 `default`。

您可以设置 `LANGSMITH_PROJECT` 环境变量，为整个应用程序运行配置自定义项目名称。请在运行应用程序之前设置此项：

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
export LANGSMITH_PROJECT=my-custom-project
```

<Warning>
  `LANGSMITH_PROJECT` 标志仅在 JS SDK 版本 >= 0.2.16 中受支持，如果您使用的是较旧版本，请改用 `LANGCHAIN_PROJECT`。
</Warning>

如果指定的项目不存在，LangSmith 将在接收第一条追踪时自动创建它。

## 动态设置目标项目

您也可以在程序运行时以多种方式设置项目名称，具体取决于您如何[注释代码以进行追踪](/langsmith/annotate-code)。当您希望在同一个应用程序中将追踪记录到不同项目时，这非常有用：

* 在装饰或配置时传递项目名称。
* 在每次单独调用时覆盖它。
* 在直接构造运行时设置它。

<Note>
  使用以下方法之一动态设置项目名称会覆盖由 `LANGSMITH_PROJECT` 环境变量设置的项目名称。
</Note>

<CodeGroup>
  ```python Python expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import openai
  from langsmith import traceable
  from langsmith.run_trees import RunTree

  client = openai.Client()
  messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"}
  ]

  # 使用带有 'project_name' 参数的 @traceable 装饰器将追踪记录到 LangSmith
  # 确保设置了 LANGSMITH_TRACING 环境变量，以便 @traceable 正常工作
  @traceable(
    run_type="llm",
    name="OpenAI Call Decorator",
    project_name="My Project"
  )
  def call_openai(
    messages: list[dict], model: str = "gpt-5.4-mini"
  ) -> str:
    return client.chat.completions.create(
        model=model,
        messages=messages,
    ).choices[0].message.content

  # 调用装饰函数
  call_openai(messages)

  # 您也可以通过 project_name 参数指定项目
  # 这将覆盖 @traceable 装饰器中指定的 project_name
  call_openai(
    messages,
    langsmith_extra={"project_name": "My Overridden Project"},
  )

  # 包装的 OpenAI 客户端接受与 @traceable 装饰函数相同的 langsmith_extra 参数，
  # 并自动将追踪记录到 LangSmith。
  # 确保设置了 LANGSMITH_TRACING 环境变量，以便包装器正常工作。
  from langsmith import wrappers
  wrapped_client = wrappers.wrap_openai(client)
  wrapped_client.chat.completions.create(
    model="gpt-5.4-mini",
    messages=messages,
    langsmith_extra={"project_name": "My Project"},
  )

  # 或者，创建一个 RunTree 对象
  # 您可以使用 project_name 参数设置项目名称
  rt = RunTree(
    run_type="llm",
    name="OpenAI Call RunTree",
    inputs={"messages": messages},
    project_name="My Project"
  )
  chat_completion = client.chat.completions.create(
    model="gpt-5.4-mini",
    messages=messages,
  )
  # 结束并提交运行
  rt.end(outputs=chat_completion)
  rt.post()
  ```

  ```typescript TypeScript expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import OpenAI from "openai";
  import { traceable } from "langsmith/traceable";
  import { wrapOpenAI } from "langsmith/wrappers";
  import { RunTree} from "langsmith";

  const client = new OpenAI();
  const messages = [
    {role: "system", content: "You are a helpful assistant."},
    {role: "user", content: "Hello!"}
  ];

  const traceableCallOpenAI = traceable(async (messages: {role: string, content: string}[], model: string) => {
    const completion = await client.chat.completions.create({
        model: model,
        messages: messages,
    });
    return completion.choices[0].message.content;
  },{
    run_type: "llm",
    name: "OpenAI Call Traceable",
    project_name: "My Project"
  });

  // 调用可追踪函数
  await traceableCallOpenAI(messages, "gpt-5.4-mini");

  // 创建并使用 RunTree 对象
  const rt = new RunTree({
    run_type: "llm",
    name: "OpenAI Call RunTree",
    inputs: { messages },
    project_name: "My Project"
  });
  await rt.postRun();

  // 执行聊天补全并在 RunTree 中处理
  rt.end({outputs: chatCompletion});
  await rt.patchRun();
  ```

  ```java Java expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import com.langchain.smith.otel.OtelConfig;
  import com.langchain.smith.otel.OtelSpanCreator;
  import com.langchain.smith.otel.OtelTraceExporter;
  import io.opentelemetry.api.trace.Span;
  import io.opentelemetry.api.trace.StatusCode;
  import io.opentelemetry.api.trace.Tracer;
  import java.time.Duration;
  import java.util.HashMap;
  import java.util.Map;

  /**
   * 简单示例：将单个 OpenTelemetry 追踪发送到 LangSmith。
   *
   * 用法：
   *   export LANGSMITH_API_KEY=your_api_key
   *   export LANGSMITH_PROJECT=your_project_name  # 可选，默认为 "default"
   */
  public class OtelLangSmithSimpleExample {
      public static void main(String[] args) throws Exception {
          // 获取 API 密钥和项目名称
          String apiKey = System.getenv("LANGSMITH_API_KEY");
          if (apiKey == null || apiKey.isEmpty()) {
              System.err.println("错误：需要 LANGSMITH_API_KEY 环境变量！");
              return;
          }

          String projectName = System.getenv("LANGSMITH_PROJECT");
          if (projectName == null || projectName.isEmpty()) {
              projectName = "default";
          }

          // 配置导出器
          Map<String, String> headers = new HashMap<>();
          headers.put("x-api-key", apiKey);
          headers.put("Langsmith-Project", projectName);

          OtelConfig config = OtelConfig.builder()
                  .enabled(true)
                  .endpoint("https://api.smith.langchain.com/otel/v1/traces")
                  .headers(headers)
                  .timeout(Duration.ofSeconds(30))
                  .serviceName("langsmith-java-simple")
                  .build();

          OtelTraceExporter exporter = OtelTraceExporter.fromConfig(config);
          Tracer tracer = exporter.getTracer();

          // 创建一个简单的 span
          Span span = OtelSpanCreator.createLlmSpan(
                  tracer, "simple.llm.call", "openai", "gpt-4", projectName, null);

          try {
              OtelSpanCreator.setInput(span, "Hello, world!");
              Thread.sleep(100); // 模拟处理
              OtelSpanCreator.setOutput(span, "Hello! How can I help you?");
              OtelSpanCreator.setTokenUsage(span, 5, 8);
              span.setStatus(StatusCode.OK);
          } finally {
              span.end();
          }

          // 刷新并关闭
          exporter.flush().join(5, java.util.concurrent.TimeUnit.SECONDS);
          exporter.shutdown().join(2, java.util.concurrent.TimeUnit.SECONDS);

          System.out.println("✓ 追踪已发送到 LangSmith！");
      }
  }
  ```
</CodeGroup>

## 动态设置目标工作区

如果您需要根据运行时配置（例如，将不同用户或租户路由到单独的工作区）将追踪动态路由到不同的 LangSmith [工作区](/langsmith/administration-overview#workspaces)，方法因语言而异：

* **Python**：使用具有 [`tracing_context`](/langsmith/annotate-code#use-the-trace-context-manager-python-only) 的特定于工作区的 LangSmith 客户端。
* **TypeScript**：将自定义客户端传递给 [`traceable`](/langsmith/annotate-code#use-%40traceable-%2F-traceable)，或使用带有回调的 `LangChainTracer`。

此方法适用于多租户应用程序，您希望在工作区级别按客户、环境或团队隔离追踪。

### 先决条件

* 一个具有访问多个工作区权限的 [LangSmith API 密钥](/langsmith/create-account-api-key)。
* 每个目标工作区的[工作区 ID](/langsmith/set-up-hierarchy#set-up-a-workspace)。

### 通用跨工作区追踪

对于希望根据运行时逻辑（例如，客户 ID、租户或环境）将追踪动态路由到不同工作区的通用应用程序，请使用此方法。

**关键组件：**

1. 为每个工作区初始化单独的 `Client` 实例，并指定各自的 `workspace_id`。
2. 使用 `tracing_context`（Python）或将特定于工作区的 `client` 传递给 `traceable`（TypeScript）来路由追踪。
3. 通过应用程序的运行时配置传递工作区配置。

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import os
  import contextlib
  from langsmith import Client, traceable, tracing_context

  # 具有访问多个工作区权限的 API 密钥
  api_key = os.getenv("LS_CROSS_WORKSPACE_KEY")

  # 为不同工作区初始化客户端
  workspace_a_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_A_ID>"  # 例如 "abc123..."
  )

  workspace_b_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_B_ID>"  # 例如 "def456..."
  )

  # 示例：基于客户 ID 进行路由
  def get_workspace_client(customer_id: str):
      """根据客户路由到相应的工作区。"""
      if customer_id.startswith("premium_"):
          return workspace_a_client, "premium-customer-traces"
      else:
          return workspace_b_client, "standard-customer-traces"

  @traceable
  def process_request(data: dict, customer_id: str):
      """使用特定于工作区的追踪处理客户请求。"""
      # 您的业务逻辑在此处
      return {"status": "success", "data": data}

  # 使用 tracing_context 路由到相应的工作区
  def handle_customer_request(customer_id: str, request_data: dict):
      client, project_name = get_workspace_client(customer_id)

      # 此上下文内的所有内容都将被追踪到选定的工作区
      with tracing_context(enabled=True, client=client, project_name=project_name):
          result = process_request(request_data, customer_id)

      return result

  # 示例用法
  handle_customer_request("premium_user_123", {"query": "Hello"})
  handle_customer_request("standard_user_456", {"query": "Hi"})
  ```

  ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { Client } from "langsmith";
  import { traceable } from "langsmith/traceable";

  // 具有访问多个工作区权限的 API 密钥
  const apiKey = process.env.LS_CROSS_WORKSPACE_KEY;

  // 为不同工作区初始化客户端
  const workspaceAClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_A_ID>", // 例如 "abc123..."
  });

  const workspaceBClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_B_ID>", // 例如 "def456..."
  });

  // 示例：基于客户 ID 进行路由
  function getWorkspaceClient(customerId: string): {
    client: Client;
    projectName: string;
  } {
    if (customerId.startsWith("premium_")) {
      return {
        client: workspaceAClient,
        projectName: "premium-customer-traces",
      };
    } else {
      return {
        client: workspaceBClient,
        projectName: "standard-customer-traces",
      };
    }
  }

  // 通过将客户端传递给 traceable 将追踪路由到相应的工作区
  async function handleCustomerRequest(
    customerId: string,
    requestData: Record<string, any>
  ) {
    const { client, projectName } = getWorkspaceClient(customerId);

    // 使用特定于工作区的客户端创建可追踪函数
    const processRequest = traceable(
      async (data: Record<string, any>, customerId: string) => {
        // 您的业务逻辑在此处
        return { status: "success", data };
      },
      {
        name: "process_request",
        client,
        project_name: projectName,
      }
    );

    return await processRequest(requestData, customerId);
  }

  // 示例用法
  await handleCustomerRequest("premium_user_123", { query: "Hello" });
  await handleCustomerRequest("standard_user_456", { query: "Hi" });
  ```
</CodeGroup>

### 覆盖 LangSmith 部署的默认工作区

当[部署代理](/langsmith/deployment)到 LangSmith 时，您可以使用图生命周期上下文管理器覆盖追踪发送到的默认工作区。当您希望根据通过 `config` 参数传递的运行时配置，将已部署代理的追踪路由到不同工作区时，这非常有用。

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import os
  import contextlib
  from typing_extensions import TypedDict
  from langgraph.graph import StateGraph
  from langgraph.graph.state import RunnableConfig
  from langsmith import Client, tracing_context

  # 具有访问多个工作区权限的 API 密钥
  api_key = os.getenv("LS_CROSS_WORKSPACE_KEY")

  # 为不同工作区初始化客户端
  workspace_a_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_A_ID>"
  )

  workspace_b_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_B_ID>"
  )

  # 定义用于工作区路由的配置模式
  class Configuration(TypedDict):
      workspace_id: str

  # 定义图状态
  class State(TypedDict):
      response: str

  def greeting(state: State, config: RunnableConfig) -> State:
      """生成特定于工作区的问候语。"""
      workspace_id = config.get("configurable", {}).get("workspace_id", "workspace_a")

      if workspace_id == "workspace_a":
          response = "Hello from Workspace A!"
      elif workspace_id == "workspace_b":
          response = "Hello from Workspace B!"
      else:
          response = "Hello from the default workspace!"

      return {"response": response}

  # 构建基础图
  base_graph = (
      StateGraph(state_schema=State, config_schema=Configuration)
      .add_node("greeting", greeting)
      .set_entry_point("greeting")
      .set_finish_point("greeting")
      .compile()
  )

  @contextlib.asynccontextmanager
  async def graph(config):
      """根据配置将追踪动态路由到不同工作区。"""
      # 从配置中提取 workspace_id
      workspace_id = config.get("configurable", {}).get("workspace_id", "workspace_a")

      # 路由到相应的工作区
      if workspace_id == "workspace_a":
          client = workspace_a_client
          project_name = "production-traces"
      elif workspace_id == "workspace_b":
          client = workspace_b_client
          project_name = "development-traces"
      else:
          client = workspace_a_client
          project_name = "default-traces"

      # 为选定的工作区应用追踪上下文
      with tracing_context(enabled=True, client=client, project_name=project_name):
          yield base_graph

  # 用法：使用不同的工作区配置进行调用
  # await graph({"configurable": {"workspace_id": "workspace_a"}})
  # await graph({"configurable": {"workspace_id": "workspace_b"}})
  ```

  ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { Client } from "langsmith";
  import { LangChainTracer } from "@langchain/core/tracers/tracer_langchain";
  import { StateGraph, Annotation } from "@langchain/langgraph";

  // 具有访问多个工作区权限的 API 密钥
  const apiKey = process.env.LS_CROSS_WORKSPACE_KEY;

  // 为不同工作区初始化客户端
  const workspaceAClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_A_ID>", // 例如 "abc123..."
  });

  const workspaceBClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_B_ID>", // 例如 "def456..."
  });

  // 定义图状态
  const StateAnnotation = Annotation.Root({
    response: Annotation<string>(),
  });

  async function greeting(state: typeof StateAnnotation.State, config: any) {
    const workspaceId = config?.configurable?.workspace_id || "workspace_a";

    let response: string;
    if (workspaceId === "workspace_a") {
      response = "Hello from Workspace A!";
    } else if (workspaceId === "workspace_b") {
      response = "Hello from Workspace B!";
    } else {
      response = "Hello from the default workspace!";
    }

    return { response };
  }

  // 构建基础图
  const baseGraph = new StateGraph(StateAnnotation)
    .addNode("greeting", greeting)
    .addEdge("__start__", "greeting")
    .addEdge("greeting", "__end__")
    .compile();

  // 获取特定于工作区的客户端和项目的辅助函数
  function getWorkspaceConfig(workspaceId: string): {
    client: Client;
    projectName: string;
  } {
    if (workspaceId === "workspace_a") {
      return { client: workspaceAClient, projectName: "production-traces" };
    } else if (workspaceId === "workspace_b") {
      return { client: workspaceBClient, projectName: "development-traces" };
    }
    return { client: workspaceAClient, projectName: "default-traces" };
  }

  // 使用特定于工作区的追踪调用图
  async function invokeWithWorkspaceTracing(
    workspaceId: string,
    input: typeof StateAnnotation.State
  ) {
    const { client, projectName } = getWorkspaceConfig(workspaceId);

    // 使用特定于工作区的客户端创建 LangChainTracer
    const tracer = new LangChainTracer({
      client,
      projectName,
    });

    // 通过回调附加追踪器来调用图
    // 所有追踪都将路由到选定的工作区
    return await baseGraph.invoke(input, {
      configurable: { workspace_id: workspaceId },
      callbacks: [tracer],
    });
  }

  // 示例用法
  await invokeWithWorkspaceTracing("workspace_a", { response: "" });
  await invokeWithWorkspaceTracing("workspace_b", { response: "" });
  ```
</CodeGroup>

### 关键点

* **通用跨工作区追踪**：使用 `tracing_context`（Python）或将特定于工作区的 `client` 传递给 `traceable`（TypeScript）以将追踪动态路由到不同工作区。
* **LangGraph 跨工作区追踪**：对于 [LangGraph 应用程序](/oss/python/langgraph/overview)，使用带有特定于工作区客户端的 `LangChainTracer`，并通过 `callbacks` 参数附加它。
* **LangSmith 部署覆盖**：使用图生命周期上下文管理器（Python）根据运行时配置覆盖默认部署工作区。
* 每个 `Client` 实例通过 `workspaceId` 参数维护与特定工作区的连接。
* 您可以为每个路由自定义工作区和项目名称。
* 此模式适用于任何 LangSmith 兼容的追踪（LangChain、OpenAI、自定义函数等）。

<Note>
  使用跨工作区追踪进行部署时，请确保您的服务密钥或 PAT 具有所有目标工作区的必要权限。我们建议在生产部署中使用多工作区服务密钥。对于 LangSmith 部署，您必须将具有跨工作区访问权限的服务密钥添加到环境变量中（例如 `LS_CROSS_WORKSPACE_KEY`），以覆盖部署生成的默认服务密钥。
</Note>

## 使用副本将追踪写入多个目标

副本允许您**同时**将每条追踪发送到多个项目或工作区。与每条追踪只发送到一个目标的动态路由模式不同，副本会将追踪并行复制到所有配置的目标。

副本可用于：

* 将生产追踪镜像到暂存或个人项目中进行调试。
* 写入多个工作区以实现多租户隔离，而无需更改任何应用程序代码。
* 将追踪发送到同一服务器下的不同项目，并按副本覆盖元数据。

### 通过环境变量配置副本

将 `LANGSMITH_RUNS_ENDPOINTS` 环境变量设置为 JSON 值。支持两种格式：

* **对象格式**：将每个端点 URL 映射到其 API 密钥：

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  export LANGSMITH_RUNS_ENDPOINTS='{
  "https://api.smith.langchain.com": "ls__key_workspace_a",
  "https://api.smith.langchain.com": "ls__key_workspace_b"
  }'
  ```

* **数组格式**：副本对象列表，当您需要多个副本指向同一 URL 或希望为每个副本设置 `project_name` 时很有用：

  ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  export LANGSMITH_RUNS_ENDPOINTS='[
  {"api_url": "https://api.smith.langchain.com", "api_key": "ls__key1", "project_name": "project-prod"},
  {"api_url": "https://api.smith.langchain.com", "api_key": "ls__key2", "project_name": "project-staging"}
  ]'
  ```

<Warning>
  您不能同时使用 `LANGSMITH_RUNS_ENDPOINTS` 和 `LANGSMITH_ENDPOINT`。如果同时设置两者，LangSmith 会引发错误。仅使用其中一个来配置您的端点。
</Warning>

### 在运行时配置副本

您也可以在代码中直接传递副本，当目标因请求或租户而异时，这非常有用。

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from langsmith import traceable, tracing_context
  from langsmith.run_trees import WriteReplica, ApiKeyAuth

  @traceable
  def my_pipeline(query: str) -> str:
      # 您的应用程序逻辑在此处
      return f"Answer to: {query}"

  replicas = [
      WriteReplica(
          api_url="https://api.smith.langchain.com",
          auth=ApiKeyAuth(api_key="ls__key_workspace_a"),
          project_name="project-prod",
      ),
      WriteReplica(
          api_url="https://api.smith.langchain.com",
          auth=ApiKeyAuth(api_key="ls__key_workspace_b"),
          project_name="project-staging",
          # 可选：覆盖复制运行上的字段
          updates={"metadata": {"environment": "staging"}},
      ),
  ]

  with tracing_context(replicas=replicas):
      my_pipeline("What is LangSmith?")
  ```

  ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { traceable } from "langsmith/traceable";

  const myPipeline = traceable(
    async (query: string): Promise<string> => {
      // 您的应用程序逻辑在此处
      return `Answer to: ${query}`;
    },
    {
      name: "my_pipeline",
      replicas: [
        {
          apiUrl: "https://api.smith.langchain.com",
          apiKey: "ls__key_workspace_a",
          projectName: "project-prod",
        },
        {
          apiUrl: "https://api.smith.langchain.com",
          apiKey: "ls__key_workspace_b",
          projectName: "project-staging",
          // 可选：覆盖复制运行上的字段
          updates: { metadata: { environment: "staging" } },
        },
      ],
    }
  );

  await myPipeline("What is LangSmith?");
  ```
</CodeGroup>

您还可以使用 `updates` 字段将额外字段（例如[元数据或标签](/langsmith/ls-metadata-parameters)）合并到特定副本的运行中——主追踪保持不变。副本错误是非致命的：如果副本端点不可用，LangSmith 会记录错误，而不会影响主追踪。

<Warning>
  认证不会在分布式追踪中传播。当追踪跨越多个服务时，LangSmith 会自动将副本 `project_name` 和 `updates` 转发给下游服务，但不会转发 API 密钥或凭据。每个服务必须为副本目标配置自己的凭据。
</Warning>

### 在同一服务器内复制（仅项目副本）

如果所有副本都使用相同的 LangSmith 服务器，您可以省略 `api_url` 和 `auth`，仅指定 `project_name`。SDK 会重用默认客户端凭据：

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from langsmith import traceable, tracing_context
  from langsmith.run_trees import WriteReplica

  @traceable
  def my_pipeline(query: str) -> str:
      return f"Answer to: {query}"

  with tracing_context(
      replicas=[
          WriteReplica(project_name="project-prod"),
          WriteReplica(project_name="project-staging", updates={"metadata": {"env": "staging"}}),
      ]
  ):
      my_pipeline("What is LangSmith?")
  ```

  ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { traceable } from "langsmith/traceable";

  const myPipeline = traceable(
    async (query: string) => `Answer to: ${query}`,
    {
      name: "my_pipeline",
      replicas: [
        { projectName: "project-prod" },
        { projectName: "project-staging", updates: { metadata: { env: "staging" } } },
      ],
    }
  );

  await myPipeline("What is LangSmith?");
  ```
</CodeGroup>

### 在 LangSmith 和 OpenTelemetry 目标之间路由

您可以在运行时决定给定调用是将追踪发送到 LangSmith、发送到 OpenTelemetry (OTel) 后端，还是同时发送到两者，而无需重新部署或修改应用程序逻辑。当您希望按环境甚至按请求在可观测性后端之间切换时，这非常有用，在运行时做出决策。

使用 `tracing_mode` 构造函数参数或 `LANGSMITH_TRACING_MODE` 环境变量设置追踪模式。两者接受相同的值；显式的 `tracing_mode` 参数始终优先于环境变量：

* **`"langsmith"`（默认）**：将追踪原生发送到 LangSmith。
* **`"otel"`**：将追踪作为 OpenTelemetry span 导出到配置的 OTel 后端。
* **`"hybrid"`（仅限 Python）**：从单个副本同时发送到 LangSmith 和 OTel 后端。

<Note>
  如果您正在使用 `Client` 上已弃用的 `otel_enabled` 参数（仅限 Python），请迁移到 `tracing_mode`：`Client(otel_enabled=True)` → `Client(tracing_mode="hybrid")`。`otel_enabled` 参数将在下一个次要版本中移除。
</Note>

将配置好的 `Client` 直接传递到副本中，以在运行时应用所需的模式：

<CodeGroup>
  ```python Python expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from langsmith import Client, traceable, tracing_context
  from langsmith.run_trees import WriteReplica
  from langsmith.wrappers import wrap_openai
  import openai

  # 创建具有不同追踪模式的客户端
  ls_client = Client()                            # tracing_mode="langsmith"（默认）
  otel_client = Client(tracing_mode="otel")       # tracing_mode="otel"
  hybrid_client = Client(tracing_mode="hybrid")   # tracing_mode="hybrid"（两者）

  openai_client = wrap_openai(openai.Client())

  @traceable()
  def joke():
      response = openai_client.chat.completions.create(
          model="gpt-4o-mini",
          messages=[{"role": "user", "content": "Tell me a short joke."}],
      )
      return response.choices[0].message.content

  # 在单次调用中混合副本的追踪模式：
  # 一个副本通过 LangSmith 的原生格式发送，另一个作为 OTel span 发送。
  with tracing_context(replicas=[
      WriteReplica(client=ls_client),    # tracing_mode="langsmith"
      WriteReplica(client=otel_client),  # tracing_mode="otel"
  ]):
      joke()

  # 或者，单个混合副本同时发送到两者。
  with tracing_context(replicas=[WriteReplica(client=hybrid_client)]):
      joke()

  # 在运行时交换副本列表——例如基于功能标志或环境。
  def get_replicas(send_to_otel: bool):
      replicas = [WriteReplica(client=ls_client)]
      if send_to_otel:
          replicas.append(WriteReplica(client=otel_client))
      return replicas

  with tracing_context(replicas=get_replicas(send_to_otel=True)):   # LangSmith + OTel
      joke()

  with tracing_context(replicas=get_replicas(send_to_otel=False)):  # 仅 LangSmith
      joke()
  ```

  ```typescript TypeScript expandable wrap theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { Client } from "langsmith";
  import { traceable } from "langsmith/traceable";
  import { wrapOpenAI } from "langsmith/wrappers";
  import OpenAI from "openai";

  // 注意：tracingMode: "otel" 需要在创建客户端之前初始化 OTel SDK
  // （TracerProvider、SpanProcessor 等）。
  // 有关设置详情，请参阅 OpenTelemetry 集成指南。

  // 创建具有不同追踪模式的客户端
  const lsClient = new Client();                           // tracingMode: "langsmith"（默认）
  const otelClient = new Client({ tracingMode: "otel" });  // tracingMode: "otel"

  const openaiClient = wrapOpenAI(new OpenAI());

  async function jokeImpl() {
    const response = await openaiClient.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: "Tell me a short joke." }],
    });
    return response.choices[0].message.content;
  }

  // 在单次可追踪调用中混合副本的追踪模式：
  // 主客户端通过 LangSmith 发送，副本作为 OTel span 发送。
  const joke = traceable(jokeImpl, {
    name: "joke",
    client: lsClient,                    // tracingMode: "langsmith"（默认）
    replicas: [{ client: otelClient }],  // tracingMode: "otel"
  });
  await joke();

  // 动态构建副本以进行运行时切换——例如基于功能标志。
  function buildReplicas(sendToOtel: boolean) {
    return sendToOtel ? [{ client: otelClient }] : [];
  }

  const sendToOtel = process.env.ROUTE_TO_OTEL === "true";
  const jokeDynamic = traceable(jokeImpl, {
    name: "joke",
    client: lsClient,
    replicas: buildReplicas(sendToOtel),
  });
  await jokeDynamic();
  ```
</CodeGroup>

每个 `Client` 上的 `tracing_mode` 决定了该副本的导出路径。在 Python 中，`"hybrid"` 模式在单个副本中处理两个目标。在 TypeScript 中，“同时发送到两者”的情况使用两个单独的副本，每个客户端一个，因为没有 `"hybrid"` 模式。由于每个副本独立解析自己的客户端，您也可以在单个 `tracing_context` 中混合模式，例如保持一个副本发送到 LangSmith，同时通过第二个副本将相同的追踪转发到 OTel 收集器。

***

<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/log-traces-to-project.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
