jpskill.com
🛠️ 開発・MCP コミュニティ

ai-observability-langfuse

Langfuseを活用し、OpenTelemetryに基づいたLLMの追跡、評価、プロンプト管理、データセット管理などを通して、LLMの挙動を可視化し、より良い品質で本番運用するためのベストプラクティスを提供するSkill。

📜 元の英語説明(参考)

LLM observability with Langfuse — OpenTelemetry-based tracing, evaluations, prompt management, datasets, and production best practices

🇯🇵 日本人クリエイター向け解説

一言でいうと

Langfuseを活用し、OpenTelemetryに基づいたLLMの追跡、評価、プロンプト管理、データセット管理などを通して、LLMの挙動を可視化し、より良い品質で本番運用するためのベストプラクティスを提供するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o ai-observability-langfuse.zip https://jpskill.com/download/10210.zip && unzip -o ai-observability-langfuse.zip && rm ai-observability-langfuse.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10210.zip -OutFile "$d\ai-observability-langfuse.zip"; Expand-Archive "$d\ai-observability-langfuse.zip" -DestinationPath $d -Force; ri "$d\ai-observability-langfuse.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して ai-observability-langfuse.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → ai-observability-langfuse フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Langfuse 可観測性パターン

クイックガイド: Langfuse TypeScript SDK (OpenTelemetry上に構築) を使用して、LLMアプリケーションに可観測性を追加します。コアなトレースには、@langfuse/tracing@langfuse/otel、および@opentelemetry/sdk-nodeをインストールします。自動的なコンテキスト伝播にはstartActiveObservation()を使用し、関数をラップするにはobserve()を使用します。ゼロコンフィグのOpenAIトレースには、observeOpenAI()とともに@langfuse/openaiを使用します。プロンプト管理、スコア、およびデータセットには、@langfuse/clientLangfuseClientを使用します。短命なプロセスでは、常にforceFlush()またはsdk.shutdown()を呼び出してください。


<critical_requirements>

重要: このSkillを使用する前に

すべてのコードは、CLAUDE.mdのプロジェクト規約に従う必要があります (kebab-case、名前付きエクスポート、インポート順序、import type、名前付き定数)

(エントリポイントの先頭で、他のインポートの前にinstrumentation.tsをインポートして登録する必要があります -- OpenTelemetryは、モジュールがロードされる前にモジュールをインストルメントする必要があります)

(短命なプロセス (サーバーレス、スクリプト、CLIツール) では、forceFlush()またはsdk.shutdown()を呼び出す必要があります -- イベントはバッチ処理されるため、明示的なフラッシュなしでは失われます)

(OpenAI SDKのトレースには、observeOpenAI()とともに@langfuse/openaiを使用する必要があります -- ラッパーが自動的に処理する場合、OpenAI呼び出しに対して手動で生成のObservationを作成しないでください)

(環境変数を使用してLANGFUSE_SECRET_KEYLANGFUSE_PUBLIC_KEY、およびLANGFUSE_BASE_URLを設定する必要があります -- 認証情報をハードコードしないでください)

(ネストされたトレースには、startActiveObservation()またはobserve()を使用する必要があります -- 手動のstartObservation()は、明示的な.end()呼び出しを必要とし、コンテキストを自動的に伝播しません)

</critical_requirements>


自動検出: Langfuse, langfuse, @langfuse/tracing, @langfuse/otel, @langfuse/client, @langfuse/openai, LangfuseSpanProcessor, LangfuseClient, startActiveObservation, startObservation, observeOpenAI, langfuse.score, langfuse.prompt, langfuse.dataset, LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, forceFlush

使用する場面:

  • LLMアプリケーションコードへの可観測性とトレースの追加 (任意のプロバイダー)
  • 自動的なトークン/コスト追跡のためのOpenAI SDK呼び出しのラップ
  • バージョニング、ラベル、および変数コンパイルによるプロンプトテンプレートの管理
  • スコア (数値、カテゴリ、ブール値) によるLLM出力品質の評価
  • リグレッションテストのためのデータセットに対する実験の実行
  • 複数ターンの会話にわたるセッション、ユーザー、およびメタデータの追跡
  • 本番環境でのLLMコストとトークン使用量の監視

カバーする主要なパターン:

  • LangfuseSpanProcessorによるOpenTelemetryのセットアップ
  • startActiveObservationobserve、および手動のstartObservationによるトレース
  • Observationのタイプ (span, generation, agent, tool, retriever, evaluator, embedding, chain, guardrail)
  • observeOpenAI()によるOpenAI SDKの自動インストルメンテーション
  • プロンプト管理 (get, compile, text vs chat prompts, バージョニング)
  • スコアと評価 (数値、カテゴリ、ブール値)
  • テストのためのデータセットと実験
  • フラッシュ、シャットダウン、およびライフサイクル管理

使用しない場面:

  • 基本的なconsole.logデバッグのみが必要な場合 -- Langfuseは構造化された本番環境の可観測性のためのものです
  • AI SDKに組み込まれたプロバイダー固有のトレースが必要な場合 -- フレームワークにネイティブな可観測性があるかどうかを確認してください
  • APM/インフラストラクチャ監視 (CPU、メモリ、HTTPレイテンシ) が必要な場合 -- 汎用的な可観測性ツールを使用してください

Examples Index

  • Core: Setup & Configuration -- OpenTelemetryのセットアップ、インストルメンテーションファイル、クライアントの初期化、フラッシュ/シャットダウン
  • Tracing -- startActiveObservation, observe, 手動トレース、ネスト、Observationのタイプ、メタデータ
  • OpenAI Integration -- observeOpenAIラッパー、ストリーミング、トークン追跡、カスタム属性
  • Prompt Management -- getPrompt, compile, text vs chat, バージョニング、キャッシング
  • Scores & Datasets -- 数値/カテゴリ/ブール値のスコア、データセット、実験
  • Quick API Reference -- パッケージインデックス、環境変数、Observationのタイプ、スコアメソッド

<philosophy>

Philosophy

Langfuseは、OpenTelemetry上に構築されたオープンソースのLLM可観測性を提供します。SDK (v4以降、2025年8月) は、OTelをトレースのバックボーンとして使用した根本的な書き換えであり、トレースがより広範な可観測性エコシステムと自然に統合されることを意味します。

コア原則:

  1. OpenTelemetryネイティブ -- OTelスパンとコンテキスト伝播上に構築されています。LangfuseのObservationは、LLM固有の属性 (モデル、トークン、コスト) を持つOTelスパンのラッパーです。これは、OTel互換のインストルメンテーションライブラリがLangfuseと並行して動作することを意味します。
  2. ゼロレイテンシトレース -- すべてのトレースイベントはローカルでキューに入れられ、バックグラウンドバッチでフラッシュされます。アプリケーションの応答時間は、可観測性の影響を受けません。
  3. モジュール式パッケージ -- インストルメンテーション用の@langfuse/tracing、プロンプト/スコア/データセット用の@langfuse/client、OpenAIの自動インストルメンテーション用の@langfuse/openai。必要なものだけをインストールしてください。
  4. コンテキストファースト -- startActiveObservation()は、親子の関係を自動的に伝播します。ネストされたObservationは、手動のIDスレッド化なしにコンテキストを継承します。
  5. Observationのタイプ -- LLM固有のタイプ (generation, agent, tool, retriever, evaluator, embedding) は、トレースにセマンティックな意味を提供し、より豊富なダッシュボードビューとフィルタリングを可能にします。

</philosophy>


<patterns>

コアパターン

パターン1: OpenTelemetryのセットアップ

instrumentation.tsファイルを作成し、エントリポイントの先頭でインポートします。

// instrumentation.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";

const sdk = new NodeSDK({
  spanProcessors: [new LangfuseSpanProcessor()],
});

sdk.start();

export { sdk };

// index.ts -- インストルメンテーションを最初にインポート
import "./instrumentation";
// 他のすべてのインポートはインストルメンテーションの後
import { startActive

(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Langfuse Observability Patterns

Quick Guide: Use the Langfuse TypeScript SDK (built on OpenTelemetry) to add observability to LLM applications. Install @langfuse/tracing, @langfuse/otel, and @opentelemetry/sdk-node for core tracing. Use startActiveObservation() for automatic context propagation or observe() to wrap functions. Use @langfuse/openai with observeOpenAI() for zero-config OpenAI tracing. Use LangfuseClient from @langfuse/client for prompt management, scores, and datasets. Always call forceFlush() or sdk.shutdown() in short-lived processes.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST import and register instrumentation.ts at the top of your entry point BEFORE any other imports -- OpenTelemetry must instrument modules before they are loaded)

(You MUST call forceFlush() or sdk.shutdown() in short-lived processes (serverless, scripts, CLI tools) -- events are batched and will be lost without explicit flushing)

(You MUST use @langfuse/openai with observeOpenAI() for OpenAI SDK tracing -- do NOT manually create generation observations for OpenAI calls when the wrapper handles it automatically)

(You MUST set LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, and LANGFUSE_BASE_URL via environment variables -- never hardcode credentials)

(You MUST use startActiveObservation() or observe() for nested tracing -- manual startObservation() requires explicit .end() calls and does NOT propagate context automatically)

</critical_requirements>


Auto-detection: Langfuse, langfuse, @langfuse/tracing, @langfuse/otel, @langfuse/client, @langfuse/openai, LangfuseSpanProcessor, LangfuseClient, startActiveObservation, startObservation, observeOpenAI, langfuse.score, langfuse.prompt, langfuse.dataset, LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, forceFlush

When to use:

  • Adding observability and tracing to LLM application code (any provider)
  • Wrapping OpenAI SDK calls for automatic token/cost tracking
  • Managing prompt templates with versioning, labels, and variable compilation
  • Evaluating LLM output quality with scores (numeric, categorical, boolean)
  • Running experiments against datasets for regression testing
  • Tracking sessions, users, and metadata across multi-turn conversations
  • Monitoring LLM costs and token usage in production

Key patterns covered:

  • OpenTelemetry setup with LangfuseSpanProcessor
  • Tracing with startActiveObservation, observe, and manual startObservation
  • Observation types (span, generation, agent, tool, retriever, evaluator, embedding, chain, guardrail)
  • OpenAI SDK auto-instrumentation with observeOpenAI()
  • Prompt management (get, compile, text vs chat prompts, versioning)
  • Scores and evaluations (numeric, categorical, boolean)
  • Datasets and experiments for testing
  • Flush, shutdown, and lifecycle management

When NOT to use:

  • You only need basic console.log debugging -- Langfuse is for structured production observability
  • You want provider-specific tracing built into an AI SDK -- check if your framework has native observability
  • You need APM/infrastructure monitoring (CPU, memory, HTTP latency) -- use a general-purpose observability tool

Examples Index

  • Core: Setup & Configuration -- OpenTelemetry setup, instrumentation file, client init, flush/shutdown
  • Tracing -- startActiveObservation, observe, manual tracing, nesting, observation types, metadata
  • OpenAI Integration -- observeOpenAI wrapper, streaming, token tracking, custom attributes
  • Prompt Management -- getPrompt, compile, text vs chat, versioning, caching
  • Scores & Datasets -- Numeric/categorical/boolean scores, datasets, experiments
  • Quick API Reference -- Package index, environment variables, observation types, score methods

<philosophy>

Philosophy

Langfuse provides open-source LLM observability built on OpenTelemetry. The SDK (v4+, August 2025) is a ground-up rewrite using OTel as the tracing backbone, meaning traces integrate naturally with the broader observability ecosystem.

Core principles:

  1. OpenTelemetry-native -- Built on OTel spans and context propagation. Langfuse observations are wrappers around OTel spans with LLM-specific attributes (model, tokens, cost). This means any OTel-compatible instrumentation library works alongside Langfuse.
  2. Zero-latency tracing -- All trace events are queued locally and flushed in background batches. Your application's response time is not affected by observability.
  3. Modular packages -- @langfuse/tracing for instrumentation, @langfuse/client for prompts/scores/datasets, @langfuse/openai for OpenAI auto-instrumentation. Install only what you need.
  4. Context-first -- startActiveObservation() automatically propagates parent-child relationships. Nested observations inherit context without manual ID threading.
  5. Observation types -- LLM-specific types (generation, agent, tool, retriever, evaluator, embedding) provide semantic meaning to traces, enabling richer dashboard views and filtering.

</philosophy>


<patterns>

Core Patterns

Pattern 1: OpenTelemetry Setup

Create an instrumentation.ts file and import it at the top of your entry point.

// instrumentation.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";

const sdk = new NodeSDK({
  spanProcessors: [new LangfuseSpanProcessor()],
});

sdk.start();

export { sdk };
// index.ts -- import instrumentation FIRST
import "./instrumentation";
// All other imports AFTER instrumentation
import { startActiveObservation } from "@langfuse/tracing";

Why good: OTel must instrument modules before they are loaded; importing instrumentation first ensures all subsequent imports are traced automatically

// BAD: importing instrumentation after other modules
import { startActiveObservation } from "@langfuse/tracing";
import "./instrumentation"; // TOO LATE -- tracing won't capture earlier imports

Why bad: Auto-instrumentation of LLM SDKs requires OTel to be initialized before those modules are imported

See: examples/core.md for environment variables, sampling, masking, and production configuration


Pattern 2: Tracing with startActiveObservation

The primary instrumentation pattern. Creates an observation, makes it the active context, and automatically ends it when the callback completes.

import { startActiveObservation } from "@langfuse/tracing";

async function handleRequest(query: string): Promise<string> {
  return await startActiveObservation("handle-request", async (span) => {
    span.update({ input: { query } });

    // Nested observation -- automatically becomes a child
    const result = await startActiveObservation(
      "process-query",
      async (child) => {
        child.update({ input: { query } });
        const answer = await callLLM(query);
        child.update({ output: { answer } });
        return answer;
      },
    );

    span.update({ output: { result } });
    return result;
  });
}

Why good: Automatic context propagation, automatic end on callback completion, nesting creates parent-child hierarchy without manual ID management

// BAD: using startObservation without ending it
import { startObservation } from "@langfuse/tracing";

const span = startObservation("my-span");
await doWork();
// span.end() never called -- observation stays open forever

Why bad: Manual startObservation requires explicit .end() calls; forgetting creates open-ended observations

See: examples/tracing.md for observe wrapper, observation types, metadata, and manual tracing


Pattern 3: The observe() Wrapper

Wraps a function to automatically capture inputs, outputs, timings, and errors.

import { observe } from "@langfuse/tracing";

const classifyIntent = observe(
  async (query: string) => {
    const result = await callLLM(query);
    return result.intent;
  },
  { name: "classify-intent", asType: "generation" },
);

// Usage -- automatically traced
const intent = await classifyIntent("Book a flight to Paris");

Why good: Declarative tracing, inputs/outputs captured automatically, asType tags the observation type for richer dashboard filtering


Pattern 4: OpenAI Auto-Instrumentation

Use observeOpenAI() to wrap the OpenAI client for automatic tracing of all calls.

import OpenAI from "openai";
import { observeOpenAI } from "@langfuse/openai";

const openai = observeOpenAI(new OpenAI());

// All calls automatically traced with model, tokens, cost
const completion = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "Hello" }],
});

Why good: Zero manual instrumentation, captures model name, token counts, estimated costs, latency, and streaming metrics automatically

// BAD: manually creating generation observations for OpenAI calls
await startActiveObservation("openai-call", async (span) => {
  const result = await rawOpenai.chat.completions.create({ ... });
  span.update({
    model: "gpt-4o",
    input: messages,
    output: result.choices[0].message.content,
  });
}, { asType: "generation" });

Why bad: observeOpenAI handles all of this automatically with more accurate token/cost data; manual tracking is error-prone and duplicates effort

See: examples/openai-integration.md for streaming, custom attributes, and token tracking on streams


Pattern 5: Prompt Management

Fetch versioned prompts, compile with variables, and link to traces.

import { LangfuseClient } from "@langfuse/client";

const langfuse = new LangfuseClient();

// Fetch a text prompt (production label by default)
const prompt = await langfuse.prompt.get("summarize-article");
const compiled = prompt.compile({ topic: "AI safety", length: "brief" });
// -> "Write a brief summary about AI safety."

// Fetch a chat prompt
const chatPrompt = await langfuse.prompt.get("assistant-v2", { type: "chat" });
const messages = chatPrompt.compile({ userName: "Alice" });
// -> [{ role: "system", content: "You are helping Alice..." }, ...]

Why good: Centralized prompt management with versioning, labels for A/B testing, variable compilation, and built-in caching

See: examples/prompt-management.md for versioning, labels, cache control, and linking prompts to traces


Pattern 6: Scores and Evaluations

Attach quality measurements to traces and observations.

import { LangfuseClient } from "@langfuse/client";

const langfuse = new LangfuseClient();

// Numeric score
langfuse.score.create({
  traceId: "trace-123",
  name: "relevance",
  value: 0.95,
  dataType: "NUMERIC",
});

// Categorical score
langfuse.score.create({
  traceId: "trace-123",
  name: "quality",
  value: "good",
  dataType: "CATEGORICAL",
});

// Boolean score (0 or 1)
langfuse.score.create({
  traceId: "trace-123",
  name: "contains-hallucination",
  value: 0,
  dataType: "BOOLEAN",
});

// Score a specific observation within a trace
langfuse.score.create({
  traceId: "trace-123",
  observationId: "obs-456",
  name: "accuracy",
  value: 0.88,
  dataType: "NUMERIC",
});

// Flush in short-lived processes
await langfuse.score.flush();

Why good: Three data types cover all evaluation needs, scores attach at trace or observation level, fire-and-forget API with batching

See: examples/scores-datasets.md for active observation scoring, session scores, datasets, and experiments


Pattern 7: Flush and Shutdown

Always flush in short-lived processes. The SDK batches events and sends them asynchronously.

import { sdk } from "./instrumentation";
import { LangfuseClient } from "@langfuse/client";
import { LangfuseSpanProcessor } from "@langfuse/otel";

const langfuse = new LangfuseClient();

async function main() {
  // ... do work ...

  // Flush scores
  await langfuse.score.flush();

  // Shutdown OTel SDK (flushes all pending spans)
  await sdk.shutdown();
}

main();

Why good: Explicit flush/shutdown ensures all events are sent before the process exits; without this, data is silently lost in serverless and scripts

// BAD: exiting without flushing
async function handler() {
  await startActiveObservation("my-trace", async (span) => {
    span.update({ output: "done" });
  });
  // Process exits -- batched events never sent
}

Why bad: Langfuse batches events locally; if the process exits before the flush interval, events are lost

</patterns>


<performance>

Performance Optimization

Sampling for High-Volume Applications

Reduce costs by sampling a subset of traces:

import { TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";

const sdk = new NodeSDK({
  sampler: new TraceIdRatioBasedSampler(0.2), // Sample 20% of traces
  spanProcessors: [new LangfuseSpanProcessor()],
});

Or via environment variable: LANGFUSE_SAMPLE_RATE=0.2

Key Optimization Patterns

  • Batch flush tuning -- Configure LANGFUSE_FLUSH_AT (default 10) and LANGFUSE_FLUSH_INTERVAL (default 1s) for your workload
  • Span filtering -- Use shouldExportSpan on LangfuseSpanProcessor to drop noisy non-LLM spans
  • Data masking -- Redact PII before transmission with the mask option to avoid storing sensitive data
  • Stream token tracking -- Set stream_options: { include_usage: true } on OpenAI streaming calls so observeOpenAI captures token counts

</performance>


<decision_framework>

Decision Framework

Which Packages to Install

What do you need?
+-- Tracing LLM calls?
|   +-- YES -> npm install @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node
|   +-- Also using OpenAI SDK?
|       +-- YES -> npm install @langfuse/openai
+-- Prompt management, scores, or datasets?
|   +-- YES -> npm install @langfuse/client
+-- Both tracing AND client features?
    +-- YES -> Install all: @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node @langfuse/client

Which Tracing Method to Use

How do you want to instrument?
+-- Wrapping a function? -> observe() (declarative, auto-captures inputs/outputs)
+-- Block of code with nesting? -> startActiveObservation() (context propagation, auto-end)
+-- Need manual start/end control? -> startObservation() (requires explicit .end())
+-- OpenAI SDK calls? -> observeOpenAI() (zero-config auto-instrumentation)
+-- Update active span without reference? -> updateActiveObservation()

Which Observation Type (asType)

What is this observation?
+-- LLM call (prompt -> completion) -> "generation"
+-- AI agent decision-making step -> "agent"
+-- External API or function call -> "tool"
+-- Vector store or DB retrieval -> "retriever"
+-- Quality assessment step -> "evaluator"
+-- Embedding creation -> "embedding"
+-- Link between application steps -> "chain"
+-- Content safety / jailbreak check -> "guardrail"
+-- Generic duration operation -> "span" (default)
+-- Point-in-time event -> "event"

</decision_framework>


<red_flags>

RED FLAGS

High Priority Issues:

  • Not importing instrumentation.ts before other modules (auto-instrumentation silently fails)
  • Exiting short-lived processes without forceFlush() or sdk.shutdown() (events are silently lost)
  • Hardcoding LANGFUSE_SECRET_KEY or LANGFUSE_PUBLIC_KEY in source code (credential exposure)
  • Using manual generation observations when observeOpenAI() would handle it automatically (duplicated effort, less accurate data)
  • Using startObservation() without calling .end() (observation stays open indefinitely)

Medium Priority Issues:

  • Not setting stream_options: { include_usage: true } on OpenAI streaming calls (token counts missing from observeOpenAI traces)
  • Forgetting to call langfuse.score.flush() in short-lived processes (scores are batched and may be lost)
  • Using startObservation() when startActiveObservation() would work (no automatic context propagation or auto-end)
  • Not using asType on observations (all observations appear as generic spans, losing semantic meaning)
  • Not setting LANGFUSE_BASE_URL for self-hosted instances (defaults to cloud.langfuse.com)

Common Mistakes:

  • Importing @langfuse/openai without setting up the OTel NodeSDK first -- the OpenAI wrapper requires OTel context to send traces
  • Confusing LangfuseClient (from @langfuse/client, for prompts/scores/datasets) with the OTel tracing functions (from @langfuse/tracing)
  • Using prompt.compile() without matching all {{variable}} placeholders -- unmatched variables remain as literal {{name}} in output
  • Calling langfuse.score.create() with a value of type string for NUMERIC scores or number for CATEGORICAL scores (type mismatch)
  • Running dataset experiments without OTel setup -- experiment tasks run inside startActiveObservation which requires OTel

Gotchas & Edge Cases:

  • observeOpenAI() does NOT support the OpenAI Assistants API -- only Chat Completions and Responses API
  • The SDK's default span filter only exports Langfuse and GenAI spans. If you use a custom instrumentation library, you must configure shouldExportSpan to include it.
  • LangfuseClient.prompt.get() caches prompts with a default TTL. If you update a prompt and don't see changes, set cacheTtlSeconds: 0 to bypass caching.
  • Boolean scores use float values (0 or 1), not JavaScript booleans (true/false).
  • Self-hosted Langfuse requires platform version >= 3.95.0 for TypeScript SDK v4 compatibility.
  • score.create() is fire-and-forget (synchronous) -- it queues the score for batched delivery. You only need await on flush().
  • Dataset names with slashes (evaluation/qa-dataset) must be URL-encoded when used as path parameters.
  • The v4+ SDK is a complete rewrite from v3 -- Langfuse class, trace(), span(), generation() from v3 are replaced by OTel-based APIs.

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST import and register instrumentation.ts at the top of your entry point BEFORE any other imports -- OpenTelemetry must instrument modules before they are loaded)

(You MUST call forceFlush() or sdk.shutdown() in short-lived processes (serverless, scripts, CLI tools) -- events are batched and will be lost without explicit flushing)

(You MUST use @langfuse/openai with observeOpenAI() for OpenAI SDK tracing -- do NOT manually create generation observations for OpenAI calls when the wrapper handles it automatically)

(You MUST set LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, and LANGFUSE_BASE_URL via environment variables -- never hardcode credentials)

(You MUST use startActiveObservation() or observe() for nested tracing -- manual startObservation() requires explicit .end() calls and does NOT propagate context automatically)

Failure to follow these rules will produce silent data loss, missing traces, or credential exposure in LLM observability.

</critical_reminders>