メインコンテンツへスキップ

Langfuse v4で何が良くなったのか — Agentの問題箇所を探し、分析し、監視するまで

著者
KAMON Nobuchika

AI Agentのトレースは、1回の実行にLLM CallやTool Callが数百単位で並ぶことも珍しくありません。実行が失敗したことは分かっても、原因になった1つの処理にたどり着くまでに時間がかかります。

Langfuse v4は、この「探す」工程を作り直したリリースです。

v3ではTraceを開いてから中を掘り下げるのが基本でした。v4では、問題のあるLLM CallやTool Callそのものを、複数のTraceをまたいで直接検索できます。そこから異常が起きた時間帯へ移動し、同じ条件のまま傾向をグラフで確認し、しきい値を超えたら通知を受け取る。この一連の流れがつながりました。

土台にあるのは、Langfuseが「Observations-first」と呼ぶデータモデルの変更です。内部のテーブル構造も大きく変わっていますが、本記事では設計そのものより、日々の調査と運用がどう変わるのかを中心に扱います。

v4はLangfuse Cloudとセルフホスト版の双方で正式に利用できます。セルフホスト版は、2026年7月29日にv4.0.0として初回GAリリースされました。なお、本記事の「v4」はLangfuse本体のバージョンを指します。後述するSDKのメジャーバージョンとは別の体系です。

Langfuse v3とv4の主な違い
#

やりたいことLangfuse v3Langfuse v4
エラー箇所を探すTraceを入口に、内部の処理へ掘り下げる使い方が中心エラーになったLLM CallやTool CallをObservationsテーブルから横断検索
遅い処理を探すTraceやObservationを個別に確認Observationをレイテンシー条件で絞り込む
高コストな処理を探すTrace全体のコストから原因を確認Generation単位でコストを比較
入出力から対象を探す既存の検索やフィルターを利用inputとoutputの全文検索を構造化フィルターと併用
傾向を見るカスタムダッシュボードを作成ObservationsテーブルをChart表示へ切り替え
異常を検知するAPIや外部監視基盤と組み合わせるLangfuse上でMonitorを設定
APIから分析するTrace、Observation、MetricsのAPIを用途別に利用Observations API v2とMetrics API v2で行取得と集計を分離
本番のLLM-as-a-Judgeを実行するTrace単位のEvaluatorを利用Observation単位のEvaluatorが推奨
大量データを扱うJOINと読み取り時の重複排除が負荷になりやすいObservation中心のデータモデルで負荷を抑える

v3にもObservationsテーブルやフィルターはありました。v4で変わったのは、Observationが調査や分析の中心に置かれたことです。

Langfuseは、この設計を「Observations-first data model」と呼んでいます。公式説明では、大規模プロジェクトで長期間を対象としたダッシュボードが少なくとも10倍高速化し、大量データのテーブル初期表示も秒単位からミリ秒単位へ改善したとされています。

この方針転換は、記録の仕方にも及んでいます。v4では、Trace自身にinputとoutputを持たせる方式が非推奨になりました。テーブル、LLM-as-a-Judge、Exportはいずれも、関連するObservation(通常はRoot Observationや該当するLLM Call)のinputとoutputを参照します。リクエスト全体の入出力はRoot Observationへ、個別処理の入出力はそれぞれのObservationへ記録する、という整理です。


Traceを開く前に、問題のある処理を探す
#

v3では、問題が起きたTraceを見つけ、その中にあるLLM CallやTool Callを確認する流れが一般的でした。

Traceを探す
Traceを開く
問題のある処理を探す

単純なチャットアプリであれば、この方法でもそれほど困りません。一方、AI Agentでは、1つのTraceに多数のLLM Call、Tool Call、Retrieval、Sub-agentの処理が含まれます。

実際に調べたいのは、次のような内容です。

  • エラーが増えているTool Call
  • レイテンシーが悪化したモデル呼び出し
  • コストを押し上げているGeneration
  • 低い評価が集中しているRetriever

v4では、こうした処理をObservationsテーブルから直接検索します。

問題のあるObservationを探す
対象のLLM CallやTool Callを確認する
必要に応じてTrace全体を見る

TraceのWaterfall表示は引き続き利用できます。個々の処理を横断して調べるときはObservationを使い、1回の実行全体の流れや因果関係を追うときはTraceを見る、という使い分けになります。


inputやoutputから対象を特定する
#

本番環境で問い合わせを受けたとき、Trace IDまで分かっているケースは多くありません。利用者から届くのは、たとえば次のような情報です。

返金について質問したら、途中でエラーになった。

この場合、「返金」という入力内容や、画面に表示されたエラーメッセージが手掛かりになります。

画面上部の全文検索では、TraceとObservationのinputおよびoutputを検索できます。期間、環境、Observation Type、モデル、ログレベルなどの条件とも組み合わせられます。

Filter Search Barでは、次のような形式で条件を入力します。

level:ERROR type:TOOL environment:production latency:>2

サイドバーのフィルターと同じ条件をテキストで操作でき、比較演算子やワイルドカード、入力補完にも対応しています。条件はURLに保存されるため、調査中の絞り込みをそのままリンクで共有できます。

Filter Search Barにフィールド名を付けずに語句を入力した場合は、ID、名前、input、outputが横断検索されます。対象を片方に限定したい場合は、input:またはoutput:を付けます。

全文検索は単純な部分一致ではなく、ClickHouseのトークンベース検索です。検索語はトークン単位で照合され、複数の検索語は連続したフレーズとして扱われます。公式ドキュメントでは、errorerrorに一致する一方、errorsには一致しないと説明されています。

Observations API v2では、UIの検索対象に加えて、指定したキーの文字列metadataにもmatches条件を利用できます。なお、inputとoutputのmatchesは大文字と小文字を区別しませんが、metadataに対するmatchesは区別します。


Pulseから異常が起きた時間帯へ移動する
#

問題が発生していることは分かっていても、いつから始まったのか判断できない場合があります。

Observationsテーブルの上部にあるPulseでは、選択した期間を時間帯ごとに区切り、次の指標を確認できます。

  • Observation数
  • 合計コスト
  • レイテンシーのp95またはp50

初期状態ではObservation数が表示されます。コストでは時間帯ごとの合計額、レイテンシーではp95または中央値のp50を選べます。

Pulseの指標をLatencyのp95に切り替えた画面。上部に時間帯ごとの棒グラフが並び、その下にObservationsテーブルが表示されている
Latency / p95 に切り替えた Pulse

特定の時間帯だけ棒が高くなっていれば、その時間帯で処理件数、コスト、またはレイテンシーが増えています。棒をクリックすると、Observationsテーブルが該当時間帯に絞り込まれます。複数の棒をドラッグして、少し広い期間を選ぶこともできます。

スパイクを見つける
該当する時間帯を選ぶ
その時間帯のObservationを確認する

大量の行を並べ替えて原因を探すより、異常が起きた時間帯から入る方が対象を絞り込めます。

ただし、Pulseには2つの制約があります。1つは、Observationsテーブルを特定のUserまたはSessionだけに絞って表示している場合、Pulseが表示されないことです。

もう1つは、すべてのフィルターがPulseの集計に使われるわけではないことです。レイテンシー、コスト、Token数の数値フィルター、Score、metadata、コメント、全文検索、input:output:による検索、has:による存在チェックなどが有効な場合、Pulseは無効になります。テーブル側には引き続き条件が適用されるため、Pulseが表と異なる対象を集計しているように見えることはありません。


調査中の条件を、そのままグラフにする
#

Observationsテーブルは、TableとChartを切り替えられます。たとえば、次のような条件でObservationを絞り込んだとします。

type = generation
environment = production
model = model-a

Chartへ切り替えると、同じ期間と、Chart側で扱えるフィルターを使ってデータが可視化されます。設定できる主な項目は次のとおりです。

  • Line、Area、Bars、Ranked、Pie、Numberなどの表示形式
  • 件数、レイテンシー、コスト、Tokenの指標
  • モデル、名前、ログレベル、Type、環境などの分類軸

集計方法は指標によって選択肢が変わります。レイテンシーでは平均、p50、p95、p99、最大、最小を、コストとTokenでは合計、平均、p95、最大を選べます。件数に集計方法の選択肢はありません。

継続して確認したい内容であれば、そのままカスタムダッシュボードへ追加できます。保存されるのは、グラフの形式、指標、集計方法、分類軸、適用可能なフィルターです。表示期間は追加先のダッシュボードで設定します。

Pulseと同様に、すべてのフィルターがグラフに反映されるわけではありません。全文検索、metadata、Score、コメント、数値条件、has:による存在チェックなど、集計チャートで扱えない条件は画面上で無効化されます。どの条件がChartに反映されていないかは、その場で確認できます。


Langfuse上で異常を監視する
#

問題を見つけやすくなっても、毎日画面を確認し続ける運用には限界があります。

v4のMonitors and Alertsでは、ObservationやScoreから計算した指標にしきい値を設定できます。過去のデータからベースラインを学習して逸脱を自動判定する仕組みではなく、利用者が演算子としきい値を明示する、しきい値監視です。

監視できる内容の例は次のとおりです。

  • 平均レイテンシーが設定した上限を超えた
  • p95コストが設定した上限を超えた
  • 品質スコアが設定した下限を下回った
  • Observation数が設定した下限を下回った
  • Boolean Scoreの成功率が一定値を下回った

Alertの前段階としてWarningのしきい値を設定できるほか、評価期間、データがない場合の扱い、異常が継続した場合の再通知も指定できます。通知先はSlack、Webhook、GitHub Actionsです。

品質スコアが基準を下回る
Slackへ通知する
対象のObservationを調査する

v3でも、Public APIや外部の監視基盤を組み合わせれば、同様の仕組みは作れました。v4では、よく使う監視条件をLangfuse上で設定し、通知までつなげられます。MonitorsはLangfuse Cloudの各プランと、セルフホスト版v4以降で利用できます。

2026年8月時点で、Langfuse Cloudの組織あたりMonitor数の上限は、Hobbyが2件、Coreが20件、Proが50件、Enterpriseが100件です。セルフホスト版にはMonitor数の上限がありません。


Root Observationだけを表示することもできる
#

Observations-firstでは、すべてのLLM CallやTool Callが一覧に並びます。Trace一覧に比べて行数は確実に増えます。

Python SDK v4.7.0以降、またはJS/TS SDK v5.4.0以降を使っているプロジェクトでは、Observationsテーブルを初期状態で開いた際に、Is Root Observation = trueが自動的に設定されます。これにより、たとえば次のようなアプリケーションの入口を中心に表示できます。

  • 1回のユーザーリクエスト
  • 1回のAgent実行
  • 1回のバックグラウンド処理

子のLLM CallやTool Callを横断検索する場合は、Rootのフィルターを外します。

通常時
  Root Observationを中心に見る

問題調査時
  Rootフィルターを外して個々の処理を見る

保存済みViewや共有URLに別の条件がある場合は、その設定が優先されます。一度Rootフィルターを外すと、その選択も記憶されます。また、Root Observationが1件も見つからず、ほかのObservationが存在する場合は、一覧が空に見えないようLangfuseがRootフィルターを自動的に解除します。

LangfuseのRoot Observationには、物理的な親を持たないObservationだけでなく、SDKがアプリケーションの入口としてマークしたApp Rootも含まれます。そのため、OTelコンテキスト上では親Spanを持っていても、LangfuseではRoot Observationとして扱われる場合があります。

Observations API v2でも、物理的な親を示すparentObservationIdと、論理的なRootであることを示すisRootObservationは別のフィールドとして扱われています。


APIは行データと集計データで使い分ける
#

v4では、UIだけでなく、データ取得用のAPIもObservation中心に整理されています。主に使うのは次の2つです。

API主な用途
Observations API v2個々のObservationを取得する
Metrics API v2コスト、Token、件数、レイテンシー、Scoreなどを集計する

Observations API v2は、Span、Generation、Eventなどの行データを取得するためのAPIです。

fieldsパラメーターを使い、corebasiciometadatamodelusagemetricsなど、必要なフィールドグループだけを指定できます。指定しなかったグループのフィールドは、nullではなくレスポンス自体から省略されます。

ページングにはカーソル方式を採用しています。レスポンスのmeta.cursorを次のリクエストへ渡し、続きを取得します。1ページあたりのデフォルト件数はv1、v2ともに50件で、最大取得件数はv1の100件からv2では1,000件へ増えています。

Metrics API v2は、すべてのObservationを取得して利用者側で集計するのではなく、Langfuse側で計算した結果を返します。たとえば、次のような用途に使えます。

  • モデル別の合計コスト
  • 環境別のObservation数
  • 日別のToken使用量
  • Observation名別の平均レイテンシー
  • Numeric、Categorical、Boolean Scoreの集計

v2ではobservationsを中心としたViewと、Score種別ごとのViewが提供されています。idtraceIduserIdsessionIdなどの高カーディナリティな項目はフィルターには使えますが、GROUP BYの対象にはできません。

社内ダッシュボードやコスト配賦、定期レポートでは、集計をLangfuse側に寄せることで、取得件数とアプリ側の処理を減らせます。


評価対象を個々の処理に絞る
#

AgentのTraceには、複数のLLM Call、Tool Call、Retrieval、Sub-agentが含まれます。Trace全体をまとめて評価すると、スコアが低いことは分かっても、原因となった処理までは特定できない場合があります。

v4では、本番トラフィックに対するLLM-as-a-Judgeの対象として、Observation単位のEvaluatorが推奨されています。たとえば、次のように対象を分けられます。

  • 最終回答を生成したGeneration
  • 検索結果を返したRetriever
  • 特定のTool Call
  • Sub-agentを表すObservation

Observation単位のEvaluatorは、対象となったObservationのinput、output、metadataを評価に利用します。兄弟や子のObservationを自動的に読み込むわけではないため、処理全体の入出力を評価したい場合は、それらを記録したLogical Root Observationを対象にします。

Trace単位のEvaluatorの扱い
#

Trace単位のEvaluatorは非推奨になりました。ただし、切り替え猶予があります。

セルフホスト版の移行では、書き込みモードをlegacydualevents_onlyから選びます。これはサーバーの環境変数LANGFUSE_MIGRATION_V4_WRITE_MODEで指定する、デプロイ全体の設定です。組織やプロジェクト単位では切り替えられません。

Trace単位のEvaluatorと、レガシーなDatasetベースのEvaluatorは、legacydualで運用している間は動作を続け、UI上はLegacyと表示されます。

一方、v4の既定であるevents_onlyへ切り替えた時点で、これらは停止します。v4のデータモデルではサポートされないためです。legacydualは移行用の一時的なモードであり、将来のメジャーバージョンで削除されることが公式に明言されています。恒久的な運用モードとして使えるものではないため、events_onlyへ切り替えるまでにObservation単位のEvaluatorへ移行しておく必要があります。

なおdualの期間中は、新しい画面に切り替えるかどうかをUIのトグルでユーザーごとに選べます。書き込みはデプロイ全体で1つ、読み取りはユーザーごと、という構成です。

Scoreの付与先は変わっていない
#

Score自体がObservation専用になったわけではありません。Trace、Observation、Session、Dataset RunへのScore付与は引き続き利用できます。なお、LangfuseではExperimentとDataset Runが同じ意味で使われる場合があります。

これまでTraceに付けていたScoreやコメントは、対応するRoot Observation上に表示されます。


これらの変更を支えるObservations-first
#

ここまで紹介した機能の土台にあるのが、Observations-firstのデータモデルです。

v3では、TraceとObservationを別々のエンティティとして扱っていました。概念的には次のような構造です。

traces
├─ trace_id
├─ user_id
├─ session_id
└─ tags

observations
├─ observation_id
├─ trace_id
├─ model
├─ latency
└─ cost

「特定ユーザーのLLM利用コスト」を計算するには、Trace側のuser_idとObservation側のcostを、trace_idで結び付ける必要があります。

v4では、Trace単位で利用する属性を各Observationにも持たせます。

observations
├─ observation_id
├─ trace_id
├─ user_id
├─ session_id
├─ tags
├─ model
├─ latency
└─ cost

特定ユーザーのGenerationや、特定Session内のTool Callを、Observation側のデータだけで検索できる構造です。Traceは同じtrace_idを共有するObservationの論理的なグループとして扱われ、user_idsession_id、tags、metadataなどのTrace属性は、対応SDKのコンテキスト伝播を通じて各Observationへ持たされます。

同じ値を複数のObservationへ保存するため、データには重複が生まれます。その代わり、検索時のJOINを減らせます。

v3ではTraceとObservationのJOINに加え、ReplacingMergeTreeに書き込まれた複数バージョンから最新行を選ぶ処理も、長期間のクエリで負荷になっていました。v4は、wideで原則として不変なObservationモデルを採用し、JOINと読み取り時の重複排除を避けています。


「1つのObservationテーブル」は論理モデルの話
#

Langfuseはv4について、概念的には1つのObservationテーブルであると説明しています。これは論理モデルの話で、Langfuseの全データが物理的に1つのClickHouseテーブルへ保存されるわけではありません。

Observation関連の新しい読み取りモデルは、主に次の2つの物理テーブルで構成されています。

テーブル役割
events_fullinput、output、metadataを含む完全なObservationデータを保持
events_core一覧や集計に使う軽量なプロジェクション

events_coreは、events_fullからMaterialized Viewによって生成されます。サイズが大きくなりやすいinput、output、metadataを切り詰め、テーブルやグラフのクエリを軽くしています。

概略化すると、次の構成です。

Observationを書き込む
   events_full
   完全なデータ
        │ Materialized View
   events_core
   一覧・集計向け

テーブルやChartでは主にevents_coreを使い、個別のObservationの完全な内容が必要な場合はevents_fullを参照します。

Scoresなど、Observation以外のデータまでこの2テーブルに統合されたわけではありません。つまりv4の変更は、ClickHouseの物理テーブルを1個にまとめたという話ではなく、TraceとObservationを別々の読み取りモデルとして扱うのをやめ、Trace属性を含むObservation中心の論理モデルへ統一した、という変更です。


まとめ
#

v3のTrace中心の画面は、1件のリクエストを追う用途では今も十分に機能します。1 Traceあたりの処理数が少ないチャットボットのようなアプリケーションであれば、v4に移っても体感は大きく変わらないかもしれません。

差が出るのは、「どのTraceが失敗したか」ではなく「どのTool Callがエラーを返しているか」を起点に調べたいときです。

セルフホスト版で移行を検討するなら、書き込みモードの切り替えが分かれ目になります。events_onlyにした時点で、Trace単位のEvaluatorに加えて、非推奨の読み取りAPI(traces、observations、sessions、scores、metrics、dataset runs)とレガシーなバッチ取り込みAPIが停止します。SDKのバージョン、API利用箇所、Evaluatorの3つを先に棚卸ししておくと安全です。