Skip to main content
本页介绍如何控制 LangSmith 将您的追踪发送到何处:

静态设置目标项目

LangSmith 使用 项目 的概念来分组追踪。如果未指定,项目将设置为 default 您可以设置 LANGSMITH_PROJECT 环境变量,为整个应用程序运行配置自定义项目名称。请在运行应用程序之前设置此项:
export LANGSMITH_PROJECT=my-custom-project
LANGSMITH_PROJECT 标志仅在 JS SDK 版本 >= 0.2.16 中受支持,如果您使用的是较旧版本,请改用 LANGCHAIN_PROJECT
如果指定的项目不存在,LangSmith 将在接收第一条追踪时自动创建它。

动态设置目标项目

您也可以在程序运行时以多种方式设置项目名称,具体取决于您如何注释代码以进行追踪。当您希望在同一个应用程序中将追踪记录到不同项目时,这非常有用:
  • 在装饰或配置时传递项目名称。
  • 在每次单独调用时覆盖它。
  • 在直接构造运行时设置它。
使用以下方法之一动态设置项目名称会覆盖由 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
# 确保设置了 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()

动态设置目标工作区

如果您需要根据运行时配置(例如,将不同用户或租户路由到单独的工作区)将追踪动态路由到不同的 LangSmith 工作区,方法因语言而异:
  • Python:使用具有 tracing_context 的特定于工作区的 LangSmith 客户端。
  • TypeScript:将自定义客户端传递给 traceable,或使用带有回调的 LangChainTracer
此方法适用于多租户应用程序,您希望在工作区级别按客户、环境或团队隔离追踪。

先决条件

通用跨工作区追踪

对于希望根据运行时逻辑(例如,客户 ID、租户或环境)将追踪动态路由到不同工作区的通用应用程序,请使用此方法。 关键组件:
  1. 为每个工作区初始化单独的 Client 实例,并指定各自的 workspace_id
  2. 使用 tracing_context(Python)或将特定于工作区的 client 传递给 traceable(TypeScript)来路由追踪。
  3. 通过应用程序的运行时配置传递工作区配置。
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"})

覆盖 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"}})

关键点

  • 通用跨工作区追踪:使用 tracing_context(Python)或将特定于工作区的 client 传递给 traceable(TypeScript)以将追踪动态路由到不同工作区。
  • LangGraph 跨工作区追踪:对于 LangGraph 应用程序,使用带有特定于工作区客户端的 LangChainTracer,并通过 callbacks 参数附加它。
  • LangSmith 部署覆盖:使用图生命周期上下文管理器(Python)根据运行时配置覆盖默认部署工作区。
  • 每个 Client 实例通过 workspaceId 参数维护与特定工作区的连接。
  • 您可以为每个路由自定义工作区和项目名称。
  • 此模式适用于任何 LangSmith 兼容的追踪(LangChain、OpenAI、自定义函数等)。
使用跨工作区追踪进行部署时,请确保您的服务密钥或 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_ENDPOINTSLANGSMITH_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?")
您还可以使用 updates 字段将额外字段(例如元数据或标签)合并到特定副本的运行中——主追踪保持不变。副本错误是非致命的:如果副本端点不可用,LangSmith 会记录错误,而不会影响主追踪。
认证不会在分布式追踪中传播。当追踪跨越多个服务时,LangSmith 会自动将副本 project_nameupdates 转发给下游服务,但不会转发 API 密钥或凭据。每个服务必须为副本目标配置自己的凭据。

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

如果所有副本都使用相同的 LangSmith 服务器,您可以省略 api_urlauth,仅指定 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?")

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