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

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

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

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

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



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

// 旧版 (壊れていた)
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";                   // 細マッチョ予備軍
}
解釈
体脂肪率(効率)0贅肉だらけ・最悪
筋力(集客力)15高い・強い
持久力(適応力)4低い
骨格(基盤)10普通
神経系(判断速度)16高い
合計45ガリガリ型レンジ(31-50)に該当
// 新版 (プロファイル解析型)
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 ペアプロでも変わらず、人間ユーザーの審美眼が品質を決定づけます。 :::details 完成版コードの全文(data.ts 抜粋)
export type ScoreResult = {
  total: number;
  axisScore: Record<AxisKey, number>;
  type: TypeId;
  reason: string;  // 新規追加
};

function determineType(
  axisScore: Record<AxisKey, number>,
  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設定方法