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

使用 Temporal 进行追踪

学习如何使用 OpenTelemetry 在 LangSmith 中追踪 Temporal 工作流和活动。

Temporal 是一个持久执行平台,它让开发者能够构建有韧性的分布式应用。本指南展示如何使用 OpenTelemetry 在 LangSmith 中追踪 Temporal 工作流和活动。

LangSmith 支持 OpenTelemetry(OTEL)追踪摄取,它与 Temporal 原生的 OpenTelemetry 拦截器无缝集成。这能够跨你的工作流执行、活动以及其中的任何 LLM 调用实现完整的分布式追踪。

前置条件

  • 一个 LangSmith 账户和 API 密钥
  • 正在运行的 Temporal 服务器(本地或云)
  • 适用于你所用语言的 OpenTelemetry SDK

环境变量

为所有实现设置以下环境变量:

变量必需描述
LANGSMITH_API_KEY是你从 Settings 获得的 LangSmith API 密钥。
LANGSMITH_PROJECT否项目名称(默认为 "default")。

NOTE

对于区域 SaaS 或自托管 LangSmith 安装,还要将 LANGCHAIN_BASE_URL 设为你的 LangSmith 实例 URL。

设置追踪

Go 使用 langsmith-go SDK 配合 Temporal 的 OpenTelemetry 拦截器,自动追踪工作流和活动。

Install

安装 LangSmith Go SDK、Temporal SDK 和 OpenTelemetry 拦截器:

bash
go get github.com/langchain-ai/langsmith-go@v0.1.0-alpha.7
go get go.temporal.io/sdk
go get go.temporal.io/sdk/contrib/opentelemetry

Initialize tracer

初始化 LangSmith tracer,创建 Temporal 的 OpenTelemetry 拦截器,并将其注册到 Temporal 客户端和 worker:

go
package main

import (
	"context"
	"log"

	"github.com/langchain-ai/langsmith-go"
	"go.temporal.io/sdk/client"
	"go.temporal.io/sdk/contrib/opentelemetry"
	"go.temporal.io/sdk/interceptor"
	"go.temporal.io/sdk/worker"
)

func main() {
	ctx := context.Background()

	// Initialize LangSmith tracer (reads LANGSMITH_API_KEY and LANGSMITH_PROJECT)
	ls, err := langsmith.NewTracer(
		langsmith.WithServiceName("temporal-worker"),
	)
	if err != nil {
		log.Fatal("Failed to initialize LangSmith tracer:", err)
	}
	defer ls.Shutdown(ctx)

	// Create Temporal tracing interceptor
	tracer := ls.Tracer("temporal-app")
	tracingInterceptor, err := opentelemetry.NewTracingInterceptor(
		opentelemetry.TracerOptions{Tracer: tracer},
	)
	if err != nil {
		log.Fatal("Failed to create tracing interceptor:", err)
	}

	// Create Temporal client with tracing
	c, err := client.Dial(client.Options{
		Interceptors: []interceptor.ClientInterceptor{tracingInterceptor},
	})
	if err != nil {
		log.Fatal("Failed to create Temporal client:", err)
	}
	defer c.Close()

	// Create worker with tracing (uses same client)
	w := worker.New(c, "my-task-queue", worker.Options{})
	w.RegisterWorkflow(MyWorkflow)
	w.RegisterActivity(MyActivity)

	// Start worker
	if err := w.Run(worker.InterruptCh()); err != nil {
		log.Fatal("Worker failed:", err)
	}
}

Define workflow and activity

定义一个执行活动的工作流。该活动演示如何为 LangSmith 可见性添加自定义 span 属性:

go
package main

import (
	"context"
	"fmt"
	"time"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
	"go.temporal.io/sdk/activity"
	"go.temporal.io/sdk/workflow"
)

// MyWorkflow executes an activity
func MyWorkflow(ctx workflow.Context, input string) (string, error) {
	ao := workflow.ActivityOptions{
		StartToCloseTimeout: 10 * time.Second,
	}
	ctx = workflow.WithActivityOptions(ctx, ao)

	var result string
	err := workflow.ExecuteActivity(ctx, MyActivity, input).Get(ctx, &result)
	return result, err
}

// MyActivity processes input with custom span attributes
func MyActivity(ctx context.Context, input string) (string, error) {
	logger := activity.GetLogger(ctx)
	logger.Info("Processing", "input", input)

	// Get the span created by Temporal's interceptor
	span := trace.SpanFromContext(ctx)

	// Add Gen AI attributes for LangSmith visibility
	span.SetAttributes(
		attribute.String("gen_ai.prompt", input),
		attribute.String("gen_ai.operation.name", "chat"),
	)

	result := fmt.Sprintf("Processed: %s", input)

	// Set completion attribute
	span.SetAttributes(
		attribute.String("gen_ai.completion", result),
	)

	return result, nil
}

Execute workflow

在一个单独的客户端应用中,初始化 tracer 并执行工作流:

go
// In a separate function or client application
func executeWorkflow() {
	ctx := context.Background()

	// Initialize tracer for client
	ls, err := langsmith.NewTracer(
		langsmith.WithServiceName("temporal-client"),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer ls.Shutdown(ctx)

	// Create client with tracing
	tracer := ls.Tracer("temporal-app")
	tracingInterceptor, err := opentelemetry.NewTracingInterceptor(
		opentelemetry.TracerOptions{Tracer: tracer},
	)
	if err != nil {
		log.Fatal(err)
	}

	c, err := client.Dial(client.Options{
		Interceptors: []interceptor.ClientInterceptor{tracingInterceptor},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	// Execute workflow
	workflowOptions := client.StartWorkflowOptions{
		ID:        "my-workflow-1",
		TaskQueue: "my-task-queue",
	}

	we, err := c.ExecuteWorkflow(ctx, workflowOptions, MyWorkflow, "Hello World")
	if err != nil {
		log.Fatal(err)
	}

	var result string
	if err := we.Get(ctx, &result); err != nil {
		log.Fatal(err)
	}

	log.Printf("Workflow result: %s", result)
}

在 LangSmith 中查看追踪

一旦配置完成,追踪就会出现在你的 LangSmith 项目中:

  1. 导航到你的 LangSmith 实例。
  2. 选择你的项目。
  3. 在 Tracing 标签中查看追踪。
  4. 点击各个追踪以查看完整的 span 层级。

配置选项

设置自定义服务名

设置一个自定义服务名以区分不同的 Temporal worker 或服务:

go
ls, err := langsmith.NewTracer(
    langsmith.WithServiceName("my-temporal-worker"),
)
python
resource = Resource.create({
    SERVICE_NAME: "my-temporal-worker",
})
typescript
const provider = new NodeTracerProvider({
  resource: new Resource({
    [ATTR_SERVICE_NAME]: 'my-temporal-worker',
  }),
});

添加自定义 span 属性

添加自定义属性以丰富你的追踪:

go
import "go.opentelemetry.io/otel/attribute"

span := trace.SpanFromContext(ctx)
span.SetAttributes(
    attribute.String("user.id", userID),
    attribute.String("workflow.version", "v2"),
)
python
from opentelemetry import trace

span = trace.get_current_span()
span.set_attribute("user.id", user_id)
span.set_attribute("workflow.version", "v2")
typescript
import { trace } from '@opentelemetry/api';

const span = trace.getActiveSpan();
span?.setAttribute('user.id', userId);
span?.setAttribute('workflow.version', 'v2');

配置采样

对于高吞吐量的工作流,配置采样以减少追踪量:

go
// Note: langsmith.NewTracer() uses default sampling
// For custom sampling, use the TracerProvider directly
tp := sdktrace.NewTracerProvider(
    sdktrace.WithBatcher(exporter),
    sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)), // 10% sampling
)
python
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased

provider = TracerProvider(
    resource=resource,
    sampler=TraceIdRatioBased(0.1),  # 10% sampling
)
typescript
import { TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';

const provider = new NodeTracerProvider({
  resource: resource,
  sampler: new TraceIdRatioBasedSampler(0.1), // 10% sampling
});

故障排查

追踪未出现

  1. 核实 API 密钥:确保 LANGSMITH_API_KEY 设置正确
  2. 检查端点:确认你使用的是 https://api.smith.langchain.com/otel/v1/traces
  3. 关闭时刷新:调用 provider.shutdown() 以在应用退出前刷新待处理的 span
  4. 检查项目:核实追踪被发送到正确的项目(默认为 "default")

活动 span 缺失

确保追踪拦截器同时在客户端和 worker 上都已配置:

  • 客户端:需要用于启动工作流的拦截器
  • worker:需要用于执行活动的拦截器

上下文传播问题

核实传播器(propagator)配置正确:

  • Go:langsmith.NewTracer() 会自动配置传播器
  • Python/TypeScript:确保 OpenTelemetry SDK 已用追踪传播器正确初始化

worker 关闭挂起

如果追踪没有刷新,请确保你用合适的超时调用了关闭方法:

go
defer ls.Shutdown(context.Background())
python
finally:
    provider.shutdown()
typescript
finally {
  await provider.shutdown();
}

后续步骤

其他资源


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