Skip to content
LangSmith 文档版本 跟随上游 源快照 a075e6e8 更新于 2026-10-18

将追踪记录到特定项目

使用环境变量或 SDK,将 LangSmith 追踪路由到一个命名项目而非默认项目。

本页介绍如何控制 LangSmith 将你的追踪发往何处:

静态设置目标项目

LangSmith 用一个项目的概念来分组追踪。若未指定,项目被设为 default。

你可以设置 LANGSMITH_PROJECT 环境变量,为整个应用运行配置一个自定义项目名。在运行你的应用之前设置它:

bash
export LANGSMITH_PROJECT=my-custom-project

WARNING

LANGSMITH_PROJECT 标志仅在 JS SDK 0.2.16 或更高版本中受支持,如果你使用较早的版本,请改用 LANGCHAIN_PROJECT。

如果所指定的项目不存在,LangSmith 会在第一条追踪被摄取时自动创建它。

动态设置目标项目

你也可以在程序运行时以多种方式设置项目名,具体取决于你如何为你的代码插桩以进行追踪。当你想把追踪记录到同一应用内的不同项目时,这很有用:

  • 在装饰或配置时传入项目名。
  • 在单个调用上覆盖它。
  • 在直接构造一个运行时设置它。

NOTE

使用下列方法之一动态设置项目名,会覆盖由 LANGSMITH_PROJECT 环境变量设置的项目名。

python
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!"}
]

# Use the @traceable decorator with the 'project_name' parameter to log traces to LangSmith
# Ensure that the LANGSMITH_TRACING environment variables is set for @traceable to work
@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 the decorated function
call_openai(messages)

# You can also specify the Project via the project_name parameter
# This will override the project_name specified in the @traceable decorator
call_openai(
  messages,
  langsmith_extra={"project_name": "My Overridden Project"},
)

# The wrapped OpenAI client accepts all the same langsmith_extra parameters
# as @traceable decorated functions, and logs traces to LangSmith automatically.
# Ensure that the LANGSMITH_TRACING environment variables is set for the wrapper to work.
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"},
)

# Alternatively, create a RunTree object
# You can set the project name using the project_name parameter
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,
)
# End and submit the run
rt.end(outputs=chat_completion)
rt.post()
typescript
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"
});

// Call the traceable function
await traceableCallOpenAI(messages, "gpt-5.4-mini");

// Create and use a RunTree object
const rt = new RunTree({
  run_type: "llm",
  name: "OpenAI Call RunTree",
  inputs: { messages },
  project_name: "My Project"
});
await rt.postRun();

// Execute a chat completion and handle it within RunTree
rt.end({outputs: chatCompletion});
await rt.patchRun();
java
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;

/**
 * Simple example: Send a single OpenTelemetry trace to LangSmith.
 *
 * Usage:
 *   export LANGSMITH_API_KEY=your_api_key
 *   export LANGSMITH_PROJECT=your_project_name  # Optional, defaults to "default"
 */
public class OtelLangSmithSimpleExample {
    public static void main(String[] args) throws Exception {
        // Get API key and project name
        String apiKey = System.getenv("LANGSMITH_API_KEY");
        if (apiKey == null || apiKey.isEmpty()) {
            System.err.println("ERROR: LANGSMITH_API_KEY environment variable is required!");
            return;
        }

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

        // Configure exporter
        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();

        // Create a simple span
        Span span = OtelSpanCreator.createLlmSpan(
                tracer, "simple.llm.call", "openai", "gpt-4", projectName, null);

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

        // Flush and shutdown
        exporter.flush().join(5, java.util.concurrent.TimeUnit.SECONDS);
        exporter.shutdown().join(2, java.util.concurrent.TimeUnit.SECONDS);

        System.out.println("✓ Trace sent to LangSmith!");
    }
}

动态设置目标工作区

如果你需要根据运行时配置,动态地将追踪路由到不同的 LangSmith 工作区(例如把不同用户或租户路由到分开的工作区),方法因语言而异:

  • Python:使用工作区专属的 LangSmith 客户端,配合 tracing_context。
  • TypeScript:将一个自定义客户端传给 traceable,或使用带回调的 LangChainTracer。

这种方式对工作区级别按客户、环境或团队隔离追踪的多租户应用很有用。它适用于任何与 LangSmith 兼容的追踪,包括 LangChain、OpenAI 和用 @traceable 装饰的自定义函数。

前提条件

通用的跨工作区追踪

把这种方式用于你希望根据运行时逻辑(例如客户 ID、租户或环境)动态地将追踪路由到不同工作区的通用应用。

关键组件:

  1. 用各自的 workspace_id 为每个工作区初始化独立的 Client 实例。
  2. 使用 tracing_context(Python)或将工作区专属的 client 传给 traceable(TypeScript)来路由追踪。
  3. 通过你应用的运行时配置传入工作区配置。
  4. 按路由同时覆盖工作区和项目名,以在每个工作区内进一步组织追踪。
python
import os
import contextlib
from langsmith import Client, traceable, tracing_context

# API key with access to multiple workspaces
api_key = os.getenv("LS_CROSS_WORKSPACE_KEY")

# Initialize clients for different workspaces
workspace_a_client = Client(
    api_key=api_key,
    api_url="https://api.smith.langchain.com",
    workspace_id="<YOUR_WORKSPACE_A_ID>"  # e.g., "abc123..."
)

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

# Example: Route based on customer ID
def get_workspace_client(customer_id: str):
    """Route to appropriate workspace based on customer."""
    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):
    """Process a customer request with workspace-specific tracing."""
    # Your business logic here
    return {"status": "success", "data": data}

# Use tracing_context to route to the appropriate workspace
def handle_customer_request(customer_id: str, request_data: dict):
    client, project_name = get_workspace_client(customer_id)

    # Everything within this context will be traced to the selected workspace
    with tracing_context(enabled=True, client=client, project_name=project_name):
        result = process_request(request_data, customer_id)

    return result

# Example usage
handle_customer_request("premium_user_123", {"query": "Hello"})
handle_customer_request("standard_user_456", {"query": "Hi"})
typescript
import { Client } from "langsmith";
import { traceable } from "langsmith/traceable";

// API key with access to multiple workspaces
const apiKey = process.env.LS_CROSS_WORKSPACE_KEY;

// Initialize clients for different workspaces
const workspaceAClient = new Client({
  apiKey: apiKey,
  apiUrl: "https://api.smith.langchain.com",
  workspaceId: "<YOUR_WORKSPACE_A_ID>", // e.g., "abc123..."
});

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

// Example: Route based on customer 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",
    };
  }
}

// Route traces to the appropriate workspace by passing the client to traceable
async function handleCustomerRequest(
  customerId: string,
  requestData: Record<string, any>
) {
  const { client, projectName } = getWorkspaceClient(customerId);

  // Create a traceable function with the workspace-specific client
  const processRequest = traceable(
    async (data: Record<string, any>, customerId: string) => {
      // Your business logic here
      return { status: "success", data };
    },
    {
      name: "process_request",
      client,
      project_name: projectName,
    }
  );

  return await processRequest(requestData, customerId);
}

// Example usage
await handleCustomerRequest("premium_user_123", { query: "Hello" });
await handleCustomerRequest("standard_user_456", { query: "Hi" });

为 LangSmith 部署覆盖默认工作区

当你把 agent部署到 LangSmith 时,你可以用一个图 lifespan 上下文管理器来覆盖追踪被发送到的默认工作区。当你想根据通过 config 参数传入的运行时配置,把来自某个已部署 agent 的追踪路由到不同工作区时,这很有用。

python
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 key with access to multiple workspaces
api_key = os.getenv("LS_CROSS_WORKSPACE_KEY")

# Initialize clients for different workspaces
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>"
)

# Define configuration schema for workspace routing
class Configuration(TypedDict):
    workspace_id: str

# Define the graph state
class State(TypedDict):
    response: str

def greeting(state: State, config: RunnableConfig) -> State:
    """Generate a workspace-specific greeting."""
    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}

# Build the base graph
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):
    """Dynamically route traces to different workspaces based on configuration."""
    # Extract workspace_id from the configuration
    workspace_id = config.get("configurable", {}).get("workspace_id", "workspace_a")

    # Route to the appropriate workspace
    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"

    # Apply the tracing context for the selected workspace
    with tracing_context(enabled=True, client=client, project_name=project_name):
        yield base_graph

# Usage: Invoke with different workspace configurations
# await graph({"configurable": {"workspace_id": "workspace_a"}})
# await graph({"configurable": {"workspace_id": "workspace_b"}})
typescript
import { Client } from "langsmith";
import { LangChainTracer } from "@langchain/core/tracers/tracer_langchain";
import { StateGraph, Annotation } from "@langchain/langgraph";

// API key with access to multiple workspaces
const apiKey = process.env.LS_CROSS_WORKSPACE_KEY;

// Initialize clients for different workspaces
const workspaceAClient = new Client({
  apiKey: apiKey,
  apiUrl: "https://api.smith.langchain.com",
  workspaceId: "<YOUR_WORKSPACE_A_ID>", // e.g., "abc123..."
});

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

// Define the graph state
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 };
}

// Build the base graph
const baseGraph = new StateGraph(StateAnnotation)
  .addNode("greeting", greeting)
  .addEdge("__start__", "greeting")
  .addEdge("greeting", "__end__")
  .compile();

// Helper to get workspace-specific client and project
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" };
}

// Invoke the graph with workspace-specific tracing
async function invokeWithWorkspaceTracing(
  workspaceId: string,
  input: typeof StateAnnotation.State
) {
  const { client, projectName } = getWorkspaceConfig(workspaceId);

  // Create a LangChainTracer with the workspace-specific client
  const tracer = new LangChainTracer({
    client,
    projectName,
  });

  // Invoke the graph with the tracer attached via callbacks
  // All traces will be routed to the selected workspace
  return await baseGraph.invoke(input, {
    configurable: { workspace_id: workspaceId },
    callbacks: [tracer],
  });
}

// Example usage
await invokeWithWorkspaceTracing("workspace_a", { response: "" });
await invokeWithWorkspaceTracing("workspace_b", { response: "" });

NOTE

用跨工作区追踪部署时,确保你的服务密钥或 PAT 对所有目标工作区拥有必要权限。我们推荐在生产部署中使用一个多工作区服务密钥。对于 LangSmith 部署,你必须向环境变量中添加一个带跨工作区访问权限的服务密钥(例如 LS_CROSS_WORKSPACE_KEY),以覆盖你的部署生成的默认服务密钥。

用副本将追踪写入多个目的地

副本让你能够同时把每条追踪发送到多个项目或工作区。与每条追踪去一个目的地的动态路由模式不同,副本并行地把追踪复制到所有已配置的目的地。

副本可能对以下情形有用:

  • 将生产追踪镜像到一个 staging 或个人项目以进行调试。
  • 在不更改任何应用代码的情况下,为多租户隔离写入多个工作区。
  • 在每副本元数据覆盖下,把追踪发送到同一服务器下的不同项目。

通过环境变量配置副本

将 LANGSMITH_RUNS_ENDPOINTS 环境变量设为一个 JSON 值。支持两种格式:

  • 对象格式:把每个端点 URL 映射到它的 API 密钥:

    bash
    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
    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_ENDPOINT 的同时使用 LANGSMITH_RUNS_ENDPOINTS。如果你两者都设,LangSmith 会抛出一个错误。只用其一配置你的端点。

在运行时配置副本

你也可以直接在代码中传入副本,这在目的地因请求或租户而异时很有用。

python
from langsmith import traceable, tracing_context
from langsmith.run_trees import WriteReplica, ApiKeyAuth

@traceable
def my_pipeline(query: str) -> str:
    # Your application logic here
    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",
        # Optionally override fields on the replicated run
        updates={"metadata": {"environment": "staging"}},
    ),
]

with tracing_context(replicas=replicas):
    my_pipeline("What is LangSmith?")
typescript
import { traceable } from "langsmith/traceable";

const myPipeline = traceable(
  async (query: string): Promise<string> => {
    // Your application logic here
    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",
        // Optionally override fields on the replicated run
        updates: { metadata: { environment: "staging" } },
      },
    ],
  }
);

await myPipeline("What is LangSmith?");

你还可以使用 updates 字段,仅为某个特定副本将额外字段(例如元数据或标签)合并到一次运行中——主追踪保持不变。副本错误是非致命的:如果某个副本端点不可用,LangSmith 会记录该错误而不影响主追踪。

WARNING

身份验证不会在分布式追踪中传播。当一条追踪跨越多个服务时,LangSmith 会自动将副本的 project_name 和 updates 转发给下游服务,但不会转发 API 密钥或凭据。每个服务都必须为副本目的地配置它自己的凭据。

在同一服务器内复制(仅项目副本)

如果你所有的副本都使用同一个 LangSmith 服务器,你可以省略 api_url 和 auth,只指定一个 project_name。SDK 会复用默认的客户端凭据:

python
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
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?");

在所有副本实例上留下反馈

当你使用副本时,每个副本都会收到每次运行的一个副本。要为某个特定副本上的一次运行提交反馈,你需要该副本的运行 ID。从 Python SDK 0.10.8 和 JS SDK 0.8.5 起,你可以指定一个副本作为主副本,并使用 compute_run_id_for_secondary_replica 来确定性地计算所有其他副本的运行 ID。

主副本保持原来的运行 ID 不变。每个次副本都会收到一个从原运行 ID 和该次副本的项目名派生的确定性运行 ID。使用 compute_run_id_for_secondary_replica(original_run_id, project_name) 计算次运行 ID,并在调用 create_feedback 时传入它。

python
from langsmith import (
    Client,
    compute_run_id_for_secondary_replica,
    trace,
    tracing_context,
)

primary_client = Client(api_key="primary-key")
secondary_client = Client(api_key="secondary-key")

primary_project = "production"
secondary_project = "backup-project"

with tracing_context(
    replicas=[
        {
            "project_name": primary_project,
            "primary": True,
            "client": primary_client,
        },
        {
            "project_name": secondary_project,
            "primary": False,
            "client": secondary_client,
        },
    ]
):
    with trace("answer-question", inputs={"question": "Capital of France?"}) as run:
        run.outputs = {"answer": "Paris"}

# Compute the secondary replica's run ID from the original run ID and project name
secondary_run_id = compute_run_id_for_secondary_replica(
    run.id,
    secondary_project,
)

# Each replica has its own project; resolve the corresponding project UUIDs
primary_session_id = primary_client.create_project(project_name=primary_project, upsert=True).id
secondary_session_id = secondary_client.create_project(project_name=secondary_project, upsert=True).id

# Submit feedback to the primary replica using the original run ID
primary_client.create_feedback(
    trace_id=run.id,
    key="user-rating",
    score=1,
    session_id=primary_session_id,
)

# Submit feedback to the secondary replica using the computed run ID
secondary_client.create_feedback(
    trace_id=secondary_run_id,
    key="user-rating",
    score=1,
    session_id=secondary_session_id,
)
typescript
import { Client } from "langsmith";
import { traceable, getCurrentRunTree } from "langsmith/traceable";
import { computeRunIdForSecondaryReplica } from "langsmith";

const primaryClient = new Client({ apiKey: "primary-key" });
const secondaryClient = new Client({ apiKey: "secondary-key" });

const primaryProject = "production";
const secondaryProject = "backup-project";

let primaryRunId: string | undefined;

const answerQuestion = traceable(
  async (question: string) => {
    primaryRunId = getCurrentRunTree()?.id;
    return { answer: "Paris" };
  },
  {
    name: "answer-question",
    client: primaryClient,
    replicas: [
      {
        projectName: primaryProject,
        primary: true,
        client: primaryClient,
      },
      {
        projectName: secondaryProject,
        primary: false,
        client: secondaryClient,
      },
    ],
  }
);

await answerQuestion("Capital of France?");

if (primaryRunId) {
  // Compute the secondary replica's run ID
  const secondaryRunId = computeRunIdForSecondaryReplica(
    primaryRunId,
    secondaryProject
  );

  // Each replica has its own project; resolve the corresponding project UUIDs
  const { id: primarySessionId } = await primaryClient.createProject({
    projectName: primaryProject,
    upsert: true,
  });
  const { id: secondarySessionId } = await secondaryClient.createProject({
    projectName: secondaryProject,
    upsert: true,
  });

  // Submit feedback to the primary replica using the original run ID
  await primaryClient.createFeedback({
    runId: primaryRunId,
    sessionId: primarySessionId,
    key: "user-rating",
    score: 1,
  });

  // Submit feedback to the secondary replica using the computed run ID
  await secondaryClient.createFeedback({
    runId: secondaryRunId,
    sessionId: secondarySessionId,
    key: "user-rating",
    score: 1,
  });
}

NOTE

compute_run_id_for_secondary_replica / computeRunIdForSecondaryReplica 助手在 Python SDK 0.10.8 或更高版本和 JS SDK 0.8.5 或更高版本中可用。如果你使用更早的 SDK 版本,请升级以使用此功能。

在 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 参数将在下一个次版本中移除。

将一个配置好的 Client 直接传入一个副本,以在运行时应用所需的模式:

python
from langsmith import Client, traceable, tracing_context
from langsmith.run_trees import WriteReplica
from langsmith.wrappers import wrap_openai
import openai

# Create clients with different tracing modes
ls_client = Client()                            # tracing_mode="langsmith" (default)
otel_client = Client(tracing_mode="otel")       # tracing_mode="otel"
hybrid_client = Client(tracing_mode="hybrid")   # tracing_mode="hybrid" (both)

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

# Mix tracing modes across replicas in a single invocation:
# one replica sends via LangSmith's native format, another as OTel spans.
with tracing_context(replicas=[
    WriteReplica(client=ls_client),    # tracing_mode="langsmith"
    WriteReplica(client=otel_client),  # tracing_mode="otel"
]):
    joke()

# Alternatively, a single hybrid replica sends to both simultaneously.
with tracing_context(replicas=[WriteReplica(client=hybrid_client)]):
    joke()

# Swap replica lists at runtime — e.g. based on a feature flag or environment.
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 only
    joke()
typescript
import { Client } from "langsmith";
import { traceable } from "langsmith/traceable";
import { wrapOpenAI } from "langsmith/wrappers";
import OpenAI from "openai";

// Note: tracingMode: "otel" requires OTel SDK initialization
// (TracerProvider, SpanProcessor, etc.) before creating the client.
// See the OpenTelemetry integration guide for setup details.

// Create clients with different tracing modes
const lsClient = new Client();                           // tracingMode: "langsmith" (default)
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;
}

// Mix tracing modes across replicas in a single traceable call:
// the primary client sends via LangSmith, the replica sends as OTel spans.
const joke = traceable(jokeImpl, {
  name: "joke",
  client: lsClient,                    // tracingMode: "langsmith" (default)
  replicas: [{ client: otelClient }],  // tracingMode: "otel"
});
await joke();

// Build replicas dynamically for runtime switching — e.g. based on a feature flag.
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();

每个 Client 上的 tracing_mode 决定该副本的导出路径。在 Python 中,"hybrid" 模式在单个副本内处理两个目的地。在 TypeScript 中,由于没有 "hybrid" 模式,"两者都发" 的情况使用两个独立的副本,每个对应一个客户端。由于每个副本都独立解析自己的客户端,你也可以在单个 tracing_context 内混合模式,例如让一个副本保持发送到 LangSmith,同时通过第二个副本把同一追踪转发给一个 OTel 收集器。


BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。