はじめに
こんにちは。ロリポップ・ムームードメイン事業部でエンジニアリングリードをしています kinosuke01 といいます。
ここ数年で、AIでWebサイトを生成するケースが増えてきたと思います。ただ、その品質を評価するのは難しく、「なにかイマイチ」という感想は出てくるものの、具体的な改善点までは言葉にしづらいのではないでしょうか。
この記事では、「ロリポップ!AIホームページ」(旧「ロリポップ!AIサイトエージェント」)というサービスで、AIが生成したWebサイトの品質をどう評価し、開発サイクルにどう組み込んだかを紹介します。
関連して、2026年9月5日開催の Product Engineering Conference 2026 で 「作り直せるコードは迅速に、作り直せないDBは慎重に」 というセッションに登壇します。判断の不可逆性を基準に「やり直せる領域はAIを活用して走りながら考え、やり直しの効かない領域は慎重に設計する」という使い分けを話します。本記事で扱うのは、その「走りながら考える」側にあたる取り組みの1つです。
前提:「ロリポップ!AIホームページ」について
「ロリポップ!AIホームページ」は、AIがWebサイトを制作するサービスです。「カフェのサイトを作りたい」「フリーランスのポートフォリオが欲しい」といった指示から、ページ構成・デザインテーマ・コンテンツまでを一括で生成します。サイト制作フローを「ヒアリング → 構成設計 → デザイン選定 → コンテンツ生成」のステップに分解し、決定論的ワークフローで中核処理を組んでいます。AIの役割は「サイトを表す構造データ(JSON)を生成する」ことに限定し、描画はアプリケーションが担います。コードに関する知識がない層のユーザーを対象としているため、ユーザーがコードを修正する必要が生じないよう、AIにHTMLやCSSを直接書かせる設計は採用していません。
技術スタックはNext.js、BullMQ(非同期ジョブキュー)、MySQL、生成モデルにはGoogle Cloud上のGeminiを使用しています。

背景・課題
「ロリポップ!AIホームページ」には、チャットボットやRAGとは異なる難しさがありました。チャットボットであれば回答の正誤で品質を判定できますが、Webサイトのデザインや構成に唯一の正解はありません。
「余白が広い」「コピーが硬い」といったフィードバックは、ありがたいことにたくさん集まります。ただ、それが好みの話なのか、守るべき基準からの逸脱なのかを切り分けることは困難です。また、改修したとしても「良くなった気がする」で止まってしまい、是非を評価する手段がない状況でした。これでは開発サイクルを回せません。
そこで、Webサイトの品質を定量的に評価する指標が必要となりました。
解決策の検討:品質を定量化する3つの候補
品質を定量化する方法として、3つの候補を検討しました。
| 方法 | メリット | デメリット |
|---|---|---|
| A/Bテスト + ユーザー行動指標 | 実ユーザーの反応が分かる | フィードバックループが遅い。サンプルが溜まるまで数週間かかる |
| 人手によるチェックリスト採点 | ドメイン知識を直接反映できる | スケールしない。デザイナーの工数がボトルネックになる |
| ルーブリック × LLM-as-a-Judge | 自動化できる。人手採点と同じ尺度で比較可能 | LLMの採点精度に依存する。キャリブレーション(較正)が必要 |
3つの中で、フィードバックループを速く回せるのは、ルーブリック × LLM-as-a-Judgeでした。ルーブリックは、評価の観点ごとに「どの状態なら何点か」を段階で定めた採点基準表です。LLM-as-a-Judgeは、人が評価する代わりに、生成物の良し悪しをLLMに判定させる手法を指します。
A/Bテストは数週間単位の待ちが発生し、人手採点はプロンプトを直すたびにデザイナーの工数を確保する必要があります。LLM-as-a-Judgeには採点精度のリスクがありますが、自動で回せること、そして人手採点と同じ尺度を使えるため後からキャリブレーションで精度を上げていける点が強みです。「品質基準を人が定義し、採点をLLMに任せる。精度のリスクはキャリブレーションで段階的に潰す」。この方針で進めることにしました。
では、この方針の具体的な実装方法について、順に見ていきます。
完成品スコア:出来上がったサイトをキャプチャ1枚で採点する
最初に作ったのは、出来上がったサイトそのものの品質を測る評価指標です。以降これを「完成品スコア」と呼びます。
デザイナーの暗黙知をルーブリックに落とす
採点するには、元になる品質基準が要ります。ただ、それは文書としては存在していませんでした。そこで当社のデザイナーに、レビューのときに何を見て良し悪しを決めているのかを、他人が同じように再現できる形まで言語化していただきました。こうしてできたのが、4カテゴリ・約170項目の品質基準です。構造は「カテゴリ > サブセクション > 項目」の3階層で、カテゴリごとに評価の切り口としてサブセクションが並び、その下に具体的なチェック項目がぶら下がります。
以下は品質基準を抜粋したものになります。
## 1. 構成・情報設計
> 何を載せるか、どの順番か、どれだけの密度で見せるか。サイトの骨格。
### 1-5. 認知負荷のコントロール
#### 情報密度
- [ ] 1つのセクション(一目で見える範囲)のテキストが多すぎないか(目安120文字以内)
- [ ] 1つのカード・ブロック内の要素が5つ以下に収まっているか(多すぎると処理しきれない)
#### 視線の緩急
- [ ] 密度の高いセクションの後に、余白の広いセクションがあるか(滞留→加速のリズム)
- [ ] テキスト中心のセクションと画像中心のセクションが交互に配置され、単調になっていないか
## 3. 言葉・コピー
> 何と言うか、どんなトーンで伝えるか。デザインが「見た目」なら、コピーは「声」。
### 3-2. トーン・声の一貫性
- [ ] 敬体(です・ます)と常体(だ・である)が混在していないか
- [ ] ターゲット層の言葉で書かれているか(専門用語が多すぎないか、逆にカジュアルすぎないか)
### 3-4. マイクロコピー
- [ ] ボタンラベルが動詞で始まっているか(「送信する」「始める」「確認する」)
- [ ] リンクテキストがリンク先の内容を予測できる言葉か(「こちら」「詳細」は避ける)
次に決めたのは、何を見て採点するかです。運用の容易性とスコアリングの妥当性のバランスを取り、入力はトップページのフルページキャプチャ1枚だけとしました。入力の種類を増やせば採点は精密になりますが、そのぶん1回あたりの準備が重くなります。ここは「キャプチャさえあれば誰でも・自動で回せる」ことを優先しました。妥当性の面でも、トップページはサイトの全体感が現れる場所なので、1枚でも評価対象として成立すると判断しています。
このようなトップページのフルキャプチャを与えて評価する
この入力に合わせて、約170項目から「キャプチャ1枚だけで判定できる項目」に絞り込みました。以下のような項目は除外しています。
- 意図が前提になる項目(ゴール・ペルソナ・感情)
- 操作が必要な項目(ホバー・クリック・アニメーション)
- 複数ページの比較が必要な項目
- ページ外の要素(OGP・ファビコン)
絞り込みの結果、4カテゴリ・23サブセクションが残りました。これをルーブリックとして整理しています。
| カテゴリ | 重み | 評価の観点(例) |
|---|---|---|
| 構成・情報設計 | 35% | コンテンツの過不足、セクション順序、認知負荷、導線 |
| 見た目 | 35% | 文字のディテール、配色、余白、ヒーロー、統一感 |
| 言葉・コピー | 20% | コピーの説得力、トーン一貫性、読みやすさ |
| 体験・操作感 | 10% | スクロール体験のリズム、着地点 |
以降、カテゴリ名は「構成」「見た目」「コピー」「体験」と略記します。
各サブセクションは0〜3の4段階で採点し、画像から判定できない場合は null とします。
- 3: 観点をほぼ完全に満たす(明確な問題なし)
- 2: おおむね満たすが軽微な問題が一部ある
- 1: 満たせていない点が目立つ
- 0: ほとんど満たせていない、または致命的な問題がある
- null: 画像から判定不能(集計時に除外)
加重スコアリングで偏りを防ぐ
単純にチェック項目の合計点を使うと、項目数の多い「見た目」カテゴリが全体の約71%を占めてしまう状況でした。そこで2段階の正規化を行いました。
- サブセクション内で達成率を正規化(0〜1)
- カテゴリ重みで加重平均(構成35 / 見た目35 / コピー20 / 体験10)
実装はシンプルで、以下のような関数になります。
// 最終配点%(= カテゴリ重み × サブ重み比)。合計100。
// 番号の飛び(s1_1 など)は、キャプチャ1枚では判定できないため絞り込みで除外した項目。
const WEIGHTS: Record<string, number> = {
s1_2: 8.08, s1_3: 5.38, s1_4: 8.08, s1_5: 5.38, s1_6: 5.38, s1_7: 2.69,
s2_2: 3.33, s2_3: 3.33, s2_4: 3.33, s2_5: 5.00, s2_6: 1.67, s2_7: 1.67,
s2_8: 3.33, s2_9: 1.67, s2_10: 3.33, s2_11: 1.67, s2_12: 3.33, s2_13: 3.33,
s3_1: 7.50, s3_2: 5.00, s3_3: 5.00, s3_4: 2.50,
s4_1: 10.00,
};
type Scores = Record<string, { score: number | null } | undefined>;
function totalScore(scores: Scores): number | null {
let num = 0, wsum = 0;
for (const [k, w] of Object.entries(WEIGHTS)) {
const s = scores[k]?.score;
if (s === null || s === undefined) continue; // 判定不能は除外
num += (s / 3) * w; // 0〜3 を 0〜1 に正規化して加重
wsum += w;
}
// 除外があっても満点100換算になるよう再正規化
return wsum === 0 ? null : Math.round((num / wsum) * 100 * 10) / 10;
}
判定不能(null)のサブセクションを除外し、残りの項目だけで満点100換算になるよう再正規化します。同じ項目集合どうしであれば、キャプチャから判断できない項目があっても公平に比較できます。
LLM-as-a-Judgeの設計
ルーブリックが整ったので、次はこれをLLMに渡して自動採点させる仕組みを作ります。採点にもGeminiを使い、キャプチャとルーブリックを渡して、各サブセクションの0〜3スコアと根拠をJSONで出力させます。
なお、総合スコアの計算はLLMに任せていません。モデルには各サブセクションの素点(0〜3)だけを出させ、総合スコアは先述の totalScore 関数でコードが算出します。この分離により、再現性が確保でき、重みの調整もモデルの再評価なしで可能となります。
出力フォーマットは以下のとおりです。
{
"scores": {
"s1_2": {"score": 2, "reason": "...", "evidence": "..."},
"s1_3": {"score": 1, "reason": "...", "evidence": "..."}
},
"top_improvements": [
"グローバルナビゲーションを追加する",
"フッターに運営者情報と連絡先を配置する",
"ボタン文言を動詞始まりに書き換える"
]
}
top_improvements には、スコアの低い項目から順に改善提案を3件出させています。
ベースライン計測:目指す地点と現在地を並べる
できあがったルーブリックで、性格の異なる3種類のサイトを採点してみました。
| 対象 | 完成品スコア | 位置づけ |
|---|---|---|
| 「ロリポップ!AIホームページ」のエディタでデザイナーが制作したサイト | 91.6 | 本サービスで表現できる上限 |
| ベンチマークとしたサイト | 80.7 | まず超えたい目標ライン |
| 「ロリポップ!AIホームページ」でAIが生成したサイト | 48.3 | いまユーザーが目にしているもの |
最初に確かめたかったのは、この採点が信用できるかどうかです。出てきた点数の並びは、チームメンバーが普段それぞれのサイトに対して抱いている感覚とおおむね一致していました。厳密な検証ではありませんが、少なくとも大きくは外していないはずです。この手応えがあってはじめて、スコアを判断の材料として使えます。
そのうえで3つを並べると、目指す地点と現在地の距離がそのまま見えます。AI自動生成サイトだけが突出して低くなっています。サブセクション別に見ると、「ナビゲーションの欠落(s1_6: 0点)」「ヘッダー・フッターの不備(s2_8: 0点)」「マイクロコピーの抽象性(s3_4: 1点)」など、弱点が特定できました。
「なにかイマイチ」が「ナビゲーションが0点で、重み5.38%を占めている」に変わることで、改善の優先度がチーム内で一致するようになりました。
なお、ここまでの採点手順をClaude Codeのスキルとして切り出そうと試みましたが、うまくいきませんでした。Claudeモデルは画像の入力サイズに上限があり(参考)、フルページキャプチャのような縦長画像では文字までは読み切れず、体感とかけ離れたスコアが出てしまったためです。
工程スコア:途中成果物のJSONを、抜粋した観点だけで採点する
完成品スコアは、改修ループには重すぎる
現在地は分かりました。しかしこの評価指標だけで開発を回すのは困難です。
完成品スコアを1回出すには、サイトを生成 → 実際に描画 → フルページキャプチャを撮る → マルチモーダルモデルに投げるという手順を踏みます。プロンプトを1行直すたびにこれをやるのは現実的といえません。また、採点にもモデル呼び出しのコストがかかるので、気軽に連打できるものでもありません。
一方、実際に直したいのは「サイト全体」ではなく、ワークフローの特定のステップです。たとえばコンテンツの文言がイマイチなら、手を入れたいのは冒頭で挙げたフローのうち「コンテンツ生成」のプロンプトであって、構成設計やデザイン選定ではありません。
そこで、評価する対象を工程単位まで小さくした評価指標を別に用意しました。以降これを「工程スコア」と呼びます。
ヒアリング
│
構成設計 ──▶ JSON ──▶ 工程スコア
│
デザイン選定 ──▶ JSON ──▶ 工程スコア
│
コンテンツ生成 ──▶ JSON ──▶ 工程スコア
│
描画
│
完成したサイト ──▶ キャプチャ ──▶ 完成品スコア
テキストだけで判定できる7項目に絞る
「コンテンツ生成」ステップを担っているのは、各セクションの中身を並列で作る SectionValueParallelGenerator というクラスです。ここが出力するのは コンテンツとprops(テキスト+構造)のJSON で、配色・余白・タイポグラフィ・画像といった視覚要素はテーマやテンプレート側の責務です。つまり、このステップの出力だけでは視覚要素を採点できません。
そこで完成品スコアの23サブセクションから、テキストだけで判定できる7項目を抜き出しました。
| code | サブセクション | 重み |
|---|---|---|
| s1_2 | 業種×目的のコンテンツ過不足 | 8.08 |
| s1_4 | コンテンツの順序(心理変容) | 8.08 |
| s1_5 | 認知負荷(テキスト量・要素数) | 5.38 |
| s3_1 | コピーの説得力 | 7.5 |
| s3_2 | トーン・声の一貫性 | 5.0 |
| s3_3 | 読みやすさ・テキストの質 | 5.0 |
| s3_4 | マイクロコピー(ボタン/リンク文言) | 2.5 |
これを既存の integration test に追加しました。生成 → JSONをテキスト整形 → Judge で0〜3採点 → totalScore() で集計という流れで、1コマンドで回ります。キャプチャは撮りません。
# Judge の評価スイートだけを実行する
docker compose exec -e RUN_TEST_AI_JUDGE=1 app bash -c \
"cd path/to/core && npx vitest --run --silent=false \
path/to/section-value-parallel-generator.integration.test.ts"
なお、このテストはCIには載せていません。Judge のモデル呼び出しコストが毎回かかるので、環境変数 RUN_TEST_AI_JUDGE でガードして手で叩く運用です。CIで自動的に守ることより、直したその場で1コマンド叩ける手軽さを取りました。
工程スコアで評価駆動開発を回す
工程スコアが手軽に出せるようになったので、これを軸に改修を回します。
評価駆動開発のループ
- ベースライン計測: 現在の生成プロンプトで生成し、サブセクション別にスコアを実測する
- 弱点の特定: スコアの低いサブセクションと、その重みから改善インパクトを見積もる
- プロンプト改修: 弱点に対応する品質原則を生成プロンプトに追加する
- 再計測: 同じ基準で再評価し、改善・劣化を確認する
実例: コンテンツ品質原則の追加
ベースライン計測で特に弱かったのが「トーンの一貫性(s3_2)」と「コピーの説得力(s3_1)」でした。敬体と常体が混在したり、「こちらをクリック」のような空虚なマイクロコピーが頻出していました。
セクションごとのプロンプトを組み立てている関数(buildSectionPrompt)に、「コンテンツ品質の原則」ブロックを追加しました。Judge のルーブリック文言をそのまま転記するのではなく、普遍的なコピーライティング原則として記述しています。
- トーンの統一(敬体基調、常体混在の回避)
- 固有名詞の表記統一
- ベネフィット訴求のコピー、空虚な常套句の回避
- 簡潔さと低い認知負荷(一文60字/1ブロック120字目安)
- ボタンラベルは動詞始まりで具体的に(「こちら」回避)
計測結果
改修後、同じ基準で再計測した結果です(各n=3)。
| サブセクション | 重み | Before平均 | After平均 |
|---|---|---|---|
| s1_2 過不足 | 8.08 | 1.33 | 1.0 |
| s1_4 順序/重複 | 8.08 | 1.67 | 2.0 |
| s1_5 認知負荷 | 5.38 | 3.0 | 2.67 |
| s3_1 説得力 | 7.5 | 2.67 | 3.0 |
| s3_2 トーン一貫性 | 5.0 | 2.33 | 3.0 |
| s3_3 読みやすさ | 5.0 | 1.33 | 1.0 |
| s3_4 マイクロコピー | 2.5 | 2.67 | 1.67 |
工程スコア total は、before が 76.5 / 83 / 46(平均68.5)、after が 68.5 / 61.7 / 75(平均68.4)となりました。平均はほぼ横ばいです。
思っていたより平均は動きませんでした。ただし最低値は46 → 61.7に上がっています。Beforeで発生していた「トーン崩壊で大きく点を落とすラン」が解消されたことで、下振れがなくなりました。
なお、このループを含むいくつかの改善をした結果、完成品スコアのほうも48.3から52.1まで上がりました。
モデル変更時のスコアゲーティング
LLMベースのサービスでは、モデルのアップデートや切り替えが頻繁に発生します。「ロリポップ!AIホームページ」では、Gemini 2.5 Flash の廃止を見据えて、Gemini 3.1 Flash-Lite への移行を実施しています。
モデルの移行に伴い「品質は維持されているか?」を確認する必要があります。ここで、作成済みの2つの評価指標を活用しました。
同一の題材(美容室のサイト)を本番と同じ経路で新旧それぞれのモデルに生成させ、工程スコアと完成品スコアの両方で比較しました。旧モデル側の数値も、この比較のために取り直したものです(前節の計測とは別データです)。
| 評価指標 | Gemini 2.5 Flash | Gemini 3.1 Flash-Lite |
|---|---|---|
| 工程スコア(テキスト7項目) | 68.4 | 77.5 |
| 完成品スコア(キャプチャ23項目) | 52.1 | 62.3 |
どちらのスコアも旧モデルを下回っていないことを確認して、マージしました。開発中に積み上げた評価基準が、そのまま移行判定のゲートになりました。
あらためて現在地を確認する
完成品スコアの最初のベースラインは48.3でした。そこから 48.3 → 62.3 まで来たことになります。
しかし、まず超えたいベンチマークが80.7、その先にデザイナー制作の91.6があります。62.3は 最初のラインにも届いていません。ヘッダー・フッターの欠落(s2_8: 0点)や業種に合わない画像素材(s2_10: 0点)といった、プロンプト調整だけでは解決できない課題が残されています。
今後の課題
品質を数値で測れるようになったことで、次に何をすべきかも具体的に言えるようになりました。残っているのは大きく3つです。
1. スコアを上げる、そして上げる作業自体を自動化する
まずはこれです。48.3 → 62.3 は前進ですが、目標にはまったく届いていません。当面は評価駆動開発のループ(計測 → 弱点の特定 → プロンプト改修 → 再計測)を回し続けることになります。低スコアの項目から、ワークフローのどこが欠けているのかを自動でマッピングできる仕組みも欲しいところです。
その先に見据えているのは、このループから人間を外すことです。生成 → 採点 → 弱点の特定 → 改修までを自動で回し、人の関与なしにスコアが上がっていく状態を理想としています。採点を自動化できた以上、残りのステップも機械に寄せていけるはずです。
2. 採点の妥当性を確かめる
デザイナーの暗黙知をルーブリックに落とし込みましたが、それが本当に落とし切れているかは、まだ検証できていません。ベースライン計測でメンバーの感覚と大きくは食い違わなかった、という程度の確認にとどまっています。
必要なのは人手採点とのキャリブレーションです。同じサイトを人とLLMの両方で採点し、どの項目でどれだけズレるかを測ります。カテゴリ重み(構成35 / 見た目35 / コピー20 / 体験10)の配分が妥当かどうかも、ここではじめて検証できます。
3. ばらつきを統計的に扱う
スコアは、同じ条件で計測してもばらつきます(工程スコアのBefore 3回で total 46〜83 の幅が出ました)。この幅には、生成のばらつきと採点のばらつきの両方が含まれます。いまは複数回生成して平均と最低値を見ていますが、観測された差がばらつきの範囲内なのかどうかを、統計的に判定できてはいません。平均が68.5から68.4に動いたとき、「横ばい」と呼んでよいのかどうか、実のところ根拠を持っていないということです。
同じ生成物を繰り返し採点して採点側のばらつきだけを切り出す、試行回数を増やす、ばらつきの幅そのものを指標として持つ、といったあたりから手を付けていきます。「改善した」と言い切るためには、まずばらつきを見切る必要があります。
まとめ
デザインのように正解のないものでも、基準を言語化すれば、数字として扱いはじめることはできます。その数字がどこまで実感と合っているかは、これから確かめていく段階です。それでも、「なにかイマイチ」を「どこが何点足りないのか」として話せるようになったことで、議論と改善の土台ができました。ここから先は、精度を上げながら数字を伸ばしていきたいと考えています。