静态设置目标项目
LangSmith 使用 项目 的概念来对追踪记录进行分组。如果未指定,项目将默认为default。
您可以设置 LANGSMITH_PROJECT 环境变量,为整个应用程序运行配置自定义项目名称。在运行应用程序之前进行设置:
export LANGSMITH_PROJECT=my-custom-project
LANGSMITH_PROJECT 标志仅在 JS SDK 版本 >= 0.2.16 中受支持,如果您使用的是旧版本,请改用 LANGCHAIN_PROJECT。动态设置目标项目
您也可以在程序运行时通过多种方式设置项目名称,具体取决于您为追踪标注代码的方式。当您希望将同一应用程序中的追踪记录记录到不同项目时,这非常有用:- 在装饰或配置时传递项目名称。
- 为每次单独调用覆盖项目名称。
- 在直接构造运行时设置项目名称。
使用以下方法动态设置项目名称会覆盖通过
LANGSMITH_PROJECT 环境变量设置的项目名称。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
# 确保为 @traceable 设置了 LANGSMITH_TRACING 环境变量才能正常工作
@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()
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();
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;
/**
* 简单示例:向 LangSmith 发送单条 OpenTelemetry 追踪记录。
*
* 用法:
* 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("ERROR: 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!");
}
}
动态设置目标工作区
如果您需要根据运行时配置(例如,将不同用户或租户路由到单独的工作区)动态将追踪记录路由到不同的 LangSmith 工作区,其方法因语言而异:- Python:使用特定于工作区的 LangSmith 客户端,并配合
tracing_context。 - TypeScript:将自定义客户端传递给
traceable,或在回调中使用LangChainTracer。
@traceable 装饰的自定义函数。
先决条件
- 一个可访问多个工作区的 LangSmith API 密钥。
- 每个目标工作区的工作区 ID。
通用跨工作区追踪
将此方法用于通用应用程序,在这些应用程序中,您希望根据运行时逻辑(例如,客户 ID、租户或环境)动态将追踪记录路由到不同的工作区。 关键组件:- 使用各自的
workspace_id为每个工作区初始化单独的Client实例。 - 使用
tracing_context(Python)或将特定于工作区的client传递给traceable(TypeScript)来路由追踪记录。 - 通过应用程序的运行时配置传递工作区配置。
- 为每条路由覆盖工作区和项目名称,以进一步组织每个工作区内的追踪记录。
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"})
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" });
为 LangSmith 部署覆盖默认工作区
在将代理部署到 LangSmith 时,您可以使用图生命周期上下文管理器覆盖追踪记录发送到的默认工作区。当您希望根据通过config 参数传递的运行时配置将已部署代理的追踪记录路由到不同的工作区时,这非常有用。
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"}})
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,
});
// 使用通过回调附加的 tracer 调用图
// 所有追踪记录都将被路由到所选工作区
return await baseGraph.invoke(input, {
configurable: { workspace_id: workspaceId },
callbacks: [tracer],
});
}
// 示例用法
await invokeWithWorkspaceTracing("workspace_a", { response: "" });
await invokeWithWorkspaceTracing("workspace_b", { response: "" });
在使用跨工作区追踪进行部署时,请确保您的服务密钥或 PAT 具有访问所有目标工作区的必要权限。对于生产部署,我们建议使用多工作区服务密钥。对于 LangSmith 部署,您必须将具有跨工作区访问权限的服务密钥添加到您的环境变量(例如
LS_CROSS_WORKSPACE_KEY)中,以覆盖部署生成的默认服务密钥。使用副本将追踪记录写入多个目的地
副本允许您同时将每条追踪记录发送到多个项目或工作区。与每条追踪记录只去一个目的地的动态路由模式不同,副本会并行将追踪记录复制到所有已配置的目的地。 副本可用于:- 将生产环境的追踪记录镜像到暂存或个人项目中进行调试。
- 在不更改任何应用程序代码的情况下,写入多个工作区以实现多租户隔离。
- 将追踪记录发送到同一服务器下的不同项目,并允许每个副本覆盖元数据。
通过环境变量配置副本
将LANGSMITH_RUNS_ENDPOINTS 环境变量设置为 JSON 值。支持两种格式:
-
对象格式:将每个端点 URL 映射到其 API 密钥:
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时很有用: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"} ]'
您不能同时使用
LANGSMITH_RUNS_ENDPOINTS 和 LANGSMITH_ENDPOINT。如果两者都设置,LangSmith 会引发错误。请仅使用其中一个来配置您的端点。在运行时配置副本
您也可以在代码中直接传递副本,当目的地因请求或租户而异时,这很有用。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?")
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?");
updates 字段将附加字段(例如元数据或标签)合并到特定副本的运行中——主追踪记录保持不变。副本错误是非致命的:如果副本端点不可用,LangSmith 会记录错误,但不会影响主追踪记录。
认证信息不会在分布式追踪中传播。当一条追踪记录跨越多个服务时,LangSmith 会自动将副本的
project_name 和 updates 转发到下游服务,但不会转发 API 密钥或凭据。每个服务必须为其副本目的地配置自己的凭据。在同一服务器内复制(仅项目副本)
如果您的所有副本都使用同一个 LangSmith 服务器,您可以省略api_url 和 auth,仅指定 project_name。SDK 将重用默认的客户端凭据:
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?")
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 时传递它。
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"}
# 从原始运行 ID 和项目名称计算辅助副本的运行 ID
secondary_run_id = compute_run_id_for_secondary_replica(
run.id,
secondary_project,
)
# 每个副本都有自己的项目;解析相应的项目 UUID
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
# 使用原始运行 ID 向主副本提交反馈
primary_client.create_feedback(
trace_id=run.id,
key="user-rating",
score=1,
session_id=primary_session_id,
)
# 使用计算出的运行 ID 向辅助副本提交反馈
secondary_client.create_feedback(
trace_id=secondary_run_id,
key="user-rating",
score=1,
session_id=secondary_session_id,
)
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) {
// 计算辅助副本的运行 ID
const secondaryRunId = computeRunIdForSecondaryReplica(
primaryRunId,
secondaryProject
);
// 每个副本都有自己的项目;解析相应的项目 UUID
const { id: primarySessionId } = await primaryClient.createProject({
projectName: primaryProject,
upsert: true,
});
const { id: secondarySessionId } = await secondaryClient.createProject({
projectName: secondaryProject,
upsert: true,
});
// 使用原始运行 ID 向主副本提交反馈
await primaryClient.createFeedback({
runId: primaryRunId,
sessionId: primarySessionId,
key: "user-rating",
score: 1,
});
// 使用计算出的运行 ID 向辅助副本提交反馈
await secondaryClient.createFeedback({
runId: secondaryRunId,
sessionId: secondarySessionId,
key: "user-rating",
score: 1,
});
}
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 后端。
如果您正在使用已弃用的
Client 上的 otel_enabled 参数(仅限 Python),请迁移到 tracing_mode:Client(otel_enabled=True) → Client(tracing_mode="hybrid")。otel_enabled 参数将在下一个次要版本中移除。Client 直接传递到副本中,以在运行时应用所需的模式:
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()
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();
Client 上的 tracing_mode 决定了该副本的导出路径。在 Python 中,"hybrid" 模式在单个副本内处理两个目的地。在 TypeScript 中,“同时发送到两者”的情况使用两个单独的副本,每个副本对应一个客户端,因为没有 "hybrid" 模式。由于每个副本独立解析其自己的客户端,您也可以在单个 tracing_context 内混合模式,例如,保留一个副本发送到 LangSmith,同时通过第二个副本将相同的追踪记录转发到 OTel 收集器。
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

