論文概要(Abstract)
DSPy(Declarative Self-improving Python)は、LLMパイプラインを宣言的プログラムとして記述し、プロンプトの詳細をプログラマから分離するフレームワークである。手書きプロンプトへの依存を排除し、入出力の型仕様(Signature)・LLM操作の抽象化(Module)・自動最適化コンパイラ(Teleprompter)の三層構造により、LLM変更やドメイン変更のたびに手動でプロンプトを書き直す必要をなくす。GSM8Kで手書きプロンプト比+8%、HotPotQAで+10%の改善を実証した。
この記事は Zenn記事: LLMプロンプト管理CI/CD:Langfuse×LaunchDarklyでA/Bテストと安全ロールアウト の深掘りです。
情報源
- arXiv ID: 2310.11511
- URL: https://arxiv.org/abs/2310.11511
- 著者: Omar Khattab, Arnav Singhvi, Paridhi Maheshwari, Zhiyuan Zhang et al.(Stanford University)
- 発表年: 2023
- 分野: cs.CL, cs.AI, cs.LG
背景と動機(Background & Motivation)
LLMアプリケーションの開発では、プロンプトエンジニアリングが事実上のプログラミングとなっている。しかし、手書きプロンプトには深刻な問題がある。
- LLM変更時の脆弱性: GPT-4からClaude 3.5に切り替えるだけで、最適なプロンプトが変わる
- パイプライン構造変更時の再作業: RAGの検索ステップを追加するだけで、下流プロンプトの全面書き直しが必要
- スケーラビリティの限界: チームで50個以上のプロンプトを管理する場合、誰がいつ何を変更したか追跡困難
Zenn記事で紹介したLangfuse×LaunchDarklyアーキテクチャはプロンプトの配信・監視に焦点を当てていたが、DSPyはその上流にあるプロンプト自体の生成・最適化を自動化する。Zenn記事の3層防御(Eval→Feature Flag→Observability)にDSPyを統合すれば、「プロンプト生成→評価→配信→監視」の完全自動パイプラインが実現できる。
主要な貢献(Key Contributions)
- 宣言的パイプライン記述: プロンプトのテキストを書かずに、入出力の型・意味だけでLLMパイプラインを記述できるSignature/Module抽象を提案
- 自動プロンプト最適化(Teleprompters): BootstrapFewShot・COPRO・MIPROなどのアルゴリズムにより、Few-Shot例・命令文・CoTステップを自動生成・最適化
- コンパイラとしてのLLMパイプライン: ソースコード(DSPyプログラム)をメトリクスとデータから「コンパイル」し、特定タスク・特定LLMに最適化された推論パイプラインを出力する新パラダイム
- モジュール性と再利用性: Predict・ChainOfThought・ReAct・MultiChainComparisonなど再利用可能なモジュールを提供
技術的詳細(Technical Details)
Signatureアーキテクチャ
graph LR
A[入力フィールド\nSignature定義] --> B[DSPy Signature\n宣言的仕様]
B --> C[DSPy Module\nPredict / CoT / ReAct]
C --> D[Teleprompter\nコンパイラ]
D --> E{最適化アルゴリズム}
E --> F[BootstrapFewShot\n成功traceからFew-Shot収集]
E --> G[COPRO\n命令文の自動生成・最適化]
E --> H[MIPRO\n命令+Few-Shot同時最適化]
F --> I[コンパイル済みプログラム\nJSONに保存]
G --> I
H --> I
I --> J[本番推論\n追加LLM呼び出しなし]
K[訓練データ\ntrainset] --> D
L[評価メトリクス関数\nmetric] --> D
SignatureはLLMの呼び出しを「何をするか」の宣言として記述する。プロンプトの”How”をフレームワークに委ねる設計思想がコアにある。
1
2
3
4
5
6
7
8
9
10
# インライン記法(短縮形)
"question -> answer"
"context, question -> answer"
# クラス記法(型・説明付き)
class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
各フィールドにはdesc(説明)、prefix(プレフィックス)、型情報を付与可能。フレームワークはSignatureから適切なプロンプトを自動生成し、Few-Shot例をインターカレートする。
ModuleシステムとPyTorchとの対比
ModuleはSignatureを受け取り、実際のLLM呼び出しロジックを実装する。PyTorchのnn.Moduleに対応する設計である。
| PyTorch | DSPy | 役割 |
|---|---|---|
nn.Module | dspy.Module | 処理ロジックの抽象化 |
nn.Parameter | Demonstrations/Instructions | 最適化対象のパラメータ |
torch.optim.Adam | BootstrapFewShot | 最適化アルゴリズム |
loss.backward() | teleprompter.compile() | パラメータ更新 |
組み込みモジュール一覧:
| Module | 動作 |
|---|---|
dspy.Predict | Signatureを直接LLMに渡す基本モジュール |
dspy.ChainOfThought | 出力前に”rationale”フィールドを自動追加しCoTを誘導 |
dspy.ProgramOfThought | Pythonコードを生成・実行して答えを導出 |
dspy.ReAct | Thought-Action-Observationループを実装するエージェント |
dspy.Retrieve | RAG用の検索モジュール |
Teleprompter(コンパイラ)のアルゴリズム
TeleprompterはDSPyの「コンパイラ」で、トレーニングデータ+メトリクス関数を受け取り、プログラムのパラメータ(Few-Shotデモ・命令文・CoTプレフィックス)を最適化する。
BootstrapFewShot アルゴリズム
\[\text{D}_p = \{ (x_i^{(p)}, y_i^{(p)}, r_i^{(p)}) \mid M(x_i, \hat{y}_i) = \text{True} \}\]ここで、
- $x_i^{(p)}$: モジュール$p$の入力
- $y_i^{(p)}$: モジュール$p$の出力
- $r_i^{(p)}$: モジュール$p$のrationale(中間推論)
- $M$: メトリクス関数(正解判定)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def bootstrap_few_shot(program, trainset, metric, max_demos=4):
"""BootstrapFewShot: 成功traceからFew-Shot例を自動収集
Args:
program: DSPyプログラム(Module)
trainset: 学習データ(dspy.Exampleのリスト)
metric: 評価メトリクス関数
max_demos: 各モジュールの最大デモ数
Returns:
コンパイル済みプログラム
"""
demonstrations = defaultdict(list)
for example in trainset:
# traceを記録しながらプログラムを実行
trace = execute_with_trace(program, example)
# メトリクスを満たした場合のみ、中間ステップもデモとして収集
if metric(example, trace.output):
for module_name, module_trace in trace.items():
demonstrations[module_name].append({
"input": module_trace.input,
"output": module_trace.output,
"rationale": module_trace.rationale
})
# 各モジュールにデモを設定
for module_name, module in program.named_modules():
module.demonstrations = sample(demonstrations[module_name], max_demos)
return program
核心的なポイント: 教師プログラムが正解した例の中間ステップ(rationale・検索クエリ等)も含めてデモとして収集する。アノテーターが知らない中間出力についても、Few-Shot例が自動生成される。
COPRO(Collaborative Prompt Optimization)
命令文(instruction)を自動生成・最適化するグラジエントフリー手法。過去の命令文とスコアのペアを参照しながら、LLMが新しい命令候補を反復生成する。
\[\text{inst}^* = \arg\max_{\text{inst} \in \mathcal{I}} \frac{1}{|T|} \sum_{(x,y) \in T} M(y, P_{\text{inst}}(x))\]ここで、
- $\mathcal{I}$: 命令文の探索空間
- $T$: 訓練セット
- $P_{\text{inst}}$: 命令文instを使ったプログラム
実装のポイント(Implementation)
CI/CD統合パターン
DSPyの最大の実務的価値は、コンパイル済みプログラムをJSONで保存・ロードし、GitでバージョニングしてCI/CDに組み込める点にある。
1
2
3
4
5
6
7
# コンパイル済みプログラムの保存(Gitリポジトリに含める)
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
compiled_rag.save("prompts/compiled/rag_v2.3.json")
# 本番環境での読み込み
rag = RAG()
rag.load("prompts/compiled/rag_v2.3.json")
Zenn記事のPromptfoo品質ゲートとの統合:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# .github/workflows/dspy-compile.yml
name: "DSPy Prompt Compilation"
on:
push:
paths: ["prompts/signatures/**", "dspy_modules/**"]
jobs:
compile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install dspy-ai
- run: python scripts/compile_and_evaluate.py
- run: |
# コンパイル結果をコミット
git add prompts/compiled/
git commit -m "chore: recompile DSPy prompts"
LLM切り替え時の再コンパイル
1
2
3
4
# GPT-4oで最適化済みのプログラムをClaude 3.5用に再コンパイル
dspy.settings.configure(lm=dspy.Anthropic(model="claude-3-5-sonnet"))
recompiled = teleprompter.compile(rag, trainset=trainset)
recompiled.save("prompts/compiled/rag_claude35.json")
ハイパーパラメータのコスト管理
| パラメータ | 推奨値 | コストへの影響 |
|---|---|---|
max_bootstrapped_demos | 4-8 | デモ数が多いほどコンテキスト長増加 |
num_threads | 4-8 | 並列評価数。APIレート制限に注意 |
max_labeled_demos | 4 | ラベル付きデータからの直接サンプリング数 |
実験結果(Results)
| データセット | 手法 | LM | 精度 |
|---|---|---|---|
| GSM8K | Zero-shot CoT(ベースライン) | GPT-4 | 87.1% |
| GSM8K | DSPy BootstrapFewShot | GPT-4 | 89.5% |
| GSM8K | DSPy MIPRO | GPT-4 | 90.2% |
| GSM8K | 手動プロンプト | Llama-2 7B | 11.8% |
| GSM8K | DSPy BootstrapFewShot | Llama-2 7B | 49.3% |
| HotPotQA | 手動プロンプト+ColBERTv2 RAG | GPT-3.5 | ~35% |
| HotPotQA | DSPy MultiHop+BootstrapFewShot | GPT-3.5 | ~45% |
注目すべき結果: 小型LM(Llama-2 7B)での改善幅が特に顕著で、手動プロンプト比で+37.5ポイント(11.8%→49.3%)の改善を達成。BootstrapFineTuningと組み合わせることで72.5%まで到達し、GPT-3.5-turboの手動プロンプトを上回るケースを実証した。
LangChain等との比較: LangChainはLLMを変更するとプロンプトの書き直しが必要だが、DSPyではdspy.settings.configure(lm=new_lm)で切り替え後に再コンパイルするだけで自動最適化される。
実運用への応用(Practical Applications)
Langfuse×DSPyの統合
Zenn記事で紹介したLangfuseとの統合は以下の形で実現できる:
- Langfuseトレーシング→DSPy評価データ: Langfuseに蓄積されたトレースとユーザー評価をDSPyのtrainsetとして活用
- DSPyコンパイル→Langfuseラベル管理: コンパイル済みプロンプトをLangfuseの
prod-a/prod-bラベルとしてデプロイしA/Bテスト - LaunchDarkly段階ロールアウト: A/Bテストで有意差が確認されたプロンプトをLaunchDarkly Feature Flagで100%ロールアウト
プロダクション視点
- コンパイルコスト: trainset 100例×パイプライン深さ3で約300回のLLM呼び出し。GPT-4oで約$5-10/回のコンパイル
- 推論時コスト: コンパイル済みJSONを読み込むだけでLLM追加呼び出しなし
- スケーリング: コンパイルをCI/CDの一部として週1実行する運用が実用的
関連研究(Related Work)
- OPRO (Yang et al., 2023): LLMをメタプロンプトベースの最適化器として使用。DSPyはパイプライン全体をコンパイルするためより広範
- TextGrad (Yuksekgonul et al., 2024): テキスト勾配による自動微分。DSPyがラベルデータを使うのに対し、TextGradはゼロショット
- APE (Zhou et al., 2022): 自動プロンプトエンジニアリング。DSPyは命令文だけでなくFew-Shot例・CoTステップも同時最適化
まとめと今後の展望
DSPyは「プロンプトエンジニアリング」を「プロンプトコンパイル」へと転換する重要な研究である。Zenn記事で紹介したLangfuse×LaunchDarklyの配信・監視基盤とDSPyの自動最適化を組み合わせることで、「生成→評価→配信→監視→再最適化」の完全なフィードバックループが実現できる。
今後の課題は、コンパイルコストの削減(MIPROv2のベイズ最適化でAPIコスト70%削減を達成)と、マルチモーダルパイプラインへの対応である。
参考文献
- arXiv: https://arxiv.org/abs/2310.11511
- Code: https://github.com/stanfordnlp/dspy(MIT License)
- Related Zenn article: https://zenn.dev/0h_n0/articles/9fc2f8c4a420e4