# Career Personality

> 当用户提到MBTI、职业性格、性格测试或性格特征时调用本Skill，用于分析性格倾向及其在工作环境中的表现。

- Skill: `ahang1598/career-personality` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add ahang1598/career-personality`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ahang1598/career-personality/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ahang1598 (https://skillmd.com/u/ahang1598)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/ahang1598/career-personality

---


# MBTI 职业性格测评 Skill

## Overview

生成 MBTI 职业性格测评题目并计算结果，覆盖 4 对维度：

- EI：外倾（E） - 内倾（I）
- SN：实感（S） - 直觉（N）
- TF：思维（T） - 情感（F）
- JP：判断（J） - 知觉（P）

本测评共 44 题，每题二选一（A/B），每个选项归属一个维度字母。
8 个字母各均匀出现 11 次（共 88 个选项），保证 4 对维度的对称设计。

## When to Use This Skill

在以下场景触发本 skill：

- 用户明确说出“测MBTI”“做MBTI测试”“帮我测一下MBTI”“启动MBTI测评”这类完整指令
- 用户直接询问“我是什么MBTI”“生成我的MBTI人格报告”“帮我算我的MBTI类型”
- 用户主动提及“打开MBTI测评工具”“开始MBTI测评”
- 用户说“我要做人格测试，指定是MBTI的”“我想测MBTI的完整维度”
- 用户历史对话3轮内明确提过要做MBTI测评，当前轮次说“继续”“开始吧”“下一步”
- 用户直接发送“MBTI测评”“MBTI测试”作为唯一指令，无其他无关内容

在以下**边界场景**建议先与用户确认测评意图：用一句简短反问（例如"您是想做 44 题 MBTI 职业性格测评吗？"），用户明确确认后再启动测评流程。若当前对话模式不支持中途询问（如 Visualizer 一次性渲染 / 已直接加载题目卡片），按以下兜底规则处理：
- 同轮若还含其他无关任务（如"帮我查 MBTI，再做下测评"），不启动测评，等用户单独询问测评时再触发
- 用户连续两轮仍在同一边界场景徘徊且未明确确认，默认判定为非测评需求，按下方"When NOT to Use This Skill"处理，改用纯 MBTI 知识科普
- 7 类边界场景示例：
  - 用户只单独发送"MBTI"三个字，没有后续补充任何内容
  - 用户讨论某类MBTI人格特征后，说"我好像就是这种人""我感觉我符合这个类型"
  - 用户说"我想了解自己的性格类型""帮我做个性格测试"，没有明确指定是MBTI
  - 用户说"我想看看我的人格是什么样的""分析一下我的性格"，没有限定测评类型
  - 用户提到"我朋友说我是XX型MBTI，想验证一下"，没有直接说要启动测评
  - 用户在讨论MBTI相关话题后，说"帮我测测看"，没有明确指向其他测评工具
  - 用户说"我想做个测试看看我适合什么MBTI类型"，表述模糊未直接唤起测评

## 派生请求前置 gate（强制，先判别再走流程）

> **任何轮次触发本 skill 时，渲染任何 widget_code 之前，必须先回答下方 3 个判定问题。任一答案为"是"，本轮即进入"派生请求"模式，必须按本节 4 步顺序执行完毕后才能继续走 §0/§R 渲染流程。**

**判定 3 问（命中任一即视为派生请求）**：

1. **触发词判定**：本轮用户消息是否包含下列任一触发词/语义？
   - **重测类**："再来一份 / 再出一份 / 给我一份 / 重置再来一遍 / 我想再做一遍 / 再来一次 / 重新测 / 重测 / 重新跑一遍"
   - **复制类**："朋友也要测 / 同事也来一套 / 给 朋友 / 同事 / 孩子 / 家属 也做一套 / 把上次的卡片给我 / 给我之前那份 / 之前那张卡 / 把卡片发我"
   - **改样式类**："换个 UI / 换个样式 / 试试别的风格 / 加个主题 / 改个颜色 / 美化一下 / 精简一下 / 适配一下主题 / 加点渐变 / 加个阴影 / 字号小一点 / 按钮大一点"
   - **出报告类**（上一轮已发出卡片后）："出报告 / 算一下 / 看结果 / 跑分 / 评分 / 提交答案 / 把结果给我"
2. **会话上下文判定**：用户上一轮已收到本 skill 题目卡片或测评报告，本轮再次要求测评相关动作。
3. **轮次判定**：本对话流中 skill 已被加载 ≥ 2 次或题目卡片已被渲染 ≥ 2 次。

**派生请求进入后必须执行（强制，4 步顺序不可换）**：

1. **第一步（read 证据）**：`Read references/template.md` 并按 §T1/§T2 原样逐字符读取完整 HTML 模板；**禁止凭记忆渲染、禁止复制本对话上文已发出的 widget_code 当新产物**。
2. **第二步（数据替换）**：§T1 用 `Read references/questions.md` 解析出的题库 JSON 替换 `/*__QUESTIONS_JSON__*/`；§T2 用 `python scripts/calculate_mbti.py --display-answers <原样回传 JSON> --compact` 输出替换 `/*__REPORT_JSON__*/`（remap 与校验由脚本内置完成，见 §3.2.2，**禁止手写 remap**）。
3. **第三步（inline 自查）**：依次核对下方"前置 gate 自查"段 4 项（Read 证据 / 数据替换 / root ID / ID 后缀扫描），全部通过后方可输出 widget_code。
4. **第四步（byte-stable）**：禁止以"重测第二轮 / 朋友专用 / 主题调整"等任何理由对 widget_code 任何字符做出与首发不一致的修改（包括标题、分段文案、按钮文案、类名、颜色、字重、尺寸、圆角）。**除数据占位符外，每一个字符都必须与首发 byte-stable 一致**。

**前置 gate 的强度升级**：

- 第 119 行 "派生请求渲染约束（强制，禁止走捷径）" 段规则在派生场景下自动升级为**硬约束**——任何"复制上文 widget_code 然后改 ID 后缀"、"自绘一份卡片"行为一律按硬违规处理；禁止"参考上文""上次那么做""我没读到 template.md 但我记得差不多"等任何绕过说法。
- §7.禁止事项 段关于"提交按钮延迟出现""复制答案回传""先返部分题目再补全"等链路违反，一律按硬违规处理。

**前置 gate 自查（输出 widget_code 之前必查，否则禁止输出）**：

- [ ] **证据**：本对话流最近 5 条消息内必须出现至少 1 次 `Read references/template.md` 工具调用；不出现即视为走捷径，必须重新 Read 并重做。
- [ ] **数据替换**：题目卡片必然包含 4 个 `.q-sec` 提示词（段标题"第一部分 外倾-内倾（E/I）"等）；测评报告必然以 `#mbti-rpt{...}` 为根 CSS。
- [ ] **root ID**：widget 首项 CSS 必须以 `#mbti-card{...}` 或 `#mbti-rpt{...}` 开头；出现 `.mbti-card{` / 自定义类名 / `<div id="app">` 即违规。
- [ ] **改 ID 后缀扫描**：widget 内（除数据占位符外的 `id=` / `class=` / `data-attr`）不出现 `-v2` / `-重测` / `-friend` / `-重做` / `-retest` / `-theme` 等任何派生后缀。

任一自查项不通过，必须**整体重做**——禁止"小修小补"伪装通过；禁止绕过 gate 单独走 §0.1 渲染流程。

## When NOT to Use This Skill

以下场景**不触发**本 skill：

- 用户仅询问MBTI基础科普：“MBTI有多少种类型”“MBTI的四个维度是什么”“MBTI的起源是什么”
- 用户仅查询某类MBTI人格的特征：“INTJ的特点是什么”“INFP适合什么职业”“ESTP的恋爱观”
- 用户讨论MBTI的非测评应用场景：“MBTI面试技巧”“用MBTI做职场沟通”“MBTI社交指南”
- 用户在讨论其他完全无关的话题时，偶然提到MBTI：“我昨天和朋友聊到MBTI”“我同事是ISFJ”
- 用户明确要求其他类型的测试：“我要做九型人格测试”“帮我测DISC性格”“生成霍兰德职业测试”
- 用户的需求是内容生成类：“帮我写一篇MBTI主题的小红书文案”“生成MBTI相关的短视频脚本”
- 用户同时提出多个混合需求，且没有明确表示要做测评：“帮我查MBTI类型，再写一份职场沟通方案”
- 用户明确表示“我不想做测评”“我只是想了解MBTI的知识”，直接拦截所有测评唤起

## 题目元数据

```yaml
assessment_id: MBTI-44-001
assessment_name: "MBTI 职业性格测评"
question_total: 44
dimension_count: 8   # E/I/S/N/T/F/J/P
dimension_pairs: 4   # EI/SN/TF/JP
role_type_count: 16  # ISTJ...ESFJ
references_path: "references/questions.md"  # 数据文件形态 B（多文件，当前仓库采用）：题库在 questions.md；另有 profiles.md / dimensions.md。脚本未指定任何路径时自动加载这 3 个文件；指定 --references-path 可回退单文件模式（含全部 3 个 H2 段的合并 mbti.md）
score_calculation_path: "scripts/calculate_mbti.py"
score_function: "calculate_scores"
entrypoint: "python scripts/calculate_mbti.py --display-answers '{\"1\":\"A\",\"2\":\"B\",...,\"44\":\"B\"}'"
```

## references 数据文件结构（关键，允许单文件或多文件）

测评数据（题库 / 16 型档案 / 维度对详情）以 **3 个 H2 段** 组织，每段必须包含一段散文说明 + 一个 ```json 代码块，段顺序固定如下（**H2 标题改名会破坏脚本解析，禁止改名**）：

1. `## 题库` → 44 题题库数组
2. `## 16 型人格档案` → 16 型档案数组
3. `## 维度对详情` → 4 维度对 + 8 端详情数组

**文件形态（二选一，均受支持，按需选用）**：

- **形态 A：单文件（兼容保留，非当前采用）**：3 个 H2 段全部放在一个合并 md 文件内（如 `references/mbti.md`），脚本用 `--references-path` 指定。
- **形态 B：多文件拆分（默认，当前仓库采用）**：3 个 H2 段拆分为 3 个独立 md 文件：
  - `references/questions.md`（含 `## 题库`）
  - `references/profiles.md`（含 `## 16 型人格档案`）
  - `references/dimensions.md`（含 `## 维度对详情`）
  - 每个拆分文件内**仍必须保留对应的 H2 标题与 ```json 代码块**（解析锚点不变），仅文件边界变化；
  - 脚本**不传任何路径时自动加载这 3 个文件**；也可用 `--questions-path` / `--profiles-path` / `--dimensions-path` 分别覆盖；传入 `--references-path` 则整体回退单文件模式。

脚本 `calculate_mbti.py` 的 `load_md_section(md_path, section_title)` 按 H2 标题定位段、提取 ```json 代码块、`json.loads` 解析；**单文件与多文件两种形态共用同一解析逻辑**。若任意一段缺失或代码块未闭合，脚本会抛 `ValueError`。拆分/合并只是文件组织方式，不影响评分逻辑与输出。

## 题目加载硬约束（关键）

1. 本 skill **仅有唯一测评流程**：展示 44 题 → 用户作答 → 输出完整测评报告。不存在其他题数或版本，不询问用户"要做多少题"、不提供任何"版本二选一"入口。
2. 触发测评后必须直接展示全部 44 题进入答题流程，不得插入任何"选择题目数量""选择版本"的中间步骤。
3. 题目必须从 references 数据文件的 `## 题库` 段内 ```json 代码块中读取 `questions` 数组，按 `id` 1→44 原序展示；**禁止模型自行编造或凭印象生成题目**。题库段的数据源形态见"references 数据文件结构"一节：当前仓库采用形态 B（多文件），题库从 `references/questions.md` 读取。
4. 题库共 44 题，每题必须包含 `id` / `question` / `prompt` / `options`（每个 option 含 `option` / `content` / `dimension`）四要素，渲染时不得遗漏任一字段，尤其不得省略 `options.content`。
5. 若题库读取失败或不足 44 题，必须直接报错说明，不得用"部分题目"凑数、不得用模型自拟题补齐。
6. 一次性展示全部 44 道题，不得分页、不得"先展示前几题"、不得逐题加载。

### 题目数据加载流程

模型在渲染交互卡片前，**必须先读取题库文件**，不得凭训练数据回忆题目：

1. 通过 Read 工具读取题库文件 `references/questions.md`（当前仓库采用形态 B 多文件，题库独立存放；此文件仅 21KB，渲染题目时**只读这一个文件**，不要读取 profiles.md / dimensions.md），定位 `## 题库` 段下的 ```json 代码块，解析 `questions` 数组。
2. **同时 Read `references/template.md`**（§T1 模板，byte-stable 唯一权威源），把题库 JSON 替换模板中 `/*__QUESTIONS_JSON__*/` 占位符，按模板**原样逐字符**渲染全部 44 题（模板 JS 已内置 DISPLAY_ORDER 四段分组与视觉编号逻辑），不得打乱、不得省略、不得改写题干/提示语/选项文本/维度归属；**禁止自绘样式、禁止凭记忆渲染**。
3. 若 Read 工具调用失败、`## 题库` 段缺失、```json 代码块未闭合、返回内容不足 44 题、或解析报错，模型**必须**向用户返回明确的错误提示（如"题库文件读取失败，请检查 references/questions.md 中 `## 题库` 段是否存在且包含 44 题"），**不得**：
   - 用模型自拟/凭印象/从训练数据回忆的题目补齐
   - 用"部分题目"凑数渲染卡片
   - 返回空卡片或不做任何响应
   - 静默跳过题目渲染只输出其他元素（标题、进度条、维度标签等）

## 派生请求渲染约束（强制，禁止走捷径）

**禁止派生请求走捷径（最常见违规）**：用户说"再来一份""朋友也要测""给同事也来一套""试试换个 UI""重置再来一遍""我想再做一遍"——一律重新 `Read references/template.md` + 走上方标准流程（读题库 → 替换占位符 → 原样输出），**不允许**把"上一次的卡片"复制过来改 ID 后缀（如 `-friend` / `-v2` / `-重测`）当新产物输出。新卡片必须是 template.md 的 byte-stable 实例，所有 ID、class、style、文案与首发保持逐字符一致。

适用对象包括但不限于：

- 题目卡片：任何轮次、任何触发词唤起的测评卡片，都必须是 §T1 模板的实例（仅 `/*__QUESTIONS_JSON__*/` 数据不同）
- 测评报告：任何轮次、任何答案组输出的报告，都必须是 §T2 模板的实例（仅 `/*__REPORT_JSON__*/` 数据不同）
- 派生/重测场景：用户要求"重测""换个样式""再出一份"时，输出内容不得与首发在 CSS/类名/ID/结构/文案/交互上有任何差异（仅数据可变），**禁止**以"改个颜色""换个布局""加个主题"等方式产出与模板不符的卡片或报告

**自查（输出前必查）**：本次输出的 widget_code 是否与 template.md 对应模板逐字符一致（除数据占位符处）？若不一致，必须重新 Read template.md 重做，禁止带差异输出。

## 工作流程

本 skill 只有一条流程：**展示 44 题 → 用户在卡片内作答 → 点击"提交测评" → 模型直接输出完整测评报告**。具体步骤：

1. 渲染完整交互卡片展示全部 44 题，用户在卡片内完成作答（每题仅 A 或 B）。
2. 用户点击"提交测评"时：
   - 存在未作答题目 → 卡片内自动滚动到第一个未答题并高亮提示，**不提交、不出报告**；
   - 已答完全部 44 题 → 卡片内 JS 通过宿主回传机制（`sendPrompt()`）将答案 JSON 自动作为用户消息发送。
3. 模型收到答案后对答案进行校验，确保题目编号、答案类型、维度映射正确。
4. 调用评分脚本计算 8 个维度字母计数 + 4 对维度百分比 + dominant_type。
5. **直接**返回用户完整测评报告：16 型人格代码 + 各维度得分/百分比 + 特点分析（优势/缺点）+ 推荐职业。

**强制约束（不允许有其他链路）**：用户点击"提交测评"且答完全部题目后，模型必须**直接输出完整测评报告**。禁止以下任何行为：要求用户复制/粘贴答案回传、要求用户手动输入答案、先输出答案 JSON 等待用户确认、把出报告推迟到用户再次追问之后、以任何形式要求用户参与答案传递。

## WorkBuddy 视觉化交互卡片约束（关键）

本 skill 在 Workbody / WorkBuddy 场景下，不仅需要返回结构化 JSON，也必须支持可视化内联交互卡片：即在对话中直接渲染一个 HTML/CSS/JS 组件，模拟真实测评页面，且在聊天窗口中保持稳定的视觉结构。

### 0. 题目卡片渲染规范（强制，100% 一致）

为保证**每个用户、每次会话**触发本 skill 时，题目展示的格式与样式**完全一致**，禁止模型自行设计或自由发挥。**渲染规范以 `references/template.md` 为唯一权威源（byte-stable 标准）**：

- 模型每次渲染题目卡片前，**必须先 `Read references/template.md`**（连同 `references/questions.md`），将 §T1 模板中 `/*__QUESTIONS_JSON__*/` 占位符替换为从题库解析出的 44 题 JSON 后，**原样、逐字符**输出为交互卡片 `widget_code`。
- **禁止在未读取 template.md 的情况下**凭记忆、凭印象或按本文件文字描述自行渲染；禁止改模板任何 CSS / 类名 / ID / HTML 结构 / 文案 / JS 逻辑。
- 下方 §0.1 固定 CSS、§0.2 固定结构、§0.3 固定交互行为是模板内容的文字固化说明（供理解与自查），**一切以 template.md 实际内容为准**；若二者存在字面差异，以 template.md 为准。

#### 0.1 固定 CSS（强制）

固定 CSS 全部位于 `references/template.md` §T1 模板 `<style>` 块内（`#mbti-card` 系列，唯一权威源，含选中态 `#E8F1FF`/`#4E8CFF`/`#1E4FB8`、未选中态 `#F5F6F8`/`#E5E7EB`/`#3A3F47` 等固定色值）。渲染前必须 Read template.md 并按 §T1 原样输出；本文件不再内嵌 CSS 副本，避免与权威源两份内容产生漂移。自查时以 template.md §T1 为准。

#### 0.2 固定结构（强制）

模型渲染时必须以 `#mbti-card` 为根容器，依次输出：标题区（`.q-head` 内 `.q-title` + `.q-count`）、副标题（`.q-sub`）、进度条（`.q-track` 内含 `<i>`）、题目列表（`#mbti-list`）、底部（`.q-foot` 内 `.q-tip` + `.q-submit` 提交按钮）。44 题按"每 11 题一段"插入 4 个 `.q-sec` 分段块（标题 `.q-sec-t` + 提示词 `.q-sec-d`，文案见 §3 分段提示词要求），每题一个 `.q-item`（`.q-num` 顺序编号 = 数组下标 +1、`.q-text` 题干、`.q-prompt` 提示语、`.q-opts` 内两个 `.q-opt` 选项按钮，按钮文本为 `A. 选项内容` / `B. 选项内容`）。**选项按钮文本强制规则（关键）：按钮文本必须且只能取对应 option 对象的 `content` 字段原文，禁止使用 `dimension` 字段值（E/I/S/N/T/F/J/P 字母）作为按钮文本；`dimension` 仅供评分脚本使用，渲染时一律忽略。若任一按钮文本是单字母（如 `A. E`、`B. N`），即为渲染错误，必须改为对应 `content` 原文。**

#### 0.3 固定交互行为（强制）

- 选项点击：仅切换当前题选中态，选中按钮加 `.on` 类（互斥，同一题两端不可同时高亮），并实时更新 `.q-count` 文本（`已答 / 44`）与 `.q-track i` 宽度（已答数/44×100%）。
- 提交按钮（文案固定为 `提交测评`，始终可见可点）：存在未作答题目 → 在 `.q-tip` 显示"还有 N 题未作答，请先完成第 X 题"，给第一个未答题 `.q-item` 加 `.miss` 类并 `scrollIntoView` 滚动到该题，约 1.6s 后移除 `.miss`；已答完 → 调用宿主回传 `sendPrompt('[MBTI测评提交] 我的答案如下：' + JSON.stringify(答案对象))`，答案对象 key 使用**顺序号 1→44**（数组下标 +1），value 为 A/B。

- **渲染流程（强制）**：
  0. **派生请求前置 gate（render-time，覆盖本段渲染流程）**：本轮若是派生场景（命中上方"派生请求前置 gate"段任一判定问题），**必须**先把后文 step 1-2 的整套流程真正跑完（即 `Read references/template.md` + `Read references/questions.md` + 替换占位符 + 通过前置 gate 自查 4 项），**禁止**用"我记得模板"、"上一轮卡片复制"、"凭印象渲染"作为渲染依据；本轮若跳过本 gate 直接进入渲染流程，整段视为硬违规，必须重做。
  1. 通过 Read 工具读取数据文件 `## 题库` 段，解析 `questions` 数组（44 题，见"题目数据加载流程"）；
  2. **Read `references/template.md`**，把 `questions` 数组 JSON 替换 §T1 模板中 `/*__QUESTIONS_JSON__*/` 占位符，按模板**原样逐字符**输出完整 HTML 交互卡片（模板已内置 §0.1 CSS、§0.2 结构、§0.3 行为、§3 分段文案），作为交互组件（Visualizer show_widget）的 `widget_code` 输出；禁止自绘样式、禁止凭记忆渲染。
  3. **渲染后自查（关键）**：输出前逐题核对全部 88 个选项按钮，按钮文本必须来自对应 `content` 原文（前缀 `A.` / `B.`）；若发现任一按钮文本为单字母（E/I/S/N/T/F/J/P），立即修正为该选项的 `content` 原文后再输出。同时再跑一次上方"派生请求前置 gate"自查 4 项，全通过后再输出。
- **唯一可变量**：题目数据（题干/提示语/选项内容）。除此之外 CSS、类名、颜色、布局、分段文案、按钮行为均不得改动。
- **禁止事项**：
  - 禁止自行编写或改动 CSS（含选中态 `#E8F1FF`/`#4E8CFF`/`#1E4FB8`、未选中态 `#F5F6F8`/`#E5E7EB`/`#3A3F47` 等固定色值）；
  - 禁止更换类名、调整布局、修改四段分组标题与提示词文字；
  - 禁止在规范结构外再包裹自定义样式或追加额外脚本；
  - 禁止"凭印象"重画题目卡片或每次输出不同样式。

### 1. 渲染目标

当用户触发"开始测评"或"MBTI 测评"时，必须返回一个可渲染的内联卡片，而不是纯文本说明。

该内联卡片必须包含：

- 顶部标题：`MBTI 职业性格测评`
- 二级说明：`共 44 题，请根据真实感受选择"A"或"B"`
- 顶部进度条：显示已答题数/总题数，例如 `17 / 44`
- 维度标签：例如 `E/I · 外倾-内倾`（当前题所属维度对）
- 题目列表：每一题显示**顺序编号** + 题干 + 提示语（prompt）
  - 顺序编号必须按"从上到下依次递增"渲染：第 1 题显示 `1`、第 2 题显示 `2`、……、第 44 题显示 `44`
  - **禁止使用题库 JSON 里的 `id` 字段作为展示编号**（题库 `id` 仅用于内部作答映射，不直接呈现给用户）
  - 题目展示顺序必须与题库 `questions` 数组顺序一致，不打乱
  - 顺序号与题库 `id` 的对应关系在本次题库下恰好一致（`id=1` 即第 1 题、`id=44` 即第 44 题），但渲染层不得依赖该巧合，必须以"数组下标 +1"作为顺序号唯一来源
- 每道题的答题区域：左右两侧各有一个单选按钮，分别对应 `A` 与 `B`
  - 选项按钮文本必须显示选项内容（content）原文，而不是仅显示 A/B 字母或维度字母（E/I/S/N/T/F/J/P）
- 当前题目高亮：当前题对应的题目区域、标签和按钮状态应明显区分
- 底部交互：可前后翻题，并提供始终可见的"提交测评"按钮（禁用/启用规则见 §4 交互约束）
- 允许用户在同一对话流中直接答题，不需要跳转到其他页面

### 2. 视觉结构要求

视觉结构必须遵循以下稳定版布局：

- 整体是一个白色/浅灰背景的卡片容器，边框柔和、圆角适中
- 标题采用大字号、黑色/深灰字体
- 子标题采用中等字号、灰色字体，位置在标题下方
- 顶部右侧显示进度文本，格式固定为 `已答题数 / 44`（如 `17 / 44`），数字含义为"已作答题数"，不是"当前题号"
- 进度条位于标题区下方，长度为卡片宽度的主内容区域，宽度比例 = 已答题数 / 44
- 当前维度对标签使用绿色/浅绿色浅底色，圆角和内边距稳定
- 题目行高统一，题干左对齐，选项按钮右对齐
- **题目顺序编号（关键）**：每题左侧的编号必须按"从上到下依次递增"渲染（第 1 题显示 `1`，第 2 题显示 `2`，……，第 44 题显示 `44`），**禁止使用题库 JSON 的 `id` 字段作为展示编号**。渲染器取数组下标 `index + 1` 作为顺序号；题库 `id` 仅在提交作答时用于内部映射，不参与展示。
- `A / B` 是两个二选一按钮，选中态必须使用如下固定色值，不允许模型自行挑选颜色：
  - 未选中：浅灰底 `background: #F5F6F8`，边框 `1px solid #E5E7EB`，字色 `#3A3F47`
  - 已选中：淡蓝底 `background: #E8F1FF`，边框 `1px solid #4E8CFF`，字色 `#1E4FB8`，并可加左侧细色条 `box-shadow: inset 3px 0 0 #4E8CFF`
  - 同一题两个按钮互斥：选中 A 时 B 必须退回浅灰底，反之亦然，禁止两端同时高亮
  - 已选中按钮必须有可感知的视觉差异（背景色明显比未选中更蓝、边框颜色变深、字色更蓝），用户一眼能看出选的是哪一个
- 题目行之间用浅边框分隔
- 在页面底部可以显示辅助说明，例如 `今天帮你做些什么？@引用对话文件 / 调用技能与指令`
- 44 道题必须按四段显示，每一段前方都有明确的分段标题和提示词，不得混合成一大段连续题目

### 3. 分段提示词要求（必须严格遵守）

44 道题必须按"维度对"分成 4 个部分，并且每个部分的标题和提示文案必须按以下固定文本输出，不得改写语义、删减内容或自由替换：

1. 第一部分标题：`第一部分 外倾-内倾（E/I）`
   - 提示词：`下面列举了若干情境，请根据你通常的思考和行为方式选择最接近的答案。选 A 即倾向外倾（E），选 B 即倾向内倾（I）。请按顺序回答本部分全部 11 题。`

2. 第二部分标题：`第二部分 实感-直觉（S/N）`
   - 提示词：`下面列举了若干情境，请根据你通常接收信息的方式选择最接近的答案。选 A 即倾向实感（S），选 B 即倾向直觉（N）。请按顺序回答本部分全部 11 题。`

3. 第三部分标题：`第三部分 思维-情感（T/F）`
   - 提示词：`下面列举了若干情境，请根据你通常做决策的方式选择最接近的答案。选 A 即倾向思维（T），选 B 即倾向情感（F）。请按顺序回答本部分全部 11 题。`

4. 第四部分标题：`第四部分 判断-知觉（J/P）`
   - 提示词：`下面列举了若干情境，请根据你通常的生活方式选择最接近的答案。选 A 即倾向判断（J），选 B 即倾向知觉（P）。请按顺序回答本部分全部 11 题。`

要求：

- 四段必须依次连续出现
- 每段上方都必须显示标题与提示词
- 标题和提示词必须原样保留，不得改成中文简写、删减或替换 A/B 表述
- 每段内部题目都必须属于对应维度对，不得跨段混排

### 3.1 分组算法（强制，渲染前必须执行，不得跳过）

模型渲染题卡前，**必须先按 dimension 字段聚合再决定展示顺序**，禁止直接按 id 顺序 11 等分。具体流程：

1. **读取题库**：从 `references/questions.md` 的 `## 题库` 段 JSON 代码块解析 `questions` 数组；对每题取 `options[0].dimension` 与 `options[1].dimension`（同一题两端必然属于同一维度对两端），据此归类到 EI/SN/TF/JP 4 组。
2. **聚合成 4 组**：分别得到 EI / SN / TF / JP 4 个题号集合（按维度字母归类，注意各题两端字母可能不同：同一 EI 题一端 E、另一端 I，可任取一端归到 EI 组）。
3. **校验总数**：四组题数合计必须 = 44，且每组必须恰好 11 题；若任一组 ≠ 11 或合计 ≠ 44，**直接报错**（"题库结构异常：EI={n1}题 / SN={n2}题 / TF={n3}题 / JP={n4}题，合计={total}题"），禁止继续渲染。
4. **段内顺序**：每段组内按题号升序展示，**段内最后一道即为该维度的"复盘题"（id 41/42/43/44）**。本仓库题库四段题号集合为：

   | 段 | 维度对 | 段内题号（升序，最后一道为复盘题） |
   |---|---|---|
   | 1 | EI | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, **41** |
   | 2 | SN | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, **42** |
   | 3 | TF | 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, **43** |
   | 4 | JP | 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, **44** |

   每段展示顺序严格按上表升序：EI 段 `Q1→Q2→...→Q10→Q41`、SN 段 `Q11→...→Q20→Q42`、以此类推。
5. **段间顺序**：四段依次按 EI → SN → TF → JP 顺序排列。
6. **展示编号**：每题展示编号 = 全局累计顺序号 1→44（不切换为题库 id），与段内位置无关，第 1 题显示 1、第 44 题显示 44。
7. **禁止"按 id 顺序 11 等分"的快捷渲染**：题库原序可能并非 11 等分块结构（如本仓库题库是 10-10-10-10 + 1-1-1-1 形态）；按 id 等分 11 题一段必然跨段混排（如 Q11 属 SN 会被错位放入 EI 段，Q21-Q22 属 TF 会被错位放入 SN 段，依此类推），违反上方"不得跨段混排"硬规则。

> 段内提示词中的 A/B 映射（"选 A 即倾向 X，选 B 即倾向 Y"）仅描述维度对整体倾向，各题按钮的实际维度归属以题库 `dimension` 字段为准（部分题目的 A/B 归属与段提示相反）；按钮文本一律取 `content` 原文，禁止按段提示词的 A/B 映射推断或替换按钮文本。

### 3.2 DISPLAY_ORDER 常量与提交 Remap 协议（强制，渲染与回传链路必须遵守）

为彻底消除"卡片展示顺序 vs 答案键"之间的歧义，本节同时锁定 **DISPLAY_ORDER 常量**（视觉位置 ↔ 题库真实 id 的强制映射）和 **提交 remap 协议**。评分脚本内嵌与本节同源的 DISPLAY_ORDER 常量，`--display-answers` 在脚本内部自动完成 remap + 双向校验——**映射只维护一份（脚本内）、remap 只执行一次（脚本内）、模型零手写逻辑**，从机制上杜绝"人工翻译映射表"类错误。

#### 3.2.1 DISPLAY_ORDER（强制常量，禁止改动）

四段展示顺序的题号集合（本仓库题库固定形态，禁止重组、扩展或删减）：

```yaml
# 段内题号（升序），最后一道为该维度复盘题
section_EI_ids: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 41]
section_SN_ids: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 42]
section_TF_ids: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 43]
section_JP_ids: [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 44]
```

视觉位置 1→44 与题库真实 id 的强制映射：

```yaml
display_pos_to_real_id:
  "1": 1,   "2": 2,   "3": 3,   "4": 4,   "5": 5,   "6": 6,   "7": 7,   "8": 8,   "9": 9,   "10": 10,
  "11": 41,    # EI 段尾复盘题
  "12": 11,  "13": 12, "14": 13, "15": 14, "16": 15, "17": 16, "18": 17, "19": 18, "20": 19, "21": 20,
  "22": 42,    # SN 段尾复盘题
  "23": 21,  "24": 22, "25": 23, "26": 24, "27": 25, "28": 26, "29": 27, "30": 28, "31": 29, "32": 30,
  "33": 43,    # TF 段尾复盘题
  "34": 31,  "35": 32, "36": 33, "37": 34, "38": 35, "39": 36, "40": 37, "41": 38, "42": 39, "43": 40,
  "44": 44     # JP 段尾复盘题
```

**强制约束**：
- 卡片渲染必须按 `section_EI_ids + section_SN_ids + section_TF_ids + section_JP_ids` 拼接顺序逐题输出，每题视觉编号 = 该题在拼接序列中的索引（1→44），**禁止向学员暴露 `id=41/42/43/44` 这类原始题号**。
- 一旦题库数据发生变更（如 ids 41–44 维度错位、id 数 ≠ 11/段），必须先修此处常量、且重跑 §3.1 第 3 步 `每段=11、合计=44` 校验通过后，方可继续渲染。

#### 3.2.2 提交 Remap 协议（强制，remap 由脚本内置完成）

用户在卡片内作答回传的 JSON 形如 `{"1":"A","2":"B",...,"44":"B"}`，键为**视觉位置 1–44**。**调用 `scripts/calculate_mbti.py` 必须使用 `--display-answers` 参数原样传回该 JSON；禁止手写 remap、禁止把原 JSON 传给 `--answers`**：

```bash
python scripts/calculate_mbti.py --display-answers '{"1":"A","2":"B",...,"44":"B"}' --compact
```

脚本内部按 §3.2.1 `display_pos_to_real_id` 权威映射（内嵌 DISPLAY_ORDER 常量，与本节同源）自动把每个键从视觉位置改写为真实题号 id，再做 §3.2.3 双向校验；**任一校验失败脚本直接报错并以非零码退出，不评分**。

**原因说明**：评分脚本仅按 `q.id` 取答案 + 按 `options[].dimension` 累加字母计数，对展示顺序、维度分组无感。若直接以视觉位置键的 JSON 调用 `--answers`，id=1–40 题会被正确读取，但 id=41–44（每段复盘题）会因为 keys="11/22/33/44" 找不到对应题而**被静默丢弃**，导致每维度少计 1 题、最终人格类型可能跑偏。把映射与校验沉入脚本后，出错点从"人工翻译映射表"变为"原样传回 JSON"，在机制上被消除。

**禁止事项**：
- **禁止手写 remap 字典 / range 循环 / assert 后再传给 `--answers`**：remap 必须且只能由脚本内 DISPLAY_ORDER 常量完成，模型不做任何键改写（历史教训：手写 range 曾因起点覆盖锚点键 11/22/33 导致复盘题丢失）。
- 禁止把视觉位置键的原 JSON 传给 `--answers`（应传 `--display-answers`）。
- 禁止凭"题量差不多"等直觉判断跳过 remap。
- 禁止写"自动修复"脚本或临时程序替带 remap 协议（一切键改写都必须以 §3.2.1 为唯一权威源）。

#### 3.2.3 Remap 后双向校验（强制，由脚本内置完成）

remap 与校验均已内置于评分脚本的 `--display-answers` 分支，模型**不再手写任何 assert**。脚本内置校验项：

1. 输入必须是 JSON 对象（dict），否则报错。
2. 键必须恰好是视觉位置 `{1,2,...,44}` 全集，缺一/多一/含非法键均报错（缺键场景对应"未答完"，模型按 §4 提示用户补答）。
3. 全部 value 归一化后必须为 `A`/`B`（`normalize_answer` 兼容 Y/N/1/2）；任一项非法即报错并指出具体题号。
4. remap 后键集为 `{1,2,...,44}` 真实 id 全集（由 DISPLAY_ORDER 的排列性构造保证）。

任一校验失败，脚本输出错误信息并以非零码退出；模型**禁止**在脚本报错时强行出报告，应按报错内容回传用户处理（如"还有 N 题未作答，请先完成第 X 题"）。

### 4. 交互约束

- 一次性展示全部 44 道题，不能分页、不能逐题加载、不能"先展示前几题再继续"
- 题目须按题库原顺序从 1 到 44 连续展示，不得打乱顺序
- 每题必须只有一个有效答案：`A` 或 `B`
- 用户点击 `A` 或 `B` 时，必须只切换当前题在该题目的选中状态，不应同时多选
- 题目展示编号按"从上到下依次递增"（第 1 题显示 `1`、……、第 44 题显示 `44`），与题库 `id` 解耦；详见 §2 视觉结构要求
- 进度条长度和文本必须依据已答题/总题数自动更新，但整个评测仍然保持 44 题全量展示
- **提交按钮始终可见且始终可点击（关键）**：`提交测评` 按钮必须在卡片渲染的同一帧就出现在卡片最下方，**禁止"答完才显示""达到某条件才渲染"等延迟出现行为**，**禁止 `disabled` 置灰不可点**。无论是否答完，按钮都必须处于 `enabled` 可点击态。点击行为分两种：
  - 存在未作答题目 → **禁止提交、禁止出报告**，自动滚动到第一个未作答题目并高亮提示"还有 N 题未作答，请先完成第 X 题"（跳转规则见下条"未答跳转"）
  - 已答完全部 44 题 → 直接触发评分计算，**立即输出完整测评报告**（见"提交后直接出报告"一节），不允许任何中间链路
- **触发流程稳定性（关键）**：无论用户在第几轮对话、用什么措辞（"开始测评"/"做 MBTI"/"测测我的 MBTI"/"启动 MBTI 测评"等任一触发词）唤起本 skill，**首次返回的卡片必须完整包含标题、进度条、维度标签、44 题列表、A/B 双选按钮、提交按钮**全部要素。**禁止"先返回题目卡片，等下一轮再补提交按钮"**、**禁止"先返回部分题目，等用户催促再补全"**、**禁止"渲染时只返回 JSON 不返回交互卡片"**。若模型因上下文长度或工具限制无法一次渲染完整卡片，必须明确报错而非返回残缺卡片。
- **未答跳转（关键）**：用户点击 `提交测评` 按钮时，若存在未作答题目，**禁止提交并禁止直接出报告**，必须自动将焦点/视图滚动到第一个未作答的题目上，并高亮提示"还有 N 题未作答，请先完成第 X 题"。第一个未答题判定规则：按渲染顺序 1→44（即数组下标 +1）遍历，遇到第一道 `answers` 中对应位置为空（即未选 A/B）的题即为锚点题。跳转后该题的 `A/B` 双选按钮区域必须获得视觉焦点（边框高亮、自动滚入可见区），不得仅做文字提示而不滚动
- **提交后直接出报告（关键）**：用户点击 `提交测评` 且已答完全部 44 题时，卡片内 JS 必须调用宿主回传机制（`sendPrompt()`）将答案 JSON 以自动生成的用户消息形式回传给对话流，**模型收到答案后立即调用评分脚本计算并输出完整测评报告**。**禁止任何中间链路**：禁止要求用户复制/粘贴答案回传、禁止要求用户手动输入答案 JSON、禁止"先把答案发我"再等下一轮出报告、禁止输出答案 JSON 后等待用户确认。
- 交互必须发生在当前对话流的内联组件中，而不是返回普通纯文本
- 该组件的页面流必须保持"44 题一次性展示 + 全量答题 + 提交"的连续逻辑

### 5. HTML / Visualizer 输出要求

如果系统支持 WorkBuddy 的 Visualizer / 内联 HTML 渲染，返回内容必须满足：

- 以 HTML 片段或可渲染组件形式输出，而不是纯 Markdown
- 必须保留视觉层结构：标题、进度、维度对标签、题目列表、双选按钮
- 不能出现散乱的自然语言说明替代卡片
- 不能直接输出仅有 JSON 字段而没有交互容器
- 不能让前端自行"自由发挥"生成不同布局
- 只允许按本 skill 规定的结构渲染，不允许引入无关内容

### 7. 禁止事项

- 不允许输出纯文本描述替代卡片
- 不允许缺失"A / B"双选按钮
- 不允许没有维度对标签
- 不允许没有进度条和题号
- 不允许使用题库 `id` 作为题目展示编号，必须用"从上到下依次递增"的顺序号
- **不允许提交按钮延迟出现**：提交按钮必须在卡片首次渲染的同一帧就出现在卡片最下方，禁止"答完才显示""下一轮才补"
- **不允许提交按钮置灰不可点击**：无论是否答完，提交按钮必须处于 `enabled` 可点击态；未答完点击执行"未答跳转"，已答完点击直接出报告
- **不允许首次渲染卡片时缺失提交按钮**：无论用户在第几轮对话唤起测评，首次返回的卡片必须包含提交按钮
- **不允许"先返问题卡片、再补提交按钮"或"先返部分题目、再补全"的两段式渲染**：必须一次性返回完整卡片
- 不允许在页面中出现无关长文案或随机推理内容
- 不允许模型自行更换布局结构，必须保持 WorkBuddy 交互卡片的稳定格式
- **不允许模型自绘题目卡片（关键）**：题目卡片必须严格遵循本文件"题目卡片渲染规范"（§0.1 固定 CSS / §0.2 固定结构 / §0.3 固定交互行为）输出，禁止自行编写 CSS/布局/颜色，确保每个用户看到的题目格式与样式完全一致

## 输出格式

### 报告渲染规范（强制，100% 一致）

为保证**每个用户、每次提交**测评后输出的报告格式与样式**完全一致**，禁止模型自行设计。**报告渲染以 `references/template.md` 为唯一权威源（byte-stable 标准）**：

- 模型每次渲染报告前，**必须先 `Read references/template.md`**，将 §T2 模板中 `/*__REPORT_JSON__*/` 占位符替换为评分脚本（`--compact`）输出的报告 JSON 后，**原样、逐字符**输出为报告 `widget_code`。
- **禁止在未读取 template.md 的情况下**凭记忆、凭印象或按本文件文字描述自行渲染；禁止改模板任何 CSS / 类名 / ID / HTML 结构 / 文案 / JS 逻辑。
- 下方 §R.1 固定 CSS、§R.2 固定结构是模板内容的文字固化说明（供理解与自查），**一切以 template.md 实际内容为准**；若二者存在字面差异，以 template.md 为准。

> **仲裁规则（关键，先读）**：§R.1 的 CSS 已与渲染平台（Visualizer）设计系统对齐——无渐变、无阴影、无 blur/glow/neon，字重仅 400/500，全部使用宿主 CSS 变量（`var(--color-*)`）或设计系统色板色值，通过宿主变量与 `prefers-color-scheme` 自动适配明暗主题，二者**已无冲突**。渲染时必须以本规范字面值为唯一依据：禁止任何"适配主题 / 优化 / 简化 / 美化 / 压缩"行为；若渲染平台 read_me 说明与本规范存在任何字面差异，**以本规范为准**（本规范即对齐后的最终形态）。

#### R.1 固定 CSS（强制）

固定 CSS 全部位于 `references/template.md` §T2 模板 `<style>` 块内（`#mbti-rpt` 系列，唯一权威源）：无渐变、无阴影、无 blur/glow/neon，字重仅 400/500，全部使用宿主 CSS 变量（`var(--color-*)`）或设计系统色板色值（`#185FA5`/`#3B6D11`/`#534AB7`/`#BA7517` 及暗色模式对应值，封装为 `t-ei`/`t-sn`/`t-tf`/`t-jp` 的 `--dc`/`--dbg`）。渲染前必须 Read template.md 并按 §T2 原样输出；本文件不再内嵌 CSS 副本，避免与权威源两份内容产生漂移。自查时（CSS 是否逐字一致、色板内色值）以 template.md §T2 为准。

#### R.2 固定结构（强制）

模型渲染时必须以 `#mbti-rpt` 为根容器，依次输出：标题区（`.r-hdr` 内 `.r-type` = `dominant_type` + `.r-nm` = `role_detail.name`；**无背景、无渐变**，仅底部 0.5px 分隔线）、指标卡（`.r-meta` 内 3 个 `.r-mc`：倾向强度 `display_score`、人群占比 `proportion%`、胜出维度 `dimension_pairs[].result` 按 `·` 连接；浅灰表面卡，无边框）、8 字母计数条（`.r-bar8`，按 E/I/S/N/T/F/J/P 固定顺序；每个字母格为一个 `<div>`，内部为字母 + 数值（数值用 `.v`）；**胜出字母的 div 必须同时加 `win` 类和对应维度主题类**（例：`<div class="win t-ei">I<span class="v">7</span></div>`）→ 自动按主题类上维度色（`--dbg` 底 + `--dc` 字）；非胜出字母不加类，灰底灰字；条下加 `.r-cap` 说明`胜出维度标色 · 数值=该端答对题数`）、4 个维度卡（`.r-dim`，按 EI/SN/TF/JP 顺序，**每个维度卡必须按维度对加主题类**：EI→`t-ei`、SN→`t-sn`、TF→`t-tf`、JP→`t-jp` → 自动获得左侧 3px 维度色边条；每卡含 `.r-dimh` 标题（`dimension_name` + `胜出：result result_name` 徽标 `.r-win`，徽标底色/字色随主题类自动变化）、`.r-pair` 两端对比条（`option+name1`、`score1 题 · percent1%`、宽度=percent 的 `.r-track i`；**胜出端的 `.r-side` 必须加 `win` 类** → 进度条/百分比/feature 自动变维度色；非胜出端不加类，进度条灰色）、`<details>` 展开区固定三段（模型必须按此顺序与文案输出）：`.r-sec` 标题 `胜出端特质` + 胜出端 `result_traits` 原文、`.r-sec` 标题 `维度解读` + `dimension_description`/`dimension_prompt` 原文、`.r-sec` 标题 `两端详情对比` + 两端 `option1_detail`/`option2_detail` 的 `.r-duo` 两卡 `.r-sc`（`name`/`feature`/`traits`/`characteristics` 原文，卡片带维度色左缘条））、特点分析卡（`.r-dim` 不加主题类：`.r-sec` 标题固定为 `特点分析`，内部 `.r-feats` 双卡并排——`.r-feat2 adv` 标题固定为 `优势` + `role_detail.advantages` 原文、`.r-feat2 dis` 标题固定为 `缺点` + `role_detail.disadvantages` 原文；**禁止展示 `role_detail.description`**）、职业推荐卡（`.r-dim` 不加主题类：`.r-sec` 标题固定为 `职业推荐` + `analysis.summary` 的 `.r-txt` + `analysis.recommendations` 的 `.r-tag` 标签列表，标签统一样式，模型不额外加类）、底部 `.r-note` 说明。

- **维度主题色映射表（强制，模型必须按此映射加类/判定胜出）**：EI → `t-ei`（c-blue：亮色 #185FA5 / 深色 #85B7EB）、SN → `t-sn`（c-green：亮色 #3B6D11 / 深色 #C0DD97）、TF → `t-tf`（c-purple：亮色 #534AB7 / 深色 #AFA9EC）、JP → `t-jp`（c-amber：亮色 #BA7517 / 深色 #FAC775）。维度卡主题类、`.r-bar8` 胜出格、`.r-side` 的 `win` 类均由报告数据中的 `pair` 与胜出结果决定，同一份报告内必须保持一致。
- **渲染流程（强制）**：
  1. 调用 `scripts/calculate_mbti.py`（建议加 `--compact` 紧凑输出）得到报告 JSON（**唯一数据来源**，字段必须全量）；
  2. **只读脚本输出的报告 JSON 进行渲染，禁止再读取 `references/profiles.md` / `references/dimensions.md`**：脚本输出已按查表结果透传全部渲染所需原文（`role_detail` 7 字段、`dimension_pairs` 8 端 `name/feature/traits/characteristics`、`dimension_name/description/prompt`、`result_traits/result_characteristics`、`analysis` 全量，合计仅约 10KB），渲染报告所需的每个字段都在其中，无需也不得再读 references 文件（合计约 63KB，纯属浪费上下文、拖慢响应）；
  3. **Read `references/template.md`**，把报告 JSON 替换 §T2 模板中 `/*__REPORT_JSON__*/` 占位符，按模板**原样逐字符**渲染完整 HTML 报告，作为交互组件（Visualizer show_widget）的 `widget_code` 输出给用户；禁止自绘样式、禁止凭记忆渲染。
- **渲染后自查清单（强制，输出前逐项核对，任一不符必须重写后再输出）**：
  1. CSS 是否与 §R.1 **逐字一致**（未增删改任何选择器/属性/值/类名）？
  2. 是否出现 `linear-gradient` / `box-shadow` / `text-shadow` / `filter: blur` / `font-weight:600` 或 `700`？→ 出现即违规（本规范已对齐设计系统，禁止再引入渐变/阴影/重字重）；
  3. 所有颜色是否均为 `var(--color-*)` / `var(--dc)` / `var(--dbg)` 或 §R.1 色板内色值？是否出现 `#fff` / `#000` 硬编码背景或深色块？
  4. 根容器 `#mbti-rpt` 是否透明（未设背景色）？
  5. `.r-bar8` 是否 E/I/S/N/T/F/J/P 顺序、胜出格是否带 `win t-xx` 双类？
  6. 4 个维度卡是否 EI/SN/TF/JP 顺序、主题类是否 `t-ei`/`t-sn`/`t-tf`/`t-jp`、胜出端 `.r-side` 是否带 `win`？
  7. 报告字段是否完整（`role_detail` 7 字段、两端 8 端详情、`analysis` 全量）无截断、无 `{{}}` 残留、无"见上文"式省略？
  8. 结构顺序是否与 §R.2 完全一致（标题区→指标卡→bar8→4 维度卡→特点分析→职业推荐→note）？
  9. 是否只读脚本输出 JSON 渲染（未读 references 大文件、未混入自拟文案）？
- **唯一可变量**：报告数据（各字段值）。除此之外 CSS、类名、颜色、布局、文案均不得改动；`t-ei`/`t-sn`/`t-tf`/`t-jp` 与 `win` 为规范类名，属 §R.2 固定结构的一部分，模型必须按映射表使用，不得增删或改色。
- **禁止事项**：
  - 禁止自行编写 CSS / 更换类名 / 调整颜色或布局；
  - 禁止改写、省略或截断 `role_detail`（7 字段原文）、`dimension_pairs` 两端 8 端详情（`name`/`feature`/`traits`/`characteristics` 原文）、`analysis`（summary + recommendations 全量）等字段；
  - 禁止用 Markdown 表格、纯文本替代报告渲染；
  - 禁止"凭印象"画报告或每次输出不同样式。

## 确定性输出约束（关键）

为避免 Workbody 生成结果每次都变化，本 skill 必须执行严格的确定性规则：

1. 结果必须以脚本计算结果为唯一准绳，不允许模型自由推断分数或人格类型。
2. `dimension_pairs` 必须按固定顺序输出 4 对：`["EI", "SN", "TF", "JP"]`。
3. `dominant_type` 必须由 4 对维度结果字母按 EI → SN → TF → JP 顺序拼接，不能由模型自行命名或重写。
4. 每对维度的胜出字母规则：当两端计数相等时取 option1（即 E/S/T/J），即 `score1 >= score2 ? option1 : option2`。
5. 百分比必须保留两位小数，使用 `decimal.ROUND_HALF_UP`（与 Java `BigDecimal.ROUND_HALF_UP` 一致），不允许 Python 默认 banker's rounding。
6. `display_score` 为 4 个胜出端百分比的平均值取整数，使用 `decimal.ROUND_HALF_UP`，不允许模型自由发挥。
7. `role_detail` 必须从 `references/profiles.md` 的 `## 16 型人格档案` 段按 `dominant_type` 查表得到，16 型全覆盖；**字段值必须为原文，不允许模型自行撰写或改写人格描述、优势、劣势、职业推荐**（**该查表由评分脚本完成**，模型直接使用脚本输出 JSON 中已透传的原文，无需自行读文件）。
8. `dimension_pairs[].result_name/result_feature/result_traits/result_characteristics` 与 `option1_detail/option2_detail` 必须从 `references/dimensions.md` 的 `## 维度对详情` 段按 `dimension` + `option` 查表，使用原文（含 `title/feature/traits/characteristics` 字段），不允许模型自行撰写（**该查表由评分脚本完成**，模型直接使用脚本输出 JSON 中已透传的原文，无需自行读文件）。
9. `analysis.summary` 必须使用固定模板，且只基于 `dominant_type` 与 `role_detail.name` / `role_detail.careers` 前 5 项生成。
10. `analysis.recommendations` 必须为 `role_detail.careers` 字符串按顿号切分后的字符串数组，顺序固定，不得截断。
11. 任何场景都不允许输出随机、模糊、口语化的结论；必须稳定输出统一结构。
12. 如果题目不完整，必须返回 `incomplete`，并列出缺失题号；不得在有缺失时强行生成完整结论。
13. 生成结果时必须以 JSON 对象返回，不能返回 Markdown、自然语言说明、额外说明块或解释性文本。
14. 同一组 `answers` 必须在多次调用间产生 byte-equal 的 JSON 输出（无随机数、无时间戳、无外部网络/DB 依赖；经 4 组答案 × 10 次运行验证通过）。
15. 报告文案来源：`references/profiles.md`（16 型档案）/ `references/dimensions.md`（维度对详情）两个拆分文件，**查表由评分脚本完成**——脚本输出 JSON 已按查表结果透传全部原文字段（约 10KB），模型渲染报告时**只读脚本输出 JSON**，**禁止再读这两个 references 文件**（合计约 63KB，纯属浪费上下文、拖慢响应）；**禁止在生成结果时混入模型自拟内容**，所有 `role_detail` / `dimension_pairs[].result_*` / `dimension_*` / `option1_detail` / `option2_detail` 字段直接使用脚本输出中的原文，不得改写。
16. **全量输出约束（关键）**：每一轮报告输出都必须包含完整 JSON 结构的全部字段，**严禁**在任何轮次出现"与上一轮一致""结果同上""向上翻阅""如前所述""参考前次输出"等省略式表述。无论同一组 `answers` 在同一对话流中被请求多少次，每次都必须输出一份**字段齐全、文案完整**的报告 JSON，包括但不限于：`dimension_counts`（8 键）、`dimension_pairs`（4 对，每对含 `option1_detail`/`option2_detail` 共 8 端详情）、`role_detail`（7 字段原文）、`analysis`（summary + recommendations 全量）。**禁止以"已生成过""缓存命中""结果不变"为由截断或省略任何字段**。
17. **禁止引用式表述**：禁止在报告 JSON 内或外层包裹文字中使用"见上文""参见第 N 轮""同前次""以上一轮为准"等指向历史轮次的表述。每轮报告必须是自包含的、可独立阅读的完整内容。
18. **样式确定性（强制）**：题目卡片必须遵循 `references/template.md` §T1、测评报告必须遵循 `references/template.md` §T2 渲染（每次渲染前必须 Read template.md，按模板原样输出，仅替换数据占位符），**对任何用户、任何轮次渲染结果必须逐字一致**（唯一差异仅为题目数据 / 报告数据）；禁止任何用户或任何轮次获得不同格式/样式的卡片或报告；禁止未读 template.md 凭记忆渲染；禁止复制上一轮输出改 ID 后缀充当新产物。

本 skill 负责在用户点击"提交测评"后**直接输出结构化的完整测评报告**，不承担前端渲染。字段级约束见下方"### 输出规范"。

### 输出规范

- `status` 必须为 `completed` 或 `incomplete`
- `display_score` 为 0-100 的整数，且必须来自脚本计算结果
- `dominant_type` 为 4 个结果字母按 EI → SN → TF → JP 拼接，例：`"ESTP"`，不能自行更改为其他文本
- `dimension_counts` 必须按固定顺序输出 `E`、`I`、`S`、`N`、`T`、`F`、`J`、`P` 八个键
- `dimension_pairs` 必须按 `EI`、`SN`、`TF`、`JP` 顺序输出 4 对
- 每对维度的 `result` 字母规则：`score1 >= score2` 时取 `option1`（即 E/S/T/J），否则取 `option2`
- `percent1` + `percent2` 必须等于 100.00（仅当计数非零时）
- `role_detail` 必须从 `references/profiles.md` 的 `## 16 型人格档案` 段按 `type` 查表，包含 `type/name/proportion/description/advantages/disadvantages/careers` 七个字段，**全部使用原文，不得改写、不得省略、不得由模型自行撰写**
- `dimension_pairs[].result_name/result_feature/result_traits/result_characteristics` 必须从 `references/dimensions.md` 的 `## 维度对详情` 段按 `dimension` + 胜出端 `option` 查表（`title` → `result_name`，`feature/traits/characteristics` 原样透传）
- `dimension_pairs[].option1_detail/option2_detail` 必须按对应维度对的 `option1/option2` 字母查 `## 维度对详情` 段，`name` 字段对应 `title`
- `dimension_pairs[].dimension_name/dimension_description/dimension_prompt` 必须从 `## 维度对详情` 段的 `name/description/prompt` 字段原样透传
- `analysis.summary` 必须使用固定中文模板：`"用户在 {4个胜出字母用顿号连接} 四个维度胜出，人格类型为 {dominant_type}（{role_name}），适合 {careers 前5项顿号连接} 等方向。"`
- `analysis.recommendations` 必须为 `role_detail.careers` 按顿号切分后的字符串数组，顺序固定，不得截断
- 若题目未完成，则返回 `incomplete`，并输出 `missing_questions`（缺失题号字符串数组）
- 输出必须为纯 JSON，不允许嵌套说明、Markdown 代码块或额外字段

### 渲染约束（关键）

本 skill 约束的是"生成结果的渲染契约"，而不是页面实现细节。具体要求如下：

- `dominant_type` 必须作为页面主标题字段使用（如 `ESTP`）。
- `role_detail.name`（中文名如 `企业家`）必须作为副标题或角色名展示字段使用。
- `role_detail.proportion` 必须作为占比展示字段使用（值为百分数，如 `4.3` 表示 4.3%，前端按需补 `%`）。
- `display_score` 必须作为总分展示字段使用。
- `dimension_pairs` 必须作为各维度得分表格/柱状图数据源。
  - 每对维度必须展示两侧字母、名称、计数与百分比，以及胜出端高亮
  - 每对维度必须展示 `dimension_name`（如 `外倾E&内倾I`）作为该对的展示名
  - 每对维度展开后必须展示 `dimension_description` 与 `dimension_prompt` 的原文（可作为该对的说明/作答引导）
  - 每对维度必须展示胜出端的 `result_name` / `result_feature` / `result_traits` / `result_characteristics` 四段原文
  - **8 端详情必须全部展示（关键）**：每对维度的 `option1_detail` 与 `option2_detail` 共 8 端（E/I/S/N/T/F/J/P 各一端），每端必须完整展示 `name` / `feature` / `traits` / `characteristics` 四段字段的原文。**禁止只展示胜出端**，**禁止省略 `option1_detail` 或 `option2_detail`**，**禁止只展示 `name`/`feature` 而省略 `traits`/`characteristics`**。两端必须左右并列或上下并列，便于用户对比两端倾向。
- `role_detail.advantages` / `disadvantages` 必须在"特点分析"卡中分别作为"优势"/"缺点"两块展示，使用原文（含换行符原样渲染）；`role_detail.description` 不在报告中展示（JSON 输出仍保留该字段，全量输出约束不变）
- `role_detail.careers` 必须作为职业推荐展示字段使用，按顿号切分后呈现为列表
- 结果页最底部必须按 `EI`、`SN`、`TF`、`JP` 的顺序展开展示，每对维度至少包含"胜出端"与"两端详情"两部分
- `analysis.summary` 必须作为结果说明文本展示
- `analysis.recommendations` 必须作为建议列表渲染数据
- 任何前端都不能自行生成新的字段名来替代上述结构
- 前端只能根据这几个字段进行展示，不能依赖自由文本解析

### 禁止事项

- 不允许返回自由文本替代 JSON
- 不允许缺少 `dimension_pairs`
- 不允许缺少 `dominant_type`
- 不允许 `dimension_pairs` 中遗漏任一对维度
- 不允许 `role_detail` 缺失任一字段
- 不允许在 skill 中混合前端渲染逻辑
- 不允许前端自行扩展未定义字段覆盖结果解释
- 不允许使用 Python 默认 `round()`（banker's rounding），必须用 `decimal.ROUND_HALF_UP`
- **不允许只展示胜出端而省略非胜出端的 `option1_detail`/`option2_detail`**：8 端详情（E/I/S/N/T/F/J/P）必须全部展示，每端的 `name/feature/traits/characteristics` 四段缺一不可
- **不允许用户点击"提交"按钮时若有未答题就出报告**：必须先跳转到第一个未答题并提示，未答完毕前禁止触发评分
- **不允许"复制答案回传"链路（关键）**：用户点击"提交测评"且答完全部题目后，模型必须直接基于回传的答案立即计算并输出完整测评报告。禁止要求用户复制/粘贴答案、禁止要求用户手动输入答案 JSON、禁止"先把答案发我"或任何让用户参与答案传递的中间环节
- 其他确定性约束（脚本查表透传、禁止自拟文案、禁止引用式表述、禁止自绘样式）见上方"## 确定性输出约束"第 7/8/15/16/17/18 条，不再重复。

## 评分入口

评分脚本位置：

- `scripts/calculate_mbti.py`
- 评分函数：`calculate_scores(answers, questions)`
- 输出：JSON 格式的分数与人格类型结果

示例命令：

```bash
# 不传任何路径：自动加载 references/questions.md / profiles.md / dimensions.md
# 推荐加 --compact：紧凑输出，报告 JSON 约 10KB，渲染报告时模型只读它（无需再读 references 大文件）
# 推荐用 --display-answers：原样传回卡片回传的视觉位置键 JSON，脚本自动 remap + 内置校验（禁止手写 remap）
python scripts/calculate_mbti.py \
  --display-answers '{"1":"A","2":"B","3":"A","4":"B",...,"44":"B"}' --compact

# 兼容保留：--answers 接收真实题号 id 键 JSON（remap 由调用方完成，仅测试/内部场景使用）
python scripts/calculate_mbti.py \
  --answers '{"1":"A","2":"B","3":"A","4":"B"}' --compact
```

> 参数二选一：`--display-answers`（推荐，卡片回传链路）与 `--answers`（真实 id 键，兼容旧链路）不能同时提供，也不能都不提供，否则脚本报错退出。

> 评分算法（维度计数→百分比→结果字母→拼接→display_score→role 查表）由脚本独立实现，详见 `scripts/calculate_mbti.py` 源码；本文件不再重复说明，避免与脚本实现不一致。

## 重要原则

- 题库必须明确给出题目编号、题干、提示语和选项维度归属；
- 评分逻辑必须单独写在脚本文件中，不能隐含在对话里；
- 用户提交题目后，必须返回测试结果、得分和人格类型；
- 若题目缺失或答案格式不合法，先要求用户补全，不要直接伪造结果；
- 选项维度映射必须保持一一对应；
- 同一组作答必须产出 byte-equal 的 JSON（无随机性）。

## 资源说明

- 题库 + 16 型档案 + 维度对详情：**形态 B（多文件，当前仓库采用）**，拆分为 3 个独立文件，每段仍保留对应的 H2 标题 + 散文说明 + 一个 ```json 代码块：
  - `references/questions.md`：`## 题库` 段，44 题题库（**渲染题目卡片时只读此文件**，约 21KB，避免把其余数据读入上下文导致输出不稳定）
  - `references/profiles.md`：`## 16 型人格档案` 段
  - `references/dimensions.md`：`## 维度对详情` 段
  - 脚本不传路径自动加载 3 文件；`--questions-path` 等可分别覆盖；`--references-path` 可整体回退单文件模式
  - `## 题库` 段：44 道题的二选一职业性格测评题库
  - `## 16 型人格档案` 段：16 型人格档案
  - `## 维度对详情` 段：4 对维度对 + 8 端字母详情
- 渲染规范：**模板文件 `references/template.md` 为两道渲染链路（题目卡片 §T1 / 测评报告 §T2）的唯一权威源（byte-stable 标准）**：渲染前必须 Read 该文件并按模板原样输出（题目卡片将 `/*__QUESTIONS_JSON__*/` 替换为题库 JSON，报告将 `/*__REPORT_JSON__*/` 替换为评分脚本输出 JSON），**禁止自绘样式、禁止凭记忆渲染、禁止复制上一轮输出改 ID 后缀**；本文件 §0 / §R 为模板内容的文字固化说明，供理解与自查
- 评分脚本：`scripts/calculate_mbti.py`，通过 `load_md_section(md_path, section_title)` 按 H2 标题切段、提取 ```json 代码块解析；支持多文件（默认，无参数自动加载 3 文件）与单文件（`--references-path`）两种形态；`--compact` 紧凑输出（渲染报告时推荐，输出约 10KB）；`--display-answers`（推荐，卡片回传链路）内嵌与 §3.2.1 同源的 DISPLAY_ORDER 常量，自动 remap 视觉位置→真实 id 并内置 44 键/A-B 校验，任一失败报错不评分；`--answers`（兼容保留）接收真实 id 键 JSON，二选一互斥
- **H2 标题文本（`## 题库` / `## 16 型人格档案` / `## 维度对详情`）是 `calculate_mbti.py#load_md_section` 解析锚点，禁止改名**，否则脚本会抛 `ValueError`（拆分多文件时每个文件内同样必须保留对应 H2 标题）。

## 交互输出要求

用户完成题目后，系统应返回：

1. 测评 ID 与名称
2. 状态（completed / incomplete）
3. 16 型人格代码（dominant_type）
4. 4 对维度的计数与百分比
5. 角色详情（type / name / proportion / description / advantages / disadvantages / careers）
6. 4 对维度的详情（含胜出端 name/feature/traits/characteristics 与两端详情，及 dimension_name/description/prompt 原文）
7. 总分（display_score）
8. 若未通过/未完成，给出缺失题号列表

本 skill 仅有唯一测评流程：展示 44 题 → 用户作答 → 输出完整测评报告。评分逻辑独立于题库内容；后续如需切换题库，只需替换 `references/questions.md` 中 `## 题库` 段的 ```json 代码块，无需改动评分脚本。报告文案（`role_detail` / `dimension_pairs[].result_*` / `dimension_*`）全部来自数据文件的 `## 16 型人格档案` / `## 维度对详情` 段（形态 B 下为 `references/profiles.md` / `references/dimensions.md`，形态 A 下为合并的 mbti.md），如需更新文案，直接替换对应 H2 段的 ```json 代码块即可，无需改动评分脚本。**展示格式/样式锁定机制：`references/template.md` 为唯一权威源**——题目卡片模板（§T1，含 `/*__QUESTIONS_JSON__*/` 占位符）与测评报告模板（§T2，含 `/*__REPORT_JSON__*/` 占位符）均固化其中；任何用户、任何轮次（含"再来一份""朋友也要测""重置再来一遍"等派生触发）都必须重新 Read template.md 并按模板原样输出，结果必须完全一致（byte-stable），禁止自绘样式、禁止复制上一轮输出改 ID 后缀。

