Skip to content

您可以通过环境变量静态设置或运行时动态设置追踪的目标项目。

静态设置目标项目

追踪概念部分所述,LangSmith 使用 Project(项目)的概念来对追踪进行分组。如果未指定,项目将设置为 default。您可以设置 LANGSMITH_PROJECT 环境变量来为整个应用程序运行配置自定义项目名称。这应在执行应用程序之前完成。

export LANGSMITH_PROJECT=my-custom-project

LANGSMITH_PROJECT 标志仅在 JS SDK 版本 >= 0.2.16 中受支持,如果您使用的是旧版本,请改用 LANGCHAIN_PROJECT

如果指定的项目不存在,它将在首次接收追踪时自动创建。

动态设置目标项目

您还可以根据为追踪标注代码的方式,以多种方法在程序运行时动态设置项目名称。当您希望在同一应用程序中将追踪记录到不同项目时,这非常有用。

使用以下任一方法动态设置项目名称会覆盖由 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!"}
]

# 使用带有 '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-4o-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-4o-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-4o-mini",
  messages=messages,
)
# 结束并提交运行
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"
});

// 调用 traceable 函数
await traceableCallOpenAI(messages, "gpt-4o-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();

动态设置目标工作区

如果您需要根据运行时配置(例如,将不同用户或租户路由到单独的工作区)动态地将追踪路由到不同的 LangSmith 工作区,Python 用户可以使用带有 tracing_context 的特定工作区 LangSmith 客户端,而 TypeScript 用户可以将自定义客户端传递给 traceable 或与回调一起使用 LangChainTracer

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

先决条件

通用的跨工作区追踪

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

关键组件:

  1. 为每个工作区使用其各自的 workspace_id 初始化单独的 Client 实例。
  2. 使用 tracing_context(Python)或将特定工作区的 client 传递给 traceable(TypeScript)来路由追踪。
  3. 通过应用程序的运行时配置传递工作区配置。
python
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
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);

  // 使用特定工作区的客户端创建 traceable 函数
  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 参数传递的运行时配置,将来自已部署代理的追踪路由到不同工作区时,这非常有用。

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 密钥
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
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: "" });

关键点

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

使用跨工作区追踪进行部署时,请确保您的 API 密钥对所有目标工作区都具有必要的权限。对于 LangSmith 部署,您必须将具有跨工作区访问权限的 API 密钥添加到环境变量中(例如,LS_CROSS_WORKSPACE_KEY),以覆盖部署生成的默认服务密钥。

LangChain 中文文档