一行要約
eval を作る前に成功基準を Specific / Measurable / Achievable / Relevant で書き下すのが先で、eval 設計の要諦は「タスク固有であること」「自動採点できる形に問いを設計すること」、そして 品質より量を優先すること。
要点
良い成功基準の4条件
| 条件 | 内容 |
|---|---|
| Specific(具体的) | 「良い性能」ではなく「正確な感情分類」のように、何を達成したいかを明確に定義する |
| Measurable(測定可能) | 定量指標か、明確に定義された定性スケール。倫理や安全のような「もやっとした」主題も定量化できる |
| Achievable(達成可能) | 業界ベンチマーク、過去実験、AI 研究、専門知識に基づいて目標を置く |
| Relevant(関連性) | アプリの目的とユーザーのニーズに合わせる。引用の正確さは医療アプリでは critical だが雑談ボットではそうでもない |
Your success metrics should not be unrealistic to current frontier model capabilities. 成功指標は、現在のフロンティアモデルの能力に照らして非現実的であってはならない。
安全性の基準の書き方:
| 基準 | |
|---|---|
| 悪い | Safe outputs |
| 良い | Less than 0.1% of outputs out of 10,000 trials flagged for toxicity by our content filter. |
タスク忠実度の基準の書き方:
| 基準 | |
|---|---|
| 悪い | The model should classify sentiments well |
| 良い | Our sentiment analysis model should achieve an F1 score of at least 0.85 (Measurable, Specific) on a held-out test set of 10,000 diverse Twitter posts (Relevant), which is a 5% improvement over our current baseline (Achievable). |
指標の種類
定量指標
- タスク固有: F1 score、BLEU score、perplexity
- 汎用: accuracy、precision、recall
- 運用: 応答時間(ms)、uptime(%)
定量的な手法
- A/B テスト: ベースラインや旧バージョンとの比較
- ユーザーフィードバック: タスク完了率のような暗黙の指標
- エッジケース分析: エラーなく処理できたエッジケースの割合
定性スケール
- Likert scale: “Rate coherence from 1 (nonsensical) to 5 (perfectly logical)”
- 専門家 rubric: 言語学者が定義済み基準で翻訳品質を採点する
成功基準のよくあるカテゴリ(8軸)
| 軸 | 問い |
|---|---|
| Task fidelity | タスクをどれだけうまくこなす必要があるか。エッジケースの扱いは |
| Consistency | 似た入力に対して応答はどれだけ似ている必要があるか。意味的類似性は重要か |
| Relevance and coherence | 問いにどれだけ的確に答えるか。論理的な提示は重要か |
| Tone and style | 出力の文体は期待に合っているか。読み手に適切か |
| Privacy preservation | 個人情報・機微情報の扱いの成功指標は |
| Context utilization | 与えた文脈を有効に使えているか。履歴の上に積み上げているか |
| Latency | 許容できる応答時間は |
| Price | 実行予算は |
Most use cases need multidimensional evaluation along several success criteria. 多くのユースケースでは、複数の成功基準に沿った多次元の評価が必要になる。
多次元の基準の書き方の例(感情分析):
On a held-out test set of 10,000 diverse Twitter posts, the sentiment analysis model should achieve:
- an F1 score of at least 0.85
- 99.5% of outputs are non-toxic
- 90% of errors would cause inconvenience, not egregious error
- 95% response time < 200ms
多様な Twitter 投稿1万件の held-out テストセットにおいて、感情分析モデルは次を達成すべきである。
- F1 スコアが 0.85 以上
- 出力の 99.5% が有害でない
- 誤りの 90% は重大な誤りではなく、不便で済む程度
- 応答時間の 95% が 200ms 未満
eval 設計の3原則
1. タスク固有にする(Be task-specific)
現実のタスク分布を反映させる。エッジケースを忘れない。原典が挙げるエッジケースの類型:
- 無関係・存在しない入力データ
- 過度に長い入力やユーザー入力
- (チャット用途)質の悪い / 有害な / 無関係なユーザー入力
- 人間でも合意が難しい曖昧なテストケース
2. 可能なら自動化する(Automate when possible)
自動採点できるように問いを設計する — 多肢選択、文字列一致、コードによる採点、LLM による採点。
3. 品質より量を優先する(Prioritize volume over quality)
More questions with slightly lower signal automated grading is better than fewer questions with high-quality human hand-graded evals. 信号がやや弱くとも自動採点で問題数を増やすほうが、人手で丁寧に採点した少数の eval より良い。
これは直感に反するが、この文書で最も実務的な原則。少数の高品質な人手採点より、シグナルがやや弱くても多数の自動採点のほうが良い。
LLM に採点させるときの注意
Generally best practice to use a different model to evaluate than the model used to generate the evaluated output. 評価対象の出力を生成したモデルとは別のモデルで評価するのが、一般にベストプラクティスである。
この注意書きは、LLM ベースの例(Likert / binary / ordinal)すべてに繰り返し付記されている。
採点手法と用途の対応(6例)
| 例 | 測る軸 | 手法 | 何を測るか | テストケース数 |
|---|---|---|---|---|
| 1 | Task fidelity | exact match | 空白と大文字小文字を正規化した上で正解と一致するか。感情分析のような明確に分類できるタスクに最適 | 人手ラベル付きツイート 1,000件 |
| 2 | Consistency | cosine similarity | 文埋め込み(SBERT)のなす角の cos。1 に近いほど類似。言い回しが違っても意味的に近い答えを返すか | 言い換え数種を含む 50グループ |
| 3 | Relevance and coherence | ROUGE-L | 生成要約と参照要約の最長共通部分列。高いほど要点を一貫した順序で捉えている | 参照要約付き記事 200件 |
| 4 | Tone and style | LLM-based Likert scale | LLM に 1〜5 でトーンを採点させる。共感・professional さ・忍耐など従来指標で測りにくいものに最適 | 目標トーン付き問い合わせ 100件 |
| 5 | Privacy preservation | LLM-based binary classification | 応答が PHI を含むか。規則ベースでは見逃す暗黙的・微妙な PHI も文脈から拾える | PHI を含むものを混ぜた模擬患者クエリ 500件 |
| 6 | Context utilization | LLM-based ordinal scale | 1〜5 の固定順序スケール。会話履歴をどの程度参照し、その上に積み上げているかの度合いを捉える | 文脈依存の問いを含む多ターン会話 100件 |
エッジケースの作り込みが具体的な点が重要。例1では皮肉(“I just love it when my flight gets delayed for 5 hours. bestdayever” → negative)と混合感情が、例2ではタイポ・冗長な質問・無関係な情報の混入が、例5では明示的 PHI / 仮定形の PHI / 暗黙的 PHI が、例6ではずっと前の文脈への依存と唐突な話題転換が、それぞれテストケースに埋め込まれている。
そのまま使える具体例
exact match の骨格:
def evaluate_exact_match(model_output, correct_answer):
return model_output.strip().lower() == correct_answer.lower()
outputs = [
get_completion(
f"Classify this as 'positive', 'negative', 'neutral', or 'mixed': {tweet['text']}"
)
for tweet in tweets
]
accuracy = sum(
evaluate_exact_match(output, tweet["sentiment"])
for output, tweet in zip(outputs, tweets)
) / len(tweets)
print(f"Sentiment Analysis Accuracy: {accuracy * 100}%")エッジケースの埋め込み方(例1のテストデータ):
tweets = [
{"text": "This movie was a total waste of time. 👎", "sentiment": "negative"},
{"text": "The new album is 🔥! Been on repeat all day.", "sentiment": "positive"},
{
"text": "I just love it when my flight gets delayed for 5 hours. #bestdayever",
"sentiment": "negative",
}, # Edge case: Sarcasm
{
"text": "The movie's plot was terrible, but the acting was phenomenal.",
"sentiment": "mixed",
}, # Edge case: Mixed sentiment
# ... 996 more tweets
]cosine similarity による一貫性の採点:
def evaluate_cosine_similarity(outputs):
model = SentenceTransformer("all-MiniLM-L6-v2")
embeddings = model.encode(outputs)
norms = np.linalg.norm(embeddings, axis=1)
cosine_similarities = np.dot(embeddings, embeddings.T) / np.outer(norms, norms)
return np.mean(cosine_similarities)
for faq in faq_variations:
outputs = [get_completion(question) for question in faq["questions"]]
similarity_score = evaluate_cosine_similarity(outputs)
print(f"FAQ Consistency Score: {similarity_score * 100}%")LLM による Likert 採点(トーンの評価。他のトーン軸にもそのまま流用できる):
def evaluate_likert(model_output, target_tone):
tone_prompt = f"""Rate this customer service response on a scale of 1-5 for being {target_tone}:
<response>{model_output}</response>
1: Not at all {target_tone}
5: Perfectly {target_tone}
Output only the number."""
# Generally best practice to use a different model to evaluate than the model used to generate the evaluated output
response = client.messages.create(
model="claude-opus-5",
max_tokens=50,
messages=[{"role": "user", "content": tone_prompt}],
)
return int(
next(block.text for block in response.content if block.type == "text").strip()
)LLM による二値分類(PHI 検出。判定基準を prompt 内に具体的に列挙しているのが要点):
binary_prompt = f"""Does this response contain or reference any Personal Health Information (PHI)?
PHI refers to any individually identifiable health data that is created, used, or disclosed in the course of providing healthcare services. This includes information related to an individual's physical or mental health condition, the provision of healthcare to that individual, or payment for such care.
Key aspects of PHI include:
- Identifiers: Names, addresses, birthdates, Social Security numbers, medical record numbers, etc.
- Health data: Diagnoses, treatment plans, test results, medication records, etc.
- Financial information: Insurance details, payment records, etc.
- Communication: Notes from healthcare providers, emails or messages about health.
<response>{model_output}</response>
Output only 'yes' or 'no'."""原典で言及されている関連文書
- demystifying-evals-for-ai-agents — 本ページの単発 eval を、多ターン・ツール使用のエージェントへ拡張したもの
- agent-skills-best-practices — 「eval を先に作る(Build evaluations first)」の出所
- claude-prompting-best-practices — 成功基準を prompt 側でどう表現するか
- https://sbert.net/ — Sentence-BERT(cosine similarity の例で使用)
未取得の派生リンク
- https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/reduce-hallucinations
- https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/increase-consistency
- https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks
- https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/reduce-latency