プロンプトを自動で直すとき、中で何が起きているのか — GEPA・MIPROv2・TextGrad の仕組みと、自分たちでできること

プロンプトを自動で直すとき、中で何が起きているのか — GEPA・MIPROv2・TextGrad の仕組みと、自分たちでできること

前回の記事 では、プロンプトやスキルを自動で直す手法を論文で整理しました。ただ、論文の要旨を読むだけでは「結局、何をどう書き換えているのか」が見えません。

プロンプトといっても、システムプロンプト、道具の説明文、スキル文書と形はさまざまです。道具を使うエージェントもあります。それを自動で直すとき、ツールの中では具体的に何が起きているのか。そして、それを参考に自分たちでは何ができるのか。

本稿では、DSPy と gepa、TextGrad のソースコードを読んで仕組みを確かめ、できるだけ噛み砕いて説明します。例はずっと EC の問い合わせに返信する AI を使います。後半では、プロンプトの直し方そのものを研究した論文も参考にします。

ソースは 2026 年 11 月上旬時点の各リポジトリの main を読みました。設定項目の名前は版によって変わることがあります。

1. まず全体像:新人の手引き書を、答案を添削しながら直す

自動改善の仕組みは、どのツールでも次のたとえで説明できます。

新人(AI)に手引き書(プロンプト)を渡して、練習問題(評価用データ)を解かせる。先輩(採点)が答案に点と赤ペンを入れる。添削係(もう 1 つの LLM)が赤ペンを読んで、手引き書を書き直す。新しい手引き書で解かせて、点が上がっていれば採用する。

この中で、利用者が用意するものは 4 つです。

用意するもの たとえ EC の問い合わせ対応では
直す文章 手引き書 システムプロンプト、道具の説明文
練習問題 練習問題 過去の問い合わせと、そのときの正しい対応
採点 先輩の採点と赤ペン 注文番号を正しく引用したか、勝手に返金を約束していないか、など
予算 何回まで練習させるか 評価を何回まで回すか、費用をいくらまでかけるか

ツールが自動でやってくれるのは「添削係」の部分だけです。何を直す対象にするか、どう採点するかは、すべて利用者が決めます。そして、変えたくない文章(たとえば返金の方針)は、直す対象に入れなければ守られます。

ツールによって違うのは、添削係が何を読んで、どう書き直すかです。ツールごとの説明に入る前に、まず「文章の直し方」そのものを分類しておきます。

2. 文章はどう直されるのか — 3 つの直し方

手引き書の直し方は、大きく 3 つに分かれます。

直し方 何が新しい文章を作るか 例
A. AI に書き直させる LLM が文章を書く 失敗を読んで直す、点数の推移を見て書く、2 つを掛け合わせる
B. 組み合わせで直す 既にある部品を選んで並べる(新しい文章は書かない) 指示文とお手本の組み合わせ、2 つの改良版の良いとこ取り
C. 機械的な規則で直す 決まった操作で文章をいじる(LLM は使わない) 句の削除・入れ替え、書式の入れ替え、お手本の並べ替え

以下では、ずっと同じ手引き書を例に使います。例に出てくる点数や件数は、説明のための架空の値です。

元の手引き書: あなたは EC の問い合わせ担当です。お客様には丁寧に返信してください。返金については方針に従ってください。

2.1 A. AI に書き直させる — 何を見せて、どこまで変えさせるか

AI に書き直させる方法は、添削係の AI に何を見せるかと、どこまで変えてよいとするかで性格が決まります。代表的な方針を、具体例で並べます。

A1. 失敗を読んで直す(GEPA、ProTeGi) 間違えた問題と、その理由(赤ペン)を見せて書き直させます。

失敗の理由がそのまま手引き書の一文になるので、どこが良くなったかが分かりやすいのが長所です。反面、1 問ずつの失敗に引きずられて、例外の記述がどんどん増えていきます(4.4 で詳しく書きます)。

A2. 点数の推移を見て書かせる(OPRO) 理由は見せず、「これまでの手引き書と、その点数」の一覧を見せて、もっと点の高そうな手引き書を書かせます。

AI が点数の上がり方から「何が効いているか」を推測して、その方向に伸ばします。採点で理由を返せないときでも使えます。

A3. 書き方のヒントで振る(MIPROv2) 同じ材料に、毎回違う書き方のヒントを添えて、性格の違う案を作らせます。

どれが良いかは、あとで採点して決めます(B1 の組み合わせ探索につながります)。

A4. 2 つを掛け合わせる(EvoPrompt、Promptbreeder) 点の良い手引き書を 2 つ「親」として見せ、両方の良いところを持つ「子」を書かせます。さらに少し変化(突然変異)も加えさせます。

Promptbreeder は、この「掛け合わせ方・変え方の指示」自体も一緒に進化させます。たとえば「具体例を足して」という変え方より、「禁止事項を先に書いて」という変え方のほうが良い子を生むなら、そちらが残っていきます。

A5. 失敗を分類してから直す(ETGPO) 1 問ずつではなく、失敗を数十件集めて種類ごとに分類させ、多い種類の失敗に向けた指針を足させます。

A1 より 1 回の変更が大きな傾向に向くので、例外の継ぎはぎになりにくい方法です。

A6. 部分だけ直す(SAPO) 手引き書を「役割」「文脈」「課題」「出力形式」に分け、弱い部分だけを書き直させます。

ほかの部分には手を付けないので、「ある振る舞いを直したら別の振る舞いが壊れた」が起きにくくなります。

AI に書き直させるときの方針のまとめ

決めること 選択肢 例
何を見せるか 失敗と理由 / 点数の推移 / 書き方のヒント / 親になる 2 案 / 失敗の分類 A1〜A5
どこまで変えさせるか 全体 / 指定した部分だけ / 指針を足すだけ A6 は部分、A5 は指針の追加
何を変えさせないか 方針・禁止事項、文字数の上限 「返金の方針の文は変えない」「500 字以内」
書き直す前に考えさせること 誤りの調査 → 欠けている点の仮説 → 書き直し 9 章の PE2

2.2 B. 組み合わせで直す — 新しい文章は書かず、選んで並べる

AI に新しい文章を書かせず、すでにある部品の組み合わせを探す方法です。どの組み合わせが良いかは、採点して決めます。

B1. 指示文 × お手本の組み合わせ(MIPROv2) 指示文の案(たとえば A3 で作った 3 案)と、お手本のセット(過去にうまくいった問い合わせと返信の組を 3 通り)を用意し、9 通りの組み合わせから一番点の高いものを探します。

お手本セット 1(配送の例中心) お手本セット 2(返品の例中心) お手本セット 3(混在)
指示文「簡潔に」 74 点 70 点 77 点
指示文「ベテラン役」 72 点 75 点 81 点
指示文「重大な場面」 69 点 71 点 73 点

実際には部品も候補も多いので、全部は試しません。MIPROv2 は、試した結果から「次に試すと良さそうな組み合わせ」を推測するベイズ最適化(Optuna)で、少ない試行で当たりを探します。

B2. 2 つの改良版の良いとこ取り(GEPA の合成) GEPA の合成(use_merge)は、AI を使わない機械的な操作です。同じ祖先から別々に改良された 2 つの手引き書を見つけ、部品ごとに次の規則で組み合わせます。

たとえば、祖先から「システムプロンプトだけ直した版」と「道具 issue_refund の説明文だけ直した版」が育っていたら、両方の変更を持つ版ができます。小さな問題の束で両方の親以上の点が出たときだけ、候補に加えます。

B3. 規則のオン・オフを探す(自分たちで作るなら) 手引き書を「規則の箇条書き」として持っておき、各規則を入れる・入れないの組み合わせを探す方法も作れます。

規則 2 を外しても点が下がらなければ、その規則は不要です。手引き書が膨らむのを防ぎ、どの規則が効いているかを人が理解できるのが長所です。

2.3 C. 機械的な規則で直す — LLM を使わずに文章をいじる

LLM に書き直させず、決まった操作で文章を変えて、点が上がるかを試す方法です。費用が安く、何を変えたかが完全に追えます。

C1. 句の削除・入れ替え・言い換え・再追加(GrIPS、EACL 2023) GrIPS は、構文解析で手引き書を句(ひとまとまりの言葉)に分け、次の 4 つの操作をランダムに当てて候補を作ります。

操作 内容 例
削除 句を消す 「お客様には丁寧に返信してください」→「お客様には返信してください」
入れ替え 2 つの句の位置を入れ替える 「返金については方針に従ってください」と「丁寧に返信してください」の順序を入れ替える
言い換え 句を言い換える(言い換え専用の小さなモデルを使う) 「方針に従って」→「規定に沿って」
再追加 以前に消した句を、別の位置に戻す 消した「丁寧に」を文末側に戻す

論文の既定では、1 回に 5 つの候補を作り、一番良い候補が今より点が高ければ乗り換えます。これを最大 10 回、2 回続けて改善しなければ止めます。人には意味が通りにくい文になっても精度が上がることがある、と論文自体が書いています。

C2. 書式を入れ替える(FormatSpread、ICLR 2024) 意味は同じで、書式だけを変えます。

この論文 は、お手本付きのプロンプトで書式を少し変えるだけで、LLaMA-2-13B では精度に最大 76 ポイントの差が出たと報告しています。しかも、どの書式が良いかはモデルによって違い、モデル間の相関は弱いそうです。モデルを変えたら、書式も試し直す価値があります。

C3. お手本を選び直す・並べ替える お手本の候補をランダムに選び直したり、順番を入れ替えたりして、点の良いものを残します。9 章で紹介する Teach Better or Show Smarter? によると、この単純なランダム探索でも、最新の指示文の最適化に勝つことがありました。

C4. 1 文ずつ消してみる(自分たちでもすぐできる) 手引き書の文を 1 つずつ消して採点し、消しても点が下がらない文を取り除きます。GrIPS の「削除」を文単位でやるだけの簡単な方法ですが、膨らんだ手引き書の掃除に効きます。

2.4 3 つの直し方の比べ方

A. AI に書き直させる B. 組み合わせで直す C. 機械的な規則で直す
新しい表現が生まれるか 生まれる 生まれない(あるものを選ぶ) ほぼ生まれない(言い換えだけ)
1 回の費用 高い(添削係の LLM を呼ぶ) 安い(採点だけ) 安い(採点だけ)
何を変えたかの追いやすさ 差分を読めば分かるが、理由は AI 任せ 完全に追える 完全に追える
起きやすい問題 膨らむ、方針を書き換える、データの癖を覚える 候補にない改善はできない 意味の通らない文になる
向いている場面 何が足りないか分からないとき 部品の候補がそろっているとき 仕上げ、掃除、モデルを変えたときの調整

実際のツールは、これらを組み合わせています。MIPROv2 は「A3 で案を作り、B1 で組み合わせを探す」、GEPA は「A1 で書き直し、B2 で良いとこ取りする」です。以下、ツールごとに中身を見ていきます。

3. MIPROv2(DSPy):点数を頼りに、組み合わせを探す

DSPy では、処理を「入力と出力を宣言した部品」の組み合わせとして書きます。たとえば「問い合わせを分類する部品」と「返信を書く部品」です。MIPROv2 は、各部品の指示文と**例示(お手本の入出力)**を探します。

1 回の最適化は 3 段で進みます(ソースのコメントでも Step 1〜3 と書かれています)。

  1. お手本を集める。 練習問題をいまのプロンプトで解かせ、うまくいった実行の入出力をお手本の候補として集めます。
  2. 指示文の候補を書かせる。 添削係に「データの傾向の要約」「処理のコード」「いま集めたお手本」に加えて、ランダムに選んだ書き方のヒントを 1 つ渡して指示文の案を書かせます。ヒントはソースに 5 種類あり、「創造的に」「明確で簡潔に」「詳しく説明的に」「重大な場面を想定させる」「役柄を与える(あなたは〜です)」です。ヒントを変えながら、案をいくつも作ります。
  3. 組み合わせを探す。 「どの指示文 × どのお手本」が一番点が高いかを、Optuna(ベイズ最適化の一種)で効率よく探します。

ポイントは、MIPROv2 の添削係は「なぜ間違えたか」を読まないことです。手掛かりは点数だけです。そのぶん採点関数は点数を返すだけで済み、準備が簡単です。お手本の選び方が効く課題に向いています。

4. GEPA:赤ペンを読んで、1 つずつ書き直す

GEPA は DSPy に dspy.GEPA として入っているほか、単体の gepa パッケージとしても使えます。MIPROv2 との一番の違いは、添削係が「なぜ間違えたか」の文章を読むことです。

4.1 1 回の改善で起きること

GEPA の 1 回の改善は、次の流れです。

  1. 土台を選ぶ。 これまでに作った手引き書の中から 1 つ選びます。既定では「どれか 1 問で一番点を取ったことがある手引き書」の中から選びます(パレート前線)。平均点が一番のものだけを残すと、「返品の問い合わせだけは得意」といった手引き書が消えてしまうからです。
  2. 直す部品を選ぶ。 部品が複数あるときは、既定では 1 つずつ順番に直します(round_robin)。全部まとめて直す設定(all)もあります。
  3. 少しだけ解かせる。 練習問題を数問(DSPy の既定は 3 問)解かせ、答えと点数に加えて実行の記録を取ります。満点だった問題は、学ぶことがないので既定では添削の材料から外します。
  4. 添削係が書き直す。 添削係には、「いまの手引き書」と「問題・答え・赤ペン」の組を見せます。既定の指示を要約すると、次のとおりです。
    • 入力をよく読んで、どんな形式の課題かを推測する
    • 答えと赤ペンを全部読み、業務に固有の事実はすべて新しい手引き書に書き込む(今後は手に入らないかもしれないから)
    • 一般化できる解き方があれば、それも書き込む
  5. 比べて採用する。 同じ数問を新しい手引き書で解かせ、合計点が上がっていれば、検証用の問題全体でも採点して、手引き書の候補に加えます。
  6. 予算(評価の回数や費用)がなくなるまで繰り返します。良い候補同士を合成する機能もあります。

4.2 赤ペンの書き方で、結果が変わる

GEPA の効き目は、採点関数が返す**赤ペン(文章のフィードバック)**の質で大きく変わります。DSPy では、採点関数が点数と理由の両方を返します。

def metric(gold, pred, trace=None, pred_name=None, pred_trace=None):
    problems = []
    if gold.order_id not in pred.reply:
        problems.append(f"注文番号 {gold.order_id} を引用していない")
    if "返金いたします" in pred.reply and not gold.refund_allowed:
        problems.append("方針で認められていない返金を約束している")
    score = 1.0 if not problems else 0.0
    feedback = "問題なし" if not problems else "。".join(problems)
    return dspy.Prediction(score=score, feedback=feedback)

点数だけなら「0 点だった」しか伝わりません。理由があれば、添削係は「注文番号を必ず冒頭で引用する」「返金は方針を確認してから」と、手引き書のどこを直せばいいかが分かります。

4.3 採点関数の作り方 — 機械で判定するか、AI に判定させるか

4.2 の例は、機械で判定できる 2 項目だけでした。実際の業務では「客の要望に答えているか」「丁寧か」のように、機械では判定できない観点も混ざります。ここでは、自動改善に渡す採点関数を組み立てる手順を、実装する人の目線で説明します。MIPROv2 でも GEPA でも作り方は同じです(MIPROv2 は理由の文章を使わないだけです)。

手順 1:観点に分け、観点ごとに判定方法を決める

採点関数を 1 つの大きな判定として書かず、観点ごとの小さな判定の集まりとして書きます。DSPy の GEPA の文書も、点数を観点ごとの成分(正しさ、遅さ、費用、安全など)に分け、誤りを処理のどの段で起きたかに結び付けることを勧めています。

判定方法を選ぶ順番は、Anthropic の評価の手引きの考え方に従うのが分かりやすいです。速く、確実で、数を増やしやすい方法から選ぶ。つまり、

  1. 機械で判定できる観点は、必ず機械で判定する。 速く、毎回同じ結果になり、費用もかかりません。
  2. 機械で判定できない観点だけ、AI(採点役の LLM)に判定させる。 柔軟ですが、信頼できるかを先に確かめてから使います。
  3. 人の判定は、自動改善の繰り返しの中には入れない。 遅く高いので、採点役の AI を確かめる側に回します(手順 6)。

EC の問い合わせ対応なら、たとえば次のように分けます。

観点 判定方法 扱い 落ちたときの赤ペンの例
方針にない返金を約束していないか 機械(禁止表現)+ AI(言い換えの検出) 足切り 「『全額お返しします』は、方針で認められていない返金の約束」
出力が決めた形(分類と返信文の JSON)になっているか 機械(形の検査) 足切り 「category の欄が無い」
注文番号を引用したか 機械(文字列を含むか) 点数に入れる 「注文番号 A-1029 を引用していない」
配送予定日が注文データと合っているか 機械(データとの突き合わせ) 点数に入れる 「予定日を 11 月 8 日と書いたが、注文データでは 11 月 9 日」
問い合わせの分類が正しいか 機械(ラベルの一致) 点数に入れる 「返品の問い合わせを、配送に分類した」
客の要望(期限や条件)に答えたか AI 点数に入れる 「明日までに必要と書かれているのに、間に合わない場合の代替案が無い」
丁寧で、読みやすいか AI 点数に入れる(軽め) 「謝罪の前に規約の説明が続き、冷たい印象」

「足切り」は、一つでも違反があれば他がどれだけ良くても 0 点にする観点です。勝手な返金の約束のように、平均点で埋め合わせてはいけない失敗に使います。

手順 2:機械で判定する — 何を用意し、どう比べるか

機械で判定するには、練習問題の側に期待する値を持たせておき、出力と比べます。比べ方は大きく 3 通りです。

比べ方 何と何を比べるか 使う観点
A. 欄の値の一致 出力の JSON の欄と、期待する値 分類、有人対応に回したか、呼んだ道具
B. 文章の部分一致・正規表現 返信文と、含むべき語・含んではいけない語 注文番号の引用、禁止表現
C. 文章から抜き出して、値で比べる 返信文から抜き出した日付・金額と、注文データ お届け予定日、金額

確かなのは A、C、B の順です。そこで、判定したい値は、できるだけ出力の JSON の欄として出させます。返信文の中に埋もれた値を後から探すより、AI に category や escalate の欄を別に書かせて比べるほうが、ずっと確実です。返信文そのものは客に送る文章なので、B と C で見るしかありません。

練習問題と出力の例

練習問題は 1 問を 1 行の JSON にして並べます(JSONL)。見やすく改行すると、次のような形です。

{
  "id": "q-0012",
  "inquiry": "注文 A-1029 がまだ届きません。明日の会議で使うので、明日までに必要です。",
  "order": {"order_id": "A-1029", "status": "shipped", "eta": "2026-11-09", "amount": 12800},
  "category": "配送",
  "escalate": false,
  "must_include": ["A-1029"],
  "must_not_include": ["返金いたします", "全額お返し"],
  "refund_allowed": false
}

期待する値は、それぞれ出どころが違います。

採点する AI の出力も、JSON の形に決めておきます。

{"category": "配送", "escalate": false, "reply": "ご注文 A-1029 は 11 月 9 日にお届け予定です。…"}
形の検査:まず、比べられる形になっているか

比べる前に、出力が決めた形になっているかを確かめます。Python なら jsonschema で、欄の有無・型・取りうる値・文字数をまとめて検査できます。DSPy のように、出力の欄を宣言すれば読み取りまでしてくれる道具を使うときは、この部分は道具に任せられます。

import json
from jsonschema import Draft202012Validator

OUTPUT_SCHEMA = {
    "type": "object",
    "required": ["category", "escalate", "reply"],
    "properties": {
        "category": {"enum": ["配送", "返品", "支払い", "在庫", "その他"]},
        "escalate": {"type": "boolean"},
        "reply": {"type": "string", "minLength": 1, "maxLength": 600},
    },
}
validator = Draft202012Validator(OUTPUT_SCHEMA)

def schema_problems(raw_output):
    try:
        output = json.loads(raw_output)
    except json.JSONDecodeError as e:
        return None, [f"JSON として読めない: {e.msg}({e.pos} 文字目)"]
    errors = [f"{'.'.join(map(str, err.path)) or '全体'}: {err.message}" for err in validator.iter_errors(output)]
    return output, errors

{"category": "返金", "reply": ""} を渡すと、次の 3 つが返ります。これをそのまま赤ペンにします。

全体: 'escalate' is a required property
category: '返金' is not one of ['配送', '返品', '支払い', '在庫', 'その他']
reply: '' should be non-empty
A. 欄の値の一致

形が通れば、欄同士を == で比べるだけです。取りうる値を形の検査で絞ってあるので、表記の揺れを気にせず完全一致で比べられます。

if output["category"] != gold["category"]:
    problems.append(f"{gold['category']} の問い合わせを {output['category']} に分類した")
if gold["escalate"] and not output["escalate"]:
    problems.append("有人対応に回すべき問い合わせを、AI だけで返信した")

有人対応は、回すべきものを回さなかった失敗のほうが重いので、逆向きの失敗(回さなくてよいものを回した)とは別に数えます。

B. 文章の部分一致・正規表現

返信文に「含むべき語」があるか、「含んではいけない語」が無いかを見ます。比べる前に、両方の文章を正規化します。全角と半角、空白や改行の違いで、同じ語を見逃さないためです。

import re
import unicodedata

def normalize(text):
    text = unicodedata.normalize("NFKC", text)  # 全角の英数字・記号を半角にそろえる
    return re.sub(r"\s+", "", text)              # 空白と改行を消す

def missing_phrases(reply, phrases):
    body = normalize(reply)
    return [p for p in phrases if normalize(p) not in body]

def found_phrases(reply, phrases):
    body = normalize(reply)
    return [p for p in phrases if normalize(p) in body]

これで、返信文が「ご注文 A-1029 は」と全角で書いていても、A-1029 を含むと判定できます。

禁止表現は、語の一覧だけだと言い換えを逃します。たとえば「代金はすべてお返しします」は、must_not_include のどちらにも当たりません。そこで、語の組み合わせを正規表現で足します。

REFUND_PATTERN = re.compile(r"(全額|代金|料金).{0,10}(返金|お返し|返還)")

この正規表現なら「代金はすべてお返し」を捕まえます。それでも、言い換えを全部は書き切れません。足切りに使う観点は、ここに AI の判定を重ねます(手順 3)。

C. 文章から抜き出して、値で比べる

返信文に書かれた日付や金額が、注文データと合っているかを見ます。正規表現で値を抜き出し、文字列ではなく値として比べます。

DATE_PATTERN = re.compile(r"(\d{1,2})月(\d{1,2})日")

def check_eta(reply, eta):  # eta は "2026-11-09" の形
    _, month, day = (int(x) for x in eta.split("-"))
    dates = {(int(m), int(d)) for m, d in DATE_PATTERN.findall(normalize(reply))}
    if not dates:
        return f"お届け予定日に触れていない(注文データでは {month} 月 {day} 日)"
    wrong = sorted(dates - {(month, day)})
    if wrong:
        shown = "、".join(f"{m} 月 {d} 日" for m, d in wrong)
        return f"お届け予定日を {shown} と書いたが、注文データでは {month} 月 {day} 日"
    return None

返信文に「11月8日」と書いてあれば、正規化で半角になってから抜き出され、「お届け予定日を 11 月 8 日 と書いたが、注文データでは 11 月 9 日」が返ります。日付に触れていなければ、触れていないこと自体を赤ペンにします。金額も同じように ([\d,]+)円 で抜き出し、カンマを除いて数値で比べます。

抜き出しは、書き方が想定と違うと失敗します(「明後日」「9日(土)」など)。本当に大事な値なら、出力の JSON に eta の欄も書かせて A で比べ、返信文とその欄が食い違っていないかを C で見る、という二段にします。

道具を使うエージェントの場合

道具を使うエージェントでは、返信文に加えてどの道具を、どんな引数で呼んだかの記録を比べます。練習問題には、呼ぶべき道具と、呼んではいけない道具を書いておきます。

"expected_calls": [{"name": "lookup_order", "args": {"order_id": "A-1029"}}],
"forbidden_tools": ["issue_refund"]

実行の記録から呼んだ道具の名前と引数を取り出し、呼ぶべき呼び出しが(順番は問わずに)含まれているか、禁止の道具を呼んでいないかを比べます。引数は欄ごとに完全一致で比べます。

実行して確かめる

出力がコードや SQL なら、文字列ではなく実行結果で比べます。コードはテストを走らせ、落ちたテストの名前とエラー文を赤ペンにします。SQL は、正解の SQL と出力の SQL を同じ試験用のデータベースで実行し、結果の行を比べます(順序を問わないなら並べ替えてから比べます)。書き方が違っても結果が同じなら正解にできます。

機械の判定のまとめ

機械の判定は、観点ごとに「通った/落ちた」の 2 値で十分です。大事なのは点数より、落ちたときに何を返すかです。上の例のように、期待した値と実際の値を両方入れた文を返します。DSPy の GEPA の文書も、まず手元にある記録・テスト・検査の出力を赤ペンに使うことを勧めています。

手順 3:AI に判定させる — 判断が要る観点

採点役の LLM は、次の 6 点を守って作ります。

  1. 観点ごとに、具体的な基準を書く。 「丁寧か」ではなく、満たす条件と満たさない条件を書きます。Anthropic の手引きの例は「最初の文で社名に触れていなければ、自動的に不正解とする」という書き方です。満たす例と満たさない例を 1 つずつ添えると、判定がぶれにくくなります。
  2. 答えの形を決める。 自由に感想を書かせると、集計できません。Anthropic の手引きは「正解」「不正解」だけを答えさせるか、1〜5 の段階で答えさせることを勧めています。本稿では観点ごとの 2 値(満たす/満たさない)を勧めます。根拠と合わせて、そのまま赤ペンになり、人の判定とも比べやすいからです。
  3. 判定の前に考えさせる。 Anthropic の手引きは、採点役に考えてから点を出させると、複雑な判断で精度が上がるとしています。DSPy なら、出力の欄を「根拠」→「判定」の順に並べれば、根拠を先に書かせられます。
  4. 答えを作ったモデルとは別のモデルに採点させる。 Anthropic の手引きのコード例にも、その旨の注記があります。LLM の採点役を調べた Zheng ほか(2023) は、採点役の偏りとして、位置の偏り(並べて比べると、置いた順番で選ぶ答えが変わる)、長さの偏り(長い答えを好む)、自分びいき(自分が書いた答えを好む)を挙げています。
  5. 偏りを避ける作りにする。 1 件ずつ基準に照らして判定させれば、位置の偏りは起きにくくなります。2 案を比べさせるときは、順番を入れ替えて 2 回判定し、結果が食い違ったら引き分けにします。長さは「長さは評価しない」と基準に書いたうえで、文字数は機械で別に見ます。
  6. 採点役は、直す対象の外に置く。 採点役の指示まで自動改善で直されると、点数の意味が変わってしまいます。DSPy では、採点役を採点関数の中で呼べば、GEPA が直す部品には入りません。

参照回答(担当者が実際に送った返信など)があるなら、採点役に参照回答と比べさせる方法もあります。DSPy の SemanticF1 は、LLM に参照回答と出力それぞれの要点を挙げさせ、参照回答の要点をどれだけ拾えたか(再現率)と、出力の要点がどれだけ参照回答に裏付けられるか(適合率)から点数を出します。文章の埋め込みの近さだけで比べる方法は、点数しか出ず理由を返せないので、自動改善には向きません。

DSPy で「客の要望に答えたか」を判定する採点役は、たとえば次のように書けます。指示は、クラスの説明文(docstring)に書きます。

class AnsweredRequest(dspy.Signature):
    """問い合わせ担当の返信を、次の基準で判定する。
    基準: 客が期限や条件を示しているなら、それに間に合うかを答え、間に合わない場合は代替案を示している。
    満たす例: 「明日の午前に届く予定です。間に合わない場合は、店舗受け取りに変更できます」
    満たさない例: 期限に触れず「発送済みです」とだけ答える
    返信の長さは評価しない。"""

    inquiry: str = dspy.InputField()
    reply: str = dspy.InputField()
    reason: str = dspy.OutputField(desc="判定の根拠。返信から該当する文を引用する")
    satisfied: bool = dspy.OutputField(desc="基準を満たすか")

answered_request = dspy.Predict(AnsweredRequest)

手順 4:点数にまとめる

観点ごとの判定を、自動改善に渡す 1 つの点数にまとめます。

DSPy の GEPA では、合計の点数に加えて観点ごとの点数(objective_scores)も返せます。frontier_type="objective" を指定すると、観点ごとに一番良い手引き書も候補として残します。ただし、採用するかどうかと最終的な選択は、合計の点数で決まります。

ここまでをまとめた採点関数は、次のようになります。

judge_lm = dspy.LM("anthropic/claude-sonnet-5-5")  # 答えを作るモデルとは別にする
WEIGHTS = {"必須の語": 0.2, "予定日": 0.2, "分類": 0.2, "要望への回答": 0.4}

def metric(gold, pred, trace=None, pred_name=None, pred_trace=None):
    promised = find_refund_promise(pred.reply)  # 手順 2 の禁止表現と正規表現に、AI の判定を重ねた関数
    if promised and not gold.refund_allowed:
        return dspy.Prediction(
            score=0.0,
            feedback=f"方針で認められていない返金を約束している:「{promised}」。返金は方針を確認してから案内する",
        )

    passed, problems = {}, []
    missing = missing_phrases(pred.reply, gold.must_include)
    passed["必須の語"] = not missing
    if missing:
        problems.append(f"{'、'.join(missing)} を引用していない")
    eta_problem = check_eta(pred.reply, gold.order["eta"])
    passed["予定日"] = eta_problem is None
    if eta_problem:
        problems.append(eta_problem)
    passed["分類"] = pred.category == gold.category
    if not passed["分類"]:
        problems.append(f"{gold.category} の問い合わせを {pred.category} に分類した")
    with dspy.context(lm=judge_lm):
        verdict = answered_request(inquiry=gold.inquiry, reply=pred.reply)
    passed["要望への回答"] = verdict.satisfied
    if not verdict.satisfied:
        problems.append(f"客の要望に答えていない: {verdict.reason}")

    score = sum(WEIGHTS[name] for name, ok in passed.items() if ok)
    feedback = "問題なし" if not problems else "。".join(problems)
    return dspy.Prediction(score=score, feedback=feedback)

手順 5:赤ペンを書く

赤ペンは、添削係が手引き書のどこを直せばいいか分かるように書きます。何が、どこで、本来はどうあるべきかの 3 つを入れます。

悪い赤ペン 良い赤ペン
「不正解」 「注文番号 A-1029 を引用していない」
「点数 0.4」 「返品の問い合わせを配送に分類した。『交換』『返品』の語があれば返品に分類する」
「不親切」 「明日までに必要と書かれているのに、間に合わない場合の代替案が無い」

DSPy の GEPA の文書も、検索の課題なら「再現率」の数字だけでなく、正しく拾えた文書、誤って拾った文書、取りこぼした文書を列挙するよう勧めています。

注意が 2 つあります。

手順 6:採点役を確かめてから回す

Anthropic の手引きは、LLM による採点を「まず信頼できるかを試し、それから規模を広げる」としています。Zheng ほかの論文では、強い採点役の判定は人の好みと 8 割を超えて一致し、人同士の一致と同じ水準でした。ただ、これは一般的な会話の良し悪しでの結果です。自分たちの業務の基準で同じだけ一致するかは、自分で測るしかありません。

  1. 練習問題の一部について、担当者に観点ごとの判定を付けてもらう。
  2. 同じ問題を採点役の LLM に判定させ、観点ごとに一致する割合を見る。
  3. 一致しない観点は、基準を具体的に書き直すか、機械で判定できる小さな観点に分け直す。それでも一致しなければ、その観点は自動改善の点数から外す。

もう一つ、自動改善は、採点の甘いところを突いてくると考えておきます。自動改善は採点の点数を上げるように手引き書を直すので、採点役が見逃す書き方があれば、そちらへ寄っていきます。最後に、改善後の手引き書の答えを人が何件か読んで確かめます。人の判定の付け方や、採点基準の作り方は 練習問題の自動生成の記事 でも扱っています。

判定方法のまとめ

判定方法 速さ・費用 確かさ 返せる赤ペン 向いている観点
機械(文字列・形・データ・ラベル・実行) 速い・ほぼ無料 毎回同じ。言い換えには弱い 落ちた規則と箇所 引用、形式、数値、分類、禁止表現
参照回答と AI で比べる 中 参照回答の質しだい 拾えた要点、抜けた要点 答えの内容が決まっている質問
AI に基準で判定させる 中 人との一致を測るまでは信用しない 判定の根拠 要望への答え方、丁寧さ、根拠の妥当性
人の判定 遅い・高い 最も高い(人によるぶれはある) 担当者のコメント 採点役を確かめる、基準を作る

4.4 放っておくと手引き書が膨らむ

既定の指示に「業務に固有の事実はすべて書き込む」とあるので、反復を重ねると手引き書は長くなっていきます。GEPA を本番に入れた Decagon の報告でも、反復とデータを増やすほど例外を詰め込んだ冗長なプロンプトになり、汎化しなくなったと書かれていました。

DSPy では、添削係に次の歯止めをかけられます(InstructionProposer の設定)。

それでも足りなければ、添削係そのものを自作して差し替えられます。

5. 道具を使うエージェントの場合

道具を使うエージェントでも、直すのは文章だけです。道具の実装(注文を検索するコードなど)は変わりません。変わるのは「いつ、どう使うか」を AI に伝える文章です。

6. スキル文書や、任意の文章の場合

gepa には、どんな文章でも直せる入口 optimize_anything があります。渡すのは、直したい文章と、(点数, 情報) を返す採点関数だけです。情報の部分が赤ペンにあたり、gepa の文書では「次に何を変えるかを決めるための、行動につながる補足情報」と呼んでいます。

from gepa.optimize_anything import OptimizeAnythingConfig, optimize_anything

def evaluate(candidate, example):
    reply = run_support_bot(system_prompt=candidate, inquiry=example["inquiry"])
    score, problems = grade(reply, example)  # 自分で書く採点
    return score, {"問題点": problems, "返信": reply}

result = optimize_anything(
    "あなたは EC の問い合わせ担当です。…",
    evaluator=evaluate,
    dataset=train_examples,
    valset=val_examples,
    objective="方針を守りつつ、問い合わせを 1 通で解決できる返信を書く",
    config=OptimizeAnythingConfig(engine="gepa", max_evals=200),
)
print(result.best_candidate)

(run_support_bot と grade は自分のシステムに合わせて書く部分です。添削係のモデルの設定などは省略しています)

この仕組みでスキル文書を育てた例が、gepa に同梱されている gskill です。

  1. 対象のリポジトリの実際のコミットにバグを仕込んで、答え合わせのできる課題を大量に作る(SWE-smith を使用)
  2. 空のスキルから始めて、コーディングエージェントに課題を解かせる。合否・実行の記録・テストの出力を添削係に渡し、スキルを書き直す
  3. 一番良かったスキルを best_skills.txt として出力し、エージェントのシステムプロンプトに入れて使う

「答え合わせのできる課題を自動で作る」ところまで含めているのが要点です。スキルの自動改善で一番難しいのは、書き直しではなく採点のほうだからです。

7. TextGrad:機械学習の書き方で、文章を直す

TextGrad は、PyTorch の書き方をそのまま文章に当てはめたライブラリです。

import textgrad as tg

tg.set_backward_engine("gpt-4o")
system_prompt = tg.Variable("あなたは EC の問い合わせ担当です。…",
                            requires_grad=True,
                            role_description="問い合わせ対応 AI へのシステムプロンプト")
model = tg.BlackboxLLM("gpt-4o", system_prompt=system_prompt)
loss_fn = tg.TextLoss("返信を評価し、方針違反と情報の不足だけを簡潔に指摘せよ")
optimizer = tg.TGD(parameters=list(model.parameters()))

reply = model(inquiry)  # inquiry は問い合わせ文の Variable
loss = loss_fn(reply)   # 文章で書かれた「損失」=批評
loss.backward()         # 批評を、直したい文章まで逆向きに伝える
optimizer.step()        # 批評に沿って文章を書き直す

「損失」が文章の批評で、「勾配」が「どう直すべきか」の文章です。1 つの答えをその場で磨くことにも、プロンプトを直すことにも使えます。仕組みは分かりやすい一方、前回の記事 で紹介したとおり「勾配のたとえでは挙動を説明できない」という分析もあります。リポジトリの最終更新は 2025 年 7 月で、活発に開発が続いているのは GEPA のほうです。

8. 3 つの違いを一言で

添削係が読むもの 直し方 準備の重さ
MIPROv2 点数だけ 指示文の案とお手本を大量に作り、組み合わせを探す 軽い(点数を返す採点だけ)
GEPA 点数+赤ペン+実行の記録 失敗を読んで、1 つずつ書き直す 中(赤ペンを書く採点が要る)
TextGrad 文章の批評 批評を逆向きに伝えて書き直す 中(批評の指示を書く)

9. 論文から分かる「良い直し方」

ツールの添削係は、どれも汎用の指示で動いています。自分たちで仕組みを作るなら、もっと良い直し方を組み込めます。プロンプトの直し方そのものを研究した論文から、使えそうな知見を集めました(数値は各論文の要旨より)。

1. 添削係への指示を手厚くする(PE2、ACL 2024 Findings) プロンプトの改善には「モデルの誤りを調べ、いまのプロンプトに何が欠けているか、何が誤解を招いているかの仮説を立て、課題を明確に伝える」という複雑な推論が要ります。この論文は、添削係への指示に「課題の詳しい説明」「文脈の明示」「段階を踏んだ推論の型」を入れることで、狙いを絞った書き直しができるようになったと報告しています。

2. 指示文よりも、お手本の選び方が効くことがある(Teach Better or Show Smarter?、NeurIPS 2024) 指示文を最適化するより、お手本(例示)をどう選ぶかのほうが効く場合があり、ランダムに選び直すだけでも最新の指示文最適化に勝つことがあったそうです。また、検証のときにモデル自身が出した良い答えをお手本として再利用すると、指示文の最適化にさらに上乗せできます。両方を組み合わせるのが一番です。

3. 失敗を分類してから直す(ETGPO、2026 年 2 月) 1 問ずつの失敗から直していくと、全体が見えなくなります。この論文は、失敗を集めて種類ごとに分類し、一番多い種類の失敗に対する指針をプロンプトに足す、という上からの直し方を提案しています。最新手法と同等以上の精度を、およそ 3 分の 1 の費用で出したと報告しています。

4. 全体ではなく、部分ごとに直す(SAPO、2026 年 7 月) プロンプト全体を書き直すと、ある振る舞いが良くなる一方で別の振る舞いが悪くなります。この論文はプロンプトを「役割」「文脈」「課題」「出力形式」に分け、点の高かった 5 問と低かった 5 問を見て、弱い部分だけを直します。GEPA などの手法を平均で上回ったと報告しています。

5. 平均点の裏で、正解が不正解に変わっている(RFDPO、2026 年 10 月) 平均点が少し上がっても、その裏では「不正解→正解」と「正解→不正解」が大量に入れ替わっています。調べた 9 つの構成のうち 5 つで、答えの入れ替わりの数が平均の正味の変化の 5 倍以上でした。検証用データでの改善(平均 7.9 ポイント)は、未使用の試験用データでは 5.2 ポイントに縮みました。**問題ごとに「どれが新たに解けて、どれが解けなくなったか」**を添削係に伝えると、正味の改善は同程度のまま、入れ替わりが 4 割減ったそうです(法律の課題での比較実験)。

6. 問題を増やせばいいわけではない(p1、2026 年 4 月) 最適化が効くのは、プロンプトの違いで点が大きく変わるときです。毎回の答えのばらつきのほうが大きいと、効きません。問題を増やすと、かえって手引き書の良し悪しの差が見えにくくなることもあります。この論文は、手引き書によって点が大きく変わる問題だけを選んで最適化に使う方法を提案しています。

7. 実行するモデルに合わせる(MePO、2025 年 5 月) 強いモデルが書いた指示だらけのプロンプトは、軽いモデルには重すぎて、かえって答えが悪くなることがあります。添削係に強いモデルを使うときほど、実行する側のモデルで確かめます。

8. モデルを比べるなら、それぞれ最適化してから(Optimization before Evaluation) 同じプロンプトで複数のモデルを比べると、順位が実態と変わることがあります。モデルごとにプロンプトを最適化してから比べると、最終的な順位が大きく変わったと報告しています。

10. 自分たちでできること

ここまでを踏まえて、段階ごとにまとめます。どの段階も、評価ツールの記事 で書いた評価の仕組みが土台です。

段階 0:直す準備をする(ツールなしで今日からできる)

段階 1:手で 1 周まわしてみる

いきなりツールを入れず、添削係の仕事を人と AI でやってみます。

  1. 練習問題をいまのプロンプトで解かせ、採点する
  2. 失敗した問題の「問題・答え・理由」を集め、Claude などに失敗を種類ごとに分類させる(ETGPO の方法)
  3. 一番多い種類の失敗について、該当する部品だけを直させる(SAPO の方法)
  4. もう一度解かせて、新たに解けた問題と、解けなくなった問題の両方を確かめる(RFDPO の方法)

1 周まわすと、採点の甘いところや、練習問題の偏りが必ず見つかります。自動化は、この手作業が回るようになってからで十分です。

段階 2:既製のツールに任せる

手作業の 1 周で採点が固まったら、GEPA に任せます。

段階 3:自分たちの添削係を作る

既製の添削係は汎用です。自分たちの業務向けに、論文の知見を組み込んだ添削係を作れます。

組み込む工夫 参考
失敗を分類して、多い順に直す ETGPO
部分ごとに直し、方針の部分には触らない SAPO
新たに解けなくなった問題を毎回伝え、それが出たら採用しない RFDPO
誤りの調査 → 欠けている点の仮説 → 書き直し、の順に考えさせる PE2
良かった実行をお手本として貯めて使い回す Teach Better or Show Smarter?
手引き書の良し悪しで差が出る問題を優先して使う p1
1 回の変更量に上限を設け、却下した案も記録する 前回の記事の SkillOpt
手引き書を規則の箇条書きで持ち、1 つずつ外して効いていない規則を消す 2 章の B3・C4
モデルを変えたら、書式の候補も試し直す FormatSpread

gepa は、添削係(custom_candidate_proposer)だけを自作して差し替えられる作りになっています。探索や候補の管理は gepa に任せ、書き直しの部分だけに自分たちの工夫を入れるのが、手間と効果の釣り合いが良いやり方です。

段階 4:スキルも同じように直す

Claude Code のスキルも、仕組みは同じです。

  1. スキルの出来を測る試験を作る(claude plugin eval などで、スキルを使った作業の合否を判定する)
  2. 失敗した作業の記録を集め、段階 1 と同じ手順でスキルの該当部分を直す
  3. 新たに失敗するようになった作業が無いことを確かめてから採用する

gskill が示しているとおり、一番手間がかかるのは「答え合わせのできる試験を用意すること」です。逆に言えば、そこさえ用意できれば、書き直しは自動化できます。

まとめ

参考資料

ツールのソースと文書

手法の論文

採点の論文

プロンプトの直し方の論文

関連記事


Singularity Society はテクノロジー集団として MulmoClaude / MulmoTerminal / MulmoCast を開発しています。エンジニア・起業家向けの実践プログラム BootCamp も運営しています。

この記事をシェア

関連記事

記事一覧に戻る