こちらの続きです。
プロンプトを管理できるようになったら、異なるバージョンのプロンプトを評価、比較します。マニュアルはこちら。
こちらのノートブックを翻訳してウォークスルーします。
MLflowによるシンプルなプロンプト評価
このノートブックでは、テキスト要約タスクを用いたプロンプト評価の方法を紹介します。
プロンプトのバージョン作成、評価用データセットの構築、パフォーマンス比較の手順を学びます。
主なポイント:
- 要約用の複数プロンプトバージョンを登録
- 期待される事実を含む評価データセットを作成
- 事実の正確性でプロンプトの性能を比較
- 本番利用に最適なプロンプトを選定
%pip install --upgrade "mlflow[databricks]>=3.1.0" openai
dbutils.library.restartPython()
設定
Unity Catalogのスキーマを設定します。CAN_MANAGE
権限が必要です。
# Unity Catalogのスキーマを設定
import mlflow
import pandas as pd
from openai import OpenAI
import uuid
import os
os.environ["OPENAI_API_KEY"] = dbutils.secrets.get(scope="demo-token-takaaki.yayoi", key="openai_api_key")
CATALOG = "takaakiyayoi_catalog" # ご自身のカタログ名に置き換えてください
SCHEMA = "prompts" # ご自身のスキーマ名に置き換えてください
# プロンプト名とデータセット名にユニークな名前を付与
SUFFIX = uuid.uuid4().hex[:8] # 短いユニークなサフィックス
PROMPT_NAME = f"{CATALOG}.{SCHEMA}.summary_prompt_{SUFFIX}"
EVAL_DATASET_NAME = f"{CATALOG}.{SCHEMA}.summary_eval_{SUFFIX}"
print(f"プロンプト名: {PROMPT_NAME}")
print(f"評価用データセット: {EVAL_DATASET_NAME}")
# OpenAIクライアントのセットアップ
client = OpenAI()
ステップ1: 2種類の要約プロンプトを作成
# バージョン1: 基本プロンプト
prompt_v1 = mlflow.genai.register_prompt(
name=PROMPT_NAME,
template="このテキストを要約してください: {{content}}",
commit_message="v1: 基本的な要約プロンプト"
)
print(f"プロンプトバージョン{prompt_v1.version}を作成しました")
この時点でノートブックエクスペリメントにアクセスしてプロンプトを開き、場所に上でしてしたカタログ、スキーマを指定すると、プロンプトがバージョン1として記録されていることを確認できます。
# バージョン2: 詳細なガイドライン付きプロンプト
prompt_v2 = mlflow.genai.register_prompt(
name=PROMPT_NAME,
template="""あなたは要約の専門家です。以下の内容を*必ず*2文で(多くも少なくもなく、文数に細心の注意を払って)要約してください。
ガイドライン:
- すべての重要な事実と主な発見を含める
- 明確で簡潔な言葉を使う
- 事実の正確性を保つ
- すべての主要なポイントを網羅する
- 一般の読者向けに書く
- 必ず2文でまとめる
内容: {{content}}
要約:""",
commit_message="v2: 事実網羅と2文要件を追加"
)
print(f"プロンプトバージョン{prompt_v2.version}を作成しました")
ステップ2: 評価用データセットの作成
要約対象のコンテンツと、要約に含めるべき期待される事実を持つデータセットを作成します。
# 評価用データセットを作成
eval_dataset = mlflow.genai.datasets.create_dataset(
uc_table_name=EVAL_DATASET_NAME
)
# 要約例と期待される事実を追加
# (英語のままですが、必要に応じて日本語に翻訳してください)
evaluation_examples = [
{
"inputs": {
"content": """リモートワークはチームのコラボレーションとコミュニケーションの方法を根本的に変えました。企業はビデオ会議、プロジェクト管理、ファイル共有のための新しいデジタルツールを導入しています。多くの場合、生産性は安定または向上していますが、企業文化の維持、ワークライフバランスの確保、タイムゾーンを超えた分散チームの管理などの課題もあります。この変化はデジタルトランスフォーメーションを加速させ、採用活動もローカルからグローバルへと変化しています。"""
},
"expectations": {
"expected_facts": [
"リモートワークによるコラボレーションの変化",
"デジタルツールの導入",
"生産性は安定または向上",
"企業文化の課題",
"ワークライフバランスの問題",
"グローバル人材の採用"
]
}
},
{
"inputs": {
"content": """電気自動車はバッテリー技術の進歩と充電インフラの拡大により普及が進んでいます。大手自動車メーカーは電動化にコミットし、新モデルを次々と発表しています。政府のインセンティブや環境規制が消費者の関心を高めていますが、初期費用の高さ、地方での充電ステーション不足、バッテリー寿命や交換コストへの懸念など課題も残っています。今後10年で市場は大きく成長すると予想されています。"""
},
"expectations": {
"expected_facts": [
"電気自動車の普及",
"バッテリー技術の進歩",
"充電インフラの拡大",
"政府のインセンティブ",
"初期費用の高さ",
"地方での充電不足",
"市場成長の見込み"
]
}
},
{
"inputs": {
"content": """人工知能は診断画像、創薬、個別化治療を通じて医療を変革しています。機械学習アルゴリズムは従来よりも早期かつ高精度に疾患を検出できるようになりました。AI搭載ロボットは手術や患者ケアを支援していますが、データプライバシーやアルゴリズムバイアス、規制監督の必要性などの懸念もあります。医療従事者はイノベーションと患者安全・倫理のバランスを取る必要があります。"""
},
"expectations": {
"expected_facts": [
"AIによる医療変革",
"診断画像の進歩",
"創薬の加速",
"個別化治療",
"早期疾患検出",
"データプライバシーの懸念",
"アルゴリズムバイアスの問題",
"規制監督の必要性"
]
}
}
]
eval_dataset.merge_records(evaluation_examples)
print(f"{len(evaluation_examples)}件の要約例を評価用データセットに追加しました")
ステップ3: 評価関数の作成
def create_summary_function(prompt_name: str, version: int):
"""指定したプロンプトバージョン用の要約関数を作成します。"""
@mlflow.trace
def summarize_content(content: str) -> dict:
# プロンプトバージョンを読み込む
prompt = mlflow.genai.load_prompt(
name_or_uri=f"prompts:/{prompt_name}/{version}"
)
# プロンプトを整形
formatted_prompt = prompt.format(content=content)
# 一貫性のため低温度でLLMを呼び出す
response = client.chat.completions.create(
model="gpt-4o-mini", # コスト効率のためminiを使用
messages=[{"role": "user", "content": formatted_prompt}],
temperature=0.1 # 一貫した要約のため低温度
)
return {"summary": response.choices[0].message.content}
return summarize_content
ステップ4: カスタムメトリクスを作成し、両バージョンを評価
from mlflow.genai.scorers import Correctness, scorer
from mlflow.genai.judges import custom_prompt_judge
# 2文要件の遵守を評価するカスタムプロンプトジャッジを作成
sentence_count_judge = custom_prompt_judge(
name="sentence_count_compliance",
prompt_template="""この要約が2文要件を守っているか評価してください:
要約: {{summary}}
文数を慎重に数え、適切な評価を選んでください:
[[correct]]: ちょうど2文 - 指示通り
[[incorrect]]: 2文でない - 指示違反
""",
numeric_values={
"correct": 1.0,
"incorrect": 0.0
}
)
# MLflow評価用にジャッジをスコアラーでラップ
@scorer
def sentence_compliance_scorer(inputs, outputs, trace) -> bool:
"""プロンプトジャッジで2文要件遵守を評価するカスタムスコアラー。"""
result = sentence_count_judge(summary=outputs.get("summary", ""))
return result.value == 1.0 # 数値スコアをブール値に変換
# 標準とカスタム両方のスコアラーを利用
scorers = [
Correctness(), # 期待される事実をチェック
sentence_compliance_scorer, # カスタム2文遵守ジャッジ
]
# 各バージョンを評価
results = {}
for version in [1, 2]:
print(f"\nバージョン{version}を評価中...")
with mlflow.start_run(run_name=f"summary_v{version}_eval"):
mlflow.log_param("prompt_version", version)
# 評価実行
eval_results = mlflow.genai.evaluate(
predict_fn=create_summary_function(PROMPT_NAME, version),
data=eval_dataset,
scorers=scorers,
)
results[f"v{version}"] = eval_results
print(f" 正確性スコア: {eval_results.metrics.get('correctness/mean', 0):.2f}")
print(f" 2文遵守率: {eval_results.metrics.get('sentence_compliance_scorer/mean', 0):.2f}")
バージョン1を評価中...
正確性スコア: 1.00
2文遵守率: 0.00
バージョン2を評価中...
正確性スコア: 1.00
2文遵守率: 1.00
評価ランの名前にバージョン番号を含めているので、列ラン名を追加するとよりわかりやすくなります。
ステップ5: 結果を比較し最適なバージョンを選定
# すべての指標でバージョンを比較
print("=== バージョン比較 ===")
for version, result in results.items():
correctness_score = result.metrics.get('correctness/mean', 0)
compliance_score = result.metrics.get('sentence_compliance_scorer/mean', 0)
print(f"{version}:")
print(f" 正確性: {correctness_score:.2f}")
print(f" 2文遵守率: {compliance_score:.2f}")
print()
# 複合スコア(重み付き平均)を計算
print("=== 複合スコア ===")
composite_scores = {}
for version, result in results.items():
correctness = result.metrics.get('correctness/mean', 0)
compliance = result.metrics.get('sentence_compliance_scorer/mean', 0)
# 正確性を70%、遵守率を30%で重み付け
composite = 0.7 * correctness + 0.3 * compliance
composite_scores[version] = composite
print(f"{version}: {composite:.2f}")
# 最適バージョンを決定
best_version = max(composite_scores.items(), key=lambda x: x[1])
print(f"\n最も優れたバージョン: {best_version[0]}(スコア: {best_version[1]:.2f})")
# このバージョンが最適な理由を表示
best_results = results[best_version[0]]
print(f"\n{best_version[0]}が最適な理由:")
print(f"- 期待される事実の{best_results.metrics.get('correctness/mean', 0):.0%}をカバー")
print(f"- 2文要件を{best_results.metrics.get('sentence_compliance_scorer/mean', 0):.0%}の割合で遵守")
=== バージョン比較 ===
v1:
正確性: 1.00
2文遵守率: 0.00
v2:
正確性: 1.00
2文遵守率: 1.00
=== 複合スコア ===
v1: 0.70
v2: 1.00
最も優れたバージョン: v2(スコア: 1.00)
v2が最適な理由:
- 期待される事実の100%をカバー
- 2文要件を100%の割合で遵守
まとめ
このノートブックで学べること:
- プロンプトバージョンの作成 - 要約タスク用に異なるプロンプトテンプレートを登録
- 評価データセットの構築 - 客観的評価のため期待される事実を定義
- カスタムジャッジの作成 - 指示遵守を判定するプロンプトベースのジャッジを利用
- プロンプトの比較 - 複数指標で性能を客観的に測定
- 最適バージョンの選定 - スコアを組み合わせて最も高性能なプロンプトを特定
この例では、MLflowを使ってテキスト生成タスクを事実の正確性(期待される事実)と指示遵守(カスタムジャッジ)の両面から体系的に評価する方法を示しました。同じ手法は、コンテンツ生成、質問応答、コード補完など他の生成AI用途にも応用できます。