📚 本記事は 4 本シリーズの第 3 回です。
1. UTAGE API/MCPの実装制限と外部連携の現実解
2. Vercel/XServerのデプロイ先誤認を防ぐDNS確認手順
3. (現在) 診断ツールのタイプ判定アルゴリズム5つの落とし穴 ← この記事
4. SPF/DKIM/DMARCのGmail全PASS設定方法

5 月 5 日、午後 4 時 過ぎ。新規公開した診断ツールを回したユーザーから、画面のスクリーンショット付きで指摘が来ました。

チャートと回答がおかしくないか? なぜ?
筋力集客力が 15 あるのに 新規集客が弱いってどういうこと?
どういう判断ロジックなん?

確かに、レーダーには「筋力(集客力) 15/20」と高得点が表示されているのに、判定タイプは「ガリガリ型(筋肉不足、新規集客が弱い状態)」。矛盾しています。本記事は、この破綻を構造的に分析し、再設計するまでの 30 分の記録です。

何が起きていたか — 旧ロジックの限界

旧ロジックは極めて単純でした。

``typescript
// 旧版 (壊れていた)
function determineType(axisScore, total) {
// 合計スコアの範囲だけで判定
if (total <= 30) return "A"; // メタボ型
if (total <= 50) return "B"; // ガリガリ型
if (total <= 65) return "C"; // 標準型
if (total <= 80) return "D"; // マッチョ型
return "E"; // 細マッチョ予備軍
}
`

5 軸(体脂肪率・筋力・持久力・骨格・神経系)それぞれを 0-20 点で算出し、合計 0-100 点の範囲でタイプに振り分ける、という素直な設計です。多くの診断クイズもこの作りで動いています。

問題が起きたユーザーのスコアは以下でした。

解釈
体脂肪率(効率)0贅肉だらけ・最悪
筋力(集客力)15高い・強い
持久力(適応力)4低い
骨格(基盤)10普通
神経系(判断速度)16高い
合計45ガリガリ型レンジ(31-50)に該当

合計 45 点 → ガリガリ型(筋肉不足) と判定された一方で、実際の筋力は 15 点と高水準。判定タイプの説明文「筋肉不足、新規集客が弱い状態」とユーザーの体感が完全に乖離していました。

💡 KEY TAKEAWAYS
合計スコア型は「軸別の偏り」を情報として捨ててしまう設計です。同じ合計 45 点でも、5 軸均等な人と 1 軸極端に低い人ではプロファイルが全く違います。タイプ説明と実態が合わないのは当然の帰結でした。

再設計 — プロファイル解析型へ

修正方針は 2 つでした。

  1. 軸の偏りを優先順位付きルールで判定する
  2. 判定理由テキストを自動生成して結果画面に表示する
プロファイル解析型ロジックの優先順位ルール
プロファイル解析型ロジックの優先順位ルール

新しい優先順位ルール(6 段階)

`typescript
// 新版 (プロファイル解析型)
function determineType(axes, total) {
const { fat, muscle, stamina, bone, nerve } = axes;
const min = Math.min(fat, muscle, stamina, bone, nerve);
const max = Math.max(fat, muscle, stamina, bone, nerve);

// 1. 全軸が高水準
if (min >= 14) return { type: "E", reason: "5軸すべて14点以上の高水準" };

// 2. 集客力は高いが贅肉or鈍重
if (muscle >= 14 && (fat <= 10 || nerve <= 10)) {
const issue = fat <= 10 ? "効率の低さ" : "判断速度の鈍さ";
return { type: "D", reason:
集客力(筋力${muscle})は高水準だが、${issue}が次の成長の足かせ };
}

// 3. 効率が突出して悪い
if (fat <= 6) return { type: "A", reason:
体脂肪率${fat}点という低さが突出 };

// 4. 集客が突出して弱い
if (muscle <= 8 && fat >= 8) return { type: "B", reason:
効率は悪くないが集客力${muscle}が極端に弱い };

// 5. バランス型(全軸の差が4以内)
if (max - min <= 4) return { type: "C", reason:
5軸全てが${min}〜${max}でフラット };

// 6. フォールバック: 合計スコアで判定
if (total <= 40) return { type: "A", reason:
総合${total}点と低水準 };
if (total <= 55) return { type: "B", reason:
集客力強化が次の成長の鍵 };
if (total >= 75) return { type: "D", reason:
売上力はあるが機動性に課題 };
return { type: "C", reason: "中庸プロファイル" };
}
`

問題のユーザーケース(0, 15, 4, 10, 16)を新ロジックに通すと:

  • ステップ 1: min = 0 で 14 未満 → E ではない
  • ステップ 2: muscle = 15 ≥ 14 && fat = 0 ≤ 10 → D マッチョ型
  • 判定理由: 「集客力(筋力 15)は高水準だが、効率(体脂肪率 0)の低さが次の成長の足かせ」

矛盾感が解消されました。「集客力ある中堅企業だが、業務膨張で機動力が落ちている」 という典型的な「マッチョ型」プロファイルに合致する判定になりました。

判定理由テキストの効果

各ルールに reason` フィールドを持たせ、結果画面に「🧠 なぜこのタイプと判定されたか」セクションとして表示しました。

判定理由の表示効果
判定理由の表示効果

これだけで以下の 3 つが同時に解決しました。

  1. 納得感: ユーザーが「なぜ?」と思った瞬間に答えがある
  2. 教育効果: 5 軸スコアの読み解き方を自然に学べる
  3. 行動への接続: 「○○ の低さが足かせ」と書いてあるので「○○ を改善すべき」と直結する

💡 KEY TAKEAWAYS
診断結果は「タイプ名」だけでは不十分です。「なぜそう判定したか」を 1 行で示すことで、ユーザーの納得感とその後の行動意欲が両方上がります。

御社の判定ロジック、ユーザーの「なぜ?」に 1 行で答えられますか?
細マッチョ企業診断 / 3 分 8 問
診断する

設計の落とし穴 5 個

このプロセスで気づいた、診断クイズ設計時の頻出落とし穴を共有します。

落とし穴 1: 「合計スコア型」を最初に選んでしまう

実装が単純なので最初の選択肢になりがちですが、5 軸あるなら 5 軸の偏りを情報として活用すべきです。Day 1 から優先順位ルール型で設計するのが正解です。

突破: 各タイプの「定義条件」を先に言語化(「マッチョ型 = 売上ある中堅企業の業務膨張」)し、それを軸スコアの数式に翻訳します。

落とし穴 2: 軸名の比喩が直感に逆行する

「体脂肪率」は値が高い=贅肉が少ない、という意味で設計しましたが、医学的な感覚とは逆です。レーダーで「体脂肪率 18/20」と出ても、ユーザーが「贅肉だらけ?」「贅肉少ない?」のどちらか即判別できないリスクがあります。

突破: 軸ラベル横に「(効率)」「(集客力)」「(基盤)」などの 直感的な補助テキスト を置きます。比喩のブランド力を保ちつつ、解釈ミスを減らせます。

落とし穴 3: 判定理由がない結果画面

結果画面が「あなたはガリガリ型です」だけで終わると、ユーザーは納得できません。「なぜ」が常に必要 です。

突破: 各ルールに reason フィールドを持たせ、自動生成テキストで表示します。手書きで全パターン用意する必要はなく、変数埋込で十分です。

落とし穴 4: タイプ間の境界が曖昧

合計 50 点と 51 点で B → C に切り替わる、というレンジ判定は、ユーザーにとって不公平感を生みます。「もう 1 点高ければ別のタイプだったのに…」と。

突破: 範囲マッピングではなく 特徴量で判定 することで、境界の納得感が上がります。「muscle ≥ 14 かつ fat ≤ 10 ならマッチョ型」のように、明示的な閾値を持つルールにします。

落とし穴 5: 業種別カスタマイズなしの設計

5 軸の意味が業種で大きく違います。サービス業の「効率」と製造業の「効率」では指す業務が異なるからです。

突破: 共通質問 6 + 業種別質問 2 の構成にし、業種選択をクイズ最初の Q0 に置きます。質問の言語が業種に最適化されると、回答精度と納得感が同時に上がります。

修正にかかった時間 — 30 分

具体的な作業時間の内訳は以下です。

工程所要
ユーザー指摘の理解 + 矛盾点の構造化5 分
新ロジック設計(優先順位 6 段)10 分
実装(data.ts 修正・reason フィールド追加)5 分
UI 修正(判定理由カード追加)5 分
ローカルビルド + 本番デプロイ5 分
合計30 分

最も価値が高かったのは「ユーザーの一言指摘」でした。設計者が気づかない盲点を、ユーザーは 1 回触っただけで看破します。これは AI ペアプロでも変わらず、人間ユーザーの審美眼が品質を決定づけます。

完成版コードの全文(data.ts 抜粋)

``typescript
export type ScoreResult = {
total: number;
axisScore: Record;
type: TypeId;
reason: string; // 新規追加
};

function determineType(
axisScore: Record,
total: number
): { type: TypeId; reason: string } {
const { fat, muscle, stamina, bone, nerve } = axisScore;
const values = [fat, muscle, stamina, bone, nerve];
const min = Math.min(...values);
const max = Math.max(...values);

if (min >= 14) {
return { type: "E", reason:
5軸すべてが14点以上の高水準。最適化の最終段階に到達。 };
}
if (muscle >= 14 && (fat <= 10 || nerve <= 10)) {
const issue = fat <= 10 && nerve <= 10
?
効率(${fat})と判断速度(${nerve})の両方の低さ
: fat <= 10 ?
効率(体脂肪率${fat})の低さ : 判断速度(神経系${nerve})の鈍さ;
return { type: "D", reason:
集客力(筋力${muscle})は高水準だが、${issue}が次の成長の足かせ。 };
}
if (fat <= 6) {
return { type: "A", reason:
体脂肪率(効率)${fat}点という低さが突出。無駄業務を削ぎ落とすことが最優先。 };
}
if (muscle <= 8 && fat >= 8) {
return { type: "B", reason:
効率(${fat})は悪くないが、集客力(筋力${muscle})が極端に弱い。 };
}
if (max - min <= 4) {
return { type: "C", reason:
5軸すべてが${min}〜${max}点でフラット。 };
}
if (total <= 40) return { type: "A", reason:
総合${total}点と低水準。 };
if (total <= 55) return { type: "B", reason:
総合${total}点。集客強化で大きく伸びる余地あり。 };
if (total >= 75) return { type: "D", reason:
総合${total}点と高め。組織機動力の最適化で次のステージへ。 };
return { type: "C", reason:
総合${total}点。中庸プロファイル。 };
}
``

次のアクション

診断ツールのロジック設計は、「合計スコア型 → プロファイル解析型」の進化を一度経るのが定石です。最初から完璧な設計は不可能なので、ユーザーの指摘を受け取った瞬間に修正できる組織体力が重要になります。

御社が「ユーザーの一言で即直せる組織」か「気合と根性で対応する組織」かは、企業体型に直結します。細マッチョ企業診断 で 5 軸スコアを測ってみてください。神経系(判断速度)スコアが、こうした即応力の指標になります。

→ 次の記事: SPF/DKIM/DMARCのGmail全PASS設定方法