---
title: promptfooを使ったLLM評価基盤設計 ― 評価サイクルを回す3つの工夫
tags:  #promptfoo #llm #llm評価 #生成ai  
author: [KJR020](https://docswell.com/user/KJR020)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/87DKPRGMJG.jpg?width=480
description: 社内勉強会で発表した資料を、社外公開向けにリライトしたものです。  promptfooを使って、複数のLLM呼び出しを含む処理の評価基盤を設計した話です。
published: August 25, 26
canonical: https://docswell.com/s/KJR020/Z27J97-promptfoo-llm-evaluation-platform
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/87DKPRGMJG.jpg)

promptfoo を使った
LLM評価基盤設計
評価サイクルを回し切るために試した、3つの工夫
2026/05
@KJR020


# Page. 2

![Page Image](https://bcdn.docswell.com/page/VJPKNW3QE8.jpg)

目次
promptfoo とは
パフォーマンス改善の経緯
課題 1 ─ 複数の LLM 呼び出しを含む処理を、どう評価するか？
課題 2 ─ 評価を変えるたびに、生成からやり直しが生じる
課題 3 ─ 全候補をまともに評価すると、サイクルが回らない
まとめ
2 / 18


# Page. 3

![Page Image](https://bcdn.docswell.com/page/2EVVK84PEQ.jpg)

promptfoo とは
LLM の比較・評価を体系的に回せる CLI
評価一式を、設定ファイルで宣言的に管理できる
テストケース・プロンプト・モデル・評価観点が対象。Git や CI に乗せられる
モデル × プロンプト × テストケースの全組み合わせを、1コマンドで一括比較できる
OpenAI・Anthropic などの API からローカルモデルまで、同じ設定で比較できる
出力の検証を assertion として宣言し、自動評価できる
ルール検証と model-graded（llm-rubric）に対応し、score・latency・cost も記録される
Custom Provider で拡張でき、自前の処理をそのまま評価対象にできる
GitHub で2万件超のスターを持つ MIT ライセンスの OSS。2026年3月、OpenAI による買収合意が発表された
3 / 18


# Page. 4

![Page Image](https://bcdn.docswell.com/page/57GLQ51QEL.jpg)

設定を宣言すれば、実行から検証まで行える
4 / 18
providers・prompts・tests を宣言すると、promptfoo eval の1コマンドで全組み合わせ
が実行される
# promptfooconfig.yaml
providers:
- google:gemini-2.5-pro
- google:gemini-2.5-flash
prompts:
- file://prompts/answer.txt
tests:
- vars:
question: &quot;
assert:
- type: llm-rubric
value: &quot;
#
比較したいモデルを並べる
#
比較したいプロンプト
経費精算の上限は？&quot;
# 出力を LLM に採点させる
根拠となる文書箇所を明示している&quot;


# Page. 5

![Page Image](https://bcdn.docswell.com/page/4EQYKZDWJP.jpg)

promptfoo で LLM 処理のパフォーマンス改善に取り組んだ
品質を維持しながら、回答のレイテンシを下げることが目的だった
多段の LLM 呼び出しは待ち時間が積み上がりやすく、レイテンシの削減が課題だった
LLM as a Judge で理想回答と比較し、観点別にスコアリングさせた
Judge
LLM as a Judge
Dataset
テストケース
高評価だった想定問答
Generate
promptfoo
回答生成 + Latency測定
理想回答と比較し採点
モデル / プロンプト /
パラメータを変えて反復
Analyze
集約・分析
5 / 18


# Page. 6

![Page Image](https://bcdn.docswell.com/page/KJ4WP3Z171.jpg)

対象は、複数の LLM 呼び出しを含む多段処理だった
6 / 18
実際のアプリと同じ構造を、法令に基づく適法性判定の例で説明する
「この広告表現は適法？」という質問を入力する
判断に必要な N 個の論点へ分解し、確認する法令と順序を計画する
論点ごとに法令の原文を参照し、適法性を個別に判定する
個別判定を統合し、根拠とともに総合判定を生成する
Input
適法性の質問
Step 1
論点分解・計画
Step 2 × N
論点別判定
法令集
該当条文
※ 実際のアプリの処理を、法令に基づく適法性判定に置き換えて説明
N 論点を個別に処理
Step 3
総合判定
Output
適法 / 違法
+ 根拠


# Page. 7

![Page Image](https://bcdn.docswell.com/page/LE1Y91R57G.jpg)

promptfoo を入れたが…
いざ実検討に入ると課題が見つかり、すぐには評価サイクルを回すことができなかった
ぶつかった課題
1
複数の LLM 呼び出しを含む処理を、どう評価するか？
2
標準の assert では、生成と Judge が直列で回しにくい
3
全候補をまともに評価すると、サイクルが回らない
試行錯誤しながら対処した、3つの課題と対応を紹介する
7 / 18


# Page. 8

![Page Image](https://bcdn.docswell.com/page/GEWGQ81WJ2.jpg)

課題 1 ─ 複数の LLM 呼び出しを含む処理を、どう評価するか？
処理が多段になると、E2E 評価だけでは変更の影響を切り分けにくい
Input
適法性の質問
Step 1
論点分解・計画
Step 2 × N
論点別判定
法令集
N 論点を個別に処理
該当条文
分解・計画結果と選ばれた条文が、N 個の論点別判定へ入力される
E2E でまとめて評価すると、変数が多く、どの変更が効いたのか比較しづらい
そこで、Step 単体の評価と E2E 評価を使い分けた
Step 単体 … 変更の影響を切り分ける
E2E … 最終品質を確認する
Step 3
総合判定
Output
適法 / 違法
+ 根拠
8 / 18


# Page. 9

![Page Image](https://bcdn.docswell.com/page/47ZLW8P2J3.jpg)

課題 1 の対応 ─ Step 単体の変更差分を比較する
どの変更が効いたのかを特定するため、条件を揃えて Step 単体への影響を切り分けた
Fixed
分解・計画結果
Target × N
論点別判定
モデル / プロンプトだけ変更
Fixed
法令の原文
前段の分解・計画結果と、各論点に対応する条文を基準データとして固定した
判定 Step だけを変え、同じ入力条件で変更差分を比較した
例 「この広告表現は適法？」
固定：論点「表示内容／対象商品／例外要件」・確認計画・該当条文 → 変更：論点別判定 Step
Score
出力を採点
9 / 18


# Page. 10

![Page Image](https://bcdn.docswell.com/page/YJ6WGP46JV.jpg)

課題 1 の対応 ─ パイプライン全体の出力品質を確認する
10 / 18
最終出力の品質とパフォーマンスを担保するため、各ステップを組み合わせた最終出力を確
認した
promptfoo
eval
callApi
Custom Provider
Step1 → Step2 → Step3
多段処理全体を1回の呼び出しとして
扱う
Output
根拠付き回答
ステップ単体の評価だけでは、各ステップを組み合わせたE2E の挙動はわからない
promptfoo でパイプライン全体を検証するため、Custom Provider を定義し、処理全体を1回にまとめた


# Page. 11

![Page Image](https://bcdn.docswell.com/page/GJ5MVKQ5J4.jpg)

課題 1 の対応 ─ Custom Provider の実装
実装するのは id() と callApi() の2つ
（擬似コード）
// providers/e2e_chain.js
class E2EChainProvider {
id() { return &#039;e2e-chain&#039;; }
}
async callApi(prompt, context) {
const step1Output = await step1(prompt);
const references = await loadReferences(step1Output);
const drafts = await step2(step1Output, references);
const answer = await step3(drafts);
return { output: answer };
}
//
//
//
//
//
前段処理
参照情報
個別処理
統合
評価対象
中で何を呼んでもよいため、複数の LLM 呼び出しを含む処理全体をそのまま実装できる
promptfoo からは「入力を渡すと出力が返る1つのモデル」に見えるため、既存の評価設定をそのまま使える
11 / 18


# Page. 12

![Page Image](https://bcdn.docswell.com/page/LE3WDZV5E5.jpg)

課題 2 ─ 評価を変えるたびに、生成からやり直しが生じる
標準の assert では生成と Judge が直列になり、生成と評価が密結合になっていた
Input
テストケース
Generate
回答生成
Assert: llm-rubric
Judge 評価
直列
この密結合により、検証に次の不便が生じた
Judge モデルや評価プロンプトを変えて、評価だけやり直すことができない
同じ回答に複数回 Judge を走らせて、スコアを平均化することができない
評価まで直列なので 1 run が重く、試行を回しにくい
Result
実行結果
12 / 18


# Page. 13

![Page Image](https://bcdn.docswell.com/page/8EDKPR8Y7G.jpg)

課題 2 の対応 ─ 実行と評価を分ける
13 / 18
回答生成は promptfoo、評価（Judge）は自前スクリプトに分離した
実行
promptfoo
eval
n回試行
Logs
実行ログ JSON
runごと
評価
Flatten
Judge
1回答 = 1行
1レコードずつ
回答 CSV
採点
Merge
結合テーブル
promptfoo で回答だけを生成し、回答csv（1回答 = 1レコード）に平坦化して保存した
csv の1レコードずつに Judge を実行し、採点結果を結合して分析した
分離の効果 → Judge だけ再評価可能になり、データの再利用性が上がった
Analyze
Notebook 分析


# Page. 14

![Page Image](https://bcdn.docswell.com/page/V7PKNW82J8.jpg)

課題 3 ─ 全候補をまともに評価すると、サイクルが回らない
Candidates
モデル × プロンプト
× パラメータ
Generate
回答生成
× 試行回数
Judge
品質評価
LLM as a Judge
検証要素が多く、候補 × 試行 × 採点で評価のコストは掛け算で膨らんだ
候補は、モデル × プロンプト × パラメータの組み合わせで増える
試行は、スコアのばらつきを見るため1候補あたり何度も生成する
採点（品質評価・LLM as a Judge）でも LLM 呼び出しが発生し、最終確認は人手が担う
Result
結果
14 / 18


# Page. 15

![Page Image](https://bcdn.docswell.com/page/2JVVK8NXJQ.jpg)

課題 3 の対応 ─ 手軽な評価から順に、段階的に足切りする
15 / 18
評価ゲートを設定し、段階的に足切り可能な構成にすることで ゲートをクリアした候補だけ
を、高コストな次工程の評価へ送る運用ができた
Candidates
多数の候補
速い候補だけ
1. Latency
速度で足切り
追加コストゼロ
有望な候補だけ
Latency は生成ログから算出でき、追加コストがかからない
2. LLM as a Judge
品質を自動採点
変更前とのベンチ比較で速い候補だけを残す（分布も確認する）。課題 2 の対応が、ここで効いた
LLM as a Judge が自動で品質を採点する
これも足切りであって、最終判断ではない
人手チェックは、ドメインエキスパートによる最終品質担保
最もコストが高いため、絞った少数だけに行った
3. Expert
ドメインエキスパート
人手で最終チェック


# Page. 16

![Page Image](https://bcdn.docswell.com/page/5EGLQ5KRJL.jpg)

課題 3 の対応 ─ LLM as a Judge の採点を確認する
自動採点は、基準回答・reason・スコア分布を確認してから足切りに使った
1
基準回答を固定する
2
reason（判断理由）を確認する
3
複数回採点し、分布を見る
理想回答には、実運用で高評価だった回答セットを使った
低スコアの候補は理由を読み、採点内容を点検した
同じ候補を複数回試行し、単一スコアで判断しない
16 / 18


# Page. 17

![Page Image](https://bcdn.docswell.com/page/4JQYKZNY7P.jpg)

評価基盤が整い軽量モデルへの切替を根拠を持って判断できた
モデルの進化で生まれた候補を、3つの工夫で品質と速度の両面から比較した
1
軽量モデルでも品質を維持できる候補が生まれた
モデルの進化で、切替の可否を検証する対象になった
2
1ケース処理時間：275秒 → 53秒
3
「切り替えても大丈夫」と判断できた
222秒短縮、約81%削減（約8割）
評価結果を根拠に、品質を落とさないモデル変更を進められた
17 / 18


# Page. 18

![Page Image](https://bcdn.docswell.com/page/K74WP3GZE1.jpg)

まとめ
ツールを導入するだけでは、LLM の品質と速度を継続的に改善できない
1
課題：複数の LLM 呼び出しを含む処理を、どう評価するか？
対応：Step 単体で変更の影響を切り分け、E2E で全体品質を確認する
2
課題：評価を変えるたびに、生成からやり直しが生じる
対応：回答生成と Judge 評価を分離し、生成結果を再利用する
3
課題：全候補をまともに評価すると、サイクルが回らない
対応：Latency → Judge → Expert の順に候補を絞る
改善サイクルを回す運用設計が本質だった
18 / 18


