0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIに自分のプロンプトを改善させたら、長くなりすぎたので進化的最適化してみた

0
Posted at

⚠️ この記事はAI(Claude)と一緒に作成しました。
内容は執筆時点(2026年10月)の情報です。

最近、

「自分で新しいAIシステムを一から組む」
よりも、
「既存の、もっと質のいいAIシステムをどう使うか」

が大事になってきていると感じています。(個人の感想ですが…)
そのどう使うかで大切になるのが一つにプロンプトですね。
後述するAWS記事でAIがプロンプトをどんどん改良していく考えを見てすごい面白い!と
感じたため、今回はAWSのBedrockとClaude Haikuを使ってECサポートAIに、
わざと手抜きなプロンプトを渡しタスクをやらせて失敗から学ばせて、
AI自身に自分のプロンプトを最大10世代まで自動で書き直させるという実験をしました。
元ネタはAWSが出している

「Optimizing agent system prompts with Amazon Bedrock AgentCore」

というブログで、エージェントの実行ログを「Reflector」役のAIに読ませて、
システムプロンプトを自動で書き直させる仕組みが紹介されています。

AWSには同じ仕組みを公式のAPI(AgentCore Optimization)でやってくれる機能も
ありますが、分析対象のログをCloudWatchに溜まったテレメトリから読みに行く設計に
なっています。(AgentCore Runtimeみたいな専用のデプロイ先が必要)
今回みたいにローカルでBedrockを直接叩くだけの構成だとそのログが無いので、
同じ考え方だけ真似て自分で実装しています。

結論

  • AIに自分のプロンプトを直させたら、精度は100%になったけど、
    コストと長さまで含めて評価すると「何もしなかった方がマシ」という評価だった。
    別途プロンプト圧縮のロジックを追加することで対策できた。
  • 進化的最適化を試すと同じ精度100%を上記よりも安く・短く達成できた

本記事の「精度100%」は、今回用意した評価データにおける成功率を指し、
未知のすべての入力に対する性能を保証するものではありません。

プロンプト改善の2つの方向性

(さっきの記事とは関係なく)僕自身の考えですが、
プロンプトの改善には大きく2つの方向性があると考えています。

  • ①書き直して精度を上げる:足りない指示を追加して、失敗を減らす方向
  • ②圧縮して精度を保つ:精度は変えずに、プロンプトを短く・安く・速くする方向

AWSの記事も、巷のプロンプト最適化の話も大体①の話しかしていません。
今回は①を10世代の自己改善ループで試した後、②を追加で検証しています。

①と②は全然別物で、①の仕組みをそのまま使っても②は起きませんでした。

何をやったか(ざっくり)

最初は
「ユーザーの問い合わせに対応してください」くらいの激ゆるプロンプトだけ渡しました。
これを上の図のループで繰り返し改善させるというだけの実験です(=さっきの①の方向)。
この記事では、「1体のプロンプトを直接書き換えていく方法」を便宜上
「Reflector方式」 と呼ぶことにします。

Reflector方式を試してみたら

1世代で頭打ちになった

世代 成功率 危険な操作(承認なしキャンセル等) プロンプトの長さ
Gen0(最初のポンコツ) 90.3% 9.7% 94文字
Gen1(1回直した後) 100% 0% 551文字
Gen2〜Gen9 100%(変化なし) 0% 600文字(変化なし)

1回直すだけで精度も安全性も十分に上がりました。
ところが2世代目以降、ハッシュ値まで完全に同じプロンプトが続きました。

理由は単純で、失敗がなくなったら、直すAIに渡す「失敗の情報」も無くなり、
「変える理由がないから変えない」をずっと繰り返していたからです。
10世代やる気で始めたのに、実質1回でゴールしてしまいました。

🔧 試す際は
自己改善ループを作ったら、md5sum prompt_gen_*.txt のように
各世代のプロンプトのハッシュ値を並べてみてください。
2世代以上連続で同じハッシュが続いたら、それ
「まだ改善中」ではなく「もう探索が止まっている」サインです。
ループを止めるか、別の切り口を試すタイミングです。

その膨らんだ結果を、精度を保ったまま圧縮できた

Reflector方式は精度を100%にした代わりに、プロンプトを23文字→183文字と
約8倍に膨らませました。プロンプトが長いと、入力トークンが増えますし、
Lost in the Middle問題も起きやすくなります。
そこでこの183文字のプロンプトを精度を落とさずに圧縮できるか試しました。

というかそれも自動でやってくれると思っていたのですが、
直すAIに渡していた情報が「失敗した理由」だけだったので、
2回目以降の精度100%プロンプトを渡しても「直す理由ない」とそのまま返していました。
「縮めてもいいよ」と一言も言っていなかったので、まあ当然です。

そこでMutation(プロンプトの一部を書き換える処理)に

「失敗が無い場合は精度を落とさず圧縮を試みてほしい」

という指示と、現在の文字数を明示的に渡すように直して再挑戦しました。

項目 圧縮前(Gen1) 圧縮後(最良個体)
プロンプトの長さ 183文字 105文字(-43%)
検証用データの精度 100% 100%(変化なし)
未知データの精度 100% 100%(変化なし)
スコア(後述、小さいほど良い) 0.526 0.345

結果、精度を落とさずに43%圧縮できました。
途中のCrossover(2つのプロンプトを組み合わせる処理)では
一時的に長さが戻ることもありましたが(152文字、111文字)、
良い個体だけを残す仕組み(エリート保存、詳細は次の章)のおかげで
悪化した個体は次の世代に残らず、全体としては着実に縮んでいきました。

「スコアの計算式が縮めると得だと言っている」だけでは意味が薄くて、
「縮めてほしい」と探索方法(AIへの指示)側にもちゃんと書き込む必要がある
というのが分かりました。

🔧 試す際:
Reflector役のAIに以下の2行を追加するといい感じに動きます。

- 失敗トレースが「失敗なし」の場合は、精度を落とさない前提でプロンプトの圧縮を積極的に検討してください
- 「現在のプロンプトは◯◯文字です」という情報も一緒に渡し、変更しない理由にしない

ポイントは「縮めてもいい」ではなく「失敗が無いなら縮める方を試せ」と、
デフォルトの挙動そのものを変える指示にすることです。
「縮めてもいい」だけだと、何もしない方が安全な選択肢として残ってしまいます。

進化的最適化なら、最初から賢く縮んだ

前座を通して気になったのは、

Reflector方式は「精度を上げる」と「コンパクトに保つ」を同時にはやってくれない

ということでした。
精度を上げるために全面リライトしてプロンプトが8倍に膨らみ、
それを戻すには別途「圧縮してほしい」という指示を用意してやり直す必要がありました。

そこで、

「最初から精度・コスト・長さのバランスを考えながら複数の候補を探索する方法があれば、そもそも膨らませずに済むのでは?」

と考えて、1体だけを直接書き換えるのではなく、
遺伝的アルゴリズムの考え方(Population・Selection・Mutation・Crossover・Elitism)で
プロンプトの集団を進化させる 進化的最適化(Evolutionary Prompt Optimization) で
試してみました。

Reflector方式は、「毎世代、現行プロンプトを1つの全面リライトで置き換える」方法です。
進化的最適化はこれとは違い、複数のプロンプト候補を同時に競わせながら少しずつ良くしていきます。

やっていることは4つだけです。

  • Elitism:スコアが一番良い個体は無条件で次の世代にそのまま残す
  • Mutation:1個体を選んで、失敗ログを見ながら小さく書き換える(全面リライトは×)
  • Crossover:2個体を選んで、矛盾がないか確認しながら良い指示を1つに統合する
  • Selection:個体はスコアが良いものほど選ばれやすくする

ここでの「スコア」は、精度だけじゃなく
コストとプロンプトの長さも混ぜた式で決めています。

$$
J(P) = (1 - S(P)) + 0.1 \times \frac{C(P)}{C_{ref}} + 0.05 \times \frac{L(P)}{L_{ref}}
$$

$S(P)$は精度、$C(P)$はコスト、$L(P)$はプロンプトの長さです。
$C_{ref}$・$L_{ref}$は、改善前のプロンプトを1回実行した値を基準として固定しています
。$J(P)$が小さいほど「良いプロンプト」という扱いです。(私が勝手に定義しました!)

📚 評価関数の考え方について

今回の評価関数は独自に定義したものですが、
精度だけでなくコストや長さも評価する考え方は既存研究でも扱われています。

  • CAPO(AutoML 2025):
    進化的プロンプト最適化において、タスク性能とプロンプト長を考慮した多目的最適化を提案
  • CROP(2026):
    精度と生成トークン長のバランスを考慮したプロンプト最適化を提案
    $$
    S(p)=Accuracy(p)-\lambda L_{norm}(p)
    $$

今回の実験では、これらと共通する考え方から、
失敗率・推論コスト・プロンプト長を組み合わせた評価関数を設計しました。
なお、各項目の重み(0.1、0.05)は実験用に設定したものであり、
理論的な最適値ではありません。

今回の実験では、これらと共通する考え方をもとに、失敗率・推論コスト・プロンプト長を組み合わせた独自の評価関数を使用しました。

なお、係数は今回の実験用に仮設定したもので、理論的な最適値ではありません。

結果がこちらです。

方式 プロンプトの長さ 精度 スコア $J(P)$
Reflector方式 23字 → 183字(8倍) 100% 0.526
進化的最適化 23字 → 44字(2倍) 100% 0.208(やったぜ)

精度だけ見れば両方「100%」で引き分けです。
ただ、「コストと長さ」まで混ぜたスコアで見ると、

8倍に膨らませたReflector方式(0.526)より、
2倍の増加で済んだ進化的最適化(0.208)のほうが良い

という評価になりました。むしろReflector方式はスコアの上では、
何も改善していないポンコツプロンプト(スコア0.349)より悪い評価です。
長くなった分のペナルティが精度向上分を食い潰してしまったからです。

さらに、最適化にかかったトークン消費量(≒コスト)を世代ごとに積み上げて比べると、
進化的最適化はReflector方式の約37%のコストで済んでいました。
一度評価したプロンプトと同じ文章がキャッシュするようになってるので、
Elitismで残り続ける個体がこのキャッシュにずっと乗っかってくれたからです。

精度だけで良い悪いを決めるのは良くないことが改めてわかりました。

🔧 試す際:
今使っている改善ループに、まずこの式を足すだけで多目的評価に変えられます。

$$
\text{score} = (1 - S) + 0.1 \times \frac{C}{C_0} + 0.05 \times \frac{L}{L_0}
$$

$C_0$・$L_0$は、改善前のプロンプトを1回実行した値を固定で使えばOKです。
$\lambda=0.1$, $\mu=0.05$は仮の値なので、精度の改善幅と比べて、
大きすぎ/小さすぎたら調整してください。

思ったこと

これは完全に個人の感想ですが、最近は「自分で新しいAIシステムを一から組む」よりも、「既存の、もっと質のいいAIシステムをどう使うか」の方が大事になってきている気がしています。この実験もモデルやエージェント基盤を自作したわけではなく、既存のBedrock・Claude Haikuをどう使うか(プロンプトの書き方、改善をどう評価するか=スコアの作り方)だけで結果がガラッと変わりました。自分でゴリゴリ作り込むより、「既存の良いものをどう使うか」の設計の方が、意外とサボれないところだなと思いました。

まとめ

  • 自己改善は効くけど、思ったよりすぐ頭打ちになる
  • 「精度」だけで良し悪しを判断すると、無駄に膨らんだだけの改善を「成功」扱いする
  • 1体だけ書き直す方法よりも「精度・コスト・長さ」のバランスが良いプロンプトに到達できた

ソースコード一式はまだ上げれてないですが、このレベルならすぐ試せると思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?