🙌

DSPy入門メモ:プロンプトを改善してみる(GEPA)

に公開

DSPyのモジュールの使い方をなんとなく理解したところで,DSPyを使ったプロンプトの改善を試してみたい.

モジュールの使い方を完全に理解した編はこちら:
https://zenn.dev/retore/articles/5800d535cb18f5

プロンプトを改善するって,どうやって

「改善」と言うからにはinput(プロンプト)に対するoutputがあり,そしてそのoutputに対する良し悪しの評価があり,inputを変更した時にoutputが良くなるか?悪くなるか?を繰り返し評価することが必要になる.

DSPyではそれを実現するOptimizersという仕組みが用意されており,複数のアルゴリズムが提供されている.
https://dspy.ai/api/optimizers/GEPA/overview/

GEPA,MIPROv2など複数のアルゴリズムがある……が,それぞれのアルゴリズムの特徴や良し悪しは追って気が向いたら学ぶことにして,まずは公式のチュートリアルからGEPAによる数学の問題を解くサンプルを試してみる.

チュートリアルを試す:GEPA for AIME

まずは以下のチュートリアルをそのまま動かしてみる.
https://dspy.ai/tutorials/gepa_aime/

まず,全く最適化していない時は

2025/11/09 05:16:26 INFO dspy.evaluate.evaluate: Average Metric: 8 / 150 (5.3%)

ということで,正答率は5%ということになった.

ここで,チュートリアルのコンテンツとして提供されている以下の関数をフィードバック関数としたGEPAによる最適化を導入する.

def metric_with_feedback(example, prediction, trace=None, pred_name=None, pred_trace=None):
    correct_answer = int(example['answer'])
    written_solution = example.get('solution', '')
    try:
        llm_answer = int(prediction.answer)
    except ValueError as e:
        feedback_text = f"The final answer must be a valid integer and nothing else. You responded with '{prediction.answer}', which couldn't be parsed as a python integer. Please ensure your answer is a valid integer without any additional text or formatting."
        feedback_text += f" The correct answer is '{correct_answer}'."
        if written_solution:
            feedback_text += f" Here's the full step-by-step solution:\n{written_solution}\n\nThink about what takeaways you can learn from this solution to improve your future answers and approach to similar problems and ensure your final answer is a valid integer."
        return dspy.Prediction(score=0, feedback=feedback_text)

    score = int(correct_answer == llm_answer)

    feedback_text = ""
    if score == 1:
        feedback_text = f"Your answer is correct. The correct answer is '{correct_answer}'."
    else:
        feedback_text = f"Your answer is incorrect. The correct answer is '{correct_answer}'."

    if written_solution:
        feedback_text += f" Here's the full step-by-step solution:\n{written_solution}\n\nThink about what takeaways you can learn from this solution to improve your future answers and approach to similar problems."

    return dspy.Prediction(score=score, feedback=feedback_text)

これは,フィードバックとして「正解か不正解か」だけでなく「どのように解くか」の情報を渡す関数になっている.

そしてこのフィードバック関数を使ってGEPAで最適化すると……

2025/11/09 06:34:04 INFO dspy.evaluate.evaluate: Average Metric: 10 / 150 (6.7%)

5.3%から6.7%に正解率が改善した……が絶対的には正解率が低い.
チュートリアルではGPT-4.1を使っているが自分は4o-miniを使っているあたりが原因かもしれない.
(そもそもチュートリアルのサンプルでは改善前で46.7%の正解率となっているようなので,最適化云々以前にベースのモデルの性能の差が感じられる)

optimizerの選択

上記の通り,ユーザはoptimizerの処理の詳細を知らなくても簡単に最適化を行うことができる……が,当然,それぞれのoptimizerの特徴や,どのようなケースに向いているかを理解しておく必要がある.

それらの情報は以下のページにまとまっている.
https://dspy.ai/learn/optimization/optimizers/

日本語訳しつつまとめると,

  • サンプル数が少ない(約10)→BootstrapFewShot
  • サンプル数が50以上ある→BootstrapFewShotWithRandomSearch
  • 指示の最適化のみ行いたい→MIPROv2(0-shot)
  • 40回以上の最適化試行を行いたい,200以上のサンプルがある場合→MIPROv2
  • 大規模なモデルを利用可能でかつ効率的なプログラムが必要→BootstrapFinetuneを利用してファインチューニング
    とのことである.

上記の特徴を踏まえ,自分の用途で使えそうなのはBootstrapFewShotMIPROv2のようなので,次回以降これらのアルゴリズムを試してみたい.

Discussion