「本番環境でローカルLLMを使っているが、どのモデルがどれだけのトークンを消費しているか把握できていない」
そんな悩みを抱えるLinux管理者やAIシステム開発者は多いはずです。この記事では、オープンソースのLLM可観測性プラットフォームLangfuseをLinuxサーバーにセルフホストし、OllamaのAPI呼び出しをプロンプト・レスポンス・トークン使用量の単位でトレースする手順を解説します。
Prometheusによるシステムメトリクスの監視とは異なり、Langfuseはアプリケーション層の可観測性を担います。どのクエリに時間がかかったか、どのプロンプトがエラーを起こしたか、チームごとのLLM利用量を可視化できます。Docker ComposeによるLangfuse構築からPython SDKによるOllama統合まで、Ubuntu 22.04/24.04 Serverを前提に進めます。
この記事のポイント
・LangfuseはDocker Composeでセルフホストでき、プロンプト・レスポンス・トークン使用量を完全ローカルで記録できる
・langfuse.openaiのOpenAIラッパーをOllamaのbase_urlに向けるだけで既存コードをほぼ変更なしにトレース対応にできる
・@observeデコレータを付けるだけでRAGパイプライン全体をネストスパンとして可視化できる
・ClickHouseに全文保存するためディスク容量管理とデータ保持期間の設定が本番運用の鉄則
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
LangfuseでOllamaを可観測にする構成の全体像
Langfuseのセルフホスト版はDocker Composeで起動するWebアプリケーションです。PostgreSQLとClickHouseを内部で使用し、トレースデータを保存します。Webブラウザからアクセスできるダッシュボードで、LLMの呼び出し履歴を確認できます。OllamaとLangfuseの連携方法は大きく2通りです。
・OpenAI互換API経由: LangfuseのOpenAIラッパーをOllamaのエンドポイントに向けるだけで、既存コードをほぼそのままトレース対応にできる
・手動トレース: Langfuse Python SDKのtrace()とgeneration()を直接使い、柔軟なトレース構造を定義できる
本記事では両方の手順を紹介します。OllamaのPrometheus+Grafana監視はインフラ層(GPU使用率・レスポンス時間)の監視ですが、Langfuseはアプリケーション層の可観測性を担います。両方を組み合わせることで、包括的な監視体制が整います。
Ollamaの初期構築が済んでいない場合は、Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイドを先に読んでおくとスムーズです。
Langfuseのセルフホスト環境をDocker Composeで構築する手順
1. Docker Engine と Docker Compose をインストールする
Ubuntu 22.04/24.04でDockerが未インストールの場合は先にインストールします。# Dockerの公式リポジトリからインストール $ sudo apt update $ sudo apt install ca-certificates curl gnupg -y $ sudo install -m 0755 -d /etc/apt/keyrings $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null $ sudo apt update $ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y # 動作確認 $ docker --version Docker version 27.x.x, build xxxxxxx $ docker compose version Docker Compose version v2.x.x
2. Langfuseのリポジトリをクローンして環境変数を設定する
# Langfuseのリポジトリをクローン $ git clone https://github.com/langfuse/langfuse.git $ cd langfuse # .envファイルをサンプルからコピー $ cp .env.example .env # NEXTAUTH_SECRET と SALT をランダム値で設定する(必須) $ sed -i "s|NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=$(openssl rand -hex 32)|" .env $ sed -i "s|SALT=.*|SALT=$(openssl rand -hex 16)|" .env # 設定内容を確認 $ grep -E "NEXTAUTH_SECRET|SALT" .env NEXTAUTH_SECRET=a3f8c2...(ランダム値) SALT=9d4e...(ランダム値)
3. Docker Composeで起動してWebダッシュボードにアクセスする
# Docker Composeで起動(初回はイメージのダウンロードに数分かかる) $ docker compose up -d # コンテナの起動状態を確認 $ docker compose ps NAME IMAGE STATUS langfuse-web langfuse/langfuse Up (healthy) langfuse-worker langfuse/langfuse Up langfuse-db postgres:15 Up (healthy) langfuse-ch clickhouse Up (healthy)
PythonからOllamaとLangfuseを連携させてトレースを記録する手順
1. Langfuse Python SDKをインストールする
# Python仮想環境を作成して有効化 $ python3 -m venv langfuse-env $ source langfuse-env/bin/activate # Langfuse SDK と OpenAIクライアントをインストール $ pip install langfuse openai # インストール確認 $ pip show langfuse | grep Version Version: 2.x.x
2. 環境変数でLangfuseの接続情報を設定する
LangfuseのAPIキーを環境変数に設定します。シェルのプロファイルや `.env` ファイルに書いておくと毎回の設定が不要です。# ~/.bashrc または .env に追記 export LANGFUSE_PUBLIC_KEY="pk-lf-xxxxxxxxxxxxxxxxxxxx" export LANGFUSE_SECRET_KEY="sk-lf-xxxxxxxxxxxxxxxxxxxx" export LANGFUSE_HOST="http://localhost:3000"
3. OpenAI互換ラッパーでOllamaのトレースを記録する
LangfuseのOpenAIラッパーは、OllamaのOpenAI互換API(`/v1` エンドポイント)と組み合わせることで、コードへの変更を最小限にしてトレース機能を追加できます。`from openai import OpenAI` を `from langfuse.openai import OpenAI` に1行差し替えるだけです。# openai.OpenAI の代わりに langfuse.openai.OpenAI を使う from langfuse.openai import OpenAI client = OpenAI( base_url="http://localhost:11434/v1", api_key="ollama", # Ollamaはapi_keyを使わないがクライアントの仕様で設定が必要 ) response = client.chat.completions.create( model="llama3.3:70b-instruct-q4_0", messages=[ {"role": "system", "content": "あなたはLinuxの専門家です。"}, {"role": "user", "content": "journalctlでエラーログだけを抽出する方法を教えてください。"}, ], name="journalctl-query", # Langfuseのトレース名(任意) ) print(response.choices[0].message.content) # SDKの内部バッファをフラッシュしてLangfuseに確実に送信する from langfuse.openai import langfuse_context langfuse_context.flush()
4. @observeデコレータで関数単位のネストトレースを追加する
RAGパイプラインやマルチステップ処理では、`@observe` デコレータを使うと各処理が親子関係のネストスパンとして記録されます。from langfuse.decorators import observe, langfuse_context from langfuse.openai import OpenAI client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama") @observe() def call_ollama(prompt: str, model: str = "llama3.3:70b-instruct-q4_0") -> str: response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}], ) return response.choices[0].message.content @observe() def analyze_log_line(log_line: str) -> dict: """ログ1行を分析してカテゴリと重要度を返す(@observeで親スパンになる)""" summary = call_ollama(f"以下のLinuxログを分析してください:\n{log_line}") return {"log": log_line, "analysis": summary} # analyze_log_lineが親スパン、call_ollamaが子スパンとしてLangfuseに記録される result = analyze_log_line("kernel: EXT4-fs error (device sda1): ext4_find_entry:1455") print(result["analysis"]) langfuse_context.flush()
Langfuse管理画面でトレース・トークン使用量を確認する手順
1. Tracesビューでリクエストの詳細を確認する
LangfuseのダッシュボードのTracesビューには、各APIコールの詳細が記録されます。確認できる主な項目は以下のとおりです。
・Input/Output: 送信したプロンプトの全文と生成されたレスポンスの全文
・Latency: レスポンスの生成にかかった時間(最初のトークンまでの時間も含む)
・Token Usage: 入力トークン数・出力トークン数(Ollamaのprompt_eval_countとeval_countに対応)
・Model: 使用したモデル名(量子化タグ付きで記録される)
2. Dashboardビューで集計データを把握する
「Dashboard」タブでは期間ごとの集計データを確認できます。モデルごとのトークン使用量・レイテンシの分布・エラー率をグラフで把握できます。チーム複数人が同一プロジェクトを使う場合はUser IDを設定することで、ユーザー別の利用量も集計できます。`langfuse_context.update_current_trace(user_id="tanaka")` のように設定すると、誰がどのクエリを投げたか記録できます。
3. Ollamaのトークン数を手動トレースで正確に渡す
OpenAIラッパーを使わず直接リクエストする場合は、手動トレースでトークン数を指定します。import requests from langfuse import Langfuse langfuse = Langfuse( public_key="pk-lf-xxxxxxxxxxxxxxxxxxxx", secret_key="sk-lf-xxxxxxxxxxxxxxxxxxxx", host="http://localhost:3000" ) trace = langfuse.trace(name="manual-trace-example") generation = trace.generation( name="llama3.3-generation", model="llama3.3:70b-instruct-q4_0", input=[{"role": "user", "content": "Linuxのdiskio監視方法は?"}], ) res = requests.post("http://localhost:11434/api/chat", json={ "model": "llama3.3:70b-instruct-q4_0", "messages": [{"role": "user", "content": "Linuxのdiskio監視方法は?"}], "stream": False, }) data = res.json() # Ollamaのレスポンスに含まれるトークン数をLangfuseに記録する generation.end( output=data["message"]["content"], usage={ "input": data.get("prompt_eval_count", 0), "output": data.get("eval_count", 0), } ) langfuse.flush()
バッチ処理やRAGパイプラインのトレースをLangfuseに送信する手順
複数ドキュメントのバッチ処理や検索+生成の2ステップRAGパイプラインでは、ネストしたスパン構造でトレースを記録することでボトルネックを特定しやすくなります。1. バッチ処理全体を1トレースとして記録する
from langfuse.decorators import observe, langfuse_context from langfuse.openai import OpenAI client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama") @observe() def summarize_document(doc_id: str, content: str) -> str: """1ドキュメントを200字で要約する(子スパン)""" response = client.chat.completions.create( model="llama3.3:70b-instruct-q4_0", messages=[{"role": "user", "content": f"以下を200字で要約してください:\n{content}"}], ) return response.choices[0].message.content @observe() def batch_summarize(documents: list) -> list: """ドキュメントリストをバッチ要約する(親スパン)""" results = [] for doc in documents: summary = summarize_document(doc_id=doc["id"], content=doc["body"]) results.append({"id": doc["id"], "summary": summary}) return results docs = [ {"id": "doc-001", "body": "Linuxのカーネルパラメータ調整..."}, {"id": "doc-002", "body": "iptablesのルール設定..."}, {"id": "doc-003", "body": "systemdサービス管理..."}, ] batch_summarize(docs) langfuse_context.flush()
OllamaのバッチドキュメントをシェルスクリプトやPythonで自動処理するにあたっては、Ollamaの基本セットアップが完了していることが前提になります。
本番運用での注意点とトラブル対処
LangfuseサーバーがダウンしてもOllamaの応答は継続する
LangfuseのPython SDKはトレースデータを非同期でバッファリングして送信します。Langfuseサーバーが停止していても、Ollamaへのリクエスト自体は即座に完了します。サーバー停止中に溜まったバッファは、接続が復旧した際に自動的に送信されます。本番では `LANGFUSE_FLUSH_AT`(バッファの最大件数)と `LANGFUSE_FLUSH_INTERVAL`(フラッシュ間隔・ミリ秒)環境変数でバッチ送信の挙動を調整できます。
ClickHouseのディスク容量を定期的に確認する
Langfuseはプロンプトとレスポンスの全文をClickHouseに保存します。大量のバッチ処理を実行すると、数週間でGB単位のデータが蓄積します。Langfuseの管理画面「Settings > Data Retention」で保持期間を設定するか、不要なトレースを定期的に削除します。# Langfuseコンテナのボリューム使用量を確認 $ docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Volumes 4 4 8.7GB 0B # ClickHouseデータボリュームのサイズを個別確認 $ docker volume inspect langfuse_clickhouse-data --format '{{.Mountpoint}}' | xargs sudo du -sh 3.2G /var/lib/docker/volumes/langfuse_clickhouse-data/_data
Langfuseコンテナが起動しない場合
典型的な原因は `.env` ファイルの設定ミスです。`NEXTAUTH_SECRET` と `SALT` が設定されているか確認します。# Webコンテナのエラーログを確認 $ docker compose logs langfuse-web --tail 50 # よくあるエラー: NEXTAUTH_SECRET が未設定 Error: You must set NEXTAUTH_SECRET environment variable. # 対処: .envにNEXTAUTH_SECRETが正しく設定されているか確認する $ grep NEXTAUTH_SECRET .env NEXTAUTH_SECRET=a3f8c2...(空でないこと) # 設定変更後はコンテナを再起動する $ docker compose down && docker compose up -d
Ollamaのモデル名が正しく記録されない場合
OpenAIラッパーを使う際、`model` パラメータに量子化タグまで含めた正確なモデル名を指定します。例: `llama3.3:70b-instruct-q4_0` のようなフルname形式が正確なトレースデータの記録につながります。社内でローカルLLMのモデルを選定する段階では、ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイントが参考になります。
また、機密データを扱う社内向け導入の意思決定プロセスについては、社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢が情シス担当者向けに整理されています。
まとめ:OllamaとLangfuseによるローカルLLM可観測性の実装
Langfuseのセルフホストを使ってOllamaのLLM呼び出しをトレースする手順をまとめます。| 設定内容 | 主なコマンド・操作 | 効果 |
|---|---|---|
| Langfuse起動 | docker compose up -d | Webダッシュボードをポート3000で起動 |
| SDKインストール | pip install langfuse openai | PythonからLangfuse連携を有効化 |
| OpenAI互換トレース | from langfuse.openai import OpenAI | 既存コード1行変更でトレース対応 |
| 関数単位トレース | @observe() デコレータを付与 | ネストスパンでパイプラインを可視化 |
| 手動トークン記録 | generation.end(usage={...}) | prompt_eval_count/eval_countを記録 |
| バッファ送信確認 | langfuse_context.flush() | プロセス終了前にデータを確実に送信 |
| ディスク容量確認 | docker volume inspect langfuse_clickhouse-data | ClickHouseの肥大を早期検知 |
PrometheusによるGPU使用率・レスポンス時間のシステム監視と、LangfuseによるプロンプトContent・トークン使用量のアプリケーション監視を組み合わせることで、ローカルLLMの包括的な可観測性体制が整います。Langfuseはプロンプトとレスポンスをローカル環境内で完結させて記録できるため、機密データを扱う社内システムとの相性も優れています。
ローカルLLMの本番運用を2日間のハンズオンで習得する
Langfuseによるトレース設計からOllamaの本番構成まで、実機GPU環境で手を動かしながら習得したい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。
・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:OllamaとElasticsearchでハイブリッド検索RAGを構築する方法|BM25とベクター検索を組み合わせてLinuxで精度の高い社内文書QAを実現する手順
- この記事の属するカテゴリ:ローカルLLMへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます