- Published on
トレースが費用になるとき — Langfuseの保持、サンプリング、マスキング
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに — 量が費用に変わる瞬間
- 費用が付く四つの地点
- サンプリング — trace単位で決まる
- サンプリングから外すもの
- マスキング — プロンプトに個人情報が入るとき
- 保持ポリシー — 既定値は削除しない
- 原本イベントバケットのライフサイクル
- 大きなペイロードを扱う
- 費用の軸をデータで見る
- おわりに — 減らす順番
- 試してみる
- シリーズ
- 参考資料
はじめに — 量が費用に変わる瞬間
トレーシングは導入初期にはほぼ無料のように感じられます。一日のリクエストが数千件のうちは、何を全部残しても目立ちません。問題はその時期の習慣が固まることです。リクエストが一日数百万件になり、利用者が貼り付けた文書がプロンプトに丸ごと入り始めると、保存費用と個人情報が同時に来ます。二つは同じ問題の両面です。必要以上に多く残しているということですから。
構成と設定名は2026-08-15に公式ドキュメントで確認しました。Langfuseはバージョンによってアーキテクチャが変わるため、利用中のバージョンのドキュメントを再確認してください。料金プランごとの詳細のように時間とともに変わる値については特にそうです。
費用が付く四つの地点
第三回で見た保存層を費用の観点で見直すと、地点は四つに分かれます。
| 地点 | 何が積み上がるか | 何で減らすか |
|---|---|---|
| ClickHouse | trace、observation、score の行 | サンプリング、保持ポリシー |
| オブジェクトストレージの原本イベント | 受信したイベント全体 | バケットのライフサイクルポリシー |
| オブジェクトストレージのメディア | 画像と添付 | サイズ上限、保持ポリシー |
| ネットワークとワーカー | スループットそのもの | サンプリング、エクスポートの絞り込み |
注目すべきは、サンプリングが上の二行に同時に効くことです。アプリケーションからそもそも送らなければ原本イベントも生まれません。一方で保持ポリシーは、すでに保存されたものを後から消す仕組みです。順序としてはサンプリングが先です。
サンプリング — trace単位で決まる
サンプリングのドキュメントが定義する設定は二つの経路です。環境変数のLANGFUSE_SAMPLE_RATE、そしてコンストラクタ引数であるPythonのsample_rateとJS/TSのsampleRateです。値の範囲は0から1で、既定値は1、つまり全件収集です。
from langfuse import Langfuse, get_client
Langfuse(sample_rate=0.5) # 50パーセントだけ収集
langfuse = get_client()
肝心なのは決定の単位です。ドキュメントは、SDKがtraceのレベルでサンプリングすると明示しています。traceが選ばれればその中のすべてのobservationとscoreも一緒に選ばれ、選ばれなければそのtraceのobservationもscoreも送られません。
なぜ重要かは逆の場合を考えると分かります。observation単位でサンプリングすると、ツリーに穴の空いたトレースが残ります。親はあるのに子がない状態で遅延を分解すれば数字が狂います。trace単位の決定は、残るものは完全で、ないものはまったくないという性質を保証します。
JS/TS SDKはOpenTelemetryの標準サンプラーを使います。
import { TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
// 20パーセント収集
const sampler = new TraceIdRatioBasedSampler(0.2);
サンプリングから外すもの
比率によるサンプリングにはよく知られた罠があります。まれにしか起きない失敗も一緒に消えるという点です。一日に十回出るエラーを10パーセントでサンプリングすれば一日一回しか残らず、調査が必要なのはまさにその十回です。ですから現実的な構成は比率一つで終わりません。
- 正常系は低い比率で残します。遅延と費用の分布を見るには標本で足ります。
- 失敗と異常系はすべて残します。コードでエラーを捕まえた地点の実行は別扱いにします。
- 評価対象のデータセットはサンプリングとは無関係に扱います。
もう一つの軸は、そもそも送らないことです。第二回で見たshould_export_spanがその場所です。すでにOpenTelemetryを使っているアプリケーションなら、LLMの観測に必要のないHTTPやデータベースのスパンまで流れ込んできますが、is_genai_spanのような判定関数で絞ればその分だけスループットが減ります。
マスキング — プロンプトに個人情報が入るとき
LLMトレーシングの特殊性がここで最も大きく出ます。一般的なアプリケーションのトレースは識別子と状態コードを残しますが、LLMのトレースは利用者が入力した文そのものを残します。そして利用者は何でも貼り付けます。
マスキングのドキュメントが示すPython側の方式は二つで、違いは適用の時点です。推奨されるmask_otel_spansはエクスポートの段階、つまりLangfuseがどのOpenTelemetryスパンを出すかを決めた後にスパン属性を扱います。以前の方式であるmaskはSDKが属性を作る時点で同期的に動きます。
from langfuse import Langfuse
def mask_otel_spans(*, params: MaskOtelSpansParams) -> Optional[MaskOtelSpansResult]:
patches = {}
for identifier, span in params.spans.items():
patches[identifier] = OtelSpanPatch(
delete_attributes=("gen_ai.prompt.0.content",),
set_attributes={"masking.applied": True},
)
return MaskOtelSpansResult(span_patches=patches)
langfuse = Langfuse(mask_otel_spans=mask_otel_spans)
JS/TSはLangfuseSpanProcessorにmask関数を渡す一つの方式です。ドキュメントはこの関数がobservationの入力、出力、メタデータに送信前へ適用されると説明しています。
import { LangfuseSpanProcessor } from "@langfuse/otel";
const spanProcessor = new LangfuseSpanProcessor({
mask: ({ data }) => {
return data.replace(/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, "***MASKED***");
},
});
必ず理解しておくべき性質が一つあります。マスキングはアプリケーションの中で、データがインフラを離れる前に起きます。サーバー側の機能ではありません。マスキングを有効にしていないサービスが一つでもあれば、そのサービスのプロンプトは原文のまま保存されるので、デプロイ単位ごとに確認する必要があります。
正規表現によるマスキングはカード番号のように形が決まった値にはよく効きますが、氏名や住所には弱いです。ですから二重に構えます。形のある値はマスキングで消し、そもそも残す理由のないフィールドは第二回のcapture_inputとcapture_outputを切って収集自体を止めます。
保持ポリシー — 既定値は削除しない
データ保持のドキュメントが明示する既定の挙動が重要です。保持ポリシーがなければLangfuseはイベントデータを自動的に削除せず、セルフホストのインスタンスは既定でデータを無期限に保持します。何も設定しなければ積み上がり続けるという意味です。
設定はプロジェクト単位です。プロジェクト設定の画面でオーナーと管理者が調整するか、プロジェクトAPIで設定します。ドキュメントが示す最小の保持期間は3日です。削除の対象と基準になる時刻もドキュメントに正確に載っています。
| 対象 | 基準となる時刻フィールド |
|---|---|
| trace | timestamp |
| observation | start_time |
| score | timestamp |
| メディア資産 | created_at |
削除は夜間の処理として実行され、ドキュメントは削除された資産を復元できないと明言しています。保持期間を短くする前に、オブジェクトストレージへのエクスポートを設定してS3、GCS、Azureへ自動同期しておくのがドキュメントの案内です。
セルフホストには追加の条件があります。保持機能を有効にするにはLangfuseのIAMロールにすべてのバケットに対するs3:DeleteObject権限が必要で、バージョニングが有効なS3バケットでは削除マーカーと過去のバージョンを手動で消すかライフサイクル規則で処理する必要があるとドキュメントが明示しています。これを見落とすと、保持ポリシーを有効にしてもバケット容量が減りません。
クラウドはプランによって既定のアクセス期間が異なります。ドキュメントを読んだ時点でHobbyが30日、Coreが90日、ProとEnterpriseが3年です。こうした値は変わるので、利用中のプランのドキュメントで確認してください。
原本イベントバケットのライフサイクル
ClickHouseの保持ポリシーだけを掛けても半分です。第三回で見たとおり、オブジェクトストレージには受信イベントの原本がそのまま積み上がります。オブジェクトストレージのドキュメントは、一定の日数が過ぎたオブジェクトを自動的に失効させるバケットのライフサイクルポリシーを設定するよう推奨し、参考値としてLangfuseクラウドの30日を挙げています。
期間を決めるときに考えるのは、ドキュメントが示す原本イベントの用途です。再試行ジョブ、リプレイと災害復旧、任意のレコード組み立てのうち何をいつまでできる必要があるかが、そのまま保持期間になります。
ドキュメントの助言がもう一つあります。Langfuseはアップロードされたファイルを ClickHouse のblob_storage_file_logテーブルで追跡しており、このテーブルの寿命をバケットのポリシーと合わせると処理の最適化に役立ちます。片側だけ縮めると、すでに消えたオブジェクトを指す行が残り続けます。
大きなペイロードを扱う
マルチモーダル入力と長い文脈は別の軸です。設定ドキュメントに出てくる関連する値は次のとおりです。
# メディア関連の上限(コメント内がドキュメント基準の既定値)
LANGFUSE_S3_MEDIA_MAX_CONTENT_LENGTH=1000000000 # 既定 1,000,000,000 バイト
LANGFUSE_S3_MEDIA_DOWNLOAD_URL_EXPIRY_SECONDS=3600 # 既定 3600 秒
# バッチエクスポートの上限
BATCH_EXPORT_PAGE_SIZE=500 # 既定 500
BATCH_EXPORT_ROW_LIMIT=1500000 # 既定 1,500,000 行
# S3 の同時実行
LANGFUSE_S3_CONCURRENT_WRITES=50 # 既定 50
LANGFUSE_S3_CONCURRENT_READS=50 # 既定 50
既定の1GBという上限は、事実上ほぼ制限がないのと同じです。実際のサービスではもっと低く設定しておくほうが安全です。誤って大容量ファイルがトレースに付く経路ができたとき、バケットではなくアップロードの段階で止まるからです。
ペイロードそのものを減らす方法もあります。検索結果二十件の原文を全部残す必要はたいていありません。文書の識別子とスコア、上位数件の要約だけ残せば、調査に必要な情報は保ちながら大きさは大幅に減ります。これは設定ではなく計測設計の問題で、第二回で決めた境界を見直す作業です。
費用の軸をデータで見る
ここまではトレーシングシステム自体の費用でしたが、このシステムが観測する対象であるモデル呼び出しの費用も同じデータの中にあります。
第一回で見たとおり、generationにはusage_detailsとcost_detailsが付きます。トークンとコスト追跡のドキュメントは、使用量が取り込まれるか推定され、価格を持つモデル定義に一致すれば取り込み時点で費用が計算されると説明します。セルフホストのモデルや社内モデルは、プロジェクト設定のモデル項目で正規表現パターンと価格を登録し、利用者が定義したモデルがLangfuseの管理するモデルより優先されます。
モデル定義を登録しなければ、その呼び出しの費用が0に見えるという意味です。費用のダッシュボードが実際の請求と合わないなら、真っ先に見る場所がここです。
おわりに — 減らす順番
順番はこうです。まず送らないものを決めます。エクスポートの絞り込みと収集の停止です。次にどれだけ送るか、つまりサンプリングを決めます。次に何を隠すか、つまりマスキングを決めます。最後にどれだけ長く置くかを決め、保持ポリシーとバケットのライフサイクルを一緒に掛けます。前のほうほど費用と危険を同時に減らし、後ろのほうほどすでに起きたものを片づける作業です。
今できる点検は二つです。保持ポリシーが設定されているかをプロジェクト設定で確認し、バケットのライフサイクル規則が掛かっているかをストレージ側で確認してください。どちらも空なら、今積み上がっているすべてが永久に残っています。
試してみる
- LLM API コスト計算機 — モデル呼び出しの費用を先に計算すると、トレーシングの保存費用とどちらが大きいか見当がつきます。
- SLO エラーバジェット 計算ツール — サンプリング比率を決めるとき、どれだけの標本があれば目標を判定できるかを併せて検討してください。
シリーズ
- 前の記事: Langfuseのセルフホスト
- 次の記事: 集めたあと — ダッシュボード、メトリクスAPI、評価
参考資料
- Langfuse サンプリング: https://langfuse.com/docs/observability/features/sampling
- Langfuse マスキング: https://langfuse.com/docs/observability/features/masking
- Langfuse データ保持: https://langfuse.com/docs/administration/data-retention
- Langfuse オブジェクトストレージ: https://langfuse.com/self-hosting/infrastructure/blobstorage
- Langfuse 設定変数: https://langfuse.com/self-hosting/configuration
- Langfuse トークンとコスト追跡: https://langfuse.com/docs/observability/features/token-and-cost-tracking