LLMパイプライン(エルエルエムパイプライン)
LLMへの入力構築から推論実行・出力解析・後処理までを一連のステージとして構造化し、再利用可能な処理フローを構築する設計パターン。
LLMパイプラインとは
LLMパイプラインは、大規模言語モデル(LLM)を使った処理を「入力構築→プロンプト注入→モデル推論→出力解析→後処理」の一連のステージに分解し、各ステージを独立したコンポーネントとして実装する設計パターンである。Unix のパイプ(|)と同様に、前段の出力が次段の入力になる直線的なデータフローを基本とする。
パイプラインの典型的なステージ
| ステージ | 処理内容 | 実装例 |
|---|---|---|
| データ取得 | 処理対象の取得 | DB検索、ファイル読み込み、API呼び出し |
| コンテキスト構築 | RAG検索・履歴注入 | ベクトル検索で関連文書Top5取得 |
| プロンプト組立 | テンプレートに変数展開 | Jinja2テンプレート、f-string |
| モデル推論 | LLM API呼び出し | Claude 4 Sonnet、GPT-4o |
| 出力解析 | 構造化データへの変換 | JSON抽出、正規表現マッチ |
| バリデーション | スキーマ検証・品質チェック | Pydanticモデル検証、Zod解析 |
| 後処理 | 結果の加工・保存 | DB書き込み、ファイル出力 |
LangChainにおけるパイプライン実装
LangChainではLCEL(LangChain Expression Language)を使い、パイプを | 演算子で連結する宣言的な構文でパイプラインを記述できる。
retriever | prompt | llm | output_parser
この記法により、各コンポーネントを独立して差し替えられる。retrieverをベクトルDBからBM25検索に変更しても、他のコンポーネントは影響を受けない。ストリーミング・バッチ処理・非同期実行もLCELが透過的に処理する。
プロダクション向けの設計考慮
リトライとフォールバック
LLM APIはレート制限・一時的障害が頻繁に発生するため、パイプラインにはリトライロジックが必須である。指数バックオフ(1s→2s→4s→8s)でリトライし、3回失敗したら代替モデルにフォールバックする設計が標準的。LiteLLMを使えばプロバイダ間のフォールバックを設定ファイル1つで実現できる。
ストリーミング
チャットUIでは応答の即時性が重要なため、パイプラインの最終段階(LLM推論→出力)でServer-Sent Events(SSE)によるストリーミングを実装する。LangChainの.stream()メソッドやVercel AI SDKのstreamText()がこの用途に対応している。
バッチ処理
大量のデータを処理する場合は、パイプラインをバッチモードで実行する。100件のドキュメント要約を行う場合、1件ずつ逐次処理すると数時間かかるが、並列度10で並行処理すれば1/10の時間に短縮できる。ただしLLM APIのレート制限(RPM: Requests Per Minute)を考慮した並列度の調整が必要。
評価パイプライン
LLMの出力品質を定量評価するための専用パイプラインも重要である。入力セット→モデル推論→人間/LLM評価→スコア集計の流れで、モデル変更やプロンプト変更の影響を定量的に測定する。
LLM-as-a-Judge パターンでは、別のLLM(通常はより高性能なモデル)が評価者として機能する。Claude OpusがGPT-4o miniの出力を5段階で評価し、平均スコアが閾値を下回ったらデプロイをブロックするCI/CDパイプラインが実用化されている。
オブザーバビリティ
パイプラインの各ステージで以下のメトリクスを記録する。
- レイテンシ: 各ステージの処理時間(P50/P95/P99)
- トークン消費: 入力/出力トークン数とコスト
- エラー率: ステージごとの失敗率
- 品質スコア: 出力のバリデーション通過率
Langfuse、LangSmith、Arize Phoenix などのLLMオブザーバビリティツールがこれらの記録・可視化をサポートしている。
FAQ
Q1: LLMパイプラインとLLMチェインの違いは何ですか?
実質的にはほぼ同義だが、パイプラインは「データが流れる配管」の比喩で前処理・後処理を含む全体の流れを指し、チェインは「LLM呼び出しの連鎖」に焦点を当てる傾向がある。LangChainの文脈ではChainが逐次処理、パイプラインがLCELによる宣言的記述を指すことが多い。
Q2: パイプラインの実行時間を短縮するには?
最も効果的なのは不要なステージの削除と並列化。独立したステージ(例: 3つの異なる観点でのレビュー)はasyncio.gather()で並列実行できる。また、プロンプトキャッシュ(Anthropicの場合90%割引)の活用で同一システムプロンプトの繰り返し送信コストを削減できる。
Q3: パイプラインのテストはどう書きますか?
各ステージを独立した関数として実装し、ユニットテストで入出力を検証する。LLM呼び出しステージはモックするか、deterministic=trueで固定出力を得る。統合テストではゴールデンデータセット(期待出力付き入力集)を用意し、パイプライン全体の出力品質をスコアリングする。