深夜 3 時に誰も見ていないところで動くエージェントの価値は、あとから何をしたのかを再構成できるかどうかで決まります。「一晩でトークンを 38 $ 分使った」は最悪の朝です。「検索ツールが空のページを返し、プランナーが 400 回リトライしたせいで 38 $ を使った」は、昼食前に直せるバグです。後者の文を書くにはトレースが要ります。
Langfuse は LLM 呼び出し、プロンプト、ツール実行、トークン数、レイテンシをひとつずつ記録し、クリックでたどれるツリーとして表示します。セルフホストすれば、顧客データや API の出力、ときには紛れ込んだ秘密情報まで含むプロンプトと応答が、他人のダッシュボードではなく自分で管理するサーバーにとどまります。
動かし続けるためのコスト
Langfuse v4 は小さなバイナリひとつではなく、6 つのコンテナです。Web アプリ、バックグラウンドの worker、Postgres、イベント用の ClickHouse、Redis、ファイル用の MinIO。テストサーバーに入れてみたところ、空のプロジェクトで約 3.5 GB の RAM を使っていました。増えていくのは ClickHouse で、送った span はすべてそこに入ります。
| 負荷 | プラン |
|---|---|
| 数個のエージェント、数週間分のトレース | Medium-IP:6 vCPU、6 GB、45 GB(月額 $20) |
| エージェント群、数か月分の履歴 | Pro プラン、RAM 32 GB から — お問い合わせ |
これより小さいプランでもインストールはできますが、ClickHouse と worker が同時にメモリを欲しがった瞬間に苦しくなります。私なら 6 GB を下回るものは選びません。
エージェントはどこにいますか? 別のマシンで動くなら、専用の IPv4 付きプランを選んでください。エージェントには HTTPS のエンドポイントが必要で、NAT プランでは送信元アドレスが自分専用ではなく、外部から届くのは SSH ポートだけです。エージェントがこのサーバー自身で動くなら、Langfuse をインターネットに出す必要はありません。試すだけなら NAT の Medium(月額 $12)で十分です。ロケーションはドイツとフィンランドの 2 か所で、空き状況は注文時に表示されます。自分ではなくエージェントに近いほうを選んでください。サーバーの支払いは暗号資産で、KYC は不要です。
インストール
新しく作った Ubuntu 24.04 または Debian 12 のサーバーで、root として作業します。まず Docker:
curl -fsSL https://get.docker.com | sh
次に Langfuse。compose ファイルのデフォルト値ではなく、本物のシークレットを使います:
git clone --depth 1 https://github.com/langfuse/langfuse.git
cd langfuse
PG=$(openssl rand -hex 16); MINIO=$(openssl rand -hex 16)
cat > .env <<EOF
NEXTAUTH_URL=https://langfuse.example.com
NEXTAUTH_SECRET=$(openssl rand -hex 32)
SALT=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$PG
DATABASE_URL=postgresql://postgres:$PG@postgres:5432/postgres
CLICKHOUSE_PASSWORD=$(openssl rand -hex 16)
MINIO_ROOT_PASSWORD=$MINIO
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=$MINIO
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=$MINIO
LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY=$MINIO
REDIS_AUTH=$(openssl rand -hex 16)
TELEMETRY_ENABLED=false
EOF
chmod 600 .env
このうち 2 つのパスワードは対になっています。Postgres のパスワードは DATABASE_URL の中にも入り、MinIO のパスワードは 3 行の S3_..._SECRET_ACCESS_KEY と一致している必要があります。そのため、それぞれをひとつの変数から取っています。
ここに落とし穴があります。compose ファイルは Web アプリをポート 3000、MinIO を 9090 で全インターフェースに公開し、Docker が公開したポートは ufw をすり抜けます。両方を localhost に縛り、入口をリバースプロキシだけにします:
sed -i 's/- 3000:3000/- 127.0.0.1:3000:3000/; s/- 9090:9000/- 127.0.0.1:9090:9000/' docker-compose.yml
docker compose up -d
curl -s http://127.0.0.1:3000/api/public/health
最後のコマンドは {"status":"OK", ...} を表示するはずです。初回起動ではデータベースのマイグレーションが走ります。私たちのテストでは、コンテナが起動してから約 15 秒でヘルスチェックが通りました。
前段に HTTPS
langfuse.example.com をサーバーの IP に向けてから:
apt install -y caddy
cat > /etc/caddy/Caddyfile <<'EOF'
langfuse.example.com {
reverse_proxy 127.0.0.1:3000
}
EOF
systemctl reload caddy
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
証明書は Caddy が自分で取得します。サイトを開いてアカウントを作り(これが最初の組織になります)、プロジェクトを追加して、その公開鍵と秘密鍵をコピーします。
そのあとは扉を閉めておきましょう。Langfuse は AUTH_DISABLE_SIGNUP を理解しますが、標準の compose ファイルはこれを web コンテナに渡さないので、langfuse-web の下に 1 行追加が必要です:
sed -i 's|^ NEXTAUTH_SECRET: \(.*\)$| NEXTAUTH_SECRET: \1\n AUTH_DISABLE_SIGNUP: ${AUTH_DISABLE_SIGNUP:-false}|' docker-compose.yml
echo "AUTH_DISABLE_SIGNUP=true" >> .env
docker compose up -d
これ以降、登録しようとすると «Sign up is disabled» が返ります。チームメンバーはプロジェクトの中から追加します。
エージェントからトレースを送る
Python SDK は関数をラップし、入れ子の呼び出しは入れ子の span になります:
python3 -m venv venv && . venv/bin/activate
pip install langfuse
export LANGFUSE_PUBLIC_KEY=pk-lf-...
export LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_BASE_URL=https://langfuse.example.com
from langfuse import get_client, observe
langfuse = get_client() # LANGFUSE_* 変数を読み込む
@observe(name="fetch-prices")
def fetch_prices(pair: str) -> dict:
return {"pair": pair, "bid": 64210.5}
@observe(name="nightly-agent-run")
def run() -> str:
data = fetch_prices("BTC/USDT")
return f"done: {data['pair']}"
print(run())
langfuse.flush() # 短いスクリプトはバックグラウンド送信が終わる前に終了してしまう
LANGFUSE_BASE_URL を省略しないでください。これがないと SDK は黙ってすべてを Langfuse のクラウドに送り、そこにはあなたの鍵が存在しないので、空のダッシュボードを眺めることになります。
span が届いたかをターミナルから確認するには:
curl -s -u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" \
"$LANGFUSE_BASE_URL/api/public/v2/observations?limit=5"
v4 では、古いエンドポイント /api/public/traces は events-only モードについてのエラーを返します。これは想定どおりの動作で、インストールが壊れているわけではありません。実際のエージェントでも、LLM 呼び出しと各ツールを同じようにラップします。さらに Langfuse には主要な LLM SDK やエージェントフレームワーク向けの連携があるので、あちこちにデコレーターを付けなくても、import を 1 行変えるだけで済むことがよくあります。
注意して見ておくこと
- ディスク。 ClickHouse と MinIO は送ったものをすべて保存します。容量がどこに使われたかは
docker system df -vでわかります。長いプロンプトと応答をトレースすると、Medium-IP の 45 GB は数年ではなく数週間で埋まります。月に一度は確認し、手狭になったら 400 GB 以上の Pro プランをお問い合わせください。 - アップデート。 compose ファイルはメジャーバージョン(
:4)を固定しています。docker compose pull && docker compose up -dでその範囲内の修正が入ります。メジャーバージョンを上げる前には changelog を読んでください。トレースのエンドポイントが消えたのは、まさに v4 でした。 - バックアップ。 大事なものは Docker ボリュームにあります。Postgres にはユーザー、プロジェクト、鍵、ClickHouse と MinIO にはトレースそのもの。定期的にサーバーの外へコピーしてください。
月 $20 の価値はあるでしょうか? エージェントがそれ以上のトークン代を使っているなら(無人のエージェントの多くはそうです)、トークンの行き先がわかることは、ループが初めて暴走したその日に元が取れます。1 日 1 回動くだけの小さなエージェントには、どれも必要ありません。ログファイルとトークンカウンターで十分です。
関連記事
- AI エージェント向け VPS:エージェント本体を動かす場所
- マルチエージェントシステム向け VPS:エージェント群全体のリソース見積もり
- LiteLLM プロキシ向け VPS:すべてのモデルキーをまとめるゲートウェイ、支出上限付き
- Pro プランのお問い合わせ:エージェント群向けに RAM 32〜80 GB
- ufw ファイアウォールの設定方法
コメント
まだコメントはありません。最初になりましょう。