見出し画像

推しのプロンプト実験管理ツール「promptfoo」を解説

最近、推したいプロンプト実験管理方法を見つけたので、そちらの紹介記事になります!これを見つけてから、めちゃ楽になりました!



前提

「ん?実験管理?」と思う方もいるかもなので、軽く背景のところから共有したいのですが、システムに組み込むプロンプト作りは、すごい難易度が高いなーと思ってます。

何が大変かというと、求められる品質が高いからです!

  • 出力形式が期待通りでないとシステムエラーになってしまう。10回中9回成功しても、それはエラー率10%なので安定とは言えない。

  • 様々な要件がある。出力形式や文字数・レイテンシ・コストなど、ルールベースで判定できるものもあれば「ちゃんと指示に従っているか?」「魅力的なアウトプットか?」など定性的なものもある。これらの要件を満たすプロンプトを作る必要がある

ということで、上記の「多様的な要件を満たす出力」「何回やっても期待する出力が出る安定性」を兼ね備えたプロンプトを作る際には、数十回(もしくは数百回)にわたる試行錯誤が必要なわけですが、この実験管理の環境を整備するのも大変なわけです。

これまでは以下の機能をその都度、簡易的に自前で作ってました。。🫠

  • 実行結果(出力内容、処理時間、トークン数)が履歴として残す

  • 実行結果の前提条件(プロンプト内容、変数)も残すようにする

  • どのプロンプトの実行結果かを識別できるようにする

  • 一回で複数の出力を確認できるように、テストケースを用意しておく

  • 最低限必要な評価ロジック(文字数・部分一致チェックなど)も実装する

などなど。

これをやらないと、僕の場合はかなり時間が溶ける傾向にあります。「あれ、なんか改善して行ったはずだけど、前の方がアウトプットの方が良かった気がする。。でも、どんなプロンプトだったっけ…?」みたいに。


そこで実験管理方法を探し続けて約1年。最近良さげなツールを見つけたのでそちらを紹介していきたいと思います!(`gpt-3.5-turbo-0301`が出てからなので、もうすぐで1年ですね!)


promptfooとは?

最近は、promptfooというツールを使って実験管理をしてます。promptfooの概要について、公式ドキュメントには以下のように記載されています。

promptfooは、LLM(Large Language Models、大規模言語モデル)の出力品質を評価するためのコマンドラインインターフェース(CLI)とライブラリです。

promptfooを使用すると、以下のことが可能になります:

・事前に定義されたテストケースを用いて、プロンプト、モデル、およびRAG(Retrieval-Augmented Generation)を体系的にテストすることができます。

・LLMの出力を並べて比較することで、品質を評価し、品質の後退を発見することができます。

・キャッシングや同時テストを利用することで、評価の速度を上げることができます。

・期待する結果を定義することで、出力を自動的にスコアリングすることができます。

・CLIとして使用することも、ライブラリとしてワークフローに統合することもできます。

・OpenAI、Anthropic、Azure、Google、HuggingFace、Llamaのようなオープンソースモデル、または任意のLLM API用のカスタムAPIプロバイダーを使用することができます。

目標は、試行錯誤ではなく、テスト駆動型のLLM開発を行うことです。

promptfooは、多くのプロンプトにわたる出力を迅速に評価できるマトリックスビューを生成します。

公式ドキュメントIntroを和訳


ちなみに個人的な推しポイントはこちらです。

  • 複数のプロンプト(あるいはLLM)を表形式で比較ができる。もちろん過去履歴も確認ができる

  • 同一内容はキャッシュが効くので無駄なお金がかからない

  • 多様な評価方法を実装いらずで利用できる

  • データが、APIプロバイダ以外の外部サーバに送られない(基本的には)


今回はこちらを使った実験管理方法について、基礎的だけど強力なpromptfooの使い方について紹介したいと思います!GitHubにもまとめてありますので、実際に試したい方はご参考ください。



promptfooでの実験管理方法

prompfooconfig.yaml というファイルで様々な設定を行います。ここにはプロンプトと利用するLLM(プロバイダー)、テストケースが記載されています。

※今回は商品のキャッチコピーを作るプロンプトを比較検証しています

# promptfooconfig.yaml
description: '商品コピーライティング検証'

prompts:
  - prompts/template_1.txt
  - prompts/template_2.txt

providers: [openai:gpt-3.5-turbo-0613]

tests:
  - vars:
      product_title: ミライ空気清浄機
      product_description: 最先端AIを搭載し、あなたの生活スタイルに合わせて自動で空気を綺麗にする画期的な空気清浄機。
  - vars:
      product_title: 無重力マットレス
      product_description: 宇宙技術を応用した特殊素材で体圧を均等に分散し、まるで宇宙にいるかのような快適な睡眠を提供します。
  - vars:
      product_title: カイネティックサンド
      product_description: この手触りが癖になる!色とりどりの砂を使って手軽に形を作れる、子供向けの創造性を刺激するおもちゃ。
  - vars:
      product_title: 無重力ヨガマット
      product_description: スペーステクノロジーを応用した、浮遊感覚を体験できる革新的なヨガマット。
# prompts/template_1.txt
与えられた商品内容から魅力的なキャッチコピーを生成してください。

## 制約
- 商品内容の情報から魅力的なキャッチコピーを一つ生成します
- 出力形式に従って生成してください

## 商品内容
商品名:{{product_title}}
商品説明:{{product_description}}

## 出力形式
{キャッチコピー}
# prompts/template_2.txt
あなたはプロのキャッチコピーライターです。
与えられた商品内容から端的に使ってみたいと思うキャッチコピーを生成してください。

## 制約
- 文字数は15文字以内にしてください
- 商品内容の情報から魅力的なキャッチコピーを一つ生成します
- 出力形式に従って生成してください

## 商品内容
商品名:{{product_title}}
商品説明:{{product_description}}

## 出力形式
{キャッチコピー}

二重波括弧 {{ }} の部分に、tests.varsの値が入力されて、それがプロンプトになるイメージです。これらのファイルを整えた後、以下のコマンドを実行すると、実験結果が走ります。

npx prompftoo@latest eval

実行すると、各テストケースに対応した生成結果が出力されます。

eval実行結果:左の二列がテストケースの変数、右二列が生成結果となります。

また出力結果の下部には、どれくらいのトークンを利用したかなどが記載されています。また Cached という項目があり、一度実行した内容はキャッシュを用いて出力してくれるのが推しポイントです💰

eval実行結果の下部

promptfoo には、WebUIの機能が備わっています。以下のコマンドを実行すると、先ほど実行した結果をWeb上で確認できます。

npx promptfoo@latest view
http://localhost:15500/prompts/ で表示される画面

レイテンシーやコストなどもデフォルトで確認できるのはアツいですね🔥

なおこの実行に関するデータは ~/.promptfoo に格納されており、ローカル環境に留まっています。利用したデータが外部サーバに残るということが基本的にないので、安心して利用ができます。

左上のドロップダウンから、過去の実験結果も振り返ることができます。

過去履歴も確認ができる

上部のPromptsタブからは、プロンプトに対してIDが振られており、そこに紐づく実験結果も確認することができます。これも便利!

Promptsタブをクリックして表示される画面
選択したプロンプトの過去の検証結果に飛べる

`Pass count`や`Fail count`が書いてありますが、これはテストケースに対して評価設定を追加した場合に、その評価結果が集計されるようになっています。

GitHubにある promptfooconfig.yaml にいくつかの評価(assert)をコメントアウトで記載していますので、試してみてください。


詳しい解説

ここからは少し踏み込んだ設定について説明していこうと思います!

promptfooのconfigファイル

実験内容については promptfooconfig.yaml ファイルで管理します。
例えば、

  • 実験の名前(description)

  • どのプロンプトを利用するか(prompts)

  • どのAPIを利用するか(provider)

  • どのような入力値を用いるか(tests.vars)

  • どのような評価方法を用いるか(tests.assert)

などが記述されています。他にも色々設定できます。promptfooのconfigの詳細仕様については公式ドキュメントをご参照ください!

# promptfooconfig.yaml
description: '商品コピーライティング検証'

prompts:
  - prompts/template_1.txt
  - prompts/template_2.txt
  # 以下の呼び出し方も可能です
  # - prompts/*.json
  # - prompts/*.js
  # - prompts/*.py

providers: 
  - openai:gpt-3.5-turbo-0613
  # 異なる temperature での実行結果を比較することも可能です
  #     id: openai-gpt-3.5-turbo-lowtemp
  #     config:
  #       temperature: 0.2
  # - openai:gpt-3.5-turbo-0613
  #     id: openai-gpt-3.5-turbo-hightemp
  #     config:
  #       temperature: 0.8

tests:
  - vars:
      product_title: ミライ空気清浄機
      product_description: 最先端AIを搭載し、あなたの生活スタイルに合わせて自動で空気を綺麗にする画期的な空気清浄機。
  - vars:
      product_title: 無重力マットレス
      product_description: 宇宙技術を応用した特殊素材で体圧を均等に分散し、まるで宇宙にいるかのような快適な睡眠を提供します。
    # assert:
    #   - type: cost
    #     threshold: 0.0002 # コストが0.0002ドルを超えていないか
  - vars:
      product_title: 時を超える腕時計
      product_description: 伝統的な時計製造技術と最新のスマート機能が融合した、世代を超えて受け継がれる腕時計です。
    # assert:
    #   - type: latency
    #     threshold: 1500 # 推論処理が1500msを超えていないか。ただしキャッシュ利用の場合はエラーになるので`npx promptfoo@latest eval --no-cache`で実行する必要がある
  - vars:
      product_title: カイネティックサンド
      product_description: この手触りが癖になる!色とりどりの砂を使って手軽に形を作れる、子供向けの創造性を刺激するおもちゃ。
    # assert:
    #   - type: javascript
    #     value: 'output.length <= 15' # 生成されたキャッチコピーが15文字を超えていないか
  - vars:
      product_title: 無重力ヨガマット
      product_description: スペーステクノロジーを応用した、浮遊感覚を体験できる革新的なヨガマット。
    # assert: # 評価方法についてはテンプレートを記述して呼び出すことも可能(下部に記載)
    #   - $ref: "#/assertionTemplates/costCheck"
    #   - $ref: "#/assertionTemplates/latencyCheck"
    #   - $ref: "#/assertionTemplates/lengthCheck"

assertionTemplates:
  costCheck:
    type: cost
    threshold: 0.0002
  latencyCheck:
    type: latency
    threshold: 1500
  lengthCheck:
    type: javascript
    value: 'output.length <= 15'

assert機能がデフォルトで備わっているのはかなり強力です!👏

より詳細に興味がある方は、公式ドキュメントをご参考ください。上記のようなルールベースでの評価に加えて、js, pythonでのカスタム設定や、LLMによる定性的な観点での評価も簡単に実施することができます(llm-rubicなど)


プロンプトについて

promptfooconfig.yamlに記載することもできますし、別ファイルに記載してそこから呼び出すこともできます。基本的には複数行になることが多いため、別ファイル(txt, json, py, js)で管理するケースが多い気がします。

リポジトリでは prompts/ 配下に格納しています。

.txt ファイルでは記載した内容がプロンプトとして呼び出されます。ただしいくつかテキストファイル特有の注意事項があるので、以下に記載しておきます。

  • 動的変数を組み込みたい場合は、二重波括弧 {{ }} で括る。
    { } はそのまま { } として認識される

  • roleの指定はできなさそうで、おそらくデフォルトsystem roleとなる。

  • role指定したい場合は、.json か .js ファイルでプロンプトを記載する必要がある

# prompts/template_1.txt
与えられた商品内容から魅力的なキャッチコピーを生成してください。

## 制約
- 商品内容の情報から魅力的なキャッチコピーを一つ生成します
- 出力形式に従って生成してください

## 商品内容
商品名:{{product_title}}
商品説明:{{product_description}}

## 出力形式
{キャッチコピー}


他にも以下のような感じで、.json, .js, .py でも管理することができます。

# prompts/template.json
[
    {
        "role": "system",
        "content": "あなたはプロのキャッチコピーライターです。\n与えられた商品内容から端的に使ってみたいと思うキャッチコピーを生成してください。\n\n## 制約\n- 文字数は15文字以内にしてください\n- 商品内容の情報から魅力的なキャッチコピーを一つ生成します\n- 出力形式に従って生成してください\n\n## 出力形式\n{キャッチコピー}"
    },
    {
        "role": "user",
        "content": "## 商品内容\n商品名:{{ product_title }}\n商品説明:{{ product_description }}\n===\nではキャッチコピーを出力してください。"
    }
]
# prompts/template.js
module.exports = async function ({ vars }) {
    return [
      {
        role: 'system',
        content: `あなたはプロのキャッチコピーライターです。
        与えられた商品内容から端的に使ってみたいと思うキャッチコピーを生成してください。
        
        ## 制約
        - 文字数は15文字以内にしてください
        - 商品内容の情報から魅力的なキャッチコピーを一つ生成します
        - 出力形式に従って生成してください
        
        ## 出力形式
        {キャッチコピー}`,
      },
      {
        role: 'user',
        content: `## 商品内容
        商品名:${vars.product_title}
        商品説明:${vars.product_description}
        ===
        ではキャッチコピーを出力してください。`,
      },
    ];
};
# prompts/template.py
import json
import sys

system_message = """

与えられた商品内容から魅力的なキャッチコピーを生成してください。

## 制約
- 商品内容の情報から魅力的なキャッチコピーを一つ生成します
- 出力形式に従って生成してください

## 商品内容
商品名:{product_title}
商品説明:{product_description}

## 出力形式
{{キャッチコピー}}

""".strip()


if __name__ == "__main__":

    def generate_prompt(context: dict) -> str:
        return system_message.format(
            product_title=context["vars"]["product_title"],
            product_description=context["vars"]["product_description"],
        )

    print(generate_prompt(json.loads(sys.argv[1])))


プロバイダーについて

ここではどのLLMを利用するかを設定します。複数選択によってLLM同士の比較もすることが可能です。

# promptfooconfig.yaml
providers: 
  - openai:gpt-3.5-turbo-0613
  # 異なる temperature での実行結果を比較することも可能です
  #     id: openai-gpt-3.5-turbo-lowtemp
  #     config:
  #       temperature: 0.2
  # - openai:gpt-3.5-turbo-0613
  #     id: openai-gpt-3.5-turbo-hightemp
  #     config:
  #       temperature: 0.8

以下のようなユースケースを想定する場合、このプロバイダーを中心に設定することとなります。

  1. 異なるLLMでの性能比較をしたい

  2. temperature, top_pなどのパラメータチューニングをしたい

  3. プロンプトに組み込む動的データ部分の前処理/後処理方法を検討したい

3つ目の部分については、JavaScript, PythonでProviderのコードを書いて、そこでプロンプトとテストケースの変数を受け取って前処理、そしてAPIリクエストを送るという処理を記述することができます。

実際の生成AIの活用ケースを考えると、前処理/後処理などを含めた利用をすることも多いのですが、その場合も対応することができます。

JavaScriptでのカスタムプロバイダーについて

Pythonでのカスタムプロバイダーについて


テストデータについて

テストデータの数だけ、LLM推論処理が走り実行結果が、
[プロンプトの数] × [プロバイダの数] × [テストケースの数]分だけ走ります。

これは、例えば2つの(プロンプト or プロバイダー)がある場合、定義したテストケースに対応する実行結果を比較するためです。

また、このテストケースを自動的に準備する方法もあり、プロンプトの動的変数部分を読み取って、変数の中に入りそうなデータを自動生成してくれます。詳細はこちら

以下のコマンドを実行すると、テストケースが生成されます。生成には`gpt-4-1106-preview`が利用されます(OPENAI_API_KEYが環境変数として設定されている必要があります)。

npx promptfoo@latest generate dataset
$ npx promptfoo@latest generate dataset
(node:26065) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Starting dataset synthesis. We'll begin by generating up to 5 personas. Each persona will be used to generate 3 test cases.

Generating user personas from 2 prompts...

Generated 5 personas:
  - 広告代理店のクリエイティブディレクター
  - 新しいブランドを立ち上げた起業家
  - マーケティング部門で働く商品マネージャー
  - 小売業者がオンラインプロモーションキャンペーンを計画する担当者
  - フリーランスのコピーライターが締め切りに追われているとき

Extracted 2 variables from prompts:
  - product_title
  - product_description

Generating test cases for persona 1...
{
  "product_title": "星空プロジェクター",
  "product_description": "自宅をプラネタリウムに変える、リアルな夜空を映し出す革新的なプロジェクター。"
}
{
  "product_title": "未来型スマートウォレット",
  "product_description": "指紋認証でセキュリティを確保し、紛失防止機能付きであなたの貴重品を守る最先端のスマートウォレット。"
}
{
  "product_title": "デジタルデトックスランプ",
  "product_description": "特殊な光波でスマホやPCからのブルーライトを遮断し、目の疲れを軽減する健康志向の照明器具。"
}

Generating test cases for persona 2...
{
  "product_title": "終末サバイバルキット",
  "product_description": "万が一の災害に備えて、持続可能な生存ツールを詰め込んだ安心の緊急避難キット。"
}
{
  "product_title": "夢心地アロマディフューザー",
  "product_description": "厳選されたエッセンシャルオイルであなたの部屋を満たし、五感を優雅に刺激する究極のリラクゼーションデバイス。"
}
{
  "product_title": "透明スマートフォン",
  "product_description": "未来からの一歩を具現化した、完全に透明なディスプレイを持つ次世代スマートフォン。"
}

Generating test cases for persona 3...
{
  "product_title": "星空プロジェクター",
  "product_description": "お部屋を幻想的な夜空に変える、天体観測ができる室内プロジェクター。ロマンチックな夜を演出。"
}
{
  "product_title": "エコスマートシャワー",
  "product_description": "水の使用量を70%削減しながら、豪華なシャワー体験を実現する環境にやさしいシャワーヘッド。"
}
{
  "product_title": "味覚トランスレーター",
  "product_description": "舌に装着すると、食べ物の味を言語で説明してくれる画期的なデバイス。食文化の壁を越えます。"
}

Generating test cases for persona 4...
{
  "product_title": "ゼロインパクトシューズ",
  "product_description": "革新的なエコ素材を使用し、足の形に完璧にフィットする持続可能なランニングシューズ。"
}
{
  "product_title": "スマート冷蔵庫管理機",
  "product_description": "あなたの食事管理をサポートするAI搭載で、食材の賞味期限を追跡し、レシピを提案する冷蔵庫です。"
}
{
  "product_title": "ポータブル星空プラネタリウム",
  "product_description": "どこでも星空を再現できるコンパクトなデバイスで、お家にいながら壮大な宇宙の旅へと誘います。"
}

Generating test cases for persona 5...
{
  "product_title": "ゼログラビティアイマスク",
  "product_description": "重力を感じさせない特殊素材を採用し、究極の暗闇と静寂の中で眠れるアイマスクです。"
}
{
  "product_title": "忍者ステルススニーカー",
  "product_description": "最新技術の吸音素材で作られたスニーカーで、音を立てずに歩ける静かな足音を実現します。"
}
{
  "product_title": "時間逆行ペンダント",
  "product_description": "特殊な光を放つことで、持ち主に過去の美しい瞬間を思い出させる不思議なペンダントです。"
}
=============================================================================================================
New test Cases
=============================================================================================================
tests:
  - vars:
      product_title: 星空プロジェクター
      product_description: 自宅をプラネタリウムに変える、リアルな夜空を映し出す革新的なプロジェクター。
  - vars:
      product_title: 未来型スマートウォレット
      product_description: 指紋認証でセキュリティを確保し、紛失防止機能付きであなたの貴重品を守る最先端のスマートウォレット。
  - vars:
      product_title: デジタルデトックスランプ
      product_description: 特殊な光波でスマホやPCからのブルーライトを遮断し、目の疲れを軽減する健康志向の照明器具。
  - vars:
      product_title: 終末サバイバルキット
      product_description: 万が一の災害に備えて、持続可能な生存ツールを詰め込んだ安心の緊急避難キット。
  - vars:
      product_title: 夢心地アロマディフューザー
      product_description: 厳選されたエッセンシャルオイルであなたの部屋を満たし、五感を優雅に刺激する究極のリラクゼーションデバイス。
  - vars:
      product_title: 透明スマートフォン
      product_description: 未来からの一歩を具現化した、完全に透明なディスプレイを持つ次世代スマートフォン。
  - vars:
      product_title: 星空プロジェクター
      product_description: お部屋を幻想的な夜空に変える、天体観測ができる室内プロジェクター。ロマンチックな夜を演出。
  - vars:
      product_title: エコスマートシャワー
      product_description: 水の使用量を70%削減しながら、豪華なシャワー体験を実現する環境にやさしいシャワーヘッド。
  - vars:
      product_title: 味覚トランスレーター
      product_description: 舌に装着すると、食べ物の味を言語で説明してくれる画期的なデバイス。食文化の壁を越えます。
  - vars:
      product_title: ゼロインパクトシューズ
      product_description: 革新的なエコ素材を使用し、足の形に完璧にフィットする持続可能なランニングシューズ。
  - vars:
      product_title: スマート冷蔵庫管理機
      product_description: あなたの食事管理をサポートするAI搭載で、食材の賞味期限を追跡し、レシピを提案する冷蔵庫です。
  - vars:
      product_title: ポータブル星空プラネタリウム
      product_description: どこでも星空を再現できるコンパクトなデバイスで、お家にいながら壮大な宇宙の旅へと誘います。
  - vars:
      product_title: ゼログラビティアイマスク
      product_description: 重力を感じさせない特殊素材を採用し、究極の暗闇と静寂の中で眠れるアイマスクです。
  - vars:
      product_title: 忍者ステルススニーカー
      product_description: 最新技術の吸音素材で作られたスニーカーで、音を立てずに歩ける静かな足音を実現します。
  - vars:
      product_title: 時間逆行ペンダント
      product_description: 特殊な光を放つことで、持ち主に過去の美しい瞬間を思い出させる不思議なペンダントです。

=============================================================================================================
Copy the above test cases or run promptfoo generate dataset --write to write directly to the config

テストデータの準備にあたっては、特定の指示も加えることができるようなので、割と実用的な気がしています!


CI/CDとしても利用可能

詳しくはGunosyさんが記事を出してますので、そちらに譲ります!こちらの記事で promptfoo の存在を知りました。ありがとうございます!


おわりに

長くなりましたが、これまで自前で整えていた実験管理の環境は promptfoo で事足りるようになりました!

ただちょっと学習コストは高かったので、以下のリポジトリで感覚を掴んで、自分用に応用してみるのが分かりやすいかなと思います。もし実験管理について同じような悩みを抱えている方は、ぜひ試してみてください!


この記事が気に入ったらサポートをしてみませんか?