まとめ
DSPyは、LLMへの指示を文字列のプロンプトとして書く代わりに、入出力の型と処理の流れをPythonコードで宣言し、プロンプトそのものは最適化アルゴリズムに任せるフレームワークです。要点は次の5つです。
- 正式名称はDeclarative Self-improving Python。開発元はStanford大学のグループで、リポジトリは
stanfordnlp/dspy、ライセンスはMITです。 - 書くのはシグネチャ(入出力の宣言)とモジュール(処理の組み立て)だけ。実際にLLMへ送られるプロンプト文字列はDSPyが組み立てるため、
dspy.inspect_history()で中身を確認できます。 - オプティマイザの選び方は公式ガイドが2系統ある。新しい方は指示文・デモ・重みのどれがボトルネックかで選ばせ、もう一方は件数の目安(10件程度なら
BootstrapFewShot、50件以上でBootstrapFewShotWithRandomSearch、200件以上かつ40試行以上ならMIPROv2)を示します。まずはBootstrapFewShotから始めるのが無難です。 - 最適化の成果はJSONで保存・再読込できるので、最適化を実行するタイミングと本番で推論するタイミングを分離できます。
- LangChainやLangGraphとは重点が違う。モジュールの合成やReActエージェントは双方が持ちますが、DSPyの中心はプログラム内の各予測器のプロンプトをデータとメトリクスで自動的に詰めることにあります。
本記事に載せたPythonコードは、DSPy 3.3.1(2026年9月18日時点のPyPI最新安定版)とPython 3.14で実行し、掲載している出力を確認したものです。ただし実行に使ったのはテスト用のDummyLMで、有料APIへの実推論は行っていません。dspy.LMの設定例だけはインスタンス生成までの確認で、認証や推論の成否は検証していません。
DSPyとは何か:正式名称・開発元・現在地
DSPyは、大規模言語モデル(LLM)を使うアプリケーションを「プロンプトを書く」のではなく「プログラムする」ために作られたPythonフレームワークです。公式READMEはDSPy stands for Declarative Self-improving Pythonと明記しており、日本語にすれば「宣言的で自己改善するPython」となります。リポジトリ名のstanfordnlp/dspyが示すとおり、Stanford大学のNLPグループを中心に開発され、PyPIのライセンス表記はMIT License(著作権表示はStanford Future Data Systems)です。
系譜をたどると、出発点は2022年12月28日に公開された論文「Demonstrate-Search-Predict」(arXiv:2212.14024)で、当時の名前はDSPでした。GitHubリポジトリは2023年1月9日に作成され、DSPyという名前でのPyPI配布は2023年8月14日(当時の配布名はdspy-aiの0.1.9)に始まっています。2023年10月の論文「DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines」(arXiv:2310.03714)が現在よく引かれる原典です。なお、DSPからDSPyへの改名を宣言した公式告知は見当たらず、日付を1日単位で特定することはできません。確かなのは「2023年末に突然現れた新しいフレームワーク」ではなく、検索拡張の研究から派生して名前と射程を変えてきたプロジェクトだということです。
規模は、公式サイトが2026年9月18日時点で38k github stars・459+ contributors・7.5M+ monthly downloadsと表示しています。同サイトはBuilt in the open, since Dec 2022とも掲げており、前身DSPの公開時点をプロジェクトの起点としています。PyPIの最新安定版は3.3.1(2026年8月21日公開)で、対応Pythonは3.10以上3.15未満です。なお3.4.0b1というベータ版も公開されていますが、pip install dspyで入るのは安定版の3.3.1です。
読み方について補足すると、公式ドキュメントやリポジトリ内に発音を明記した記述は見当たりません。名称自体がDeclarative Self-improving Pythonの頭字語であるため、英字のまま表記するのが確実です。
チュートリアル:インストールからシグネチャ・モジュールまで
導入はpip install dspyの一行です。古い記事にあるdspy-aiも現在はPyPI上に残っていますが、これはdspy>=3.3.1を入れるだけの互換エイリアスで、説明文にもdspyを入れるよう書かれています。新規ならdspyを指定してください。本記事の出力をそのまま再現したい場合はpip install dspy==3.3.1のように版を固定してください。
pip install dspy
使い始めるときに書くものは2つだけです。シグネチャが「何を入力して何を出力するか」の宣言、モジュールが「その宣言をどう実行するか」の戦略にあたります。
シグネチャによる入出力の型宣言
シグネチャはdspy.Signatureを継承したクラスで書きます。クラスのdocstringがタスクの目的、dspy.InputFieldとdspy.OutputFieldのdescが各フィールドの説明になります。次のコードはDSPy 3.3.1で実行し、出力を確認したものです。APIキーを持っていなくても試せるよう、LLMの代わりにテスト用のDummyLMを差し込んでいます。
import dspy
from dspy.utils.dummies import DummyLM
class Classify(dspy.Signature):
"""問い合わせ本文を3カテゴリに分類する。"""
text: str = dspy.InputField(desc="問い合わせ本文")
category: str = dspy.OutputField(desc="billing / technical / other のいずれか")
# APIキー無しで動かすためのダミーLM(本番は dspy.LM に差し替える)
dspy.configure(lm=DummyLM([{"reasoning": "請求の重複が論点", "category": "billing"}]))
cot = dspy.ChainOfThought(Classify)
result = cot(text="先月の請求額が二重に引き落とされています")
print(result.category)
print(sorted(result.keys()))
dspy.inspect_history(n=1)
実行結果の先頭2行は次のとおりです。
billing
['category', 'reasoning']
ここでreasoningというフィールドが増えている点に注目してください。シグネチャにはtextとcategoryしか書いていませんが、dspy.ChainOfThoughtが推論過程の出力欄を自動で追加しています。dspy.Predictに差し替えればcategoryだけが返ります。同じシグネチャのままモジュールを取り替えるだけで戦略を切り替えられるのがDSPyの基本的な使い勝手です。
手早く試したいだけなら、クラスを定義せず文字列で書くこともできます。dspy.Predict("question -> answer")のように矢印で入出力を並べる記法です。
ChatAdapterが生成するプロンプトの確認方法
「プロンプトを書かない」と言われると、内部で何が送られているのか不安になります。dspy.inspect_history()を呼べば直近のLLM呼び出しがそのまま表示されます。上のコードの実行で得られた出力が次です。
System message:
Your input fields are:
1. `text` (str): 問い合わせ本文
Your output fields are:
1. `reasoning` (str):
2. `category` (str): billing / technical / other のいずれか
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## text ## ]]
{text}
[[ ## reasoning ## ]]
{reasoning}
[[ ## category ## ]]
{category}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
問い合わせ本文を3カテゴリに分類する。
User message:
[[ ## text ## ]]
先月の請求額が二重に引き落とされています
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## category ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
シグネチャのdocstringがyour objective is:の後ろに入り、フィールドのdescがそのまま説明文になっています。出力は[[ ## フィールド名 ## ]]という区切りで受け取る形式で、これはDSPyの既定のアダプタであるdspy.ChatAdapterの仕様です。JSON形式で受け取りたい場合はdspy.JSONAdapter、XMLならdspy.XMLAdapterに切り替えられます。プロンプトの文面は隠されているのではなく、いつでも取り出せる中間生成物だと考えてください。
実モデルへの接続設定
実際のモデルに接続するときはdspy.LMを使い、プロバイダ名/モデル名の形式で指定します。
import dspy
lm = dspy.LM("openai/gpt-5.6", api_key="...", temperature=1.0, max_tokens=16000)
dspy.configure(lm=lm)
この記法はLiteLLM互換のため、OpenAI以外にもAnthropic、Google、ローカル推論サーバなどを同じ書き方で指定できます。プロンプトを書き換えずにモデルだけ差し替えられるのが、宣言的に書いておく実利の1つです。
最適化(オプティマイザ)の使い方
ここまでのコードはプロンプトを手で書かずに済むだけで、品質はモデル任せです。DSPyの本領はオプティマイザにあります。評価関数(メトリクス)と少量の学習データを渡すと、few-shotの実例や指示文をDSPy側が探索して、プログラムに埋め込んでくれます。
最も基本的なBootstrapFewShotを動かすコードです。これもDSPy 3.3.1で実行済みです。
import dspy
from dspy.utils.dummies import DummyLM
from dspy.teleprompt import BootstrapFewShot
class Classify(dspy.Signature):
"""問い合わせ本文を3カテゴリに分類する。"""
text: str = dspy.InputField()
category: str = dspy.OutputField(desc="billing / technical / other")
trainset = [
dspy.Example(text="請求が二重に来ています", category="billing").with_inputs("text"),
dspy.Example(text="ログインできません", category="technical").with_inputs("text"),
dspy.Example(text="解約方法を教えてください", category="other").with_inputs("text"),
dspy.Example(text="クレジットカードを変更したい", category="billing").with_inputs("text"),
]
dspy.configure(lm=DummyLM([{"category": e.category} for e in trainset] * 6))
def exact_match(example, pred, trace=None):
return example.category == pred.category
program = dspy.Predict(Classify)
optimizer = BootstrapFewShot(metric=exact_match, max_bootstrapped_demos=2, max_labeled_demos=2)
compiled = optimizer.compile(program, trainset=trainset)
print("埋め込まれたデモ数:", len(compiled.demos))
print("保存される状態のキー:", list(compiled.dump_state().keys()))
evaluator = dspy.Evaluate(devset=trainset, metric=exact_match, display_progress=False, display_table=0)
print("評価件数:", len(evaluator(compiled).results))
出力は次のとおりです。
Bootstrapped 2 full traces after 2 examples for up to 1 rounds, amounting to 2 attempts.
埋め込まれたデモ数: 2
保存される状態のキー: ['traces', 'train', 'demos', 'signature', 'lm']
評価件数: 4
押さえるべき点は3つです。第一に、メトリクス次第では、学習データにラベル付きの正解を全件用意しなくてよいことです。BootstrapFewShotはプログラム自身を教師役として動かして推論過程を生成し、メトリクスを通過したものだけをデモとして採用します。ただし上のexact_matchのようにexample.categoryを参照するメトリクスではラベルが要ります。ラベルを省けるのは、出力形式の妥当性や外部ツールの実行結果など、正解ラベル以外で採点できるメトリクスを書ける場合です。第二に、最適化の成果はcompiled.demosというデータとしてプログラムに埋め込まれるだけで、Pythonのコードは書き換わりません。第三に、dump_state()の中身がそのまま保存対象です。なおBootstrapFewShotの既定値はmax_bootstrapped_demos=4、max_labeled_demos=16、max_rounds=1で、上のコードはどちらも2に絞っています。
メトリクスは(example, pred, trace=None)を受け取る普通のPython関数です。上の例は完全一致ですが、F1値でも、LLMに採点させる関数でも、コストのペナルティを含む関数でも構いません。何を良しとするかの定義が、DSPyを使ううえで一番重要な設計判断になります。なお、上のコードはダミーLMを使っているため、評価スコアの数値そのものには意味がありません。確認できるのはAPIの挙動とデータの流れです。devsetにtrainsetをそのまま渡しているのも動作確認のためで、実際に品質を測るときは学習に使っていない検証用データを別に用意してください。
どのオプティマイザを選ぶか:公式の2つの選定ガイド
DSPy 3.3.1には10種類以上のオプティマイザが入っており、最初の選択でつまずきがちです。公式ドキュメントには選び方の案内が2つあり、切り口が違うので両方を押さえておくと迷いません(Optimizers: choosing oneとDSPy Optimizers)。
ボトルネック別の選定チートシート
現行の「Optimizers: choosing one」は、学習データの件数ではなくinstructions(指示文)・demos(実例)・weights(重み)のどれを調整したいかを軸にしています。ページ末尾の選定チートシートが次の内容です。
| 状況 | 試すもの |
|---|---|
| 始めたばかりで何が効くか分からない | BootstrapFewShot |
| 試行ごとにデモの質がばらつく | BootstrapFewShotWithRandomSearch |
| 学習データが多く、入力ごとに適したデモが違う | KNNFewShot |
| 指示文が弱く、デモは問題ない | COPRO または GEPA |
| 両方弱く、予算がある | MIPROv2 または GEPA |
| 失敗例に名前を付けられる共通パターンがある | SIMBA または InferRules |
| プロンプトだけの改善が頭打ちで、モデルを調整できる | BootstrapFinetune |
| プロンプトと重みの両方を組み合わせたい | BetterTogether |
| エージェント・ツール利用のタスク | AvatarOptimizer または GEPA |
同じページには運用上重要な設計判断も明記されています。.compile()は渡したプログラムを書き換えず新しいコピーを返すため、同じプログラムに対して別のオプティマイザや別のデータで繰り返し試して比較できます。また、コンパイル済みモジュールには_compiled=Trueが立ち、それを内包する外側のプログラムを最適化しても内側は触られません。「内側を最適化してから外側を最適化する」という積み上げが可能です。
学習データ件数と探索予算からの目安
もう1つの「Which optimizer should I use?」は、学習データの件数を軸にした出発点を示しています。こちらも現在公開されており、手元のデータ量から当たりを付けたいときに有用です。
| データ件数 | 推奨されるオプティマイザ | 特徴 |
|---|---|---|
| 10件程度 | BootstrapFewShot | プログラム自身を教師役にしてデモを生成し、メトリクスを通過したものだけを採用する |
| 50件以上 | BootstrapFewShotWithRandomSearch | BootstrapFewShotを異なる乱数種で複数回かけ、検証セットで最良の候補を選ぶ |
| 200件以上(40試行以上かけられる場合) | MIPROv2 | 指示文とデモの両方を生成し、ベイズ最適化で組み合わせを探索する |
| 0-shotのまま指示文だけ磨きたい | MIPROv2(0-shot設定) | デモを増やさず指示文の改善に絞る |
| 推論を速く・安くしたい | BootstrapFinetune | プロンプトベースのプログラムを小型モデルの重み更新に蒸留する |
両ガイドの差は矛盾ではなく、件数から入るか、ボトルネックから入るかという視点の違いです。なお件数の目安はBootstrapFewShot系とMIPROv2について書かれたもので、GEPAやSIMBAには一律の下限件数は示されていません。
GEPAとSIMBAの特徴とメトリクス設計
2025年8月のDSPy 3.0で入ったGEPA(Genetic-Paretoの略)は、指示文の最適化にメトリクスからの自然言語フィードバックを使える点が他と違います。GEPAは実行の軌跡をLLM自身に振り返らせ、何がうまくいき何が失敗したかを自然言語で言語化して、その反省をプロンプトの改訂に反映します。論文「GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning」(arXiv:2507.19457、2025年7月25日投稿)は、6つのタスクで強化学習手法のGRPOに対して平均6%、最大20%上回りながら、ロールアウト回数は最大35分の1、MIPROv2に対しては10%超の改善(AIME-2025では+12%の精度)と報告しています。いずれもabstractの報告値であり、どのタスクでも同じ改善幅が出るという意味ではありません。
GEPAを使ううえで見落としやすいのがメトリクスの戻り値の形です。公式ドキュメントは、GEPAだけがPrediction(score, feedback)という形で自然言語のフィードバックを予測器ごとに読み取ると明記しています。逆に、フィードバックを返せないメトリクスしか用意できないなら、同じ予算ではCOPROやMIPROv2の方が伸びることがあるとも書かれています。メトリクスの形をオプティマイザに合わせないことが最も多い設定ミスだ、というのが公式の指摘です。
もう1つの選択肢がSIMBAで、出力のばらつきが大きい難しい事例をミニバッチから選び出し、失敗を分析して改善ルールを生成します。エージェントのような長い手順を踏むタスクに向きます。
実務上の順番としては、まず最適化なしのベースライン(あるいはdspy.LabeledFewShot)を測り、次にBootstrapFewShotで改善幅を見て、データと予算が増えてからMIPROv2やGEPAへ進むのが安全です。公式ドキュメントもGEPAやMIPROv2の1回のコンパイルで数百ドル規模のLLM呼び出しが発生しうると書いており、いきなり大きなものから始めると費用だけが膨らみます。
ReActでツールを呼ぶ:エージェント用途
DSPyはエージェントも同じ流儀で書けます。dspy.ReActに普通のPython関数を渡すだけで、docstringと型ヒントからツール定義が作られます。
import dspy
from dspy.utils.dummies import DummyLM
def search_price(product: str) -> str:
"""商品名から税込価格を返す社内ツール。"""
return {"マグカップ": "1,320円"}.get(product, "不明")
dspy.configure(lm=DummyLM([
{"next_thought": "価格表を引く", "next_tool_name": "search_price",
"next_tool_args": {"product": "マグカップ"}},
{"next_thought": "答えが出た", "next_tool_name": "finish", "next_tool_args": {}},
{"reasoning": "ツールの戻り値をそのまま answer に入れる", "answer": "1,320円"},
]))
agent = dspy.ReAct("question -> answer", tools=[search_price], max_iters=3)
result = agent(question="マグカップの税込価格は?")
print(result.answer)
print(result.trajectory["tool_name_0"], result.trajectory["observation_0"])
出力は次のとおりです。
1,320円
search_price 1,320円
max_itersの既定値は20で、上のコードは3に絞っています。戻り値のtrajectoryにはthought_0、tool_name_0、tool_args_0、observation_0という連番のキーで各ターンが記録されます。ここが辞書として取り出せるため、どのツールをどの引数で呼んだかをテストで検証できます。
DSPy 3.3.0では、これを作り直したdspy.ReActV2が実験的機能として追加されました。従来のnext_tool_argsという独自形式ではなくプロバイダのネイティブなツール呼び出しを前提とし、履歴をdspy.Historyの構造化メッセージとして保持します。リリースノートは、1つの長大なユーザーメッセージに履歴を詰め込む従来方式と比べてプロンプトキャッシュが効きやすく、社内検証で一部タスクのコストが最大50%減ったと報告しています。並列ツール呼び出しにも対応しました。詳細はDSPy ReActV2とは|ネイティブツール呼び出し前提の新ReActモジュール【2026年版】で解説しています。
運用で効く設定:保存・読み込みとキャッシュ
最適化には時間と費用がかかるので、結果を保存して本番では読み込むだけにします。DSPyのプログラムはJSONで直列化できます。
import dspy
# 最適化済みプログラムはJSONで保存でき、同じ構造のプログラムに読み戻せる
compiled = dspy.Predict("question -> answer")
compiled.demos = [dspy.Example(question="1+1は?", answer="2").toDict()]
compiled.save("classifier.json")
restored = dspy.Predict("question -> answer")
restored.load("classifier.json")
print(restored.demos)
# LM呼び出しのキャッシュ設定(既定はディスク+メモリの二段)
dspy.configure_cache(enable_disk_cache=True, enable_memory_cache=True,
disk_cache_dir="./.dspy_cache", memory_max_entries=10000)
出力は[{'question': '1+1は?', 'answer': '2'}]となり、デモがそのまま復元されます。運用上の含意は、最適化はCIやバッチで回して成果物のJSONだけをデプロイすればよいということです。アプリケーション起動のたびに最適化を走らせる必要はありません。
キャッシュは既定で有効です。DSPy 3.3.1のconfigure_cacheの既定値は、ディスクキャッシュ先が~/.dspy_cache、ディスク上限が30GB、メモリ上のエントリ上限が1,000,000件でした。キャッシュキーはリクエスト全体(モデル名・メッセージ・生成設定)をJSON化してハッシュした値なので、プロンプトを1文字でも変えれば別キーになり、LLMへ実際に送られます。つまりプロンプトを変えた比較がキャッシュに隠される心配はありません。効くのは、オプティマイザが同一のリクエストを何度も投げる場面や、失敗したコンパイルをやり直す場面です。ここでキャッシュが有効かどうかで実験のコストが大きく変わります。
DSPy 3.3.0では、保存に関する安全側の変更も入っています。カスタムLMの状態をdump_state()で直列化する際にAPIキーは除外され、独自のLMクラスを読み込むには明示的なオプトインが必要になりました。保存したJSONを共有する運用では、この挙動を前提にして構いません。
LangChain・LangGraphとの違いと使い分け
「DSPyとLangGraphのどちらを使えばよいか」という問いは頻繁に見かけますが、モジュールの合成もツールを使うエージェントも双方が持っており、機能は部分的に重なります。違うのは何に重点を置いているかです。
| 観点 | DSPy | LangChain / LangGraph |
|---|---|---|
| 主な関心 | 各予測器のプロンプト(必要なら重み)の自動調整 | 複数の処理や状態をどうつなぐか、どこで分岐・再試行するか |
| 開発者が書くもの | 入出力のシグネチャ、モジュールの組み立て、評価関数 | LangChainはcreate_agentとミドルウェア、LangGraphはノードとエッジ・状態スキーマ |
| プロンプト文字列 | DSPyが生成し、オプティマイザが書き換える | 開発者が書いて管理する |
| 学習データ | 最適化を使うなら少量でも必須(使わなければ0件でも動く) | 不要(評価は別途組む) |
最適化の対象は単一のモジュールに限りません。公式ドキュメントのMIPROv2の説明は、プログラム内のすべての予測器についてプロンプトを組み立て直すと書いており、複数段のパイプライン全体が対象です。
したがって、全体のフローはLangGraphで組み、その中の精度が効くノードだけをDSPyのモジュールに置き換えるという併用が現実的です。逆に、単一の分類や抽出タスクで精度を上げたいだけならDSPy単体で足ります。フレームワーク全体の比較はAIエージェントフレームワークとは?主要8種の比較と受託開発視点の選定基準【2026年版】に、LangChain側の現状はLangChain v1.0とは|create_agent・ミドルウェア・langchain-classicを実装例で解説にまとめています。
DSPyを選ばない方がよい場面もはっきりしています。評価関数を定義できないタスク(何が正解か決められない自由記述の生成など)、学習データを1件も用意できない場合、そしてプロンプトの文面を人が完全に管理する必要がある規制下の用途です。この3つに当てはまるなら、最適化の恩恵は受けられません。
DSPy 3.x の主な変更点と移行の注意
2024年前後の日本語記事を参考にすると、現行APIと食い違う箇所が出てきます。バージョンごとの節目を、公式リリースノートで確認できる範囲で押さえておきます。
3.0.0の新機能と互換性の変更(2025年8月12日)
アダプタと型の整備、GEPAとSIMBAの追加、MLflow 3.0との連携によるトレースが柱です。破壊的変更としてPython 3.9のサポート終了、コミュニティ製リトリーバの削除、dspy.Programエイリアスの削除が行われました。古い記事にあるリトリーバ統合のコードはそのままでは動きません。
3.3系の新機能と移行時の注意(2026年8月3日)
前述のdspy.ReActV2に加え、実験的なdspy.Flexが入りました。これはプログラムの構造そのものを探索対象にする機能で、dspy.Flex("question -> answer")と書いてGEPAで最適化すると、GEPAがモジュールの実装コード自体を書き換えて探索します。生成されたコードはCodeInterpreterのサンドボックスで実行され、結果はmodule_srcとしてプログラムの状態に保存されます。
移行時に注意が必要なのは、dspy.Image・dspy.Audio・dspy.Fileの構築方法が変わった点です。3.3以降、パスやURLに見える文字列を渡しても暗黙にファイル読み込みやダウンロードは行われません。ローカル画像を埋め込むならImage.from_path(path)、リモート画像をダウンロードして埋め込むならImage.from_url(url)を明示的に呼びます。LLMの出力をパースした値がたまたまパスの形をしていただけでファイルを読んでしまう事故を防ぐための変更です。
非推奨になったモジュールもあります。3.3.1でdspy.ProgramOfThoughtとdspy.CodeActをインスタンス化すると、いずれも「3.5で削除予定、代替はRLM」という警告が出ます。コードを書かせて実行させる用途でこの2つを使っている場合は、移行先を確認しておいてください。
あわせて、LMの境界を型付きのLMRequestとLMResponseに移行する作業が進んでいます。3.3時点では既定の挙動は変わりませんが、独自のLMクラスを実装している場合は移行計画に目を通しておくとよいでしょう。
よくある質問
DSPyは何の略ですか?
Declarative Self-improving Python(宣言的で自己改善するPython)の略です。公式READMEに明記されています。前身は2022年12月28日に公開された論文のDSP(Demonstrate-Search-Predict)です。DSPy名でのPyPI配布は2023年8月14日に始まっており、改名を宣言した公式告知は見当たらないため、改名日を1日単位で特定することはできません。
DSPyを使うのに学習データは何件必要ですか?
公式ドキュメントは10件程度から始められるとしています。10件程度ならBootstrapFewShot、50件以上ならBootstrapFewShotWithRandomSearch、過学習を避けつつMIPROv2で長い探索を回すなら200件以上が目安です。なお、入力だけのデータで済むかどうかはメトリクス次第です。完全一致のように正解ラベルを参照する評価関数を使うなら、ラベルは必要になります。
APIキーが無くてもDSPyを試せますか?
試せます。本記事のコードのようにdspy.utils.dummies.DummyLMに想定する出力を渡してdspy.configure(lm=...)すれば、LLMを呼ばずにプログラムの構造とデータの流れを確認できます。ただし返る値は指定したものがそのまま出るだけなので、評価スコアの数値には意味がありません。品質を測るには実際のモデルが必要です。
DSPyが実際に送っているプロンプトは見られますか?
見られます。dspy.inspect_history(n=1)で直近の呼び出しのシステムメッセージ・ユーザーメッセージ・応答がそのまま表示されます。既定のdspy.ChatAdapterは[[ ## フィールド名 ## ]]という区切りで出力を要求する形式を使います。
DSPyとLangGraphは併用できますか?
できます。機能は一部重なりますが、併用を妨げるものではありません。処理の連結・分岐・状態管理をLangGraphが担い、その中で精度を上げたいノードをDSPyのモジュールに置き換える構成が取りやすい形です。
最適化のたびにLLMを呼ぶと費用がかさみませんか?
最適化を推論から切り離し、コンパイル済みの状態を保存したJSONを本番へ配る運用にしてください。program.save()とprogram.load()で往復できます(読み込む側には同じ構造のプログラムのコードが必要です)。モデルを変えたり、シグネチャや評価基準を変えたりしたときは、その都度コンパイルし直して評価します。加えて、DSPyは既定でディスクとメモリの二段キャッシュを持つため、まったく同じリクエストの再実行はキャッシュにヒットし、その分の課金は発生しません。ただしモデル名・指示文・生成設定のどれかが変わればキーも変わるので、再度LLMへ送られます。