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

自定义埋点

直接在你的代码中进行埋点,以控制哪些函数会被追踪以及它们在 LangSmith 中的呈现方式。

直接向你的代码添加埋点,可以让你精确控制应用追踪哪些函数、记录哪些输入和输出,以及你的追踪层级结构如何组织。三种核心埋点方式是:

本页还涵盖:

对于 LangChain(Python 或 JS/TS),请参阅针对 LangChain 的说明。

INFO

如果你使用的 LLM 服务商或智能体框架带有内置的 LangSmith 集成,请改为参阅集成概览

前置条件

在开始追踪之前,请设置以下环境变量:

  • LANGSMITH_TRACING=true:启用追踪。设置此项即可在不更改代码的情况下开启或关闭追踪。

NOTE

LANGSMITH_TRACING 控制 @traceable 装饰器和 trace 上下文管理器。要在不更改环境变量的情况下在运行时为 @traceable 覆盖此设置,请使用 tracing_context(enabled=True/False)(Python),或直接将 tracingEnabled 传给 traceable(JS/TS)。RunTree 对象不受上述任何控制的影响;它们在提交时始终会将数据发送到 LangSmith。

  • LANGSMITH_API_KEY:你的 LangSmith API key。

  • 默认情况下,LangSmith 会将追踪记录到名为 default 的项目中。要记录到其他项目,请设置 LANGSMITH_PROJECT。更多详情请参阅将追踪记录到特定项目。

使用 @traceable / traceable

将 @traceable(Python)、traceable(TypeScript)、traceable(Kotlin)或 Tracing.traceFunction(Java)应用于任意函数,即可使其成为可被追踪的运行。LangSmith 会自动处理嵌套调用之间的上下文传播。

以下示例追踪一个简单的流水线:run_pipeline 调用 format_prompt 来构建消息,调用 invoke_llm 来调用模型,并调用 parse_output 来提取结果。

每个函数都会被单独追踪,并且由于它们是从 run_pipeline(同样被追踪)内部调用的,LangSmith 会自动将它们嵌套为子运行。invoke_llm 使用 run_type="llm" 将其标记为 LLM 调用,以便 LangSmith 能正确渲染 token 数和延迟:

python
from langsmith import traceable
from openai import Client

openai = Client()

@traceable
def format_prompt(subject):
  return [
      {
          "role": "system",
          "content": "You are a helpful assistant.",
      },
      {
          "role": "user",
          "content": f"What's a good name for a store that sells {subject}?"
      }
  ]

@traceable(run_type="llm")
def invoke_llm(messages):
  return openai.chat.completions.create(
      messages=messages, model="gpt-5.4-mini", temperature=0
  )

@traceable
def parse_output(response):
  return response.choices[0].message.content

@traceable
def run_pipeline():
  messages = format_prompt("colorful socks")
  response = invoke_llm(messages)
  return parse_output(response)

run_pipeline()
typescript
import { traceable } from "langsmith/traceable";
import OpenAI from "openai";

const openai = new OpenAI();

const formatPrompt = traceable((subject: string) => {
  return [
    {
      role: "system" as const,
      content: "You are a helpful assistant.",
    },
    {
      role: "user" as const,
      content: `What's a good name for a store that sells ${subject}?`,
    },
  ];
},{ name: "formatPrompt" });

const invokeLLM = traceable(
  async ({ messages }: { messages: { role: string; content: string }[] }) => {
      return openai.chat.completions.create({
          model: "gpt-5.4-mini",
          messages: messages,
          temperature: 0,
      });
  },
  { run_type: "llm", name: "invokeLLM" }
);

const parseOutput = traceable(
  (response: any) => {
      return response.choices[0].message.content;
  },
  { name: "parseOutput" }
);

const runPipeline = traceable(
  async () => {
      const messages = await formatPrompt("colorful socks");
      const response = await invokeLLM({ messages });
      return parseOutput(response);
  },
  { name: "runPipeline" }
);

await runPipeline();
java
import com.langchain.smith.tracing.RunType;
import com.langchain.smith.tracing.TraceConfig;
import com.langchain.smith.tracing.Tracing;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import com.openai.models.chat.completions.ChatCompletionMessageParam;
import com.openai.models.chat.completions.ChatCompletionSystemMessageParam;
import com.openai.models.chat.completions.ChatCompletionUserMessageParam;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class TraceablePipeline {
  public static void main(String[] args) {
    new TraceablePipelineRunner().run();
  }

  private static final class TraceablePipelineRunner {
    private final OpenAIClient openai = OpenAIOkHttpClient.fromEnv();

    private final Function<String, List<ChatCompletionMessageParam>> formatPrompt =
        Tracing.traceFunction(
            subject ->
                Arrays.asList(
                    ChatCompletionMessageParam.ofSystem(
                        ChatCompletionSystemMessageParam.builder()
                            .content("You are a helpful assistant.")
                            .build()),
                    ChatCompletionMessageParam.ofUser(
                        ChatCompletionUserMessageParam.builder()
                            .content("What's a good name for a store that sells " + subject + "?")
                            .build())),
            TraceConfig.builder().name("format_prompt").build());

    private final Function<List<ChatCompletionMessageParam>, ChatCompletion> invokeLlm =
        Tracing.traceFunction(
            messages ->
                openai.chat()
                    .completions()
                    .create(
                        ChatCompletionCreateParams.builder()
                            .model(ChatModel.GPT_5_5)
                            .messages(messages)
                            .build()),
            TraceConfig.builder().name("invoke_llm").runType(RunType.LLM).build());

    private final Function<ChatCompletion, String> parseOutput =
        Tracing.traceFunction(
            response -> response.choices().get(0).message().content().orElse(""),
            TraceConfig.builder().name("parse_output").build());

    private final Function<String, String> runPipeline =
        Tracing.traceFunction(
            subject -> parseOutput.apply(invokeLlm.apply(formatPrompt.apply(subject))),
            TraceConfig.builder().name("run_pipeline").build());

    void run() {
      runPipeline.apply("colorful socks");
    }
  }
}

View example trace

打开本示例的一个公开 LangSmith 运行记录。

kotlin
import com.langchain.smith.tracing.RunType
import com.langchain.smith.tracing.TraceConfig
import com.langchain.smith.tracing.traceable
import com.openai.client.okhttp.OpenAIOkHttpClient
import com.openai.models.ChatModel
import com.openai.models.chat.completions.ChatCompletion
import com.openai.models.chat.completions.ChatCompletionCreateParams
import com.openai.models.chat.completions.ChatCompletionMessageParam
import com.openai.models.chat.completions.ChatCompletionSystemMessageParam
import com.openai.models.chat.completions.ChatCompletionUserMessageParam
import kotlin.jvm.optionals.getOrNull

val openai = OpenAIOkHttpClient.fromEnv()

val formatPrompt =
    traceable(
        { subject: String ->
            listOf(
                ChatCompletionMessageParam.ofSystem(
                    ChatCompletionSystemMessageParam.builder()
                        .content("You are a helpful assistant.")
                        .build(),
                ),
                ChatCompletionMessageParam.ofUser(
                    ChatCompletionUserMessageParam.builder()
                        .content("What's a good name for a store that sells $subject?")
                        .build(),
                ),
            )
        },
        TraceConfig.builder().name("format_prompt").build(),
    )

val invokeLlm =
    traceable(
        { messages: List<ChatCompletionMessageParam> ->
            openai.chat().completions().create(
                ChatCompletionCreateParams.builder()
                    .model(ChatModel.GPT_5_5)
                    .messages(messages)
                    .build(),
            )
        },
        TraceConfig.builder().name("invoke_llm").runType(RunType.LLM).build(),
    )

val parseOutput =
    traceable(
        { response: ChatCompletion ->
            response.choices()[0].message().content().getOrNull().orEmpty()
        },
        TraceConfig.builder().name("parse_output").build(),
    )

val runPipeline =
    traceable(
        { subject: String -> parseOutput(invokeLlm(formatPrompt(subject))) },
        TraceConfig.builder().name("run_pipeline").build(),
    )

println(runPipeline("colorful socks"))

View example trace

打开本示例的一个公开 LangSmith 运行记录。

在 UI 中,你会看到一个 run_pipeline 追踪,其中 format_prompt、invoke_llm 和 parse_output 作为嵌套的子运行出现。

NOTE

当你用 traceable 包装一个同步函数时(例如前一个示例中的 formatPrompt),调用它时请使用 await 关键字,以确保追踪被正确记录。

使用 trace 上下文管理器(仅限 Python)

在 Python 中,你可以使用 trace 上下文管理器将追踪记录到 LangSmith。这在以下情况下很有用:

  1. 你想为特定的代码块记录追踪。
  2. 你想控制追踪的输入、输出和其他属性。
  3. 无法使用装饰器或包装器。
  4. 以上任意一项或全部。

该上下文管理器可与 traceable 装饰器和 wrap_openai 包装器无缝集成,因此你可以在同一个应用中一起使用它们。

以下示例展示了三者一起使用的情况。wrap_openai 包装 OpenAI 客户端,使其调用被自动追踪。my_tool 使用 @traceable 并设置 run_type="tool" 和自定义 name,以便在追踪中正确显示。chat_pipeline 本身没有装饰;而是由 ls.trace 包装调用,让你可以显式传入项目名和输入,并通过 rt.end() 手动设置输出:

python
import openai
import langsmith as ls
from langsmith.wrappers import wrap_openai

client = wrap_openai(openai.Client())

@ls.traceable(run_type="tool", name="Retrieve Context")
def my_tool(question: str) -> str:
    return "During this morning's meeting, we solved all world conflict."

def chat_pipeline(question: str):
    context = my_tool(question)
    messages = [
        { "role": "system", "content": "You are a helpful assistant. Please respond to the user's request only based on the given context." },
        { "role": "user", "content": f"Question: {question}\nContext: {context}"}
    ]
    chat_completion = client.chat.completions.create(
        model="gpt-5.4-mini", messages=messages
    )
    return chat_completion.choices[0].message.content

app_inputs = {"input": "Can you summarize this morning's meetings?"}

with ls.trace("Chat Pipeline", "chain", project_name="my_test", inputs=app_inputs) as rt:
    output = chat_pipeline("Can you summarize this morning's meetings?")
    rt.end(outputs={"output": output})

使用 RunTree API

另一种更显式地将追踪记录到 LangSmith 的方式是通过 RunTree API。该 API 让你对追踪拥有更多控制。你可以手动创建运行和子运行来组装你的追踪。你仍然需要设置 LANGSMITH_API_KEY,但此方法不需要 LANGSMITH_TRACING。

大多数用例不推荐使用此方法;与自动处理上下文传播的 @traceable 相比,手动管理追踪上下文容易出错。

python
import openai
from langsmith.run_trees import RunTree

# This can be a user input to your app
question = "Can you summarize this morning's meetings?"

# Create a top-level run
pipeline = RunTree(
  name="Chat Pipeline",
  run_type="chain",
  inputs={"question": question}
)
pipeline.post()

# This can be retrieved in a retrieval step
context = "During this morning's meeting, we solved all world conflict."
messages = [
  { "role": "system", "content": "You are a helpful assistant. Please respond to the user's request only based on the given context." },
  { "role": "user", "content": f"Question: {question}\nContext: {context}"}
]

# Create a child run
child_llm_run = pipeline.create_child(
  name="OpenAI Call",
  run_type="llm",
  inputs={"messages": messages},
)
child_llm_run.post()

# Generate a completion
client = openai.Client()
chat_completion = client.chat.completions.create(
  model="gpt-5.4-mini", messages=messages
)

# End the runs and log them
child_llm_run.end(outputs=chat_completion)
child_llm_run.patch()
pipeline.end(outputs={"answer": chat_completion.choices[0].message.content})
pipeline.patch()
typescript
import OpenAI from "openai";
import { RunTree } from "langsmith";

// This can be a user input to your app
const question = "Can you summarize this morning's meetings?";

const pipeline = new RunTree({
  name: "Chat Pipeline",
  run_type: "chain",
  inputs: { question }
});
await pipeline.postRun();

// This can be retrieved in a retrieval step
const context = "During this morning's meeting, we solved all world conflict.";
const messages = [
  { role: "system", content: "You are a helpful assistant. Please respond to the user's request only based on the given context." },
  { role: "user", content: `Question: ${question}Context: ${context}` }
];

// Create a child run
const childRun = await pipeline.createChild({
  name: "OpenAI Call",
  run_type: "llm",
  inputs: { messages },
});
await childRun.postRun();

// Generate a completion
const client = new OpenAI();
const chatCompletion = await client.chat.completions.create({
  model: "gpt-5.4-mini",
  messages: messages,
});

// End the runs and log them
childRun.end(chatCompletion);
await childRun.patchRun();
pipeline.end({ outputs: { answer: chatCompletion.choices[0].message.content } });
await pipeline.patchRun();
java
import com.langchain.smith.client.LangsmithClient;
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient;
import com.langchain.smith.tracing.RunTree;
import com.langchain.smith.tracing.RunType;
import com.langchain.smith.tracing.TraceConfig;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import com.openai.models.chat.completions.ChatCompletionMessageParam;
import com.openai.models.chat.completions.ChatCompletionSystemMessageParam;
import com.openai.models.chat.completions.ChatCompletionUserMessageParam;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class RunTreeExample {
    public static void main(String[] args) throws InterruptedException {
        LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv();
        OpenAIClient openai = OpenAIOkHttpClient.fromEnv();
        ExecutorService executor = Executors.newSingleThreadExecutor();

        try {
            String question = "Can you summarize this morning's meetings?";
            String runId = "01990f3e-7f97-74c5-a9b6-8d3f7e8e2f11";

            RunTree pipeline = RunTree.builder()
                .id(runId)
                .name("Chat Pipeline")
                .runType(RunType.CHAIN)
                .inputs(Collections.singletonMap("question", question))
                .client(langsmith)
                .executor(executor)
                .build();
            pipeline.postRun();

            String context = "During this morning's meeting, we solved all world conflict.";
            List<ChatCompletionMessageParam> messages = Arrays.asList(
                ChatCompletionMessageParam.ofSystem(
                    ChatCompletionSystemMessageParam.builder()
                        .content(
                            "You are a helpful assistant. Please respond to the user's " +
                                "request only based on the given context.")
                        .build()),
                ChatCompletionMessageParam.ofUser(
                    ChatCompletionUserMessageParam.builder()
                        .content("Question: " + question + "\nContext: " + context)
                        .build()));

            RunTree childRun = pipeline.createChild(
                TraceConfig.builder().name("OpenAI Call").runType(RunType.LLM).build());
            childRun.setInputs(Collections.singletonMap("messages", messages));
            childRun.postRun();

            ChatCompletion chatCompletion = openai.chat().completions().create(
                ChatCompletionCreateParams.builder()
                    .model(ChatModel.GPT_5_5)
                    .messages(messages)
                    .build());

            String answer = chatCompletion.choices().get(0).message().content().orElse("");
            System.out.println(answer);

            childRun.setOutputs(Collections.singletonMap("response", chatCompletion.toString()));
            childRun.setEndTime(Instant.now().toString());
            childRun.patchRun();

            pipeline.setOutputs(Collections.singletonMap(
                "answer", answer));
            pipeline.setEndTime(Instant.now().toString());
            pipeline.patchRun();
        } finally {
            executor.shutdown();
            if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                throw new IllegalStateException(
                    "Timed out waiting for LangSmith traces to submit");
            }
        }
    }
}
kotlin
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
import com.langchain.smith.tracing.RunTree
import com.langchain.smith.tracing.RunType
import com.langchain.smith.tracing.TraceConfig
import com.openai.client.okhttp.OpenAIOkHttpClient
import com.openai.models.ChatModel
import com.openai.models.chat.completions.ChatCompletionCreateParams
import com.openai.models.chat.completions.ChatCompletionMessageParam
import com.openai.models.chat.completions.ChatCompletionSystemMessageParam
import com.openai.models.chat.completions.ChatCompletionUserMessageParam
import java.time.Instant
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

val langsmith = LangsmithOkHttpClient.fromEnv()
val openai = OpenAIOkHttpClient.fromEnv()
val executor = Executors.newSingleThreadExecutor()

try {
    val question = "Can you summarize this morning's meetings?"
    val runId = "01990f3e-7f97-74c5-a9b6-8d3f7e8e2f11"

    val pipeline =
        RunTree.builder()
            .id(runId)
            .name("Chat Pipeline")
            .runType(RunType.CHAIN)
            .inputs(mapOf("question" to question))
            .client(langsmith)
            .executor(executor)
            .build()
    println("[run-tree-example] Posting parent run to LangSmith…")
    pipeline.postRun()

    val context = "During this morning's meeting, we solved all world conflict."
    val messages =
        listOf(
            ChatCompletionMessageParam.ofSystem(
                ChatCompletionSystemMessageParam.builder()
                    .content(
                        "You are a helpful assistant. Please respond to the user's " +
                            "request only based on the given context.",
                    )
                    .build(),
            ),
            ChatCompletionMessageParam.ofUser(
                ChatCompletionUserMessageParam.builder()
                    .content("Question: $question\nContext: $context")
                    .build(),
            ),
        )

    val childRun =
        pipeline.createChild(
            TraceConfig.builder().name("OpenAI Call").runType(RunType.LLM).build(),
        )
    childRun.inputs = mapOf("messages" to messages)
    println("[run-tree-example] Posting child run to LangSmith…")
    childRun.postRun()

    val chatCompletion =
        openai.chat().completions().create(
            ChatCompletionCreateParams.builder()
                .model(ChatModel.GPT_5_5)
                .messages(messages)
                .build(),
        )

    val answer = chatCompletion.choices()[0].message().content().orElse("")
    println("[run-tree-example] Answer:")
    println(answer)

    childRun.outputs = mapOf("response" to chatCompletion.toString())
    childRun.endTime = Instant.now().toString()
    childRun.patchRun()

    pipeline.outputs =
        mapOf(
            "answer" to answer,
        )
    pipeline.endTime = Instant.now().toString()
    pipeline.patchRun()
} finally {
    executor.shutdown()
    check(executor.awaitTermination(10, TimeUnit.SECONDS)) {
        "Timed out waiting for LangSmith traces to submit"
    }
}

Java 和 Kotlin 示例使用了自定义根运行 ID 和专用执行器。关闭执行器并等待其终止,可确保后台的运行提交在进程退出前完成。

用法示例

你可以扩展上一节中讲解的工具类来追踪任意代码。以下代码展示了一些扩展示例。

追踪类中的任意公共方法:

python
from typing import Any, Callable, Type, TypeVar

T = TypeVar("T")

def traceable_cls(cls: Type[T]) -> Type[T]:
    """Instrument all public methods in a class."""
    def wrap_method(name: str, method: Any) -> Any:
        if callable(method) and not name.startswith("__"):
            return traceable(name=f"{cls.__name__}.{name}")(method)
        return method

    # Handle __dict__ case
    for name in dir(cls):
        if not name.startswith("_"):
            try:
                method = getattr(cls, name)
                setattr(cls, name, wrap_method(name, method))
            except AttributeError:
                # Skip attributes that can't be set (e.g., some descriptors)
                pass

    # Handle __slots__ case
    if hasattr(cls, "__slots__"):
        for slot in cls.__slots__:  # type: ignore[attr-defined]
            if not slot.startswith("__"):
                try:
                    method = getattr(cls, slot)
                    setattr(cls, slot, wrap_method(slot, method))
                except AttributeError:
                    # Skip slots that don't have a value yet
                    pass

    return cls

@traceable_cls
class MyClass:
    def __init__(self, some_val: int):
        self.some_val = some_val

    def combine(self, other_val: int):
        return self.some_val + other_val

# See trace: https://smith.langchain.com/public/882f9ecf-5057-426a-ae98-0edf84fdcaf9/r
MyClass(13).combine(29)

指定自定义运行 ID

默认情况下,LangSmith 会为每次运行分配一个随机 ID。当你需要提前知道运行 ID(例如,在运行后立即附加反馈)、将 LangSmith 运行与外部系统的 ID 关联,或使用确定性 ID 让运行具有幂等性时,你可以覆盖此 ID。

NOTE

为自定义运行 ID 使用 UUID v7。UUIDv7 内嵌时间戳,可保留追踪中运行的正确时间顺序。LangSmith SDK 导出一个 uuid7 辅助函数(Python v0.4.43+,JS v0.3.80+):

  • Python:from langsmith import uuid7
  • JS/TS:import { uuid7 } from 'langsmith'

接受任何 UUID v7 字符串——你可以使用 SDK 辅助函数,如果你的系统已经使用 UUID v7 标识符,也可以使用你自己的。

使用以下方式之一:

  • @traceable:调用 @traceable 函数时在 langsmith_extra 中传入 run_id(Python),或在传给 traceable 的配置对象中传入 id(TypeScript):
python
from langsmith import traceable, uuid7

@traceable
def my_pipeline(question: str) -> str:
    return "answer"

run_id = uuid7()
my_pipeline("What is the capital of France?", langsmith_extra={"run_id": run_id})

# run_id can now be used to attach feedback, query the run, etc.
typescript
import { traceable } from "langsmith/traceable";
import { uuid7 } from "langsmith";

const runId = uuid7();

const myPipeline = traceable(
async (question: string) => {
    return "answer";
},
{ name: "my-pipeline", id: runId }
);

await myPipeline("What is the capital of France?");

// runId can now be used to attach feedback, query the run, etc.
  • trace 上下文管理器(仅限 Python):直接将 run_id 传给 trace 上下文管理器构造函数:

    python
    from langsmith import trace, uuid7
    
    run_id = uuid7()
    
    with trace("my-pipeline", run_id=run_id) as run:
        result = "answer"
        run.end(outputs={"result": result})
    
    # run_id can now be used to attach feedback, query the run, etc.

在退出前确保所有追踪都已提交

LangSmith 在后台线程中执行追踪,以避免妨碍你的生产应用。这意味着你的进程可能会在所有追踪成功提交到 LangSmith 之前就结束。请参考以下选项:

python
from langsmith import Client

client = Client()

@traceable(client=client)
async def my_traced_func():
# Your code here...
pass

try:
await my_traced_func()
finally:
await client.flush()
typescript
import { Client } from "langsmith";

const langsmithClient = new Client({});

const myTracedFunc = traceable(async () => {
// Your code here...
},{ client: langsmithClient });

try {
await myTracedFunc();
} finally {
await langsmithClient.flush();
}

相关


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