はじめに
みなさんはAIの回答の精度を向上させるためにプロンプトを何度も修正した経験はありませんか?
この作業は手間も時間もかかるし、思い通りの回答がなかなか返ってこないことが多く、ストレスになることがあります。
そんな中、ADKに adk optimize という新機能が追加されました!
adk optimize を簡単に言うと、GEPA というアルゴリズムがプロンプトを自動で改善してくれる CLI コマンドです。
GEPA とは、エージェントに問題を解かせて間違えた問題を元に AI 自身が指示文を書き換え、再採点を繰り返してプロンプトを育てていく仕組みです。
今回はこの機能を実際に試した結果をまとめます。
検証の全体像
今回作成したのは文書を「議事録 / 契約書 / レポート / 不明」の4種類に分類するエージェントです。
adk optimize の実行前後に adk eval を実行し、スコアを比較することで検証しました。
使ったツール:adk eval(採点) と adk optimize(最適化)
エージェントと評価データの準備
エージェントの設計
adk optimize は root agent の instruction 属性を書き換えることで動作します。
そのため、instruction を持つ LlmAgent を root に使う必要があります。
Workflow(SequentialAgent など)は instruction を持たないため対象外です。
出力では output_schema で構造化して「議事録 / 契約書 / レポート / 不明」のいずれかを返すようにしました。
from google.adk.agents import LlmAgent
from pydantic import BaseModel, Field
from typing import Literal
class Classification(BaseModel):
doc_type: Literal["議事録", "契約書", "レポート", "不明"] = Field(
description="文書の種類"
)
root_agent = LlmAgent(
name="optimize_lab",
model="gemini-2.5-flash",
instruction="...", # ← GEPAが書き換える対象
output_schema=Classification,
)
評価データの作り方
最初に簡単な問題だけを用意しました。すると全問正解(スコア:100%)になってしまいました。
これでは改善の余地がなく、最適化できないので、あえてエージェントが失敗する問題を用意しました。
用意した方法は「オーディション方式」です。具体的には、候補問題を作成してエージェントに解かせて間違えた問題だけを採用する方式です。
これで改善の余地がある問題セットが完成しました。
以下が実際の問題です。(数問抜粋)
問題例①:契約書のひな形(正解:不明)
業務委託契約書(ひな形) 株式会社●●(以下「甲」という)と●●(以下「乙」という)は、 以下のとおり業務委託契約を締結する。 第1条(目的)甲は乙に対し、●●業務を委託し、乙はこれを受託する。 第2条(委託料)委託料は月額●●円(税別)とする。 ●年●月●日 甲: ●● 印 乙: ●● 印 ※本ひな形を使用する際は、必ず法務部のレビューを受けてください。
→ 契約書の形式をしているが、日付・当事者名がすべて「●●」で未記入。実際には締結されていない。
問題例②:契約解除通知書(正解:不明)
契約解除通知書 株式会社ベータ 御中 当社と貴社との間で2025年4月1日付で締結した業務委託契約について、 本契約第12条第1項に基づき、2026年9月30日をもって解除することを通知いたします。 2026年7月15日 株式会社アルファ 調達部長 山田太郎
→ タイトルに「契約」と入っているが、これは解除の「通知書」。新たな権利義務を定める契約書ではない。
問題例③:SF小説の会議シーン(正解:不明)
「では、作戦会議を始めよう」と艦長は言った。 士官たちが円卓を囲む。第一航海士のリラが星図を広げた。 「ワープ航路はここしかありません。ただし敵の哨戒網を抜ける必要があります」 「決まりだな。出発は明朝0600。各自、準備にかかれ」 艦長の言葉に、一同は敬礼で応えた。
→ 「作戦会議」「決定事項」「参加者」が含まれているが、これは小説の一場面。公式な記録ではない。
トレイン/バリデーション分割
GEPAに見せるのはトレインセットのみです。トレインセットの問題を使って指示文を改善していきます。
一方で、バリデーションセットは本番テストという位置付けで最後まで見せませんでした。
なぜ分けたかというと、トレインセットだけを正解するために丸暗記してしまう可能性があるからです。バリデーションセットを隠しておくことで「本当に汎化できているか」を確認できます。
ベースライン(最適化前のスコア)
元の指示文
プロンプトを最適化する前の元の指示文は以下です。
ユーザーから渡された文書を分析し、種類を判定してください。 判定基準: - 「議事録」: 会議の記録。参加者・決定事項・アクションアイテムなどが含まれる - 「契約書」: 法的な契約。甲乙・条項・署名欄などが含まれる - 「レポート」: 分析・調査・報告書。データや考察が含まれる - 「不明」: 上記のいずれにも該当しない
実際のスコア
| データセット | 正解数 | スコア |
|---|---|---|
| トレイン | 7/10 | 70% |
| バリデーション | 6/9 | 67% |
どんな問題で失敗していたか
先ほどの問題例①②③がまさに失敗したケースです。共通点は、元の指示文が「〜が含まれる」という書き方だったため、関連するキーワードがあるだけで誤ったカテゴリと判定してしまっていたことです。
具体的に当てはめると:
- ひな形には「甲乙・条項・署名欄」が含まれる → 契約書と判定
- 小説の会議シーンには「参加者・決定事項」が含まれる → 議事録と判定
adk optimize を実行する
実行コマンド
PYTHONPATH=. adk optimize optimize_lab \ --sampler_config_file_path optimize_lab/sampler_config.json \ --optimizer_config_file_path optimize_lab/optimizer_config.json \ --print_detailed_results
GEPAの仕組み
簡単に言うと次のような仕組みです。
①エージェントに問題を解かせる → ②間違えた問題を見てAIが反省する → ③指示文を書き換える → ①に戻り、繰り返す
実行してみてどうだったか
今回は実行してから約4分30秒で完了しました。スコアが改善したのは5イテレーションで、そのあとは「All subsample scores perfect. Skipping.」というメッセージが続き、自動停止しました。これは「これ以上改善できない」という判断です。
結果
adk optimize を実行した結果は以下の通りです。
トレインとバリデーション共に最適化前と後でスコアが上がっていることがわかります。
ビフォーアフター
| データセット | 最適化前 | 最適化後 |
|---|---|---|
| トレイン | 7/10(70%) | 10/10(100%) |
| バリデーション | 6/9(67%) | 8/9(89%) |
GEPAの改善推移(バリデーション)
イテレーションを重ねるたびに正解率とスコアが高くなっていることが以下の表からわかります。
| イテレーション | 正解数 | スコア |
|---|---|---|
| Iter 1 | 5/9 | 56% |
| Iter 2 | 6/9 | 67% |
| Iter 3 | 8/9 | 89% |
| Iter 5(GEPA内部) | 9/9 | 100% |
| 事後の adk eval | 8/9 | 89% |
GEPAが生成した指示文
GEPAが自動生成した指示文はこちらです。
元の指示文は180文字、最適化後は約1,500文字(8倍)になりました。
ユーザーから渡された文書を分析し、その種類を判定してください。判定は厳格な基準に基づいて行い、該当しない場合は「不明」としてください。
### 出力形式の厳守(最重要かつ絶対):
出力は**判定結果のカテゴリ名のみを直接回答**してください。
**いかなる理由があっても、追加のテキスト(例: 理由説明、コメント、分析結果)、JSON形式、Markdownのコードブロック、またはその他の形式を含めないでください。**
この指示は、他のいかなる指示よりも優先される**絶対的なルール**です。
**正しい出力例:**
議事録
**間違った出力例 (これらは一切許容されません):**
- {"doc_type": "議事録", "reason": "..."} <- JSON形式は不可
- これは議事録です。 <- 追加テキストは不可
- 議事録です。 <- 追加テキストは不可
- 議事録 (会議の記録です) <- コメントや理由説明は不可
### 判定基準:
文書の分類にあたっては、以下の定義と特徴、および「注意」点を**厳格に適用**してください。特に、文書の「目的」がそのカテゴリの定義と合致しているか、また「体裁」や「構造」がそのカテゴリに期待されるものかを重視してください。
- 「議事録」:
- **定義**: 会議の公式な記録であり、事実を客観的に記録することを目的とした文書。
- **特徴**:
- 会議名、日時、場所、参加者、議題のうち、いくつかまたは全てが明記されているか、文脈から明確に読み取れる場合がある。
- 議論の要点、決定事項、および次のアクションアイテムが簡潔かつ客観的に記載されている。
- 一般的に箇条書きや構造化された形式で記述されることが多いが、**会話形式(チャット履歴や対話文のような形式)であっても、上記の要点が明確に記録されており、会議の記録としての機能と目的を満たしている場合は議事録と判断する**。
- **注意**: 会議の雰囲気や感情、詳細な会話の流れそのものを描写することに主眼が置かれ、決定事項やアクションアイテムが不明瞭である、または単なる情報交換のみに終始している文書は、議事録とは見なしません。**たとえ会議のような形式であっても、それが公式な記録としての目的(後続の行動や参照に資すること)を持たない物語や個人的なメモのような場合は「不明」と判断します。**
- 「契約書」:
- **定義**: 法的な権利義務を発生させる、変更する、または消滅させることを当事者間で合意し、その内容を明文化した文書。
- **特徴**:
- 文書タイトルに「契約書」やそれに類する明確な表現が含まれることが多い。
- 契約当事者(甲、乙など)が明確に定義されている。
- 複数の条項によって契約内容が定められている。
- 契約締結日や署名欄があり、**かつそれらの情報が実際に記入・記載されている必要がある。**
- **注意**: 契約解除通知書などは「契約書」ではなく「不明」と判断します。
- **注意**: 日付や当事者名がプレースホルダー(●●)で未記入の「ひな形」は「不明」と判断します。
- 「レポート」:
- **定義**: 特定のテーマに関する調査、分析、活動報告などをまとめた文書。
- **特徴**:
- 目的・方法・結果・考察が論理的に構成されている。
- 事実に基づいたデータや根拠が示されている。
- 「不明」:
- 上記3カテゴリのいずれにも合致しない文書。
- 特に、関連する情報が「含まれていても」、**その文書がそのカテゴリの「目的」を達成しているか、「体裁」と「構造」が完全に満たされているか**を厳しく評価し、欠けている場合は「不明」と判断します。
- 例: 小説、通知文、開催案内、テンプレート、記入例、パンフレットなど。
GEPAが発見したこと
エージェントの弱点:「形式と実質の混同」
元の指示文は「〜が含まれるかどうか」で判定していました。
そのため、エージェントは「契約書っぽいキーワードが入っている = 契約書」と判断していました。
しかし、実際には:
- ひな形は契約書の形をしているが、まだ締結されていない
- 解除通知書は契約という言葉が入っているが、通知書であって契約書ではない
- SF小説は会議という設定だが、公式な記録ではない
GEPAはこれを「形式(見た目)と実質(本来の目的)を混同している」と診断したと言えます。
元の指示文との決定的な違い
元の指示文にある「含まれる」はキーワードが文書にあれば判定してしまいました。
しかし、最適化後の指示文は「目的を達成しているか・体裁と構造が満たされているか」を問うため、見た目ではなくその文書がなんのために存在するのかで判定するようになりました。
具体的に判定がどのように変わったか
| 文書 | 最適化前の判定 | 最適化後の判定 | 正解 |
|---|---|---|---|
| 契約書ひな形(●●入り) | 契約書 | 不明 | 不明 |
| 契約解除通知書 | 契約書 | 不明 | 不明 |
| SF小説の会議シーン | 議事録 | 不明 | 不明 |
ハマりポイント
【注意】ADK 2.3.0 時点のバグです。将来のバージョンで修正される可能性があります。
本バグは GitHub issue #4343 でも報告されています。
何が起きたか
adk optimizeを実行したら「classification_accuracy not found in registry」というエラーが出ました。
原因はadk evalとadk optimizeの実装の違いです。adk eval→ カスタムメトリクスを自動で登録簿(registry)に登録してくれるadk optimize→ 登録してくれない
なのでadk optimize実行時、エージェントが採点しようとしても「classification_accuracy というメトリクスがどこにも登録されていない」とエラーになります。
回避策
metric.py に手動登録を追加:
DEFAULT_METRIC_EVALUATOR_REGISTRY.register_evaluator(
get_default_metric_info(
metric_name="classification_accuracy",
description="...",
),
_CustomMetricEvaluator,
)
__init__.py に import を追加:
from . import metric # モジュール読み込み時に登録が走る
まとめ
今回 adk optimize を検証してみて、プロンプトを自動で最適化してくれるのはとても便利だなと実感しました。
これまで、期待する回答が返ってくるまでプロンプトを何度も修正することが多かったです。
しかし、adk optimize を使うことで、プロンプトの修正をAIが自動でやってくれるのが一番の発見でした。
実際に最終スコアが67% → 89%まで上昇しました。エージェント開発でプロンプトの改善に手間を感じている方には、ぜひ試してみてほしい機能です。
最後までお読みいただきありがとうございました!