プロンプトエンジニアリングを全員参加型に!Langfuseで実現するノーコードLLM改善
はじめに
こんにちは!satto workspaceでプロダクトエンジニアをしている ryohei oyama(@ryohei_oyama)です。
LLMアプリケーションの品質向上において、プロンプトエンジニアリングは最も重要な要素の1つです。
しかし、多くの開発現場では以下のような課題に直面しています:
本記事では、これらの課題をLangfuseを使って解決する方法を、実際に動くコードと具体的な設定手順を交えて解説します。
🚀 Langfuseとは?何が解決できるのか
Langfuseは、LLMアプリケーションの観測・評価・プロンプト管理を一元化できるオープンソースプラットフォームです。
最大の特徴は、プロンプトの管理・編集・デプロイをすべてWebインターフェースで完結できることです。
📊 主要機能
| 機能 | 説明 | メリット |
|---|---|---|
| プロンプトバージョン管理 | Gitのような履歴管理 | 過去バージョンへの即座のロールバック |
| A/Bテスト | 本番環境での安全な実験 | データドリブンな意思決定 |
| パフォーマンス分析 | レスポンス時間とコストの可視化 | コスト最適化と品質向上の両立 |
| 非技術者向けUI | エンジニア不要でプロンプト編集 | チーム全体での改善サイクル |
📝 Langfuseの基本概念
🔄 プロンプトの外部管理化
import { Langfuse } from 'langfuse';
const langfuse = new Langfuse();
// バージョン指定でプロンプト取得
const prompt = await langfuse.prompt.get("user_chat_prompt", {
version: 2 // "latest"を指定すると最新版を取得
});
// 変数の注入({{ user_input }} 形式で定義)
const formattedPrompt = prompt.compile({
user_input: "商品について質問があります"
});
🎯 バージョン管理の特徴
| 機能 | 説明 | 利点 |
|---|---|---|
| 自動バージョニング | 編集するたびに新バージョンが自動作成 | 変更履歴を完全に追跡可能 |
| ロールバック機能 | 問題発生時に前のバージョンへ即座に復旧 | リスクの最小化 |
| 差分表示 | 変更箇所がハイライトされ一目瞭然 | レビューと承認が簡単 |

🧪 A/Bテストの実装方法
バリエーション分岐の実装
A/Bテスト実装コード。
import { LangfuseClient } from "@langfuse/client";
import {
updateActiveTrace,
updateActiveObservation
} from "@langfuse/tracing";
// Langfuseクライアントの初期化
const langfuse = new LangfuseClient({
secretKey: process.env.LANGFUSE_SECRET_KEY,
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
baseUrl: process.env.LANGFUSE_BASE_URL,
});
export async function POST(req: Request) {
const { messages, chatId }: {
messages: UIMessage[],
chatId: string
} = await req.json();
// 最新のメッセージを取得
const inputText = messages[messages.length - 1].parts
.find((part) => part.type === "text")?.text;
// A/Bテスト用の分岐
const variant: 'A' | 'B' = Math.random() < 0.5 ? "A" : "B";
const version = variant === "A" ? 1 : 2;
// Langfuseからプロンプトを取得
const prompt = await langfuse.prompt.get("user_chat_prompt", {
version: version
});
// アクティブトレースを更新
updateActiveTrace({
name: "chat-completion",
sessionId: chatId,
userId: "user-123", // 実際のユーザーIDを使用
input: inputText,
metadata: {
ab_test_variant: variant,
prompt_version: version
}
});
console.log(\`Using prompt variant: \${variant}, version: \${version}\`);
}
ユーザー満足度の記録
フィードバック記録コード。
interface FeedbackData {
traceId: string;
satisfaction: number;
source: string;
}
// ユーザーの満足度を記録
function recordUserFeedback(data: FeedbackData): void {
langfuse.score({
traceId: data.traceId,
name: "user_satisfaction",
value: data.satisfaction, // 1-5のスケール
metadata: { feedback_source: data.source }
});
}
🚀 実装ステップガイド
Step 1️⃣ 環境構築
🐳 Docker Composeでローカル環境
git clone https://github.com/langfuse/langfuse.git
cd langfuse
docker compose up -d
☁️ SaaS版を利用
langfuse.com でアカウント作成(無料プランあり)
Step 2️⃣ プロンプト作成
1️⃣ 管理画面にアクセス
http://localhost:3000 を開く。
2️⃣ 新規プロンプトを作成
Prompts → Create New をクリック。
3️⃣ 初期バージョンを登録
名前とプロンプト内容を設定。

Step 3️⃣ バックエンド実装
Next.js API Routeの例
app/api/chat/route.ts のコード。
// app/api/chat/route.ts
import { google } from "@ai-sdk/google";
import { createVertex } from "@ai-sdk/google-vertex";
import { LangfuseClient } from "@langfuse/client";
import {
convertToModelMessages,
stepCountIs,
streamText,
type UIMessage,
} from "ai";
import { langfuseSpanProcessor } from "@/instrumentation";
import { after } from "next/server";
import {
observe,
updateActiveObservation,
updateActiveTrace,
} from "@langfuse/tracing";
import { trace } from "@opentelemetry/api";
// Vertex AIの設定
const vertex = createVertex({
location: "global",
project: process.env.GOOGLE_VERTEX_PROJECT,
baseURL: `https://aiplatform.googleapis.com/v1/projects/${process.env.GOOGLE_VERTEX_PROJECT}/locations/global/publishers/google`,
googleAuthOptions: {
credentials: {
client_email: process.env.GOOGLE_VERTEX_CLIENT_EMAIL,
private_key: process.env.GOOGLE_VERTEX_PRIVATE_KEY,
},
},
});
// Langfuseクライアントの初期化
const langfuse = new LangfuseClient({
secretKey: process.env.LANGFUSE_SECRET_KEY,
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
baseUrl: process.env.LANGFUSE_BASE_URL,
});
// ハンドラー関数
const handler = async (req: Request) => {
const { messages, chatId }: { messages: UIMessage[], chatId: string } = await req.json();
// 入力テキストを取得(parts配列から)
const inputText = messages[messages.length - 1].parts
.find((part) => part.type === "text")?.text;
// Observation(観測)を更新
updateActiveObservation({
input: inputText,
});
// トレースを更新
updateActiveTrace({
name: "my-ai-sdk-trace",
sessionId: chatId,
userId: "123",
input: inputText,
});
// A/Bテスト用のバリアントを選択
const variant: 'A' | 'B' = Math.random() < 0.5 ? "A" : "B";
const version = variant === "A" ? 1 : 2;
// Langfuseからプロンプトを取得
const prompt = await langfuse.prompt.get("user_chat_prompt", {
version: version
});
console.log(`Using prompt variant: ${variant}, version: ${version}`);
// ストリーミングレスポンスを生成
const result = streamText({
model: vertex("gemini-2.5-flash"),
messages: convertToModelMessages(messages),
tools: {
url_context: google.tools.urlContext({}),
},
stopWhen: stepCountIs(5),
system: prompt.compile({}),
experimental_telemetry: {
isEnabled: true,
metadata: {
langfusePrompt: prompt.toJSON(),
}
},
onFinish: async (result) => {
// 成功時の観測とトレースを更新(contentプロパティを使用)
updateActiveObservation({
output: result.content,
});
updateActiveTrace({
output: result.content,
});
// スパンを手動で終了
trace.getActiveSpan()?.end();
},
onError: async (error) => {
// エラー時の観測とトレースを更新
updateActiveObservation({
output: error,
level: "ERROR"
});
updateActiveTrace({
output: error,
});
// スパンを手動で終了
trace.getActiveSpan()?.end();
},
});
// レスポンス後にLangfuseにフラッシュ
after(async () => await langfuseSpanProcessor.forceFlush());
return result.toUIMessageStreamResponse();
}
// observeでラップしたハンドラーをエクスポート
export const POST = observe(handler, {
name: "handle-chat-message",
endOnExit: false, // ストリーム終了後に観測を終了
});
Step 4️⃣ フロントエンド実装
React + Vercel AI SDKの例
app/page.tsx のコード。
// app/page.tsx
"use client";
import { useChat } from "@ai-sdk/react";
import { useState } from "react";
import { PromptInput, PromptInputTextarea } from "@/components/ai-elements/prompt-input";
import { Conversation, ConversationContent } from "@/components/ai-elements/conversation";
import { Message, MessageContent } from "@/components/ai-elements/message";
import { Response } from "@/components/ai-elements/response";
export default function ChatPage() {
const [input, setInput] = useState("");
const [chatId] = useState(() => `chat-${Date.now()}`);
const { messages, sendMessage, status } = useChat({
api: "/api/chat",
body: {
chatId, // セッションIDを送信(A/Bテスト追跡用)
},
});
const handleSubmit = (message: { text: string }) => {
if (!message.text) return;
sendMessage({
text: message.text,
});
setInput("");
};
return (
<div className="max-w-4xl mx-auto p-6 h-screen">
<div className="flex flex-col h-full">
{/* メッセージ表示エリア */}
<Conversation className="h-full">
<ConversationContent>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part, i) => {
// テキストパートのみを表示
if (part.type === "text") {
return (
<Message key={`${message.id}-${i}`} from={message.role}>
<MessageContent>
<Response>{part.text}</Response>
</MessageContent>
</Message>
);
}
return null;
})}
</div>
))}
{status === "submitted" && (
<div className="animate-pulse">入力中...</div>
)}
</ConversationContent>
</Conversation>
{/* 入力エリア */}
<PromptInput
onSubmit={handleSubmit}
className="mt-4"
>
<PromptInputTextarea
onChange={(e) => setInput(e.target.value)}
value={input}
placeholder="メッセージを入力..."
/>
</PromptInput>
</div>
</div>
);
}
Step 5️⃣ 環境変数設定
.env.local ファイルを作成:
# Langfuse設定
LANGFUSE_SECRET_KEY=your-secret-key
LANGFUSE_PUBLIC_KEY=your-public-key
LANGFUSE_BASE_URL=http://localhost:3000
# Google Vertex AI設定(任意のLLMプロバイダーに変更可)
GOOGLE_VERTEX_PROJECT=your-project-id
GOOGLE_VERTEX_CLIENT_EMAIL=service-account@example.com
GOOGLE_VERTEX_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."
📈 実運用での活用
チャットUIの実装例
実際のチャット画面では、ユーザーは通常通りメッセージを送信します。バックグラウンドでは自動的にA/Bテストが実行され、ランダムに選ばれたプロンプトバージョンが使用されます。

ユーザーには通常どおりのチャット体験が提供される
プロンプト編集画面
Langfuseの管理画面では、非技術者でも直感的にプロンプトを編集できます。各バージョンの編集履歴も自動的に保存されます。

プロンプト編集画面 - バージョン管理と変数設定が可能
A/Bテスト結果の分析
トレース情報
各会話セッションの詳細なトレース情報を確認できます。どのバリアント(A/B)が使用されたか、レスポンス時間、トークン使用量などが記録されています。

トレース一覧 - 各セッションの詳細情報を確認
メトリクス比較
Langfuseのプロンプトメトリクス画面では、各プロンプトバージョンのパフォーマンスを詳細に比較できます。

プロンプトメトリクス画面 - バージョン別のパフォーマンス指標
📊 主要な分析指標
- Version:プロンプトのバージョン番号(1、2など)
- Labels:バージョンのラベル(production、latestなど)
- Median latency:レスポンス時間の中央値(例:1.329秒 vs 1.493秒)
- Median input/output tokens:入出力トークンの中央値
- Median cost:使用コストの中央値($0.000011 vs $0.000009)
- Generations count:実行回数(10回 vs 4回)
- Trace/Generation Scores:評価スコア
- Last used:最終使用日時
この例では、バージョン2(production、latest)とバージョン1を比較できます。バージョン2はレスポンス時間が若干速く(1.329秒 vs 1.493秒)、実行回数も多くなっています(10回 vs 4回)。
👩💼 非技術者でもできる!運用ガイド
🔄 プロンプト改善のPDCAサイクル
サイクルの詳細:
- 分析:Langfuse Dashboardで現在の性能指標を確認
- 編集:管理画面でプロンプトを修正
- テスト:新バージョンでA/Bテストを開始
- 評価:数日後に結果を比較
- 採用:優れたバージョンを本番環境に適用
💡 運用のベストプラクティス
✅ Do(推奨事項)
- 小さな改善を積み重ねる(一度に大きく変えない)
- 仮説を立てる(「これで応答時間が短くなるはず」)
- 変更理由を記録する(後で振り返れるように)
❌ Don't(避けるべき事項)
- 本番環境でいきなり大きく変更する
- テストなしで完全に切り替える
- 過去のバージョンを削除する(履歴は貴重な資産)
⚠️ 運用時の3つの注意点
1️⃣ データ量に注意する
A/Bテストは最低100件以上のサンプルを集めてから判断しましょう。
2️⃣ 段階的に展開する
新プロンプトは 10% → 30% → 50% → 100% と徐々に利用率を上げましょう。
3️⃣ 定期的に整理する
月に1回は使用されていない古いバージョンをアーカイブしましょう。
🔧 補足実装例
フィードバック収集API
フィードバックAPIエンドポイント。
interface FeedbackRequest {
sessionId: string;
rating: number;
comment?: string;
category: 'helpful' | 'accurate' | 'relevant' | 'overall';
}
// app/api/feedback/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Langfuse } from 'langfuse';
const langfuse = new Langfuse({
secretKey: process.env.LANGFUSE_SECRET_KEY!,
publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
baseUrl: process.env.LANGFUSE_BASE_URL || "http://localhost:3000"
});
export async function POST(req: NextRequest) {
try {
const feedback: FeedbackRequest = await req.json();
// フィードバックをLangfuseに記録
langfuse.score.create({
traceId: feedback.sessionId, // chatIdがトレースIDとして使用される
observationId: feedback.messageId, // 特定のメッセージに紐付け
name: `user_${feedback.category}`,
value: feedback.rating, // 0 or 1
dataType: "BOOLEAN",
comment: feedback.rating === 0 ? "Not helpful" : "Helpful"
});
return NextResponse.json({ success: true, message: "フィードバックを記録しました" });
} catch (error) {
console.error('Feedback API error:', error);
return NextResponse.json(
{ error: "フィードバックの記録に失敗しました" },
{ status: 500 }
);
}
}
🎉 導入効果
開発チームへの効果
✅ 開発効率の向上
- プロンプト修正にデプロイ不要
- TypeScriptによる型安全性
- Next.jsエコシステムとの統合
✅ 品質管理の改善
- 完全なバージョン管理
- 本番環境での安全な実験
- データドリブンな意思決定
ビジネスチームへの効果
✅ 自律的な運用
- 非技術者による直接編集
- リアルタイムでの効果測定
- 迅速なロールバック
✅ 継続的改善
- A/Bテストによる最適化
- ユーザーフィードバックの活用
- コスト効率の可視化
🎯 まとめ
Langfuseは、LLMアプリケーションのプロンプト管理を民主化する強力なツールです。
🔄 導入前と導入後の変化
📊 最大の価値
1️⃣ 開発速度の劇的な向上
- プロンプト修正のリードタイムが数日→数分に短縮
- エンジニアリソースを本質的な開発に集中できる
2️⃣ データに基づく改善サイクル
- A/Bテストで効果を定量的に測定
- 勘や経験ではなく、実データに基づく意思決定
3️⃣ チーム全体でのオーナーシップ
- CS、PM、マーケティングチームも改善に参加
- 顧客に最も近い人が直接改善できる
🎯 Langfuseが特に効果的な5つのケース
こんなチームにおすすめ:
✅ プロンプトの微調整が頻繁 → デプロイ不要で即座に反映。
✅ 非技術者からの改善要望が多い → GUIで誰でも編集可能。
✅ A/Bテストを実施したい → コード変更不要でテスト実行。
✅ バージョン管理が複雑 → Gitのような履歴管理で解決。
✅ コスト最適化をしたい → 詳細なメトリクス分析が可能。
Discussion