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

Langfuse 公式プロジェクト移行スクリプトを試してみた

·14 分
著者
Shunsaku Takagi
目次

Langfuse の公式ドキュメントに掲載されている Cookbook「Migrating Data Between Langfuse Projects (Python SDK v4) 」を、4 つの移行パターンで実際に実行してみました。移行はおおむね成功しましたが、UI に出るものと出ないものが分かれ、見落としやすい落とし穴がいくつかありました。本記事では、特定の検証環境における観測結果と再現時の注意点を整理します。Cloud / セルフホスト / リージョンによる差分が Langfuse の正式仕様なのか、Feature flag や Preview UI・検証時点の実装差なのかは、本記事の範囲では切り分けていません。

想定読者は、Langfuse のプロジェクト間移行(クラウド間・セルフホスト ↔ クラウド)を検討しているが、公式スクリプトでどこまで再現できるかを把握したい方です。

1. 公式移行スクリプトの概要
#

この Cookbook は、Langfuse Python SDK v4 を使い、移行元プロジェクトから移行先プロジェクトへデータをコピーする Jupyter ノートブックです。移行元・移行先それぞれの API キーとベース URL を設定し、セクション順に実行していきます。

トレースと Observation は、元の開始・終了時刻を保つために OTLP エンドポイント経由で再取り込みします。Score、プロンプト、データセットなどは公開 API 経由です。

移行できるもの
#

Cookbook 冒頭では、次の 6 種類が移行対象として列挙されています。

  1. Score Configs(スコアの名前・型などの定義)
  2. Custom Model Definitions(ユーザー定義モデル)
  3. Prompts(すべてのバージョン)
  4. Observations(トレースとネストした Observation)
  5. Scores
  6. Datasets(items と run items)

移行できないもの・スクリプト対象外
#

Cookbook 冒頭の「What is not migrated」では、Langfuse 公開 API が現時点でプログラムから作成できないものとして、次が挙げられています。これらは手動で再作成するか、UI レベルのエクスポート経由でコピーする必要があります。

  • LLM-as-a-Judge Evaluator 設定
  • カスタムダッシュボード
  • ユーザー / RBAC / SSO
  • プロジェクト・組織の設定

2. 試した移行パターン
#

いずれも同一の Cookbook を使用し、移行元/先のAPI キーのみ変更しました。

#移行元移行先
1Langfuse CloudLangfuse Cloud
2セルフホストLangfuse Cloud
3Langfuse Cloudセルフホスト
4セルフホストセルフホスト

検証環境
#

移行結果の差分は、Langfuse 本体・SDK・Cloud 側の Preview 状態に依存する可能性があります。本記事の再現性判断の前提は次のとおりです。

項目
Langfuse(Cloud / セルフホスト)v3.198.0
Python SDK3.8.1、4.0.0
Cloud リージョンJP
Fast Preview有効

認証に使うのは、各 Langfuse プロジェクトで発行する API キー(公開鍵・秘密鍵)です。移行元と移行先が同一組織内の別プロジェクトか、組織をまたぐかは、今回の試行範囲では挙動差はほぼ観測されませんでした。

パターン 2・4 は移行元がセルフホストのため v2 Observations API が使えず、3章で述べる v1 取得への手動差し替えが前提です。パターン 1・3 は移行元が Cloud のため、Cookbook 原本のまま実行できます。パターン 1 は、JP ↔ US ↔ EU などのクラウド間移行に相当します。

3. セルフホスト移行元の注意:Observation 取得の差し替え
#

Cookbook の Observations セルには、次の注記があります。

Self-hosted deployments: the v2 Observations API is currently Cloud-only. If you are migrating from a self-hosted
Langfuse instance, replace the observations.get_many(...) calls below with src.api.trace.get(trace.id).observations
(which hits the v1 endpoint). A Langfuse self-hosted migration path for v2 is on the roadmap.

これは、移行元がセルフホストで v2 の Observation 一覧 API が使えない場合(パターン 2・4)を想定しています。 変更するのは Observation を集めるヘルパーだけで足ります。OTLP 送出や trace 単位の移行ロジックはそのままで構いません。

置き換える場所
#

  • _fetch_observations_v2 関数本体(observations.get_many とカーソルページングの部分)

公式の差し替え例
#

デフォルトの _fetch_observations_v2 は、v2 API の observations.get_many をカーソルでページングする実装です。セルフホスト移行元(パターン 2・4)では、この関数本体を Cookbook が示す v1 経路に差し替えます。

Cookbook の Section 4 説明文では src.api.trace.get(trace.id).observations_fetch_observations_v2 の docstring では次の 1 行が示されています。どちらも同じ意味で、今回の試行でもこの公式の書き方どおりに差し替えました。

def _fetch_observations_v2(source_trace_id: str) -> List[Any]:
    return with_retries(lambda: src.api.trace.get(source_trace_id)).observations

observationsNone になるケースだけ or [] を足すと安全です。リストが返る通常の trace であれば、公式の 1 行と挙動は同じです。

def _fetch_observations_v2(source_trace_id: str) -> List[Any]:
    return with_retries(lambda: src.api.trace.get(source_trace_id)).observations or []

関数名を _fetch_observations_v2 のままにして関数本体だけ差し替えれば、呼び出し側はそのままで動きます。

クラウド移行元のとき(パターン 1・3)
#

移行元が Langfuse Cloud で v2 API が使えるなら、Cookbook 原本の observations.get_many とカーソルページングのままで構いません。本章の差し替えは、移行元がセルフホストのとき(パターン 2・4)に限ります。

4. パターン共通でわかったこと
#

4 パターンいずれでも、今回の検証環境では次のような傾向が観測されました。取得 API が v1 か v2 かにかかわらず、Cookbook 本体の OTLP 再取り込みや API の制約に起因するものがほとんどです。 詳細は7章以降に記載しています。

  • Observation(Span / Generation など)単位のデータは移行できるが、Trace ツリーの見え方は元と一致しない(合成ルート Span の追加、親子リンクのずれ)
  • Score はスクリプトログ上は migrated と出ても、公開 API で取得できず、UI の一覧や Trace 上のバッジにも出ないことがあった
  • Observation 型(AGENT / TOOL / CHAIN 等)はコピーされず、SPAN や GENERATION として再登録される
  • プロンプトの版番号は移行先で新しい連番になる(元版は commit message 等に残る)
  • Dataset の schema(inputSchema / expectedOutputSchema)は移行されない
  • Langfuse Media 参照付きの dataset item は、Media 本体が移行されないため失敗しうる
  • dataset item は最新バージョン中心で、古いバージョンに紐づく run item は失敗しうる
  • 移行 Trace には OTLP 由来の metadata(langfuse-migration 等)が付く
  • データセットやプロンプトの一覧並びが、作成順の影響で元と逆に見えることがある

5. 移行先によって分かれたこと:Experiments(Dataset run)の UI
#

Experiments(Dataset run)ページに run item が表示されるかは、今回の検証環境では移行元より移行先で観測が分かれました。正式仕様としての断定はせず、以下は検証時点の観測です。詳細は7章以降に記載しています。

移行先該当パターンスクリプトログExperiments / Dataset run UI公開 API
Langfuse Cloud1, 2run item が migrated / linked と表示される何も表示されない(空)dataset run / run item を取得できなかった
セルフホスト3, 4同様に成功ログrun item が表示される(latency / cost 集計は空になりうる。7章参照)一覧取得は可能だったが、集計値は UI 上で空になることがあった

今回の検証環境では、「ログ上は移行できたのに Experiments の UI には出てこない」のは移行先が Cloud のときに限って観測されました。セルフホスト移行先では run item 自体は UI に出ます。移行先が Cloud の場合、UI だけでなく公開 API でも dataset run 関連データを取得できなかったため、保存の有無までは本記事の範囲では確認できていません。Dataset run まで要件に含める場合は、移行先が Cloud ならドライランで UI と API の両方を確認することをおすすめします。

移行先セルフホスト(右)には Latency と Cost のグラフが表示されるが、移行先 Cloud(左)では表示されない
移行先セルフホスト(右)には Latency と Cost のグラフが表示されるが、移行先 Cloud(左)では表示されない

6. うまくいったこと
#

Score config とカスタム Model 定義
#

4 パターンいずれでも、Score config とカスタム Model 定義は意図どおり移行できました。移行先に同名の Score config がある場合は既存を再利用し、無ければ新規作成する動きです。

プロンプト
#

プロンプトも、移行元で版を削除してバージョン番号が非連続になっている場合を除けば、正常に移行できました。版を削除したあとに移行すると、移行先では版番号が振り直されます。通常の連番のままであれば、本文・ラベル・タグは問題なくコピーされます。

Observation(Span)単位の移行
#

公式の差し替え例どおり v1 取得に差し替えた後、Observation 単位の入出力・コスト・時刻などは移行できました。ただし Trace ツリー全体として元と同じ見え方になるわけではなく、7章で述べるとおり構造のずれは残ります。セルフホスト移行元向けの差し替え方は、3章を参照してください。

7. 気づいたこと・想定される原因
#

Trace のツリー構造が変わる
#

移行元では、Observation が Trace 内で階層構造になっていても、移行先では、同名の Span が一段多くネストしたり、task が親から外れてルート付近に並んだりします。

これは Cookbook が trace ごとに必ず行う OTLP 再構築が主因です。Trace 一覧と Observation 一覧を取得したうえで、合成ルート Span を常に 1 本追加し、各 Observation を別 Span として再送します。

厳密に元の Trace ツリーを再現したい場合は、合成ルート Span を付けない、親 ID の読み方を直すなど、スクリプト改修が必要です。本番移行前に、代表 trace を 1 本選び、移行元・移行先でツリーを並べて比較することをおすすめします。

移行元(左)は深いネスト構造だが、移行先(右)では階層が平坦化している
移行元(左)は深いネスト構造だが、移行先(右)では階層が平坦化している

Score はログ上「成功」でも API で取得できず、UI にも出なかった
#

今回の検証環境では、スクリプトログ上は Score 移行が成功(migrated)と出ても、移行先の公開 API で当該 Score を取得できず、Scores 一覧・Trace ツリー・Observation バッジのいずれにも表示されませんでした。UI 非表示だけでなく API 取得にも失敗したため、保存されていることを保証できません。

Cookbook 上、Score config は移行対象ですが Evaluator(LLM-as-a-Judge)の設定・実行履歴は対象外です。configId 付き Score や observation ID の不一致が影響している可能性はありますが、今回の環境では API でエビデンスを取れなかったため、原因までは特定していません。

Colab の出力では Score が migrated と表示されるが、UI には出ないことがある
Colab の出力では Score が migrated と表示されるが、UI には出ないことがある

Dataset 自体の設定は一部移行できない
#

Dataset 本体の name / description / metadata は移りますが、inputSchema / expectedOutputSchema は送られません。移行元で schema を設定していた場合、移行先では空になり、item の中身だけがコピーされた状態です。schema 付き Dataset を運用している場合は、移行後に手で再設定してください。

Dataset run の latency / cost 集計が空になることがある
#

今回の検証環境では、移行先がセルフホスト(パターン 3・4)では run item は UI に出ましたが、一覧の latency / total cost が空になることが観測されました。紐づく Trace を開くと cost は見えます。移行先が Cloud(パターン 1・2)では、Experiments ページ自体が空で、公開 API でも dataset run 関連データを取得できず、集計以前の問題でした。

run 一覧の集計が空になる件については、Cookbook が run item 作成時に createdAt を渡さず移行実行時刻が入る一方、Observation は元の start_time を保持するため、時間窓から外れて集計が null になりうる、という説明が考えられます。ただし Cloud 移行先で API 取得までできなかったケースについては、集計ロジック以前の問題として切り分けが必要です。

移行元(左)では latency / cost のグラフに値があるが、移行先セルフホスト(右)では 0 になる
移行元(左)では latency / cost のグラフに値があるが、移行先セルフホスト(右)では 0 になる

移行 Trace に OTLP 由来の metadata が付く
#

Cookbook の OTLP パイプラインは service.name: langfuse-migration を固定で載せます。移行先 Trace を開くと、元の Prompt test 実行時とは違う metadata に見えることがあります。比較・監査で metadata を見ている場合は注意が必要です。

移行元(左)の トレースのメタデータ service.name と scope.name が、移行先(右)では langfuse-migration に置き換わる
移行元(左)の トレースのメタデータ service.name と scope.name が、移行先(右)では langfuse-migration に置き換わる

並び順が逆に見えることがある
#

データセット、プロンプトなどで、一覧の並びが元プロジェクトと逆に感じられることがありました。移行処理が作成 API を順に叩くため、作成順がソートに影響している可能性があります。表示上の問題であり、データ欠損とは限りません。

Dataset item は最新バージョン中心で、古い run item が失敗しうる
#

最新の dataset item バージョンしか移行されないため、古いバージョンにだけ存在していた run item は参照先が無く失敗することがあります。media 付き item では、JSON 内の @@@langfuseMedia:...@@@ 参照だけが送られ、Media 本体は移行されないため 400 エラーになり、run item が連鎖失敗することもありました。

Observation 型(AGENT / TOOL / CHAIN 等)はコピーされない
#

移行元の AGENT 等は、Cookbook の OTLP 変換で SPAN や GENERATION に置き換えられます。

_OBS_TYPE_MAP = {
    "AGENT": "span",
    "TOOL": "span",
    # ... 他の型も同様に span または generation へ
}

中身(名前・入出力)は残ることが多いですが、UI の型アイコンは変わるため、Agent フィルタでは 0 件になり、移行漏れと勘違いしやすいです。確認時は型フィルタだけに頼らず、名前や入出力で突き合わせてください。

移行元(左)の Agent アイコンが、移行先(右)では Span アイコンに変わる
移行元(左)の Agent アイコンが、移行先(右)では Span アイコンに変わる

8. Cookbook の移行マトリクス(移行される/されない)
#

公式 Cookbook の移行範囲を出発点に、4 パターンの試行で著者が拡張整理したものです。以下は v3.198.0 / SDK 3.8.1・4.0.0 / Cloud JP / Fast Preview 有効の検証環境における観測であり、Langfuse の正式仕様であるとは限りません。1章「移行できないもの・スクリプト対象外」節より詳しい検証結果が載っています。

移行される項目・されない項目

項目別の整理
#

早見
#

おおむね移行されるもの:Trace / Observation の入出力・コスト・時刻・多くの metadata、Score config、カスタム model、prompt 本文、Dataset item の最新版、Trace 単位の public。

移行されない/変形するもの:evaluator 設定、dataset schema、Observation 型、Langfuse Media 本体、Trace ツリー完全一致、検証環境では移行先 Cloud で Experiments UI・API 取得に失敗、Score は API 取得不可、prompt 版番号の維持、item の全バージョン、run 集計(createdAt 未指定)など。

Score config
#

移行される:name、data_type、description、カテゴリ定義、数値の min/max など。

移行されない・注意:アーカイブ状態。移行先に同名 config がある場合は既存を再利用。検証環境では configId 付き Score が API で取得できなかったケースあり(保存失敗かは未確認)。

カスタム Model 定義
#

移行される:model 名、match pattern、価格設定など。

移行されない:Langfuse 管理モデル。移行先に既に存在する定義。

Prompt
#

移行される:name、本文、labels、tags、config。

移行されない・変わる:版番号(移行先で新連番。commit message に元版を記載)。Cookbook 未対応の prompt 型。

Trace / Observation(OTLP)
#

移行される:id、name、user_id、session_id、入出力、metadata、時刻など。

移行されない・変わる:Observation 型そのもの、Trace ツリー構造、Media 添付、OTLP 固有 metadata、Observation 単位の public。

Score
#

移行される(Cookbook 上):id、name、value、comment、timestamp、trace_id / observation_id(マップ後)など。

検証環境での観測:スクリプトログは migrated でも、公開 API で Score を取得できず UI にも表示されなかった。source、dataset_run_id、evaluator 実行履歴との紐づけは未確認。

Dataset
#

移行される:Dataset の name / description / metadata、item の入出力、run と run item のリンク(マップ後)。

移行されない・変わる:schema、Media 本体、item の過去バージョン、run item の createdAt、検証環境では移行先 Cloud で Experiments UI・API 取得に失敗。

Cookbook 対象外(公式記載の移行しない項目)
#

LLM-as-a-Judge Evaluator 設定、カスタムダッシュボード、ユーザー・RBAC・SSO、プロジェクト・組織の設定。

9. まとめ
#

この Cookbook は、クラウド間やセルフホストからデータの大部分を API で持っていくための実用的な出発点です。今回の検証環境(Langfuse v3.198.0、SDK 3.8.1 / 4.0.0、Cloud JP、Fast Preview 有効)では、Score config、カスタム Model、プロンプト、Observation 単位のデータは移行できました。一方で、Trace ツリーの見え方、Score の API 取得・UI 表示、Observation 型、Dataset schema や Media、移行先 Cloud での Experiments の API 取得・UI 表示などは、観測された範囲ではそのまま再現されませんでした。

本番移行前に、少ない件数で試し、特に移行先が Cloud なら Dataset run の UI と API の両方、移行先がセルフホストなら run 集計まで、運用要件を満たすかを確認することをおすすめします。本記事の結果は検証環境に依存するため、バージョンや Preview 状態が異なると見え方が変わる可能性があります。

10. 参考リンク
#