promptolution: Pythonによるプロンプト最適化の実践

大規模言語モデル(LLM)を活用する際、その出力性能は入力プロンプト(指示文)の品質に大きく左右されます。この「プロンプトの感度(sensitivity)」は非常に高く、意味的に類似しているように見えるわずかな表現の違いだけで、LLMの精度が大きく異なってしまうことが分かっています。

例えば、数学の問題解決タスクであるGSM8Kのデータセットを用いた実験では、プロンプトを最適化することで、ベースラインと比較して最大で21%もの精度向上が達成されています。しかしながら、プロンプト最適化はLLMの性能を引き出す上で不可欠ですが、最適なプロンプトを手作業で探る手動のプロンプトエンジニアリングは、手間がかかり、最適な結果が保証されないという課題があります。

そこで求められているのが、この作業を体系的に行う自動プロンプト最適化です。

今回ご紹介する「promptolution」は、この課題を解決するために開発された、Pythonベースの統一的でモジュール化されたオープンソースのフレームワークです。研究者およびエンジニアの両方を対象としており、プロンプト最適化のプロセス制御と透明性に役立ちます。

プロンプト最適化と promptolution

プロンプト最適化(Prompt Optimization)とは、大規模言語モデル(LLM)の性能を最大化するために、プロンプト(指示文や文脈例)の組み合わせ空間を体系的かつ自動的に探索するアプローチです,。この技術は、古典的な機械学習におけるハイパーパラメータチューニングに似ており、与えられたタスクに対するLLMの性能を劇的に向上させるためのものです。

プロンプト最適化には、プロンプトを連続的なベクトル空間で最適化する連続アプローチ (Continuous Prompt Optimization)と、テキスト形式のプロンプトを直接反復的に改善していく離散アプローチ (Discrete Prompt Optimization)の二つがあります。promptolutionは、LLM実装に依存せず解釈性に優れる離散プロンプト最適化に焦点を当てています。このアプローチでは、多くの場合、別のLLM(メタLLM)を使って改善された候補プロンプトを生成します。

図1. 自動プロンプトエンジニアリングのフレームワークhttps://arxiv.org/pdf/2502.11560 より引用)

従来の課題とpromptolutionの役割

自動プロンプト最適化の有効性は数多くの研究論文で示されていますが、実際に開発現場で利用する際には大きな障壁がありました。

従来の課題として、主に以下の点が挙げられます。

  • 孤立した研究用コードベース: 既存の最適化手法の多くは、研究目的で作成されたコードベースに隔離されており、積極的なメンテナンスが行われていなかったり、ソフトウェアテストや適切なドキュメントが不足していたりすることが多く、実用性に欠けていました。
  • 非柔軟な設定: これらのリポジトリは設定が柔軟ではなく、単純な分類タスクなどのユースケースからの逸脱や、特定のデプロイメントへの対応に多大な労力を要しました。

promptolutionは、この課題を解決するために、複数の最新の離散プロンプト最適化手法(例:OPRO, EvoPromptDE, EvoPromptGA, CAPOなど)を単一の拡張可能なシステムに統合することで、研究者やエンジニアが簡単に利用できるようにしました。

他のフレームワークとの違い

プロンプト最適化を支援するツールは商用・オープンソース問わず存在します,。例えば、DSPyはモジュール化された宣言的なLLMパイプラインを構築するためのフレームワークとして人気があります。

しかし、promptolutionはこれらのツールやフレームワークとは一線を画す特徴を持っています。

  • 焦点の限定(低抽象度): DSPyがプロンプト最適化を高度なプログラムコンパイラの一つのコンポーネントとして統合しているのに対し、promptolutionはプロンプト最適化の段階そのものに焦点を絞っています。このため、比較的低い抽象度となります。
  • 詳細な制御: 低い抽象度で設計されていることで、ユーザーは各コンポーネント(LLM、タスク、評価、ロギングなど)を詳細に制御することができ、設定に対する完全な制御と透明性を得ることが可能です。
  • 研究・高度な実務者向け: このような設計思想により、promptolutionは、エンドツーエンドのAIアプリケーション開発者というよりも、体系的で再現性のある大規模なベンチマーク実験を行いたい研究者や、高度な機械学習エンジニア向けに特化されたツールとなっています。

promptolution の概要

promptolutionは、プロンプト最適化における実用上の課題、特に高コストの問題柔軟性の不足に対処するために設計された、モジュール化されたフレームワークです。

図2. promptolution の概要

コストを意識した最適化(CAPO)

プロンプト最適化の大きな課題の一つは、LLMの呼び出し(APIコール)が大量に発生することによる高コスト化問題です。例えば、既存の最適化手法(EvoPromptなど)では、収束までに数100万の入力トークンを消費し、商用APIでは高額な費用がかかることが指摘されています。

promptolutionに実装されている最先端の最適化アルゴリズムの一つ、CAPO (Cost-Aware Prompt Optimization)は、このコスト問題に対処するために、AutoMLの技術を統合しています。

CAPOの主な特徴は以下の通りです。

  • レーシング技術(Racing)の統合: CAPOは、統計的テスト(例: ペアードt検定)を用いて、性能の低い候補プロンプトを早期に統計的に排除する「レーシング」と呼ばれる手法を導入しています。これにより、すべてのプロンプト候補を完全なデータセットで評価する必要がなくなり、評価に必要なトークン数とコストを大幅に削減できます。実際、レーシングを導入することで、平均で評価の44%を削減できることが示されています。
  • 多目的最適化(Multi-Objective Optimization): CAPOは、単に性能(精度)を最大化するだけでなく、プロンプト長も考慮に入れます,。具体的には、プロンプトの性能に加えて、プロンプト長(トークン数)に対するペナルティを目的関数に組み込むことで、短いプロンプトを推奨します。プロンプトが短いほど、最適化プロセス中だけでなく、デプロイメント後の実行コスト(推論コスト)も削減されるというメリットがあります。

統一されたモジュラーコンポーネント

promptolutionは、再利用性、透明性、および拡張性を確保するために、統一されたインターフェースを持つ4つの主要なコンポーネントで構成されています。

コンポーネント名役割
LLM (Large Language Model)LLMとのインターフェースを提供し、応答の取得、トークン使用量の監視、並列処理を管理します。
PredictorLLMの出力から最終的な回答を抽出する方法を定義します。
Tasksデータセット、タスクの説明、評価指標を管理し、プロンプトの評価方法を定義します。
Optimizers上記のコンポーネントを組み合わせて、指定されたタスクに最適なプロンプトを反復的に見つけ出します。CAPOやOPRO、EvoPromptGA/DEなどがこれに含まれます。

LLM実装に非依存

LLMコンポーネントは、基盤となるLLMの実装に依存しないように設計されています,。これにより、ユーザーはLLMを容易に変更することが可能です。サポートされている主な実装は以下の通りです。

  • APILLM: OpenAIやAnthropicなどの商用APIベンダー経由でホストされているLLMへの呼び出しに使用します。
  • LocalLLM: Hugging FaceのTransformersライブラリを使用して、ローカル環境でモデルを実行できるようにします。
  • VLLM: 高効率な高スループット推論(High-throughput Inference)とGPUクラスターでのデプロイメントを可能にするVLLMライブラリとの統合を提供しています。

予測器(Predictor)の柔軟性

Predictorコンポーネントは、LLMからの生出力から、評価に必要な予測結果を正確に抽出する役割を担います。特に、MarkerBasedPredictor は、LLMの応答に含まれる<final_answer><final_score>のような事前定義されたHTMLライクなマーカー間のテキストを抽出することで、予測を確実に行います。例えば、GSM8Kのような数学的な推論タスクでは、このマーカーを用いて最終的な答えを正確に抽出できます。これにより、出力形式の揺らぎに左右されず、安定した評価が可能となります。

Pythonで実践するプロンプト最適化

promptolution は、Pythonベースのライブラリとして提供されており、pipコマンド一つで簡単にインストールできます。LLMのAPIを利用する場合は、[api]オプションを付けてインストールします。

$ pip install promptolution[api]

# ローカルLLMやvLLMを利用する場合
$ pip install promptolution[vllm,transformers]

前述の通り、promptolutionでは、教師データ(グラウンドトゥルース)の有無にかかわらず、様々なタスクでプロンプト最適化を実施できます。ここでは、LLMを評価者として用いる主観的・創造的なタスク(JudgeTask)の最適化を例に、実際のコードを紹介します。

import os
import pandas as pd
from promptolution.utils import ExperimentConfig, Prompt
from promptolution.helpers import run_experiment


# 1. データの準備
df = pd.DataFrame({
    "topic": [
        "感情を持ったロボット",
        "地球で最後の木",
        "猫たちの秘密結社",
        "5分前へのタイムトラベル",
        "無限の本がある図書館"
    ]
})

# 2. 初期プロンプトの定義 (修正箇所)
# 文字列ではなく、Promptオブジェクトのリストにする必要があります
init_prompts = [
    Prompt("次のお題について短い物語を書いてください:"),
    Prompt("以下のトピックに基づいて、創造的なストーリーを作成してください:"),
    Prompt("このアイデアを元に、魅力的なナラティブを作ってください:"),
]

# 評価用プロンプトの定義
custom_judge_prompt = """あなたは物語の評価を行う文芸評論家です。与えられた「お題」に対する「生成された物語」の品質を評価してください。

タスク:
{task}

お題:
{input}

生成された物語:
{prediction}

評価基準:
- お題に沿っているか
- 創造性や独創性があるか
- 文章が流暢で魅力的か

-5から+5のスコアで評価してください:
- -5: 完全に不適切、またはお題と無関係
- 0: 部分的に良いが、改善の余地が大きい
- +5: 非常に素晴らしく、創造的で完成度が高い

回答は必ず <final_score>スコア</final_score> の形式で数値のみを囲んで返してください。
"""

# 3. 実験設定
config = ExperimentConfig(
    task_type="judge",
    task_description="与えられたお題について、創造的で魅力的な短い物語を書いてください。",
    x_column="topic",

    judge_prompt=custom_judge_prompt,

    prompts=init_prompts,  # Promptオブジェクトのリストを渡す
    optimizer="evopromptga",
    n_steps=5,
    n_subsamples=5,

    api_url="https://api.openai.com/v1",
    model_id="gpt-4o-mini",
    api_key=os.getenv("OPENAI_API_KEY"),
)

# 4. 実験の実行
if __name__ == "__main__":
    print("日本語タスクの最適化を開始します...")
    results = run_experiment(df, config)

    print("\n=== 最適化された日本語プロンプト ===")
    print(results[["prompt", "score"]].head())

実装の効率化とコスト管理のヒント

プロンプト最適化は、LLMのAPI呼び出しを大量に発生させるため、特に商用LLMを使用する場合、コスト(費用と時間)の管理が非常に重要です,。promptolutionは、この課題に対処するための機能を提供しています。

設定の抽象化(ExperimentConfig)

ExperimentConfigクラスは、実験を定義する上で中心的な役割を果たします。このクラスを利用することで、LLMの種類、オプティマイザ、タスクのパラメータ、反復回数などを一つのオブジェクトとして集中的に設定できます。そして、この設定オブジェクトをrun_experiment()などのヘルパー関数に渡すだけで、複雑な実験をわずか数行のコードで実行することが可能です。大規模で再現性のあるベンチマーク実験を行いたい場合や、高度な実験で特に有効です。

コスト管理コールバック(TokenCountCallback)

プロンプト最適化のコストの大部分は、LLMの評価(Evaluation)に使用されるトークン消費に起因します。特に、LLMを評価者として使用する場合、この評価プロセスで大量のAPIコールが発生します。

promptolutionでは、TokenCountCallback は、最適化プロセス中に累積された入力および出力トークンの使用量を監視し、指定されたトークン予算の閾値を超えた場合に、自動的に最適化を停止させることができます。これにより、予期せぬAPIコストの肥大化を防ぎ、予算を意識した(Cost-Aware)な実験が可能になります。

出力の確実な抽出(MarkerBasedPredictor)

最適化プロセスを成功させるには、LLMが生成した結果(最終的な回答や、メタLLMが生成した新しいプロンプト)を正確に抽出することが不可欠です。

MarkerBasedPredictor は、LLMの応答シーケンスの中から、<final_answer><prompt>といった事前定義されたHTMLライクなマーカーを使用して、必要な情報のみを確実に抽出するためのコンポーネントです。

例えば、数学の問題解決タスク(GSM8K)では、LLMに最終的な答えを<final_answer>タグで囲ませることで、他の推論テキストに左右されることなく、正確な解答(予測値)を抽出できます。また、最適化プロセスにおいては、メタLLMが生成した新しい候補プロンプトを<prompt>タグから正確に抽出する際にも利用されます。この抽出機能により、LLMの出力形式の揺らぎや、余分なテキストの混入による評価の失敗を回避し、安定した最適化の進行が実現します。

おわりに

今回は、自動プロンプト最適化フレームワーク「promptolution」を紹介しました,。

promptolutionは、CAPO (Cost-Aware Prompt Optimization、コスト効率の高い進化アルゴリズム)を搭載しており、レーシング技術やプロンプト長へのペナルティを通じて、LLMの呼び出しコストを意識した最適化を実現します。さらに、教師データが不要なJudgeTaskや、カスタムの評価関数を定義できるRewardTaskなど、柔軟なタスク定義に対応したモジュラー設計によって、実際の業務におけるLLMの性能最大化と安定運用を強力にサポートします。

このフレームワークを利用することで、手作業による調整(プロンプトエンジニアリング)では困難だった高性能なプロンプトの探索をデータ駆動で自動化できます。これにより、JSON適合率の向上などのビジネス目標に直結する成果を、少ないリソース(コスト)と高い信頼性をもって達成することが期待されます。実際、最適化により、JSON出力の有効率を70%程度から84%以上に向上させた事例も確認されています。

さらなる詳細や導入のチュートリアルは、GitHubのドキュメントにて公開されています。ぜひこれらを参考に、業務データを用いたプロンプト最適化を体験してみましょう。

More Information