Skill: effeceee/mbti-guru (version 1.5.2)

# MBTI Guru - MBTI人格测试专家 ## 简介 / Introduction **MBTI Guru** 是一款专业的人格类型评估工具，基于迈尔斯-布里格斯类型指标（MBTI）理论，帮助用户发现自己的性格类型，并生成详尽的中英双语分析报告。 **MBTI Guru** is a professional personality assessment tool based on the Myers-Briggs Type Indicator (MBTI) theory. It helps users discover their personality type and generates detailed bilingual analysis reports in Chinese and English. --- ## 功能特点 / Features ### 1. 四个测试版本 / Four Test Versions | 版本 | 题数 | 时长 | 适用场景 | |------|------|------|----------| | Quick / 快速版 | 70题 | ~10分钟 | 快速体验 | | Standard / 标准版 | 93题 | ~15分钟 | 正式测试 | | Extended / 扩展版 | 144题 | ~25分钟 | 深度分析 | | Professional / 专业版 | 200题 | ~35分钟 | 临床级 | --- ### 2. MBTI四维度 / Four Dimensions | 维度 | 中文名称 | English | 偏好倾向 | |------|----------|---------|----------| | **E/I** | 能量倾向 | Energy | Extraversion vs Introversion | | **S/N** | 信息获取 | Information | Sensing vs Intuition | | **T/F** | 决策方式 | Decisions | Thinking vs Feeling | | **J/P** | 生活态度 | Structure | Judging vs Perceiving | --- ### 3. 16种人格类型 / 16 Personality Types | 类型 | 中文名称 | English Name | 特点 | |------|----------|--------------|------| | ISTJ | 物流师 | The Logistician | 务实可靠 | | ISFJ | 守卫者 | The Defender | 忠诚守护 | | INFJ | 提倡者 | The Advocate | 理想洞察 | | INTJ | 建筑师 | The Architect | 战略思维 | | ISTP | 鉴赏家 | The Virtuoso | 实用灵活 | | ISFP | 探险家 | The Adventurer | 艺术感性 | | INFP | 调停者 | The Mediator | 理想同理 | | INTP | 逻辑学家 | The Logician | 分析创新 | | ESTP | 企业家 | The Entrepreneur | 冒险行动 | | ESFP | 表演者 | The Entertainer | 热情社交 | | ENFP | 竞选者 | The Campaigner | 热情创意 | | ENTP | 辩论家 | The Debater | 智辩创新 | | ESTJ | 经理 | The Executive | 执行管理 | | ESFJ | 执政官 | The Consul | 关怀社交 | | ENFJ | 主人公 | The Protagonist | 领导激励 | | ENTJ | 指挥官 | The Commander | 战略领导 | --- ## 报告内容 / Report Contents ### 完整分析报告包含 / Full Report Includes: 1. **类型确认 / Type Confirmation** - MBTI类型代码（如 INFP） - 类型名称（中文+英文） - 综合清晰度指数（0-100%） 2. **维度分析 / Dimension Analysis** - 能量倾向 E/I 分析 - 信息获取 S/N 分析 - 决策方式 T/F 分析 - 生活态度 J/P 分析 3. **性格特征 / Personality Traits** - 核心优势 Strengths（5项） - 潜在劣势 Weaknesses（5项） 4. **职业建议 / Career Recommendations** - 推荐职业（5项） - 发展建议 5. **人际关系 / Relationships** - 最佳匹配类型 - 挑战类型 --- ## 使用方法 / Usage ### 运行测试 / Run Test ```bash cd mbti-guru python3 mbti.py ``` ### 选择版本 / Select Version ``` ╭──────────────────────────────────────╮ │ MBTI Guru - Choose Your Test │ ╰──────────────────────────────────────╯ 1. 快速版 (70题) ~10分钟 2. 标准版 (93题) ~15分钟 3. 扩展版 (144题) ~25分钟 4. 专业版 (200题) ~35分钟 请输入数字选择 / Enter number: ``` ### 回答问题 / Answer Questions ``` ╭──────────────────────────────────────╮ │ 问题 1/70 │ ╰──────────────────────────────────────╯ 在聚会中，你通常是： A) 和很多人热烈交谈，从中获得能量 B) 只和一两个熟悉的人深聊 请输入 A 或 B: ``` --- ## 机器人命令 / Bot Commands ### 交互功能 / Interactive Features | 命令 | 功能 | |------|------| | `/start` | 开始新测试 | | `/resume` | 继续未完成的测试 | | `/history` | 查看测试历史 | | `/status` | 查看当前状态 | | `/cancel` | 取消当前测试 | | `/progress` | 查看当前进度 | ### 进度保存与恢复 / Progress Save & Resume - **自动保存**：每10题自动保存一次进度 - **随时恢复**：使用 `/resume` 继续未完成的测试 - **状态查询**：使用 `/status` 查看当前进度 ### 实时反馈 / Real-time Feedback - 每20题发送一次维度分析反馈 - 显示当前倾向和可能的类型变化 ### 进度条 / Progress Bar ``` 📝 问题 50/70 [████████░░] 71% (50/70) ``` --- ## 报告示例 / Sample Report ``` ┌─────────────────────────────────────────────────┐ │ MBTI PERSONALITY REPORT │ │ MBTI人格分析报告 │ ├─────────────────────────────────────────────────┤ │ │ │ 🧠 INFP │ │ The Mediator │ │ 调停者 │ │ │ │ Overall Clarity / 综合清晰度: 78% │ │ │ │ Dimension Scores / 维度得分: │ │ EI: ████████░░ 82% I (内向) │ │ SN: ██████░░░░ 65% N (直觉) │ │ TF: ████████░░ 78% F (情感) │ │ JP: ███████░░░ 71% P (知觉) │ │ │ │ Strengths / 优势: │ │ 1. Idealistic and principled (理想主义) │ │ 2. Creative and expressive (创意表达) │ │ 3. Compassionate and caring (富有同情心) │ │ │ │ Weaknesses / 劣势: │ │ 1. Overly trusting (过度信任) │ │ 2. Self-critical (自我批评) │ │ │ └─────────────────────────────────────────────────┘ ``` --- ## 项目结构 / Project Structure ``` mbti-guru/ ├── SKILL.md # 本文档 / This file ├── CLAWHUB.md # ClawHub元数据 ├── mbti.py # CLI主入口 / Main entry ├── bot.py # OpenClaw集成入口 ├── lib/ │ ├── __init__.py # OpenClaw接口 │ ├── mbti_core.py # 核心逻辑（平台无关） │ ├── telegram_handler.py # Telegram适配器 │ ├── mbti_types.py # 16种人格数据 │ ├── questions.py # 题库 │ ├── question_pool.py # 题库池 │ ├── scorer.py # 评分算法 │ ├── session.py # 进度保存/恢复 │ ├── history.py # 历史记录 │ ├── pdf_generator.py # PDF报告生成 │ ├── pdf_page1.py # PDF第一页 │ ├── pdf_page2.py # PDF第二页 │ ├── pdf_combined.py # PDF合并 │ ├── cognitive.py # 认知功能分析 │ └── relationships.py # 人际关系 └── data/ ├── sessions/ # 进度数据 └── history/ # 历史记录 ``` --- ## 技术规格 / Specifications | 项目 | 说明 | |------|------| | **编程语言** | Python 3.8+ | | **题库容量** | 200+ 题目 | | **报告格式** | Markdown + PDF | | **语言支持** | 中文 / English | | **评分算法** | 标准MBTI计分系统 | | **PDF生成** | Matplotlib + ReportLab | ## 环境依赖 / Dependencies ``` matplotlib>=3.5.0 reportlab>=4.0.0 numpy>=1.20.0 ``` ## 数据存储 / Data Storage | 数据类型 | 存储位置 | 说明 | |----------|----------|------| | 测试进度 | `data/sessions/` | JSON文件，仅存储当前测试进度 | | 测试历史 | `data/history/` | JSON文件，仅存储已完成结果 | | 答题数据 | 会话目录 | 用户ID与答题答案分离存储 | **隐私说明**： - 所有数据存储在用户本地服务器 - 不上传任何数据到外部服务器 - 用户可随时删除 `data/` 目录清除所有数据 - 数据文件命名使用匿名会话ID，不关联用户身份 ## 渠道配置 / Channel Configuration ### Telegram Bot (可选) ``` TELEGRAM_BOT_TOKEN=your_bot_token ``` 在 OpenClaw 配置文件中设置 `channels.telegram.botToken` ### 其他渠道 OpenClaw 自动支持：Discord, 飞书, 微信, Slack 等 --- ## 算法说明 / Algorithm ### 计分原理 / Scoring Method MBTI使用偏好选择计分法： 1. 每个维度统计两种偏好的选择次数 2. 计算每个偏好的百分比 3. 百分比高的一方决定该维度的类型 4. 清晰度指数 = |偏好百分比 - 50%| × 2 ### 维度计算 / Dimension Calculation - **E/I**: 外向 vs 内向倾向 - **S/N**: 感觉 vs 直觉倾向 - **T/F**: 思考 vs 情感倾向 - **J/P**: 判断 vs 知觉倾向 --- ## 局限性说明 / Limitations 1. MBTI是一种自我报告测试，结果仅供参考 2. 人格是复杂的，不能简单地归类为16种之一 3. 测试结果可能受情绪、环境等因素影响 4. 不建议将测试结果用于重大人生决策 --- ## 版本历史 / Changelog | 版本 | 日期 | 说明 | |------|------|------| | v1.5.2 | 2026-03-27 | 修复ClawHub元数据，完善依赖和隐私说明 | | v1.5.1 | 2026-03-27 | 补充Python依赖和数据存储说明 | | v1.5.0 | 2026-03-27 | 平台无关架构重构，支持所有OpenClaw渠道 | | v1.4.1 | 2026-03-27 | 修复跨进程session恢复bug | | v1.4.0 | 2026-03-27 | 正式版 - 全部功能完成、PDF定版、测试通过 | | v1.0.0 | 2026-03-26 | 初始版本 | --- ## 授权 / License Apache License 2.0 --- ## 联系方式 / Contact **作者**: MBTI Guru Team **版本**: v1.5.2 (正式版) **更新**: 2026-03-27

path: CLAWHUB.md
content: # ClawHub Metadata

version: 1.5.1
slug: mbti-guru
name: MBTI Guru
description: |
  专业MBTI人格测试工具，支持中英双语PDF报告。
  提供4种测试版本(70/93/144/200题)，自动进度保存，
  历史记录，认知功能分析，职业建议和人际关系匹配。
  基于OpenClaw架构，支持Telegram、Discord、飞书、微信等所有渠道。
  
summary: MBTI personality test with bilingual PDF reports and multi-channel support
owner: effeceee

tags:
- mbti
- personality
- test
- chinese
- bilingual
- pdf
- openclaw

requirements:
  python: ">=3.8"
  packages:
    matplotlib: ">=3.5.0"
    reportlab: ">=4.0.0"
    numpy: ">=1.20.0"

features:
  - id: versions
    title: 四个测试版本
    description: 70/93/144/200题，适应不同场景
  - id: bilingual_pdf
    title: 中英双语PDF报告
    description: 专业排版，含维度分析、优势劣势、职业建议
  - id: progress_save
    title: 进度自动保存
    description: 每题保存，退出后可继续
  - id: history
    title: 测试历史记录
    description: 保存最近20条测试记录
  - id: cognitive
    title: 认知功能分析
    description: 16种人格的认知功能深度解析
  - id: relationships
    title: 人际关系匹配
    description: 职场建议和情感匹配建议
  - id: channels
    title: 全渠道支持
    description: Telegram、Discord、飞书、微信等OpenClaw支持的渠道

commands:
  - /start
    description: 开始新测试
  - /resume
    description: 继续未完成的测试
  - /history
    description: 查看测试历史
  - /status
    description: 查看当前状态
  - /cancel
    description: 取消当前测试

data_storage:
  sessions:
    path: data/sessions/
    format: json
    description: 测试进度（自动保存）
  history:
    path: data/history/
    format: json
    description: 历史记录（测试完成后保存）
  privacy: |
    所有数据存储在本地服务器，不上传外部。
    用户可删除data/目录清除所有数据。

channels:
  telegram:
    required: false
    config: openclaw_telegram_botToken
    description: 通过OpenClaw配置Telegram Bot Token
  discord:
    required: false
    description: OpenClaw自动支持
  feishu:
    required: false
    description: OpenClaw自动支持
  wechat:
    required: false
    description: OpenClaw自动支持


path: DESIGN.md
content: # MBTI Personality Test Skill - Design Document v2

## 1. Overview

**Skill Name:** MBTI Personality Analyzer
**Purpose:** Interactive personality test with detailed analysis
**Output:** 16 personality types with comprehensive reports

---

## 2. Test Versions

| Version | Questions | Time | Use Case |
|---------|----------|------|----------|
| Quick | 70 | ~10 min | Fast assessment |
| Standard | 93 | ~15 min | Formal testing |
| Extended | 144 | ~25 min | Deep analysis |
| Professional | 200 | ~35 min | Clinical use |

---

## 3. MBTI Theory

### 4 Dimensions

| Dimension | Preference A | Preference B |
|-----------|--------------|--------------|
| Energy | E (Extraversion) | I (Introversion) |
| Information | S (Sensing) | N (Intuition) |
| Decisions | T (Thinking) | F (Feeling) |
| Structure | J (Judging) | P (Perceiving) |

### 16 Personality Types

| Type | Name | E/I | S/N | T/F | J/P |
|------|------|------|-----|-----|-----|
| ISTJ | 物流师 | I | S | T | J |
| ISFJ | 守卫者 | I | S | F | J |
| INFJ | 提倡者 | I | N | F | J |
| INTJ | 建筑师 | I | N | T | J |
| ISTP | 鉴赏家 | I | S | T | P |
| ISFP | 探险家 | I | S | F | P |
| INFP | 调停者 | I | N | F | P |
| INTP | 逻辑学家 | I | N | T | P |
| ESTP | 企业家 | E | S | T | P |
| ESFP | 表演者 | E | S | F | P |
| ENFP | 竞选者 | E | N | F | P |
| ENTP | 辩论家 | E | N | T | P |
| ESTJ | 经理 | E | S | T | J |
| ESFJ | 执政官 | E | S | F | J |
| ENFJ | 主人公 | E | N | F | J |
| ENTJ | 指挥官 | E | N | T | J |

---

## 4. Question Distribution

### Version 1: Quick (70 questions)
- E/I: 17 questions
- S/N: 18 questions
- T/F: 18 questions
- J/P: 17 questions

### Version 2: Standard (93 questions)
- E/I: 23 questions
- S/N: 23 questions
- T/F: 24 questions
- J/P: 23 questions

### Version 3: Extended (144 questions)
- E/I: 36 questions
- S/N: 36 questions
- T/F: 36 questions
- J/P: 36 questions

### Version 4: Professional (200 questions)
- E/I: 50 questions
- S/N: 50 questions
- T/F: 50 questions
- J/P: 50 questions

---

## 5. Question Format

Each question includes:
- Scenario description
- Two forced choices (A and B)
- Clear wording
- No double negatives

Example:
```
问题 1/93

在社交聚会中，你通常是：

A) 和很多人热烈交谈，从不同的人那里获得能量
B) 只和一两个熟悉的人深聊，独处时更自在

请输入 A 或 B
```

---

## 6. Scoring Algorithm

### Step 1: Count Preferences
```
for each dimension:
    A_count = number of A answers
    B_count = number of B answers
```

### Step 2: Calculate Index
```
index = (A_count / total) × 100
```

### Step 3: Determine Type
```
type = 
    (E_index > 50 ? E : I) +
    (S_index > 50 ? S : N) +
    (T_index > 50 ? T : F) +
    (J_index > 50 ? J : P)
```

### Step 4: Calculate Clarity
```
clarity = |index - 50| × 2  (0-100%)
```

### Clarity Levels
- 0-25%: Slight preference
- 26-50%: Moderate preference
- 51-75%: Clear preference
- 76-100%: Very clear preference

---

## 7. Report Structure

### Section 1: Your Type
- 4-letter type (e.g., INFP)
- Type name (e.g., 调停者)
- Overall clarity percentage
- Brief summary (100 words)

### Section 2: Dimension Breakdown
For each dimension:
- Your preference
- Score (0-100 for each side)
- Clarity level
- Interpretation (50 words)

### Section 3: Personality Profile
- Strengths (5 items)
- Weaknesses (5 items)
- How others perceive you
- Communication style
- Ideal work environment

### Section 4: Relationships
- Compatible types (top 3)
- Challenging types (top 3)
- Relationship tips
- Communication strategies

### Section 5: Career Guide
- Best matching careers (5)
- Work style
- Leadership approach
- Stress responses
- Growth recommendations

### Section 6: Deep Analysis
- Decision-making pattern
- Information processing style
- Energy management
- Personal development plan

---

## 8. Interaction Flow

### Start
```
用户: MBTI测试
助手: "欢迎来到MBTI人格测试！🏠

请选择测试版本：
1. 快速版 (70题 ~10分钟)
2. 标准版 (93题 ~15分钟)
3. 扩展版 (144题 ~25分钟)
4. 专业版 (200题 ~35分钟)

输入数字选择版本。"
```

### Question Flow
```
助手: "问题 1/93

[题目内容]

A) [选项A]
B) [选项B]

请输入 A 或 B"

用户: A
助手: ✓ 已记录

(每10题显示进度: "进度: 10/93 (11%)")
```

### Result
```
助手: "🎉 测试完成！

你的MBTI类型是: INFP (调停者)
清晰度: 78%

类型简介: [50字简介]

输入数字查看详情：
1. 维度分析
2. 性格特征
3. 人际关系
4. 职业发展
5. 完整报告"
```

---

## 9. Project Structure

```
mbti-skill/
├── SKILL.md              # Skill documentation
├── _meta.json           # Metadata
├── mbti.py              # Main entry point
├── lib/
│   ├── questions/       # Question banks
│   │   ├── v70.py      # 70 questions
│   │   ├── v93.py       # 93 questions
│   │   ├── v144.py      # 144 questions
│   │   └── v200.py      # 200 questions
│   ├── scorer.py        # Scoring algorithm
│   ├── types.py         # 16 types data
│   ├── reports.py       # Report generator
│   └── conversation.py   # Interaction logic
└── data/
    └── results/         # Stored results
```

---

## 10. Technical Requirements

- Python 3.8+
- JSON data storage
- Conversation state management
- Markdown report generation

---

## 11. Version Features

### Quick (70)
- Fast completion
- Good accuracy
- Entry-level testing

### Standard (93)
- Official MBTI Form M equivalent
- Balanced depth
- Most popular

### Extended (144)
- Higher accuracy
- More nuanced results
- For serious users

### Professional (200)
- Maximum accuracy
- Clinical-level
- Comprehensive analysis


path: bot.py
content: #!/usr/bin/env python3
"""
MBTI Guru - OpenClaw Telegram Bot Entry Point
与OpenClaw集成的Telegram bot处理程序
"""

import sys
import os

# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from lib.telegram_handler import (
    handle_message,
    handle_callback,
    handle_start,
    handle_resume,
    handle_history,
    handle_status,
    handle_cancel,
    VERSIONS
)

def process_update(update: dict) -> dict:
    """
    处理Telegram更新
    
    Args:
        update: Telegram webhook更新
    
    Returns:
        回复消息
    """
    # 提取chat_id和消息
    message = update.get("message", {})
    callback = update.get("callback_query", {})
    
    chat_id = None
    text = ""
    
    if message:
        chat_id = message.get("chat", {}).get("id")
        text = message.get("text", "")
    elif callback:
        chat_id = callback.get("message", {}).get("chat", {}).get("id")
        data = callback.get("data", "")
        return handle_callback(chat_id, data)
    
    if not chat_id:
        return {"action": "send", "message": "Error: No chat_id"}
    
    return handle_message(chat_id, text)


# CLI测试模式
if __name__ == "__main__":
    print("=" * 50)
    print("MBTI Guru - CLI Test Mode")
    print("=" * 50)
    
    chat_id = 123456  # 测试用chat_id
    
    # 模拟开始
    result = handle_start(chat_id)
    print(result["message"])
    print()
    
    # 模拟选择版本
    print("--- Selecting version 2 ---")
    result = handle_message(chat_id, "2")
    print(result["message"][:200] + "...")
    print()
    
    # 模拟回答几题
    print("--- Answering questions ---")
    for i in range(5):
        answer = "A" if i % 2 == 0 else "B"
        result = handle_message(chat_id, answer)
        if result["action"] == "complete":
            print("Test completed!")
            print(result["message"])
            break
        elif result.get("feedback"):
            print(f"Q{i+1}: Answer {answer} - {result.get('feedback', '')[:50]}...")
        else:
            print(f"Q{i+1}: Answer {answer} - OK")
    
    # 测试历史
    print("\n--- Testing history ---")
    result = handle_history(chat_id)
    print(result["message"][:200] + "...")
    
    # 测试状态
    print("\n--- Testing status ---")
    result = handle_status(chat_id)
    print(result["message"])


path: docs/IMPLEMENTATION_v1.4.md
content: # MBTI Guru v1.4 详细更新迭代方案

---

## 一、当前进度

### 已完成 ✅
| 功能 | 状态 | 说明 |
|------|------|------|
| 题库系统 | ✅ | 160题（EI/SN/TF/JP各40题） |
| 随机抽取 | ✅ | 每次测试随机抽题，保证多样性 |
| PDF第二页 | ✅ | 认知功能+成长建议+职场人际+情侣匹配 |

### 进行中 🔄
| 功能 | 状态 | 说明 |
|------|------|------|
| 进度保存 | 🔄 | session.py 已创建 |
| 历史记录 | 🔄 | history.py 已创建 |
| 实时反馈 | ⏳ | 待实现 |
| 进度条 | ⏳ | 待实现 |

### 待开始 ⏳
| 功能 | 说明 |
|------|------|
| 题库扩充至600+ | 每个维度100+题目 |
| 用户交互界面 | Telegram 交互优化 |

---

## 二、已完成功能详解

### 2.1 题库扩充系统

**现状：**
- 题库总量：160题
- 每维度题数：40题
- 抽取方式：每次随机抽取指定数量

**目标：**
- 题库总量：600+题
- 每维度题数：150+题
- 题库质量：去除模糊题、平衡难度

**代码结构：**
```python
# lib/question_pool.py
EI_QUESTIONS = [...]  # 40题
SN_QUESTIONS = [...]  # 40题
TF_QUESTIONS = [...]  # 40题
JP_QUESTIONS = [...]  # 40题

def sample_questions(version):
    # 随机抽取指定数量
    # 确保每次测试题目不同
```

### 2.2 PDF第二页内容

**已包含内容：**

1. **认知功能 (Cognitive Functions)**
   - 主功能 (Dominant)
   - 辅助功能 (Auxiliary)
   - 第三功能 (Tertiary)
   - 劣势功能 (Inferior)
   - 每个功能包含：名称、描述、优势

2. **成长建议 (Growth Suggestions)**
   - 4条个性化成长建议
   - 基于认知功能弱点制定

3. **职场人际 (Workplace Relationships)**
   - 最佳工作环境
   - 团队角色定位
   - 协作建议（4条）

4. **情侣匹配 (Relationship Compatibility)**
   - 最佳匹配类型
   - 挑战类型
   - 关系建议（3条）

---

## 三、待实现功能详解

### 3.1 进度保存/恢复

**实现方案：**
```python
# lib/session.py
def save_session(chat_id, version, current_index, answers):
    # 保存到 data/sessions/{chat_id}_{timestamp}.json
    
def load_session(session_id):
    # 恢复会话
    
def get_incomplete_session(chat_id):
    # 检查是否有未完成会话
```

**用户交互：**
```
用户：/start
Bot：检测到您有未完成的测试，是否继续？
用户：继续
Bot：（恢复测试）
```

### 3.2 历史记录

**实现方案：**
```python
# lib/history.py
def save_test_result(chat_id, type_code, scores, clarity):
    # 保存到 data/history/{chat_id}/tests.json
    
def get_test_history(chat_id, limit=10):
    # 获取最近N次测试
    
def compare_tests(test1, test2):
    # 对比两次测试结果
```

**用户交互：**
```
用户：/history
Bot：您的测试历史：
1. INFP - 2026-03-27
2. ENFP - 2026-03-26
3. INTJ - 2026-03-25
```

### 3.3 实时反馈

**实现方案：**
- 每完成10题，发送简要分析
- 基于已答题目，预测维度倾向

**用户交互：**
```
Bot：📊 已完成 10/70
   能量倾向：I (55%)
   信息倾向：N (60%)
   继续加油！
```

### 3.4 进度条

**实现方案：**
- Unicode 方块字符显示进度
- 每答一题更新

**显示效果：**
```
████████████░░░░░░░░░ 60% (42/70)
```

---

## 四、题库扩充计划

### 4.1 需要新增题目

| 维度 | 现有 | 目标 | 需新增 |
|------|------|------|--------|
| EI | 40 | 150 | 110 |
| SN | 40 | 150 | 110 |
| TF | 40 | 150 | 110 |
| JP | 40 | 150 | 110 |
| **总计** | 160 | **600** | **440** |

### 4.2 题目质量标准

- ✅ 双向陈述清晰
- ✅ 选项长度一致
- ✅ 无文化特定内容
- ✅ 难度适中（中低难度为主）
- ✅ 无双重否定
- ✅ 中英双语对照

---

## 五、技术架构

### 文件结构
```
mbti-guru/
├── lib/
│   ├── questions.py       # 兼容旧版本
│   ├── question_pool.py   # 新题库系统 (160题)
│   ├── scorer.py          # 评分系统
│   ├── mbti_types.py      # 类型数据库
│   ├── cognitive.py       # 认知功能数据 ✅
│   ├── relationships.py   # 职场+情侣数据 ✅
│   ├── session.py         # 会话管理 ✅
│   ├── history.py         # 历史记录 ✅
│   ├── pdf_generator.py   # PDF双页生成 ✅
│   └── reports.py         # 终端报告
├── data/
│   ├── sessions/          # 进度保存 ⏳
│   └── history/           # 历史记录 ⏳
└── SKILL.md
```

### 数据流
```
用户答题 → session.py (保存进度) 
        → scorer.py (计算得分)
        → history.py (保存历史)
        → pdf_generator.py (生成报告)
        → Telegram (发送PDF)
```

---

## 六、测试计划

### 单元测试
- [ ] 题库随机抽取一致性
- [ ] 进度保存/恢复
- [ ] 历史记录读写
- [ ] PDF双页生成

### 集成测试
- [ ] 全流程测试（70/93/144/200题）
- [ ] 多用户并发测试
- [ ] PDF内容验证

---

## 七、发布计划

| 版本 | 内容 | 状态 |
|------|------|------|
| v1.4 | 用户体验 + 题库 + PDF第二页 | **测试中** |
| v1.4.1 | 修复已知问题 | 待定 |
| v1.5 | 深色模式 + 题库扩充完成 | 待定 |

---

_Last updated: 2026-03-27_


path: docs/UPGRADE_v1.4.md
content: # MBTI Guru v1.4-v1.5 升级方案

## 一、当前状态
- 版本: v1.0.3
- 题库: 200题/4版本，每个维度约50题
- PDF: 单页精简版
- 用户体验: 基础交互

---

## 二、v1.4 升级计划

### 2.1 用户体验模块 (Priority 1)

#### A. 进度保存
- **机制:** 用户中途退出可保存进度
- **存储:** `data/sessions/{user_id}_{timestamp}.json`
- **数据:** `{user_id, version, current_index, answers[], timestamp}`
- **恢复:** 下次启动检测未完成session，询问是否继续

#### B. 历史记录
- **存储:** `data/history/{user_id}/`
- **数据:** `{test_id, type_code, scores, clarity, version, date, answers_count}`
- **展示:** 最近10次测试，支持查看详情
- **对比:** 可选两个结果对比分析

#### C. 实时反馈
- **时机:** 每10题完成后显示简要分析
- **内容:** 当前维度倾向 + 剩余题数
- **方式:** Telegram消息推送

#### D. 进度条
- **显示:** `████████░░░░ 65% (65/100)`
- **更新:** 每答一题更新
- **格式:** Unicode方块 + 百分比 + 题数

---

### 2.2 题库扩充模块 (Priority 2)

#### A. 题库目标
| 维度 | 现有题数 | 目标题数 | 需新增 |
|------|---------|---------|--------|
| EI | ~50 | 150+ | ~100 |
| SN | ~50 | 150+ | ~100 |
| TF | ~50 | 150+ | ~100 |
| JP | ~50 | 150+ | ~100 |
| **总计** | ~200 | **600+** | **~400** |

#### B. 题库结构优化
```python
# 题目分级
question_pool = {
    "EI": {
        "E": [...],  # 偏向Extravert的题目
        "I": [...]   # 偏向Introvert的题目
    },
    ...
}

# 每次测试随机抽取，确保多样性
selected = random.sample(pool[dimension], 50)
```

#### C. 题目质量优化
- 去除双重否定题
- 去除文化特定题
- 平衡题目难度（中低难度为主）
- 确保选项长度一致

---

### 2.3 PDF第二页 (Priority 1)

#### 内容结构
```
┌─────────────────────────────────────────┐
│  Page 2: 深度分析 / Deep Analysis      │
├─────────────────────────────────────────┤
│                                         │
│  🧠 认知功能 / Cognitive Functions      │
│     - 主功能 / Auxiliary / Tertiary...   │
│                                         │
│  📈 成长建议 / Growth Suggestions       │
│     - 个人成长路径                       │
│     - 潜在优势发挥                       │
│                                         │
│  💼 职场人际 / Workplace Relationships │
│     - 最佳工作环境                       │
│     - 团队协作建议                       │
│                                         │
│  💕 情侣匹配 / Relationship Compatibility│
│     - 最佳匹配类型                       │
│     - 关系发展建议                       │
│                                         │
└─────────────────────────────────────────┘
```

---

## 三、v1.5 计划（待定）

### 3.1 PDF增强
- 添加深色模式选项（用户可选）
- 更多版式定制

### 3.2 数据分析
- 类型分布统计
- 变化趋势追踪

---

## 四、技术实现

### 文件结构
```
mbti-guru/
├── lib/
│   ├── questions.py      # 题库系统（含大题库）
│   ├── question_pool.py  # 600+题库（新增）
│   ├── scorer.py         # 评分系统
│   ├── mbti_types.py     # 类型数据库
│   ├── cognitive.py      # 认知功能数据（新增）
│   ├── growth.py         # 成长建议数据（新增）
│   ├── careers.py        # 职场人际数据（新增）
│   ├── relationships.py  # 情侣匹配数据（新增）
│   ├── session.py        # 会话管理（新增）
│   ├── history.py        # 历史记录（新增）
│   ├── pdf_generator.py  # PDF生成
│   └── reports.py        # 终端报告
├── data/
│   ├── sessions/         # 进度保存
│   └── history/          # 历史记录
└── SKILL.md
```

### 数据存储
- **会话:** JSON文件，`data/sessions/{chat_id}_{timestamp}.json`
- **历史:** JSON文件，`data/history/{chat_id}/tests.json`

---

## 五、版本计划

| 版本 | 内容 | 优先级 |
|------|------|--------|
| v1.4 | 用户体验 + 题库扩充 + PDF第二页 | 当前 |
| v1.5 | 深色模式 + 数据分析 | 待定 |

---

## 六、测试计划

- [ ] 进度保存/恢复测试
- [ ] 历史记录读写测试
- [ ] 600+题库随机抽取测试
- [ ] PDF双页生成测试
- [ ] 全流程用户测试

---

_Last updated: 2026-03-27_


path: docs/report_spec.md
content: # MBTI Personality Test Skill - Report Specification

## Bilingual Report Structure

### Report 1: Type Confirmation / 类型确认

```
╔══════════════════════════════════════════════════════════════════╗
║                    YOUR PERSONALITY TYPE                     ║
║                       您的性格类型                          ║
╠══════════════════════════════════════════════════════════════════╣
║                                                                ║
║   🧠 INFP                    🧠 INFP                        ║
║      调停者                    The Mediator                   ║
║                                                                ║
║   ─────────────────────────────────────────────────────────   ║
║                                                                ║
║   Clarity / 清晰度: 78%                                      ║
║   Type Distribution / 类型分布:                                ║
║     • Introversion (I) / 内向: 78%                          ║
║     • Intuition (N) / 直觉: 65%                              ║
║     • Feeling (F) / 情感: 82%                                ║
║     • Perceiving (P) / 知觉: 71%                            ║
║                                                                ║
║   ─────────────────────────────────────────────────────────   ║
║                                                                ║
║   Brief Summary / 简介:                                       ║
║   INFP (The Mediator) is an idealistic, creative person     ║
║   who values authenticity and seeks meaningful connections.   ║
║                                                                ║
║   INFP（调停者）是一个理想主义者、创造者，重视真实感，      ║
║   寻求有意义的联系。                                          ║
║                                                                ║
╚══════════════════════════════════════════════════════════════════╝
```

---

## Report 2: Dimension Analysis / 维度分析

### 2.1 Energy: Introversion vs Extraversion / 能量倾向

```
┌──────────────────────────────────────────────────────────────┐
│ ENERGY DIMENSION / 能量维度                                  │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  Your Type / 您的类型: I (Introversion / 内向)               │
│  Score / 得分: 78%                                           │
│  Clarity / 清晰度: 78% → Clear / 清晰                      │
│                                                              │
│  ████████████████████████████████████████████░░░ 78%       │
│                                                              │
│  Description / 描述:                                         │
│  You gain energy through solitary activities and prefer       │
│  deep, meaningful conversations over broad social interactions.│
│                                                              │
│  您通过独处活动获得能量，相比广泛的社交互动，更喜欢深入     │
│  有意义的对话。                                              │
│                                                              │
│  Strengths / 优势:                                          │
│  ✓ Deep thinking / 深度思考                                  │
│  ✓ Good listening / 善于倾听                                  │
│  ✓ Independent / 独立自主                                    │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

### 2.2-2.4 Similar structure for other dimensions...

---

## Report 3: Personality Profile / 性格特征

### 3.1 Strengths / 优势

```
┌──────────────────────────────────────────────────────────────┐
│ STRENGTHS / 优势                                           │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  1. Creative & Imaginative / 创造性与想象力               │
│     You have a rich inner world and strong creativity.     │
│     您拥有丰富的内心世界和强大的创造力。                      │
│                                                              │
│  2. Idealistic / 理想主义                                   │
│     You strive to make the world a better place.            │
│     您努力让世界变得更美好。                                │
│                                                              │
│  3. Compassionate / 有同理心                               │
│     You deeply understand and share others' feelings.         │
│     您深刻理解并分享他人的感受。                              │
│                                                              │
│  4. Loyal & Authentic / 忠诚真实                           │
│     You stay true to yourself and those you care about.      │
│     您对自己和在乎的人保持真实忠诚。                          │
│                                                              │
│  5. Flexible / 适应性强                                     │
│     You adapt well to changing circumstances.                  │
│     您能很好地适应变化的环境。                              │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

### 3.2 Weaknesses / 劣势

```
┌──────────────────────────────────────────────────────────────┐
│ WEAKNESSES / 劣势                                          │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  1. Overly Idealistic / 过于理想化                         │
│     You may set unrealistic standards for yourself and      │
│     others. / 您可能对自己和他人设定了不切实际的标准。        │
│                                                              │
│  2. Sensitive to Criticism / 敏感于批评                    │
│     Negative feedback can deeply affect you.                 │
│     负面反馈可能会深刻影响您。                              │
│                                                              │
│  3. Difficulty with Conflict / 难以面对冲突                │
│     You may avoid situations that cause discomfort.         │
│     您可能会回避引起不安的情况。                            │
│                                                              │
│  4. Overthinker / 过度思考                                 │
│     You may get stuck in analysis paralysis.                │
│     您可能会陷入分析瘫痪。                                  │
│                                                              │
│  5. Neglect Practical Matters /忽视实际事务               │
│     You may get lost in possibilities and neglect details.   │
│     您可能会迷失在各种可能性中而忽视细节。                    │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

---

## Report 4: Relationships / 人际关系

### Compatible Types / 匹配类型

```
┌──────────────────────────────────────────────────────────────┐
│ COMPATIBLE TYPES / 性格匹配                                │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  🌟 Best Match / 最佳匹配 (92%+ compatibility)             │
│     ENFJ - The Protagonist / 主人公                        │
│                                                              │
│  🌟 Strong Match / 强匹配 (85%+ compatibility)              │
│     INFJ - The Advocate / 提倡者                            │
│     ENFP - The Campaigner / 竞选者                          │
│                                                              │
│  🌟 Good Match / 良好匹配 (75%+ compatibility)             │
│     ESFJ - The Consul / 执政官                              │
│     ISFJ - The Defender / 守卫者                           │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

### Communication Tips / 沟通建议

```
┌──────────────────────────────────────────────────────────────┐
│ COMMUNICATION TIPS / 沟通建议                              │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  With INFP / 与INFP沟通:                                    │
│  • Give them space to process their thoughts                │
│    给他们思考的空间                                          │
│  • Be authentic and avoid superficiality                    │
│    保持真实，避免肤浅                                        │
│  • Appreciate their ideals and dreams                       │
│    欣赏他们的理想和梦想                                      │
│                                                              │
│  What to Avoid / 避免事项:                                  │
│  ✗ Being dismissive of their feelings                      │
│    轻视他们的感受                                            │
│  ✗ Forcing quick decisions                                │
│    强迫快速做决定                                            │
│  ✗ Overly critical or judgmental tone                      │
│    过于批评或评判的语气                                      │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

---

## Report 5: Career Guide / 职业指南

### Best Matching Careers / 最佳职业

```
┌──────────────────────────────────────────────────────────────┐
│ BEST CAREERS / 最佳职业                                    │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  🎯 Most Suitable / 最适合:                                 │
│     • Writer / 作者                                        │
│     • Counselor / 心理咨询师                                │
│     • Psychologist / 心理学家                               │
│     • Social Worker / 社会工作者                            │
│     • Teacher / 教师                                       │
│                                                              │
│  🎯 Also Suitable / 也适合:                                 │
│     • Graphic Designer / 平面设计师                        │
│     • HR Specialist / 人力资源专员                          │
│     • Researcher / 研究员                                   │
│     • Journalist / 记者                                     │
│     • Marketing Specialist / 市场营销专员                     │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

### Work Style / 工作风格

```
┌──────────────────────────────────────────────────────────────┐
│ WORK STYLE / 工作风格                                      │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  Strengths / 工作优势:                                     │
│  ✓ Creative problem-solving / 创造性解决问题               │
│  ✓ Empathy with colleagues / 对同事有同理心                  │
│  ✓ Attention to hidden needs / 关注隐藏需求                  │
│  ✓ Long-term focus / 长期关注                               │
│                                                              │
│  Preferred Environment / 偏好环境:                            │
│  • Collaborative / 协作                                    │
│  • Values-driven / 价值观驱动                               │
│  • Flexible / 灵活                                        │
│  • Low conflict / 低冲突                                   │
│                                                              │
│  Growth Tips / 成长建议:                                   │
│  • Set practical short-term goals / 设定实际的短期目标     │
│  • Practice making quick decisions / 练习快速做决定         │
│  • Accept constructive criticism / 接受建设性批评           │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

---

## Report 6: Full Analysis / 完整分析

### Title Page

```
╔══════════════════════════════════════════════════════════════════╗
║                                                                  ║
║         MBTI PERSONALITY ANALYSIS REPORT                          ║
║         MBTI 人格分析报告                                        ║
║                                                                  ║
║  ─────────────────────────────────────────────────────────────   ║
║                                                                  ║
║  Type / 类型: INFP                                             ║
║  Name / 名称: The Mediator / 调停者                          ║
║  Clarity / 清晰度: 78%                                        ║
║                                                                  ║
║  Report Date / 报告日期: 2026-03-26                           ║
║  Version / 版本: Standard (93 questions)                       ║
║                                                                  ║
╚══════════════════════════════════════════════════════════════════╝
```

### Final Summary

```
┌──────────────────────────────────────────────────────────────┐
│ FINAL SUMMARY / 最终总结                                    │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  Your INFP personality is characterized by:                 │
│  您的INFP人格特点如下：                                      │
│                                                              │
│  1. Deep introversion with 78% clarity                     │
│     深度内向，清晰度78%                                       │
│                                                              │
│  2. Strong intuition preference (N)                         │
│     强烈直觉倾向                                            │
│                                                              │
│  3. Feeling-based decision making (F)                       │
│     基于情感做决定                                          │
│                                                              │
│  4. Perceiving lifestyle (P)                                 │
│     知觉型生活方式                                          │
│                                                              │
│  Key Insight / 关键洞见:                                      │
│  "You are an idealistic creator who seeks authenticity       │
│   and meaningful connections, often finding fulfillment       │
│   in helping others and exploring creative pursuits."         │
│                                                              │
│  "您是一个寻求真实感和有意义联系的理想主义创造者，            │
│   常在帮助他人和探索创意追求中找到成就感。"                    │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

---

## Report Formatting Standards

### Header
- Centered title with box drawing
- Bilingual (Chinese + English)
- Clear hierarchy

### Sections
- Numbered with clear titles
- Box drawing for boundaries
- Consistent spacing

### Content
- Bullet points (✓ for positive, ✗ for negative)
- Bold key terms
- Emoji for visual aid
- Chinese translation for each English point

### Tables
- ASCII art borders
- Consistent width
- Aligned columns

### Final Output
- Professional, publication-quality
- Print-ready
- Screen-friendly


path: generate_all_versions.py
content: #!/usr/bin/env python3
"""
生成4个版本的完整PDF报告
"""

import sys
import os
sys.path.insert(0, os.path.dirname(__file__))

from lib.pdf_combined import create_pdf_report
from lib.scorer import calculate_type, calculate_clarity
from lib.question_pool import sample_questions
from datetime import datetime

def generate_version_report(version_num, version_name):
    """生成指定版本的完整报告"""
    print(f"\n{'='*50}")
    print(f"生成 {version_name} ({version_num}题) 报告")
    print('='*50)
    
    # 生成随机答案
    questions = sample_questions(version_num)
    answers = []
    for i, q in enumerate(questions):
        # 模拟随机选择
        choice = 'A' if (i * 7) % 2 == 0 else 'B'
        answers.append((q['id'], choice))
    
    # 计算结果
    type_code, scores = calculate_type(answers)
    clarity = {dim: calculate_clarity(score) for dim, score in scores.items()}
    
    # 生成PDF
    today = datetime.now().strftime('%Y-%m-%d')
    filename = f"MBTI-v{version_num}-{type_code}-{today}.pdf"
    output_path = create_pdf_report(type_code, scores, clarity, filename)
    
    size = os.path.getsize(output_path)
    print(f"生成: {filename} ({size} bytes)")
    print(f"类型: {type_code}")
    print(f"维度得分: {scores}")
    
    return output_path, type_code

def main():
    versions = [
        (70, "快速版"),
        (93, "标准版"),
        (144, "扩展版"),
        (200, "专业版"),
    ]
    
    pdfs = []
    for version_num, version_name in versions:
        try:
            pdf_path, mbti_type = generate_version_report(version_num, version_name)
            pdfs.append((version_name, version_num, pdf_path, mbti_type))
        except Exception as e:
            print(f"生成 {version_name} 失败: {e}")
    
    print("\n" + "="*50)
    print("生成完成!")
    print("="*50)
    for name, num, path, mtype in pdfs:
        print(f"  {name} ({num}题): {path} -> {mtype}")
    
    return pdfs

if __name__ == "__main__":
    main()


path: lib/__init__.py
content: #!/usr/bin/env python3
"""
MBTI Guru - OpenClaw Skill Interface
支持所有 OpenClaw 渠道: Telegram, Discord, 飞书, 微信等
"""

from lib.mbti_core import (
    process_message,
    process_start,
    process_resume,
    process_history,
    process_status,
    process_cancel,
    get_version_selection_text,
    get_progress_bar,
    get_history_text,
    VERSIONS
)

__all__ = [
    'process_message',
    'process_start',
    'process_resume',
    'process_history',
    'process_status',
    'process_cancel',
    'get_version_selection_text',
    'get_progress_bar',
    'get_history_text',
    'VERSIONS'
]


path: lib/cognitive.py
content: #!/usr/bin/env python3
"""
MBTI Guru - 认知功能数据
包含16种MBTI类型的8种认知功能栈
"""

COGNITIVE_FUNCTIONS = {
    # ============================================
    # NT 理性者 (Intuitive Thinking)
    # ============================================
    "INTJ": {
        "name": "建筑师 / Architect",
        "functions": {
            "dominant": {"name": "内向直觉 Ni", "description": "洞察未来可能性，善于看到事物发展的规律和趋势", "strength": "战略思维、预见性"},
            "auxiliary": {"name": "外向思考 Te", "description": "高效组织外部世界，制定逻辑性的计划和系统", "strength": "执行能力、系统构建"},
            "tertiary": {"name": "内向情感 Fi", "description": "建立个人价值观体系，关注内心情感体验", "strength": "价值判断、道德指南"},
            "inferior": {"name": "外向感觉 Se", "description": "对当下环境和具体细节的感知", "strength": "即时反应、现实感知"},
        },
        "shadow": {
            "critic": "外向直觉 Ne", "trickster": "内向思考 Ti",
            "demon": "外向情感 Fe", "devil": "内向感觉 Si"
        },
        "growth": [
            "发展Te的同时，不要忽视Fi——你的价值观同样重要",
            "学会放松，适时体验当下而非总是规划未来",
            "接受不确定性，明白不是所有事都需要逻辑解释",
            "多倾听他人的感受，而不只是逻辑分析"
        ]
    },
    "INTP": {
        "name": "逻辑学家 / Logician",
        "functions": {
            "dominant": {"name": "内向思考 Ti", "description": "深入分析内在逻辑，追求精确的理解和原理", "strength": "分析能力、逻辑严谨"},
            "auxiliary": {"name": "外向直觉 Ne", "description": "探索各种可能性，连接不同的想法和概念", "strength": "创意联想、可能性思维"},
            "tertiary": {"name": "内向感觉 Si", "description": "回顾过去的经验和细节，建立内部数据库", "strength": "经验积累、细节记忆"},
            "inferior": {"name": "外向情感 Fe", "description": "理解和适应他人的情感需求", "strength": "社交技巧、情感洞察"},
        },
        "shadow": {
            "critic": "内向直觉 Ni", "trickster": "外向思考 Te",
            "demon": "内向感觉 Si", "devil": "外向情感 Fe"
        },
        "growth": [
            "发展Fe功能，主动表达情感而非总是藏在心里",
            "学会在适当时机做决定，不过度分析",
            "珍惜人际关系，不只是把它们当作逻辑练习",
            "接受不完美，理解灰色地带的存在"
        ]
    },
    "ENTJ": {
        "name": "指挥官 / Commander",
        "functions": {
            "dominant": {"name": "外向思考 Te", "description": "高效组织外部资源，推动目标实现", "strength": "领导能力、决策效率"},
            "auxiliary": {"name": "内向直觉 Ni", "description": "形成长远的战略愿景，把握发展方向", "strength": "战略规划、直觉判断"},
            "tertiary": {"name": "外向感觉 Se", "description": "关注当下的具体现实和环境", "strength": "即时行动、细节把握"},
            "inferior": {"name": "内向情感 Fi", "description": "建立和维护个人价值观和内心感受", "strength": "自我认知、价值坚守"},
        },
        "shadow": {
            "critic": "内向思考 Ti", "trickster": "外向直觉 Ne",
            "demon": "外向感觉 Se", "devil": "内向直觉 Ni"
        },
        "growth": [
            "发展Fi，学会理解自己和他人的情感需求",
            "练习耐心倾听，不要总是急于给出解决方案",
            "接受自己也有软弱的时候，不需要一直强势",
            "注意他人的感受，而不只是效率和结果"
        ]
    },
    "ENTP": {
        "name": "辩论家 / Debater",
        "functions": {
            "dominant": {"name": "外向直觉 Ne", "description": "探索可能性，连接不同的想法和创新概念", "strength": "创意能力、可能性探索"},
            "auxiliary": {"name": "内向思考 Ti", "description": "分析内在逻辑，评估想法的合理性", "strength": "逻辑分析、客观评估"},
            "tertiary": {"name": "外向情感 Fe", "description": "关注他人的情感和需求", "strength": "人际洞察、情感共鸣"},
            "inferior": {"name": "内向感觉 Si", "description": "重视过去的经验和既有程序", "strength": "传统维护、细节执行"},
        },
        "shadow": {
            "critic": "外向思考 Te", "trickster": "内向直觉 Ni",
            "demon": "内向感觉 Si", "devil": "外向情感 Fe"
        },
        "growth": [
            "发展Si，学会坚持完成已经开始的项目",
            "练习专注，而不只是对新想法感到兴奋",
            "学习承担责任，而不只是逃避困难的决定",
            "在争论中多考虑他人的感受"
        ]
    },
    
    # ============================================
    # NF 理想主义者 (Intuitive Feeling)
    # ============================================
    "INFJ": {
        "name": "提倡者 / Advocate",
        "functions": {
            "dominant": {"name": "内向直觉 Ni", "description": "洞察本质和未来可能性，看到更深层的模式", "strength": "洞察力、预见性"},
            "auxiliary": {"name": "外向情感 Fe", "description": "理解和影响他人的情感需求", "strength": "共情能力、人际影响"},
            "tertiary": {"name": "内向思考 Ti", "description": "分析内在逻辑，确保决定的一致性", "strength": "批判思维、逻辑验证"},
            "inferior": {"name": "外向感觉 Se", "description": "感知当下的具体环境和细节", "strength": "现实适应、即时反应"},
        },
        "shadow": {
            "critic": "外向直觉 Ne", "trickster": "内向情感 Fi",
            "demon": "外向感觉 Se", "devil": "内向思考 Ti"
        },
        "growth": [
            "不要总是为了满足他人而牺牲自己",
            "学会接受冲突，不必害怕伤害他人感情",
            "发展Ti，勇于进行困难的对话",
            "多关注当下，享受此时此刻"
        ]
    },
    "INFP": {
        "name": "调停者 / Mediator",
        "functions": {
            "dominant": {"name": "内向情感 Fi", "description": "建立在个人价值观基础上的内心体验和判断", "strength": "价值观判断、情感深度"},
            "auxiliary": {"name": "外向直觉 Ne", "description": "探索外部世界的可能性和新的想法", "strength": "创意探索、可能性思维"},
            "tertiary": {"name": "内向感觉 Si", "description": "联系过去的经验和既有的做事方式", "strength": "传统维护、经验积累"},
            "inferior": {"name": "外向思考 Te", "description": "高效组织外部世界完成任务", "strength": "执行能力、效率提升"},
        },
        "shadow": {
            "critic": "内向直觉 Ni", "trickster": "外向情感 Fe",
            "demon": "内向感觉 Si", "devil": "外向思考 Te"
        },
        "growth": [
            "发展Te，不要让完美主义阻止你行动",
            "学会说“不”，保护自己的时间和精力",
            "接受现实有时与理想不同",
            "在坚持价值观和灵活变通之间找到平衡"
        ]
    },
    "ENFJ": {
        "name": "主人公 / Protagonist",
        "functions": {
            "dominant": {"name": "外向情感 Fe", "description": "真诚地关心他人，致力于共同成长", "strength": "激励能力、领导魅力"},
            "auxiliary": {"name": "内向直觉 Ni", "description": "看到更深层的意义和未来的可能性", "strength": "战略眼光、洞察本质"},
            "tertiary": {"name": "外向感觉 Se", "description": "享受当下的体验和周围的世界", "strength": "即时反应、现实感知"},
            "inferior": {"name": "内向思考 Ti", "description": "分析内在逻辑确保决定合理", "strength": "批判分析、逻辑思考"},
        },
        "shadow": {
            "critic": "内向情感 Fi", "trickster": "外向直觉 Ne",
            "demon": "外向感觉 Se", "devil": "内向直觉 Ni"
        },
        "growth": [
            "发展Ti，允许自己做出不受欢迎的决定",
            "不要过度在意他人的看法",
            "学会接受自己无法改变所有事",
            "给自己留些独处的时间充电"
        ]
    },
    "ENFP": {
        "name": "竞选者 / Campaigner",
        "functions": {
            "dominant": {"name": "外向直觉 Ne", "description": "充满热情地探索各种可能性和创新想法", "strength": "热情传染、创意激发"},
            "auxiliary": {"name": "内向情感 Fi", "description": "基于个人价值观做真实的决定", "strength": "真实自我、价值驱动"},
            "tertiary": {"name": "内向感觉 Si", "description": "联系过去的经验和熟悉的方式", "strength": "传统维护、经验运用"},
            "inferior": {"name": "外向思考 Te", "description": "高效组织和完成外部世界的任务", "strength": "执行能力、组织管理"},
        },
        "shadow": {
            "critic": "外向情感 Fe", "trickster": "内向直觉 Ni",
            "demon": "内向感觉 Si", "devil": "外向思考 Te"
        },
        "growth": [
            "发展Te，学会坚持完成而不是半途而废",
            "练习专注，不被太多可能性分散注意力",
            "学会面对困难的决定，而不只是拖延",
            "在热情和实际可行性之间找到平衡"
        ]
    },
    
    # ============================================
    # SJ 守护者 (Sensing Judging)
    # ============================================
    "ISTJ": {
        "name": "物流师 / Logistician",
        "functions": {
            "dominant": {"name": "内向感觉 Si", "description": "关注细节和过去的经验， 建立可靠的系统", "strength": "可靠性、细节把控"},
            "auxiliary": {"name": "外向思考 Te", "description": "高效组织外部世界，实现目标", "strength": "执行能力、组织效率"},
            "tertiary": {"name": "内向情感 Fi", "description": "发展个人价值观和内在标准", "strength": "道德指南、价值判断"},
            "inferior": {"name": "外向直觉 Ne", "description": "探索新的可能性和创新的想法", "strength": "可能性探索、创新思维"},
        },
        "shadow": {
            "critic": "外向感觉 Se", "trickster": "内向思考 Ti",
            "demon": "内向感觉 Si", "devil": "外向直觉 Ne"
        },
        "growth": [
            "发展Ne，适度接受变化和新想法",
            "不要过于僵化，学会灵活应变",
            "理解他人的做事方式可能与你不同",
            "允许自己尝试新的方法而不只是依赖经验"
        ]
    },
    "ISFJ": {
        "name": "守护者 / Defender",
        "functions": {
            "dominant": {"name": "内向感觉 Si", "description": "珍视传统和过去的经验，细心照顾他人的需求", "strength": "忠诚守护、细心照顾"},
            "auxiliary": {"name": "外向情感 Fe", "description": "真诚地帮助和服务他人", "strength": "服务精神、人文关怀"},
            "tertiary": {"name": "内向直觉 Ni", "description": "看到事物发展的趋势和可能性", "strength": "洞察趋势、预见发展"},
            "inferior": {"name": "外向思考 Te", "description": "完成任务和达成目标的能力", "strength": "执行能力、效率意识"},
        },
        "shadow": {
            "critic": "内向情感 Fi", "trickster": "外向感觉 Se",
            "demon": "内向直觉 Ni", "devil": "外向思考 Te"
        },
        "growth": [
            "发展Te，为自己的需求站出来说话",
            "学会接受赞美，而不只是默默付出",
            "不要把别人的问题都扛在自己身上",
            "适度放手，不必事必躬亲"
        ]
    },
    "ESTJ": {
        "name": "总经理 / Executive",
        "functions": {
            "dominant": {"name": "外向思考 Te", "description": "高效组织和管理外部事务", "strength": "管理能力、执行效率"},
            "auxiliary": {"name": "内向感觉 Si", "description": "利用过去的经验和传统确保稳定", "strength": "传统维护、系统建立"},
            "tertiary": {"name": "内向直觉 Ni", "description": "看到未来发展的可能性", "strength": "战略眼光、趋势把握"},
            "inferior": {"name": "外向情感 Fe", "description": "理解和关心他人的情感需求", "strength": "情感洞察、人文关怀"},
        },
        "shadow": {
            "critic": "外向感觉 Se", "trickster": "内向思考 Ti",
            "demon": "内向直觉 Ni", "devil": "外向情感 Fe"
        },
        "growth": [
            "发展Fe，多关注员工的情感而非只是业绩",
            "学会欣赏不同的观点和工作方式",
            "接受不是所有事都能用逻辑解决",
            "练习耐心，不只是追求效率"
        ]
    },
    "ESFJ": {
        "name": "执政官 / Consul",
        "functions": {
            "dominant": {"name": "外向情感 Fe", "description": "热情地照顾他人，营造和谐的人际关系", "strength": "社交能力、人际和谐"},
            "auxiliary": {"name": "内向感觉 Si", "description": "关注细节和既有的经验传统", "strength": "细心周到、传统维护"},
            "tertiary": {"name": "内向直觉 Ni", "description": "洞察未来的可能性和趋势", "strength": "战略思维、预见能力"},
            "inferior": {"name": "外向思考 Te", "description": "完成任务和组织外部事务", "strength": "执行能力、组织管理"},
        },
        "shadow": {
            "critic": "外向感觉 Se", "trickster": "内向情感 Fi",
            "demon": "内向直觉 Ni", "devil": "外向思考 Te"
        },
        "growth": [
            "发展Te，为自己的需求争取而不只是付出",
            "学会接受批评，不必追求每个人的认可",
            "不要过度在意社交礼仪而忽视自己的感受",
            "允许自己说“不”，不必讨好所有人"
        ]
    },
    
    # ============================================
    # SP 探险家 (Sensing Perceiving)
    # ============================================
    "ISTP": {
        "name": "鉴赏家 / Virtuoso",
        "functions": {
            "dominant": {"name": "内向思考 Ti", "description": "分析内在逻辑原理，追求精确的技术理解", "strength": "逻辑分析、技术专长"},
            "auxiliary": {"name": "外向感觉 Se", "description": "敏锐感知当下环境，擅长操作工具", "strength": "动手能力、即时反应"},
            "tertiary": {"name": "内向直觉 Ni", "description": "看到事物发展的长远趋势", "strength": "战略洞察、趋势把握"},
            "inferior": {"name": "外向情感 Fe", "description": "理解和适应他人的情感需求", "strength": "人际适应、情感洞察"},
        },
        "shadow": {
            "critic": "内向感觉 Si", "trickster": "外向思考 Te",
            "demon": "外向感觉 Se", "devil": "内向直觉 Ni"
        },
        "growth": [
            "发展Fe，表达自己的情感而不是藏起来",
            "学会承诺和坚持，而不只是保持灵活",
            "多关注他人的需求而不只是技术问题",
            "练习长期规划，而不只是即时反应"
        ]
    },
    "ISFP": {
        "name": "探险家 / Adventurer",
        "functions": {
            "dominant": {"name": "内向情感 Fi", "description": "基于个人价值观做出真实的决定", "strength": "艺术感受、价值坚守"},
            "auxiliary": {"name": "外向感觉 Se", "description": "享受当下的体验，灵活适应环境", "strength": "审美能力、灵活适应"},
            "tertiary": {"name": "内向直觉 Ni", "description": "洞察未来的可能性", "strength": "艺术洞察、直觉把握"},
            "inferior": {"name": "外向思考 Te", "description": "高效组织和完成任务", "strength": "执行能力、效率提升"},
        },
        "shadow": {
            "critic": "内向感觉 Si", "trickster": "外向情感 Fe",
            "demon": "内向直觉 Ni", "devil": "外向思考 Te"
        },
        "growth": [
            "发展Te，勇敢表达自己的需求和立场",
            "学会坚持完成任务而不只是半途而废",
            "不要过度自我批评，接受自己的不完美",
            "练习在团队中发挥领导作用"
        ]
    },
    "ESTP": {
        "name": "企业家 / Entrepreneur",
        "functions": {
            "dominant": {"name": "外向感觉 Se", "description": "敏锐感知当下环境，果断行动", "strength": "行动力、危机处理"},
            "auxiliary": {"name": "外向思考 Te", "description": "高效分析和解决问题", "strength": "分析能力、决策效率"},
            "tertiary": {"name": "内向直觉 Ni", "description": "形成对未来趋势的洞察", "strength": "趋势把握、战略眼光"},
            "inferior": {"name": "内向情感 Fi", "description": "建立和维护个人价值观", "strength": "价值坚守、道德指南"},
        },
        "shadow": {
            "critic": "外向直觉 Ne", "trickster": "内向感觉 Si",
            "demon": "外向思考 Te", "devil": "内向直觉 Ni"
        },
        "growth": [
            "发展Fi，关注自己的内心感受而非只看外在成就",
            "练习停下来思考，而不只是冲动行动",
            "学会尊重他人的感受而不只是追求刺激",
            "培养长期规划的能力"
        ]
    },
    "ESFP": {
        "name": "表演者 / Entertainer",
        "functions": {
            "dominant": {"name": "外向感觉 Se", "description": "享受当下的体验，充满活力和热情", "strength": "表现力、热情传染"},
            "auxiliary": {"name": "内向情感 Fi", "description": "基于个人价值观做真实的决定", "strength": "艺术表达、价值驱动"},
            "tertiary": {"name": "内向直觉 Ni", "description": "洞察未来的可能性", "strength": "趋势洞察、预见能力"},
            "inferior": {"name": "外向思考 Te", "description": "高效组织和分析外部世界", "strength": "组织能力、逻辑分析"},
        },
        "shadow": {
            "critic": "外向情感 Fe", "trickster": "内向感觉 Si",
            "demon": "内向直觉 Ni", "devil": "外向思考 Te"
        },
        "growth": [
            "发展Te，学会计划和组织而不只是活在当下",
            "接受批评，把它当作成长的机会",
            "不要过度追求认可，做真实的自己",
            "练习在决定前进行深思熟虑"
        ]
    },
}

def get_cognitive_functions(type_code: str) -> dict:
    """获取类型的认知功能数据"""
    return COGNITIVE_FUNCTIONS.get(type_code, {})

def get_all_functions() -> dict:
    """获取所有类型的认知功能数据"""
    return COGNITIVE_FUNCTIONS


path: lib/history.py
content: #!/usr/bin/env python3
"""
MBTI Guru - 历史记录模块
支持测试历史存储和查询
"""

import os
import json
from datetime import datetime
from typing import List, Dict, Optional

DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data", "history")

def ensure_data_dir(chat_id: int = None):
    """确保数据目录存在"""
    if chat_id:
        os.makedirs(os.path.join(DATA_DIR, str(chat_id)), exist_ok=True)
    else:
        os.makedirs(DATA_DIR, exist_ok=True)

def save_test_result(chat_id: int, type_code: str, scores: Dict, clarity: Dict, version: int, answers_count: int) -> str:
    """
    保存测试结果
    
    Args:
        chat_id: 用户ID
        type_code: MBTI类型如"INFP"
        scores: 维度得分
        clarity: 清晰度
        version: 测试版本
        answers_count: 答题数量
    
    Returns:
        test_id: 测试记录ID
    """
    import time
    ensure_data_dir(chat_id)
    
    test_id = f"{chat_id}_{int(time.time())}"
    
    test_data = {
        "test_id": test_id,
        "chat_id": chat_id,
        "type_code": type_code,
        "scores": scores,
        "clarity": {k: {"value": v[0], "label": v[1]} for k, v in clarity.items()},
        "version": version,
        "answers_count": answers_count,
        "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "timestamp": int(time.time())
    }
    
    filepath = os.path.join(DATA_DIR, str(chat_id), "tests.json")
    
    # 读取现有历史或创建新的
    history = []
    if os.path.exists(filepath):
        with open(filepath, 'r', encoding='utf-8') as f:
            history = json.load(f)
    
    # 添加新记录到开头
    history.insert(0, test_data)
    
    # 只保留最近20条
    history = history[:20]
    
    with open(filepath, 'w', encoding='utf-8') as f:
        json.dump(history, f, ensure_ascii=False, indent=2)
    
    return test_id

def get_test_history(chat_id: int, limit: int = 10) -> List[Dict]:
    """
    获取测试历史
    
    Args:
        chat_id: 用户ID
        limit: 返回数量限制
    
    Returns:
        测试历史列表
    """
    ensure_data_dir(chat_id)
    
    filepath = os.path.join(DATA_DIR, str(chat_id), "tests.json")
    
    if not os.path.exists(filepath):
        return []
    
    with open(filepath, 'r', encoding='utf-8') as f:
        history = json.load(f)
    
    return history[:limit]

def get_test_detail(chat_id: int, test_id: str) -> Optional[Dict]:
    """
    获取某次测试的详细信息
    
    Args:
        chat_id: 用户ID
        test_id: 测试ID
    
    Returns:
        测试详情或None
    """
    history = get_test_history(chat_id, limit=20)
    
    for test in history:
        if test['test_id'] == test_id:
            return test
    
    return None

def compare_tests(test1: Dict, test2: Dict) -> Dict:
    """
    对比两次测试结果
    
    Args:
        test1: 测试1数据
        test2: 测试2数据
    
    Returns:
        对比分析结果
    """
    changes = {}
    
    for dim in ['EI', 'SN', 'TF', 'JP']:
        score1 = test1['scores'].get(dim, 50)
        score2 = test2['scores'].get(dim, 50)
        diff = score2 - score1
        
        if abs(diff) < 5:
            label = "基本不变"
        elif diff > 0:
            label = f"偏好增加 +{diff:.0f}%"
        else:
            label = f"偏好减少 {diff:.0f}%"
        
        changes[dim] = {
            "test1_score": score1,
            "test2_score": score2,
            "change": diff,
            "label": label
        }
    
    type_change = "相同" if test1['type_code'] == test2['type_code'] else f"{test1['type_code']} → {test2['type_code']}"
    
    return {
        "type_change": type_change,
        "dimension_changes": changes,
        "date1": test1['date'],
        "date2": test2['date']
    }

def delete_history(chat_id: int, test_id: str = None) -> bool:
    """
    删除历史记录
    
    Args:
        chat_id: 用户ID
        test_id: 测试ID，如果为None则删除全部
    
    Returns:
        是否删除成功
    """
    if test_id is None:
        # 删除全部
        filepath = os.path.join(DATA_DIR, str(chat_id), "tests.json")
        if os.path.exists(filepath):
            os.remove(filepath)
            return True
        return False
    
    # 删除单条
    history = get_test_history(chat_id, limit=20)
    history = [t for t in history if t['test_id'] != test_id]
    
    filepath = os.path.join(DATA_DIR, str(chat_id), "tests.json")
    with open(filepath, 'w', encoding='utf-8') as f:
        json.dump(history, f, ensure_ascii=False, indent=2)
    
    return True


path: lib/mbti_core.py
content: #!/usr/bin/env python3
"""
MBTI Guru - 核心处理模块 (平台无关)
支持所有 OpenClaw 渠道: Telegram, Discord, 飞书, 微信等
"""

import os
import sys
import json
import time
from datetime import datetime
from typing import Optional, Dict, List, Tuple

# 添加 lib 到路径
sys.path.insert(0, os.path.dirname(__file__))

from lib.session import save_session, load_session, get_incomplete_session, delete_session, list_user_sessions
from lib.history import save_test_result, get_test_history, get_test_detail
from lib.question_pool import sample_questions
from lib.scorer import calculate_type, calculate_clarity

# ==================== 常量 ====================
VERSIONS = {
    "1": {"name": "快速版", "name_en": "Quick", "questions": 70, "minutes": 10},
    "2": {"name": "标准版", "name_en": "Standard", "questions": 93, "minutes": 15},
    "3": {"name": "扩展版", "name_en": "Extended", "questions": 144, "minutes": 25},
    "4": {"name": "专业版", "name_en": "Professional", "questions": 200, "minutes": 35},
}

# ==================== 用户状态管理 ====================
class UserState:
    """用户测试状态"""
    def __init__(self, chat_id: int):
        self.chat_id = chat_id
        self.state = "idle"  # idle, selecting_version, testing, completed
        self.version: Optional[int] = None
        self.questions: List[Dict] = []
        self.current_index: int = 0
        self.answers: List[Tuple[str, str]] = []
        self.session_id: Optional[str] = None
        self.start_time: Optional[float] = None
        self.last_save_time: Optional[float] = None
    
    def to_dict(self) -> Dict:
        return {
            "chat_id": self.chat_id,
            "state": self.state,
            "version": self.version,
            "current_index": self.current_index,
            "answers_count": len(self.answers),
            "session_id": self.session_id
        }

# 内存中的用户状态
_user_states: Dict[int, UserState] = {}

def get_user_state(chat_id: int) -> UserState:
    """获取或创建用户状态"""
    if chat_id not in _user_states:
        _user_states[chat_id] = UserState(chat_id)
    return _user_states[chat_id]

def clear_user_state(chat_id: int):
    """清除用户状态"""
    if chat_id in _user_states:
        del _user_states[chat_id]

# ==================== 进度保存 ====================
def save_progress(chat_id: int, state: UserState) -> str:
    """保存测试进度"""
    if state.version and state.questions:
        session_id = save_session(
            chat_id=chat_id,
            version=state.version,
            current_index=state.current_index,
            answers=state.answers,
            questions_total=len(state.questions)
        )
        state.session_id = session_id
        state.last_save_time = time.time()
        return session_id
    return ""

# ==================== 消息生成 ====================
def get_version_selection_text() -> str:
    """版本选择文本"""
    msg = "📊 **MBTI 人格测试**\n\n"
    msg += "请选择测试版本：\n\n"
    for key, info in VERSIONS.items():
        msg += f"`{key}`. {info['name']} ({info['questions']}题) ~{info['minutes']}分钟\n"
    msg += "\n发送数字 `1-4` 选择版本"
    return msg

def get_progress_bar(current: int, total: int, width: int = 10) -> str:
    """生成进度条"""
    filled = int(current * width / total)
    empty = width - filled
    pct = int(current * 100 / total)
    return f"[{'█' * filled}{'░' * empty}] {pct}% ({current}/{total})"

def get_question_text(state: UserState) -> Tuple[str, int]:
    """获取当前题目文本和题号"""
    q = state.questions[state.current_index]
    q_num = state.current_index + 1
    total = len(state.questions)
    
    progress = get_progress_bar(q_num, total)
    
    header = f"📝 **问题 {q_num}/{total}**\n{progress}\n\n"
    
    option_a = q.get("option_a", "")
    option_b = q.get("option_b", "")
    
    body = f"{q.get('question_cn', '')}\n\n"
    body += f"🅰️ {option_a}\n\n"
    body += f"🅱️ {option_b}"
    
    return header + body, q_num

def get_completion_text(type_code: str, scores: Dict, clarity: Dict) -> str:
    """完成消息文本"""
    type_names = {
        "ISTJ": "物流师", "ISFJ": "守卫者", "INFJ": "提倡者", "INTJ": "建筑师",
        "ISTP": "鉴赏家", "ISFP": "探险家", "INFP": "调停者", "INTP": "逻辑学家",
        "ESTP": "企业家", "ESFP": "表演者", "ENFP": "竞选者", "ENTP": "辩论家",
        "ESTJ": "经理", "ESFJ": "执政官", "ENFJ": "主人公", "ENTJ": "指挥官",
    }
    
    name_en = type_names.get(type_code, type_code)
    
    msg = f"🎉 **测试完成！**\n\n"
    msg += f"你的MBTI类型：**{type_code}** ({name_en})\n\n"
    msg += "**维度得分：**\n"
    
    dim_names = {"EI": "能量倾向", "SN": "信息获取", "TF": "决策方式", "JP": "生活态度"}
    for dim, (score, pref) in clarity.items():
        dim_name = dim_names.get(dim, dim)
        bar = "▓" * int(score // 10) + "░" * (10 - int(score // 10))
        msg += f"{dim} {dim_name}: {bar} {score}%\n"
    
    msg += "\n正在生成PDF报告..."
    return msg

def get_history_text(history: List[Dict]) -> str:
    """历史记录文本"""
    if not history:
        return "📭 暂无测试历史\n\n开始新测试：/start"
    
    msg = "📜 **测试历史**\n\n"
    for i, test in enumerate(history[:5], 1):
        date = test.get("date", "")[:10]
        mtype = test.get("type_code", "")
        version = test.get("version", "")
        clarity_val = test.get("clarity", {})
        
        if clarity_val:
            vals = [v.get("value", 0) for v in clarity_val.values() if isinstance(v, dict)]
            avg = sum(vals) / len(vals) if vals else 0
            clarity_str = f"{avg:.0f}%"
        else:
            clarity_str = "N/A"
        
        msg += f"`{i}`. **{mtype}** | 清晰度: {clarity_str} | {date} | v{version}题\n"
    
    if len(history) > 5:
        msg += f"\n_显示最近5条，共{len(history)}条历史_"
    
    return msg

def get_status_text(chat_id: int, state: UserState) -> str:
    """状态查询文本"""
    incomplete = get_incomplete_session(chat_id)
    
    if state.state == "testing" and state.questions:
        progress = get_progress_bar(state.current_index, len(state.questions))
        msg = f"📍 **测试进行中**\n\n"
        msg += f"版本：{state.version}题\n"
        msg += f"进度：{progress}\n"
        msg += f"已答：{len(state.answers)}题"
        return msg
    
    if incomplete:
        progress = get_progress_bar(incomplete["current_index"], incomplete["questions_total"])
        msg = f"📍 **有未完成的测试**\n\n"
        msg += f"版本：{incomplete['version']}题\n"
        msg += f"进度：{progress}\n"
        msg += f"发送 /resume 继续"
        return msg
    
    return "📍 **当前状态：** 空闲\n\n发送 /start 开始新测试"

# ==================== 核心处理 ====================
def process_start(chat_id: int) -> Dict:
    """处理开始命令"""
    state = get_user_state(chat_id)
    state.state = "idle"
    return {
        "action": "send",
        "message": get_version_selection_text(),
        "state": "selecting_version"
    }

def process_version_select(chat_id: int, version_key: str) -> Dict:
    """处理版本选择"""
    if version_key not in VERSIONS:
        return {"action": "send", "message": "请发送数字 1-4 选择版本"}
    
    version_info = VERSIONS[version_key]
    version_num = version_info["questions"]
    
    state = get_user_state(chat_id)
    state.state = "testing"
    state.version = version_num
    state.questions = sample_questions(version_num)
    state.current_index = 0
    state.answers = []
    state.start_time = time.time()
    
    question_text, _ = get_question_text(state)
    
    return {
        "action": "send",
        "message": question_text,
        "state": "testing"
    }

def process_answer(chat_id: int, answer: str) -> Dict:
    """处理回答"""
    state = get_user_state(chat_id)
    
    # 如果状态不是testing，尝试恢复session
    if state.state != "testing":
        incomplete = get_incomplete_session(chat_id)
        if incomplete:
            state.state = "testing"
            state.version = incomplete["version"]
            state.questions = sample_questions(incomplete["questions_total"])
            state.current_index = incomplete["current_index"]
            state.answers = incomplete["answers"]
            state.session_id = incomplete["session_id"]
        else:
            return {"action": "send", "message": "请先开始测试：/start"}
    
    if answer.upper() not in ["A", "B"]:
        return {"action": "send", "message": "请回复 A 或 B"}
    
    # 保存答案
    q = state.questions[state.current_index]
    state.answers.append((q["id"], answer.upper()))
    
    # 下一题
    state.current_index += 1
    
    # 每次都保存进度
    save_progress(chat_id, state)
    
    # 检查是否完成
    if state.current_index >= len(state.questions):
        type_code, scores = calculate_type(state.answers)
        clarity = {dim: calculate_clarity(score) for dim, score in scores.items()}
        
        # 保存结果到历史
        save_test_result(chat_id, type_code, scores, clarity, state.version, len(state.answers))
        
        # 清理
        if state.session_id:
            delete_session(state.session_id)
        clear_user_state(chat_id)
        
        return {
            "action": "complete",
            "message": get_completion_text(type_code, scores, clarity),
            "type_code": type_code,
            "scores": scores,
            "clarity": clarity
        }
    
    # 返回下一题
    question_text, q_num = get_question_text(state)
    
    return {
        "action": "send",
        "message": question_text,
        "q_num": q_num,
        "state": "testing"
    }

def process_resume(chat_id: int) -> Dict:
    """处理恢复"""
    incomplete = get_incomplete_session(chat_id)
    
    if not incomplete:
        return process_start(chat_id)
    
    state = get_user_state(chat_id)
    state.state = "testing"
    state.version = incomplete["version"]
    state.questions = sample_questions(incomplete["questions_total"])
    state.current_index = incomplete["current_index"]
    state.answers = incomplete["answers"]
    state.session_id = incomplete["session_id"]
    
    question_text, _ = get_question_text(state)
    
    return {
        "action": "send",
        "message": f"✅ 已恢复进度\n\n{question_text}",
        "state": "testing"
    }

def process_history(chat_id: int) -> Dict:
    """处理历史"""
    history = get_test_history(chat_id)
    return {
        "action": "send",
        "message": get_history_text(history)
    }

def process_status(chat_id: int) -> Dict:
    """处理状态"""
    state = get_user_state(chat_id)
    return {
        "action": "send",
        "message": get_status_text(chat_id, state)
    }

def process_cancel(chat_id: int) -> Dict:
    """处理取消"""
    state = get_user_state(chat_id)
    
    if state.session_id:
        delete_session(state.session_id)
    
    clear_user_state(chat_id)
    
    return {
        "action": "send",
        "message": "❌ 测试已取消\n\n发送 /start 开始新测试"
    }

# ==================== 统一入口 ====================
def process_message(chat_id: int, text: str) -> Dict:
    """
    统一消息处理入口
    适用于所有平台: Telegram, Discord, 飞书, 微信等
    """
    text = text.strip()
    
    # 命令处理
    if text == "/start":
        return process_start(chat_id)
    elif text == "/help":
        msg = "📖 **MBTI Guru 使用指南**\n\n"
        msg += "`/start` - 开始新测试\n"
        msg += "`/resume` - 继续未完成的测试\n"
        msg += "`/history` - 查看测试历史\n"
        msg += "`/status` - 查看当前状态\n"
        msg += "`/cancel` - 取消当前测试\n"
        msg += "\n直接回复 A 或 B 回答问题"
        return {"action": "send", "message": msg}
    elif text == "/resume":
        return process_resume(chat_id)
    elif text == "/history":
        return process_history(chat_id)
    elif text == "/status":
        return process_status(chat_id)
    elif text == "/cancel":
        return process_cancel(chat_id)
    
    # 版本选择
    if text in ["1", "2", "3", "4"]:
        return process_version_select(chat_id, text)
    
    # 答题
    state = get_user_state(chat_id)
    if state.state == "testing" and text.upper() in ["A", "B"]:
        return process_answer(chat_id, text.upper())
    
    # 如果收到A/B但状态不是testing，尝试恢复session
    if text.upper() in ["A", "B"]:
        incomplete = get_incomplete_session(chat_id)
        if incomplete:
            state.state = "testing"
            state.version = incomplete["version"]
            state.questions = sample_questions(incomplete["questions_total"])
            state.current_index = incomplete["current_index"]
            state.answers = incomplete["answers"]
            state.session_id = incomplete["session_id"]
            return process_answer(chat_id, text.upper())
    
    # 默认：显示版本选择
    if state.state in ["idle", "completed"]:
        return process_start(chat_id)
    
    return {"action": "send", "message": "请回复 A 或 B 回答问题"}


path: lib/mbti_types.py
content: #!/usr/bin/env python3
"""
MBTI Types Data - 16 Personality Types
"""
from typing import Dict

# 16 Personality Types / 16种人格类型
TYPES: Dict = {
    "INTJ": {
        "name_cn": "建筑师",
        "name_en": "The Architect",
        "summary": "An imaginative and strategic thinker with a plan for everything.",
        "summary_cn": "一个富有想象力且具有战略思维的思考者，对一切都制定计划。",
        "strengths": [
            "Strategic and analytical / 战略与分析",
            "Independent and determined / 独立与坚定",
            "Rational and logical / 理性与逻辑",
            "High standards / 高标准",
            "Decisive / 果断"
        ],
        "weaknesses": [
            "Arrogant and critical / 傲慢与批评",
            "Impatient with inefficiency / 对低效缺乏耐心",
            "Dismissive of emotions / 忽视情感",
            "Perfectionist / 完美主义者",
            "Socially awkward / 社交笨拙"
        ],
        "careers": [
            "Scientist / 科学家",
            "Engineer / 工程师",
            "Programmer / 程序员",
            "Financial Analyst / 金融分析师",
            "Strategist / 战略家"
        ],
        "relationships": {
            "best": ["INFJ", "ENFJ"],
            "challenging": ["ESFP", "ESTP"]
        }
    },
    "INTP": {
        "name_cn": "逻辑学家",
        "name_en": "The Logician",
        "summary": "An innovative inventor with an unquenchable thirst for knowledge.",
        "summary_cn": "一个具有永不满足求知欲的创新发明家。",
        "strengths": [
            "Analytical and objective / 分析与客观",
            "Intelligent and knowledgeable / 聪明与博学",
            "Open-minded / 开放心态",
            "Creative and innovative / 创意与创新",
            "Independent / 独立"
        ],
        "weaknesses": [
            "Detached and dismissive / 冷漠与轻视",
            "Critical of others / 批评他人",
            "Overthinker / 过度思考",
            "Struggles with practicality / 实践困难",
            "Emotionally guarded / 情感封闭"
        ],
        "careers": [
            "Research Scientist / 研究员",
            "Software Developer / 软件开发",
            "Professor / 教授",
            "Philosopher / 哲学家",
            "Data Scientist / 数据科学家"
        ],
        "relationships": {
            "best": ["ENTJ", "ENFJ"],
            "challenging": ["ESFJ", "ESTJ"]
        }
    },
    "ENTJ": {
        "name_cn": "指挥官",
        "name_en": "The Commander",
        "summary": "A bold, forthright leader with a drive for organization.",
        "summary_cn": "一个有组织驱动的大胆、直率的领导者。",
        "strengths": [
            "Bold and confident / 大胆与自信",
            "Natural leader / 天生领导者",
            "Efficient and organized / 高效与有条理",
            "Strategic thinker / 战略思考者",
            "Strong-willed / 意志坚定"
        ],
        "weaknesses": [
            "Intolerant and stubborn / 狭隘与固执",
            "Cold and ruthless / 冷漠与无情",
            "Dominating / 支配性",
            "Poor emotional expression / 情感表达差",
            "Impatient with others / 对他人不耐烦"
        ],
        "careers": [
            "CEO / 首席执行官",
            "Entrepreneur / 企业家",
            "Management Consultant / 管理顾问",
            "Lawyer / 律师",
            "Executive / 高管"
        ],
        "relationships": {
            "best": ["INTP", "INFJ"],
            "challenging": ["ISFP", "ISTP"]
        }
    },
    "ENTP": {
        "name_cn": "辩论家",
        "name_en": "The Debater",
        "summary": "A smart and curious thinker who can find any answer.",
        "summary_cn": "一个聪明好奇的思考者，能找到任何答案。",
        "strengths": [
            "Quick-witted and clever / 机智与聪明",
            "Creative and innovative / 创意与创新",
            "Enthusiastic and energetic / 热情与活力",
            "Good at debate / 善于辩论",
            "Adaptable / 适应性强"
        ],
        "weaknesses": [
            "Argumentative and critical / 争辩与批评",
            "Easily bored / 容易厌倦",
            "Insensitive to others / 对他人不敏感",
            "Struggles with follow-through / 难以坚持",
            "Disorganized / 无条理"
        ],
        "careers": [
            "Lawyer / 律师",
            "Marketing Manager / 市场经理",
            "Entrepreneur / 企业家",
            "Journalist / 记者",
            "Political Strategist / 政治战略家"
        ],
        "relationships": {
            "best": ["INFJ", "INTJ"],
            "challenging": ["ISFJ", "ISTJ"]
        }
    },
    "INFJ": {
        "name_cn": "提倡者",
        "name_en": "The Advocate",
        "summary": "A quiet protector who believes in the potential of people.",
        "summary_cn": "一个相信人类潜能的安静守护者。",
        "strengths": [
            "Principled and idealistic / 有原则与理想主义",
            "Creative and artistic / 创意与艺术",
            "Caring and compassionate / 关爱与同情",
            "Intuitive and insightful / 直觉与洞察",
            "Quiet but inspiring / 安静但鼓舞人心"
        ],
        "weaknesses": [
            "Perfectionist / 完美主义者",
            "Sensitive to criticism / 对批评敏感",
            "Reluctant to open up / 不愿敞开心扉",
            "Burnout prone / 容易倦怠",
            "Self-righteous / 自以为义"
        ],
        "careers": [
            "Counselor / 心理咨询师",
            "Writer / 作家",
            "Psychologist / 心理学家",
            "Teacher / 教师",
            "HR Specialist / 人力资源专员"
        ],
        "relationships": {
            "best": ["INFP", "ENFP"],
            "challenging": ["ESTP", "ESFP"]
        }
    },
    "INFP": {
        "name_cn": "调停者",
        "name_en": "The Mediator",
        "summary": "A people-pleaser who believes in the goodness of human nature.",
        "summary_cn": "一个相信人性善良的人。",
        "strengths": [
            "Idealistic and principled / 理想主义与原则",
            "Creative and expressive / 创意与表达",
            "Compassionate and caring / 同情与关爱",
            "Flexible and adaptable / 灵活与适应",
            "Loyal and devoted / 忠诚与投入"
        ],
        "weaknesses": [
            "Overly trusting / 过度信任",
            "Self-critical / 自我批评",
            "Disorganized and scattered / 无条理与分散",
            "Avoids conflict / 回避冲突",
            "Too idealistic / 过于理想化"
        ],
        "careers": [
            "Writer / 作家",
            "Counselor / 心理咨询师",
            "Social Worker / 社会工作者",
            "Graphic Designer / 平面设计师",
            "HR Specialist / 人力资源专员"
        ],
        "relationships": {
            "best": ["ENFP", "INFJ"],
            "challenging": ["ESTJ", "ENTJ"]
        }
    },
    "ENFJ": {
        "name_cn": "主人公",
        "name_en": "The Protagonist",
        "summary": "A charismatic leader who inspires others to be their best.",
        "summary_cn": "一个激励他人成为最好的魅力的领导者。",
        "strengths": [
            "Natural leader / 天生领导者",
            "Charismatic and inspiring / 魅力与鼓舞",
            "Caring and supportive / 关爱与支持",
            "Diplomatic and tactful / 外交与得体",
            "Enthusiastic and persuasive / 热情与说服"
        ],
        "weaknesses": [
            "Overly idealistic / 过于理想化",
            "Self-righteous / 自以为义",
            "People-pleaser / 讨好他人",
            "Inauthentic / 不真实",
            "Burnout prone / 容易倦怠"
        ],
        "careers": [
            "Teacher / 教师",
            "HR Manager / 人力资源经理",
            "Marketing Manager / 市场经理",
            "Sales Manager / 销售经理",
            "Coach / 教练"
        ],
        "relationships": {
            "best": ["INFP", "ISFP"],
            "challenging": ["ISTP", "ESTP"]
        }
    },
    "ENFP": {
        "name_cn": "竞选者",
        "name_en": "The Campaigner",
        "summary": "An enthusiastic creator with an infectious positive attitude.",
        "summary_cn": "一个具有感染性积极态度的热情创造者。",
        "strengths": [
            "Enthusiastic and energetic / 热情与活力",
            "Creative and artistic / 创意与艺术",
            "Social and outgoing / 社交与外向",
            "Optimistic and fun / 乐观与有趣",
            "Great communicator / 优秀沟通者"
        ],
        "weaknesses": [
            "Scattered and disorganized / 分散与无条理",
            "Overly emotional / 过于情绪化",
            "Needs approval / 需要认可",
            "Fear of rejection / 害怕被拒绝",
            "Difficulty with follow-through / 难以坚持"
        ],
        "careers": [
            "Writer / 作家",
            "Journalist / 记者",
            "Actor / 演员",
            "Marketing Manager / 市场经理",
            "Consultant / 顾问"
        ],
        "relationships": {
            "best": ["INFJ", "INTJ"],
            "challenging": ["ISTJ", "ESTJ"]
        }
    },
    "ISTJ": {
        "name_cn": "物流师",
        "name_en": "The Logistician",
        "summary": "A practical fact-based thinker who keeps promises.",
        "summary_cn": "一个保持承诺的实用、基于事实的思考者。",
        "strengths": [
            "Responsible and reliable / 负责与可靠",
            "Practical and traditional / 实用与传统",
            "Organized and efficient / 有条理与高效",
            "Honest and direct / 诚实与直接",
            "Patient and thorough / 耐心与细致"
        ],
        "weaknesses": [
            "Stubborn and rigid / 固执与刻板",
            "Judgmental of others / 评判他人",
            "Insensitive to emotions / 对情感不敏感",
            "Not easily adaptable / 不易适应",
            "Overly focused on rules / 过于关注规则"
        ],
        "careers": [
            "Accountant / 会计",
            "Auditor / 审计师",
            "Judge / 法官",
            "Financial Manager / 财务经理",
            "Admin Manager / 行政经理"
        ],
        "relationships": {
            "best": ["ESFJ", "ESTJ"],
            "challenging": ["ENFP", "ENTP"]
        }
    },
    "ISFJ": {
        "name_cn": "守卫者",
        "name_en": "The Defender",
        "summary": "A devoted protector who values tradition and loyalty.",
        "summary_cn": "一个重视传统和忠诚的忠诚守护者。",
        "strengths": [
            "Reliable and hard-working / 可靠与勤奋",
            "Patient and gentle / 耐心与温和",
            "Practical and helpful / 实用与助人",
            "Devoted and protective / 投入与保护",
            "Memory for details / 细节记忆"
        ],
        "weaknesses": [
            "Overly selfless / 过度忘我",
            "Neglects own needs / 忽视自己的需求",
            "Fear of change / 害怕变化",
            "Too critical of self / 过于自我批评",
            "Absorbs others' stress / 吸收他人压力"
        ],
        "careers": [
            "Nurse / 护士",
            "Social Worker / 社会工作者",
            "Accountant / 会计",
            "Teacher / 教师",
            "HR Specialist / 人力资源专员"
        ],
        "relationships": {
            "best": ["ESFP", "ESTP"],
            "challenging": ["ENTP", "ENFP"]
        }
    },
    "ESTJ": {
        "name_cn": "经理",
        "name_en": "The Executive",
        "summary": "An efficient organizer who gets things done.",
        "summary_cn": "一个高效完成事情的组织者。",
        "strengths": [
            "Practical and realistic / 实用与现实",
            "Natural leader / 天生领导者",
            "Organized and decisive / 有条理与果断",
            "Honest and direct / 诚实与直接",
            "Traditional and responsible / 传统与负责"
        ],
        "weaknesses": [
            "Inflexible and stubborn / 不灵活与固执",
            "Judgmental and critical / 评判与批评",
            "Domineering / 霸道",
            "Insensitive to emotions / 对情感不敏感",
            "Workaholic tendency / 工作狂倾向"
        ],
        "careers": [
            "Business Manager / 业务经理",
            "Police Officer / 警官",
            "Judge / 法官",
            "Financial Officer / 财务主管",
            "Teacher / 教师"
        ],
        "relationships": {
            "best": ["ISTP", "ISFP"],
            "challenging": ["INFP", "ENFP"]
        }
    },
    "ESFJ": {
        "name_cn": "执政官",
        "name_en": "The Consul",
        "summary": "A popular people-pleaser who values harmony.",
        "summary_cn": "一个重视和谐的受欢迎的讨好者。",
        "strengths": [
            "Caring and warm / 关爱与温暖",
            "Popular and outgoing / 受欢迎与外向",
            "Conscientious and organized / 尽责与有条理",
            "Practical and helpful / 实用与助人",
            "Social responsibility / 社会责任"
        ],
        "weaknesses": [
            "Needing approval / 需要认可",
            "Overly generous / 过于慷慨",
            "Insecure without harmony / 没有和谐就不安",
            "Judgmental of differences / 评判差异",
            "Self-sacrificing / 自我牺牲"
        ],
        "careers": [
            "Nurse / 护士",
            "HR Manager / 人力资源经理",
            "Sales Representative / 销售代表",
            "Teacher / 教师",
            "Receptionist / 前台接待"
        ],
        "relationships": {
            "best": ["ISFP", "ISTP"],
            "challenging": ["INTP", "INTJ"]
        }
    },
    "ISTP": {
        "name_cn": "鉴赏家",
        "name_en": "The Virtuoso",
        "summary": "A bold and practical explorer of mechanical things.",
        "summary_cn": "一个大胆且实用的机械事物探索者。",
        "strengths": [
            "Practical and realistic / 实用与现实",
            "Adventurous and spontaneous / 冒险与自发",
            "Natural mechanic / 天生机械师",
            "Logical and analytical / 逻辑与分析",
            "Calm under pressure / 压力下冷静"
        ],
        "weaknesses": [
            "Risky behavior / 冒险行为",
            "Insensitive to emotions / 对情感不敏感",
            "Bored by routine / 对例行公事感到无聊",
            "Private and reserved / 私人与内敛",
            "Avoids commitment / 回避承诺"
        ],
        "careers": [
            "Engineer / 工程师",
            "Mechanic / 机械师",
            "Pilot / 飞行员",
            "Athlete / 运动员",
            "Forensic Scientist / 法医"
        ],
        "relationships": {
            "best": ["ESTJ", "ESFJ"],
            "challenging": ["ENFJ", "INFJ"]
        }
    },
    "ISFP": {
        "name_cn": "探险家",
        "name_en": "The Adventurer",
        "summary": "A flexible artist who follows their heart.",
        "summary_cn": "一个跟随内心的灵活艺术家。",
        "strengths": [
            "Artistic and aesthetic / 艺术与美学",
            "Practical and realistic / 实用与现实",
            "Gentle and caring / 温和与关爱",
            "Flexible and adaptable / 灵活与适应",
            "Patient and present / 耐心与当下"
        ],
        "weaknesses": [
            "Overly private / 过于私密",
            "Fear of conflict / 害怕冲突",
            "Unpredictable / 不可预测",
            "Self-critical / 自我批评",
            "Disorganized / 无条理"
        ],
        "careers": [
            "Artist / 艺术家",
            "Designer / 设计师",
            "Photographer / 摄影师",
            "Musician / 音乐家",
            "Chef / 厨师"
        ],
        "relationships": {
            "best": ["ENFJ", "ESFJ"],
            "challenging": ["INTJ", "ENTJ"]
        }
    },
    "ESTP": {
        "name_cn": "企业家",
        "name_en": "The Entrepreneur",
        "summary": "A bold action-taker who lives in the moment.",
        "summary_cn": "一个活在当下的大胆行动者。",
        "strengths": [
            "Energetic and enthusiastic / 活力与热情",
            "Practical and realistic / 实用与现实",
            "Direct and honest / 直接与诚实",
            "Social and outgoing / 社交与外向",
            "Adaptable and flexible / 适应与灵活"
        ],
        "weaknesses": [
            "Impatient and reckless / 急躁与鲁莽",
            "Insensitive to others / 对他人不敏感",
            "Bored by routine / 对例行公事感到无聊",
            "Risk-taker / 冒险者",
            "Poor long-term planning / 长期规划差"
        ],
        "careers": [
            "Sales Representative / 销售代表",
            "Real Estate Agent / 房地产经纪人",
            "Entertainer / 艺人",
            "Athlete / 运动员",
            "Business Owner / 企业主"
        ],
        "relationships": {
            "best": ["ISFJ", "ISTJ"],
            "challenging": ["INFJ", "INFP"]
        }
    },
    "ESFP": {
        "name_cn": "表演者",
        "name_en": "The Entertainer",
        "summary": "A fun-loving entertainer who lives in the moment.",
        "summary_cn": "一个活在当下的热爱娱乐的人。",
        "strengths": [
            "Spontaneous and fun / 自发与有趣",
            "Social and outgoing / 社交与外向",
            "Artistic and creative / 艺术与创意",
            "Optimistic and enthusiastic / 乐观与热情",
            "Charismatic and charming / 魅力与迷人"
        ],
        "weaknesses": [
            "Easily bored / 容易厌倦",
            "Fear of criticism / 害怕批评",
            "Poor planning / 规划差",
            "Overly sensitive / 过于敏感",
            "Materialistic / 物质主义"
        ],
        "careers": [
            "Actor / 演员",
            "Singer / 歌手",
            "Event Planner / 活动策划",
            "Marketing Manager / 市场经理",
            "Sales Representative / 销售代表"
        ],
        "relationships": {
            "best": ["ISTJ", "ISFJ"],
            "challenging": ["INTJ", "INFJ"]
        }
    }
}

def get_type(type_code: str) -> Dict:
    """Get type data by code"""
    return TYPES.get(type_code.upper(), {})

def get_all_types() -> Dict:
    """Get all types"""
    return TYPES


path: lib/pdf_combined.py
content: #!/usr/bin/env python3
"""
MBTI PDF Report Generator - Combined (Page 1 + Page 2)
第一页保持原版，第二页新增深度分析
"""

import os
import sys

def create_pdf_report(type_code: str, scores: dict, clarity_levels: dict, output_path: str = "mbti_report.pdf"):
    """
    生成完整的MBTI报告（两页）
    - Page 1: 核心报告（原版v1.0.3布局）
    - Page 2: 深度分析（新增）
    """
    sys.path.insert(0, os.path.dirname(__file__))
    
    from lib.pdf_page1 import create_pdf_page1
    from lib.pdf_page2 import create_pdf_page2
    
    # 生成第一页
    page1_path = output_path.replace('.pdf', '_p1.pdf')
    create_pdf_page1(type_code, scores, clarity_levels, page1_path)
    
    # 生成第二页
    page2_path = output_path.replace('.pdf', '_p2.pdf')
    create_pdf_page2(type_code, page2_path)
    
    # 合并两页
    try:
        from PyPDF2 import PdfMerger
        merger = PdfMerger()
        merger.append(page1_path)
        merger.append(page2_path)
        merger.write(output_path)
        merger.close()
    except ImportError:
        # 如果没有PyPDF2，只保留第一页
        import shutil
        shutil.copy(page1_path, output_path)
    
    # 清理临时文件
    if os.path.exists(page1_path):
        os.remove(page1_path)
    if os.path.exists(page2_path):
        os.remove(page2_path)
    
    return output_path


if __name__ == "__main__":
    from lib.scorer import calculate_type, calculate_clarity
    from lib.questions import get_questions
    from datetime import datetime
    
    questions = get_questions(70)
    answers = []
    for q in questions:
        if q["dimension"] == "EI":
            choice = "B" if q["id"] <= 14 else "A"
        elif q["dimension"] == "SN":
            choice = "B" if q["id"] <= 30 else "A"
        elif q["dimension"] == "TF":
            choice = "B" if q["id"] <= 42 else "A"
        else:
            choice = "B" if q["id"] <= 62 else "A"
        answers.append((q["id"], choice))
    
    type_code, scores = calculate_type(answers)
    clarity_levels = {dim: calculate_clarity(score) for dim, score in scores.items()}
    
    today = datetime.now().strftime("%Y-%m-%d")
    output_filename = f"MBTI-{type_code}-{today}.pdf"
    
    output = create_pdf_report(type_code, scores, clarity_levels, output_filename)
    print(f"PDF created: {output}")


path: lib/pdf_generator.py
content: #!/usr/bin/env python3
"""
MBTI PDF Report Generator - Premium layout
"""

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch, Rectangle
import os

plt.rcParams['font.family'] = 'Noto Sans CJK JP'
plt.rcParams['axes.unicode_minus'] = False

def create_pdf_report(type_code: str, scores: dict, clarity_levels: dict, output_path: str = "mbti_report.pdf"):
    from lib.mbti_types import get_type
    
    type_data = get_type(type_code)
    name_en = type_data.get("name_en", "")
    name_cn = type_data.get("name_cn", "")
    summary = type_data.get("summary", "")
    summary_cn = type_data.get("summary_cn", "")
    strengths = type_data.get("strengths", [])
    weaknesses = type_data.get("weaknesses", [])
    careers = type_data.get("careers", [])
    relationships = type_data.get("relationships", {})
    
    total_clarity = sum(c[0] for c in clarity_levels.values()) / len(clarity_levels)
    
    PRIMARY = '#4A90D9'
    SECONDARY = '#2C3E50'
    ACCENT = '#E74C3C'
    LIGHT_BG = '#F8F9FA'
    GREEN = '#27AE60'
    PURPLE = '#8E44AD'
    
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 11.69))
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis('off')
    ax.set_facecolor('white')
    fig.patch.set_facecolor('white')
    
    y = 0.98
    left = 0.08
    right = 0.92
    content_width = right - left
    
    # Header
    ax.text(0.5, y, 'MBTI PERSONALITY REPORT', fontsize=18, fontweight='bold', color=PRIMARY, ha='center', va='top')
    y -= 0.026
    
    ax.plot([left, right], [y, y], color=PRIMARY, linewidth=2)
    y -= 0.022
    
    # Type Badge - LARGER
    badge_w = 0.26
    badge_h = 0.08
    ax.add_patch(FancyBboxPatch(((1-badge_w)/2, y-badge_h), badge_w, badge_h,
                                  boxstyle="round,pad=0.012,rounding_size=0.02", 
                                  facecolor=PRIMARY, edgecolor='none'))
    ax.text(0.5, y-badge_h/2, f'{type_code}', fontsize=46, fontweight='bold', color='white', ha='center', va='center')
    y -= (badge_h + 0.02)
    
    # Type names
    ax.text(0.5, y, f'{name_en}  |  {name_cn}', fontsize=18, fontweight='bold', color=SECONDARY, ha='center', va='top')
    y -= 0.038
    
    # Clarity box - LARGER
    ax.add_patch(FancyBboxPatch((0.28, y-0.028), 0.44, 0.028,
                                  boxstyle="round,pad=0.005,rounding_size=0.006", 
                                  facecolor=LIGHT_BG, edgecolor=PRIMARY, linewidth=2))
    ax.text(0.5, y-0.014, f'综合清晰度 / Overall Clarity: {total_clarity:.0f}%',
            fontsize=13, fontweight='bold', color=PRIMARY, ha='center', va='center')
    y -= 0.05
    
    # Dimension Scores
    ax.text(left, y, '维度得分 / Dimension Scores', fontsize=12, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.024
    
    dim_cn_labels = {"EI": "能量 Energy", "SN": "信息 Info", "TF": "决策 Decisions", "JP": "结构 Structure"}
    
    bar_height = 0.024
    bar_x = left + 0.08
    bar_width = 0.48
    right_label_x = bar_x + bar_width + 0.015
    
    for dim, score in scores.items():
        pref = dim[0] if score > 50 else dim[1]
        
        ax.add_patch(FancyBboxPatch((bar_x, y-bar_height), bar_width, bar_height,
                                    boxstyle="round,pad=0.002,rounding_size=0.005",
                                    facecolor='#E8E8E8', edgecolor='none'))
        
        fill_width = bar_width * score / 100
        if fill_width > 0.005:
            ax.add_patch(FancyBboxPatch((bar_x, y-bar_height), fill_width, bar_height,
                                        boxstyle="round,pad=0.002,rounding_size=0.005",
                                        facecolor=PRIMARY, edgecolor='none', alpha=0.85))
        
        ax.text(bar_x + 0.012, y-bar_height/2, f'{dim}',
                fontsize=13, fontweight='bold', color='black', va='center')
        
        ax.text(bar_x + bar_width - 0.012, y-bar_height/2, f'{score:.0f}% {pref}',
                fontsize=13, fontweight='bold', color=PRIMARY, va='center', ha='right')
        
        ax.text(right_label_x, y-bar_height/2, dim_cn_labels.get(dim, ""),
                fontsize=10, fontweight='bold', color='#555', va='center', ha='left')
        
        y -= (bar_height + 0.018)
    
    y -= 0.004
    
    # Summary
    ax.text(left, y, '简介 / Summary', fontsize=12, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.024
    ax.text(left+0.01, y, summary[:65], fontsize=12.5, color=SECONDARY, va='top', style='italic')
    y -= 0.02
    ax.text(left+0.01, y, summary_cn, fontsize=12.5, color=SECONDARY, va='top')
    y -= 0.04
    
    # Two columns
    col_width = (content_width - 0.06) / 2
    
    ax.text(left, y, '优势 Strengths', fontsize=12, fontweight='bold', color=GREEN, va='top')
    temp_y = y - 0.024
    for i, s in enumerate(strengths[:4], 1):
        en = s.split("/")[0].strip()
        cn = s.split("/")[1].strip() if "/" in s else ""
        ax.text(left+0.01, temp_y, f'{i}. {en}', fontsize=12, fontweight='bold', color=SECONDARY, va='top')
        temp_y -= 0.02
        ax.text(left+0.04, temp_y, cn, fontsize=11, color='#555', va='top')
        temp_y -= 0.024
    
    ax.text(left+col_width+0.02, y, '劣势 Weaknesses', fontsize=12, fontweight='bold', color=ACCENT, va='top')
    temp_y2 = y - 0.024
    for i, w in enumerate(weaknesses[:4], 1):
        en = w.split("/")[0].strip()
        cn = w.split("/")[1].strip() if "/" in w else ""
        ax.text(left+col_width+0.03, temp_y2, f'{i}. {en}', fontsize=12, fontweight='bold', color=SECONDARY, va='top')
        temp_y2 -= 0.02
        ax.text(left+col_width+0.06, temp_y2, cn, fontsize=11, color='#555', va='top')
        temp_y2 -= 0.024
    
    y = min(temp_y, temp_y2) - 0.018
    
    ax.text(left, y, '适合职业 / Careers', fontsize=12, fontweight='bold', color=PURPLE, va='top')
    y -= 0.018
    
    careers_en = [c.split("/")[0].strip() for c in careers[:4]]
    careers_cn = [c.split("/")[1].strip() if "/" in c else "" for c in careers[:4]]
    
    for i in range(4):
        ax.text(left+0.01, y, f'{i+1}. {careers_en[i]}  {careers_cn[i]}',
                fontsize=12, fontweight='bold', color=SECONDARY, va='top')
        y -= 0.024
    
    y -= 0.008
    
    ax.text(left, y, '人际关系 / Relationships', fontsize=12, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.024
    
    best = relationships.get("best", [])
    challenging = relationships.get("challenging", [])
    
    ax.text(left+0.01, y, f'最佳匹配 Best Match:  {", ".join(best)}',
            fontsize=12, fontweight='bold', color=SECONDARY, va='top')
    y -= 0.022
    
    ax.text(left+0.01, y, f'挑战类型 Challenging:  {", ".join(challenging)}',
            fontsize=12, fontweight='bold', color=SECONDARY, va='top')
    
    ax.plot([left, right], [0.045, 0.045], color='#CCC', linewidth=0.8)
    ax.text(0.5, 0.03, 'Generated by MBTI Guru', fontsize=8, color='#AAA', ha='center', va='top')
    
    plt.tight_layout()
    fig.savefig(output_path, format='pdf', dpi=150, facecolor='white')
    plt.close()
    
    return output_path


if __name__ == "__main__":
    import sys
    sys.path.insert(0, os.path.dirname(__file__))
    
    from lib.scorer import calculate_type, calculate_clarity
    from lib.questions import get_questions
    from datetime import datetime
    
    questions = get_questions(70)
    answers = []
    for q in questions:
        if q["dimension"] == "EI":
            choice = "B" if q["id"] <= 14 else "A"
        elif q["dimension"] == "SN":
            choice = "B" if q["id"] <= 30 else "A"
        elif q["dimension"] == "TF":
            choice = "B" if q["id"] <= 42 else "A"
        else:
            choice = "B" if q["id"] <= 62 else "A"
        answers.append((q["id"], choice))
    
    type_code, scores = calculate_type(answers)
    clarity_levels = {dim: calculate_clarity(score) for dim, score in scores.items()}
    
    today = datetime.now().strftime("%Y-%m-%d")
    output_filename = f"MBTI-{type_code}-{today}.pdf"
    
    output = create_pdf_report(type_code, scores, clarity_levels, output_filename)
    print(f"PDF created: {output}")


path: lib/pdf_page1.py
content: #!/usr/bin/env python3
"""
MBTI PDF Report Generator - Page 1 (Original v1.0.3 Layout)
保持原有版式不变
"""

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch, Rectangle
import os

plt.rcParams['font.family'] = 'Noto Sans CJK JP'
plt.rcParams['axes.unicode_minus'] = False

def create_pdf_page1(type_code: str, scores: dict, clarity_levels: dict, output_path: str = "mbti_page1.pdf"):
    from lib.mbti_types import get_type
    
    type_data = get_type(type_code)
    name_en = type_data.get("name_en", "")
    name_cn = type_data.get("name_cn", "")
    summary = type_data.get("summary", "")
    summary_cn = type_data.get("summary_cn", "")
    strengths = type_data.get("strengths", [])
    weaknesses = type_data.get("weaknesses", [])
    careers = type_data.get("careers", [])
    relationships = type_data.get("relationships", {})
    
    total_clarity = sum(c[0] for c in clarity_levels.values()) / len(clarity_levels)
    
    PRIMARY = '#4A90D9'
    SECONDARY = '#2C3E50'
    ACCENT = '#E74C3C'
    LIGHT_BG = '#F8F9FA'
    GREEN = '#27AE60'
    PURPLE = '#8E44AD'
    
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 11.69))
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis('off')
    ax.set_facecolor('white')
    fig.patch.set_facecolor('white')
    
    y = 0.98
    left = 0.08
    right = 0.92
    content_width = right - left
    
    # Header
    ax.text(0.5, y, 'MBTI PERSONALITY REPORT', fontsize=18, fontweight='bold', color=PRIMARY, ha='center', va='top')
    y -= 0.026
    
    ax.plot([left, right], [y, y], color=PRIMARY, linewidth=2)
    y -= 0.022
    
    # Type Badge - LARGER
    badge_w = 0.26
    badge_h = 0.08
    ax.add_patch(FancyBboxPatch(((1-badge_w)/2, y-badge_h), badge_w, badge_h,
                                  boxstyle="round,pad=0.012,rounding_size=0.02", 
                                  facecolor=PRIMARY, edgecolor='none'))
    ax.text(0.5, y-badge_h/2, f'{type_code}', fontsize=46, fontweight='bold', color='white', ha='center', va='center')
    y -= (badge_h + 0.02)
    
    # Type names
    ax.text(0.5, y, f'{name_en}  |  {name_cn}', fontsize=18, fontweight='bold', color=SECONDARY, ha='center', va='top')
    y -= 0.038
    
    # Clarity box - LARGER
    ax.add_patch(FancyBboxPatch((0.28, y-0.028), 0.44, 0.028,
                                  boxstyle="round,pad=0.005,rounding_size=0.006", 
                                  facecolor=LIGHT_BG, edgecolor=PRIMARY, linewidth=2))
    ax.text(0.5, y-0.014, f'综合清晰度 / Overall Clarity: {total_clarity:.0f}%',
            fontsize=13, fontweight='bold', color=PRIMARY, ha='center', va='center')
    y -= 0.05
    
    # Dimension Scores
    ax.text(left, y, '维度得分 / Dimension Scores', fontsize=12, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.024
    
    dim_cn_labels = {"EI": "能量 Energy", "SN": "信息 Info", "TF": "决策 Decisions", "JP": "结构 Structure"}
    
    bar_height = 0.024
    bar_x = left + 0.08
    bar_width = 0.48
    right_label_x = bar_x + bar_width + 0.015
    
    for dim, score in scores.items():
        pref = dim[0] if score > 50 else dim[1]
        
        ax.add_patch(FancyBboxPatch((bar_x, y-bar_height), bar_width, bar_height,
                                    boxstyle="round,pad=0.002,rounding_size=0.005",
                                    facecolor='#E8E8E8', edgecolor='none'))
        
        fill_width = bar_width * score / 100
        if fill_width > 0.005:
            ax.add_patch(FancyBboxPatch((bar_x, y-bar_height), fill_width, bar_height,
                                        boxstyle="round,pad=0.002,rounding_size=0.005",
                                        facecolor=PRIMARY, edgecolor='none', alpha=0.85))
        
        ax.text(bar_x + 0.012, y-bar_height/2, f'{dim}',
                fontsize=13, fontweight='bold', color='black', va='center')
        
        ax.text(bar_x + bar_width - 0.012, y-bar_height/2, f'{score:.0f}% {pref}',
                fontsize=13, fontweight='bold', color=PRIMARY, va='center', ha='right')
        
        ax.text(right_label_x, y-bar_height/2, dim_cn_labels.get(dim, ""),
                fontsize=10, fontweight='bold', color='#555', va='center', ha='left')
        
        y -= (bar_height + 0.018)
    
    y -= 0.004
    
    # Summary
    ax.text(left, y, '简介 / Summary', fontsize=12, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.024
    ax.text(left+0.01, y, summary[:65], fontsize=12, color=SECONDARY, va='top', style='italic')
    y -= 0.02
    ax.text(left+0.01, y, summary_cn, fontsize=12, color=SECONDARY, va='top')
    y -= 0.04
    
    # Two columns - Strengths & Weaknesses
    col_width = (content_width - 0.06) / 2
    
    ax.text(left, y, '优势 Strengths', fontsize=12, fontweight='bold', color=GREEN, va='top')
    temp_y = y - 0.024
    for i, s in enumerate(strengths[:4], 1):
        en = s.split("/")[0].strip()
        cn = s.split("/")[1].strip() if "/" in s else ""
        ax.text(left+0.01, temp_y, f'{i}. {en}', fontsize=12, fontweight='bold', color=SECONDARY, va='top')
        temp_y -= 0.02
        ax.text(left+0.04, temp_y, cn, fontsize=11, color='#555', va='top')
        temp_y -= 0.024
    
    ax.text(left+col_width+0.02, y, '劣势 Weaknesses', fontsize=12, fontweight='bold', color=ACCENT, va='top')
    temp_y2 = y - 0.024
    for i, w in enumerate(weaknesses[:4], 1):
        en = w.split("/")[0].strip()
        cn = w.split("/")[1].strip() if "/" in w else ""
        ax.text(left+col_width+0.03, temp_y2, f'{i}. {en}', fontsize=12, fontweight='bold', color=SECONDARY, va='top')
        temp_y2 -= 0.02
        ax.text(left+col_width+0.06, temp_y2, cn, fontsize=11, color='#555', va='top')
        temp_y2 -= 0.024
    
    y = min(temp_y, temp_y2) - 0.028
    
    ax.text(left, y, '适合职业 / Careers', fontsize=12, fontweight='bold', color=PURPLE, va='top')
    y -= 0.024
    
    careers_en = [c.split("/")[0].strip() for c in careers[:4]]
    careers_cn = [c.split("/")[1].strip() if "/" in c else "" for c in careers[:4]]
    
    for i in range(4):
        ax.text(left+0.01, y, f'{i+1}. {careers_en[i]}  {careers_cn[i]}',
                fontsize=12, fontweight='bold', color=SECONDARY, va='top')
        y -= 0.024
    
    y -= 0.008
    
    ax.text(left, y, '人际关系 / Relationships', fontsize=12, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.024
    
    best = relationships.get("best", [])
    challenging = relationships.get("challenging", [])
    
    ax.text(left+0.01, y, f'最佳匹配 Best Match:  {", ".join(best)}',
            fontsize=12, fontweight='bold', color=SECONDARY, va='top')
    y -= 0.022
    
    ax.text(left+0.01, y, f'挑战类型 Challenging:  {", ".join(challenging)}',
            fontsize=12, fontweight='bold', color=SECONDARY, va='top')
    
    # Footer
    ax.plot([left, right], [0.045, 0.045], color='#CCC', linewidth=0.8)
    ax.text(0.5, 0.03, 'Generated by MBTI Guru', fontsize=8, color='#AAA', ha='center', va='top')
    
    plt.tight_layout()
    fig.savefig(output_path, format='pdf', dpi=150, facecolor='white')
    plt.close()
    
    return output_path


path: lib/pdf_page2.py
content: #!/usr/bin/env python3
"""
MBTI PDF Report Generator - Page 2 (Deep Analysis)
"""

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch
import os

plt.rcParams['font.family'] = 'Noto Sans CJK JP'
plt.rcParams['axes.unicode_minus'] = False

def create_pdf_page2(type_code: str, output_path: str = "mbti_page2.pdf"):
    from lib.mbti_types import get_type
    from lib.cognitive import get_cognitive_functions
    from lib.relationships import get_relationship_data
    
    type_data = get_type(type_code)
    name_en = type_data.get("name_en", "")
    name_cn = type_data.get("name_cn", "")
    
    cognitive = get_cognitive_functions(type_code)
    rel_data = get_relationship_data(type_code)
    
    PRIMARY = '#4A90D9'
    SECONDARY = '#2C3E50'
    ACCENT = '#E74C3C'
    LIGHT_BG = '#F8F9FA'
    GREEN = '#27AE60'
    PURPLE = '#8E44AD'
    ORANGE = '#E67E22'
    
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 11.69))
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis('off')
    ax.set_facecolor('white')
    fig.patch.set_facecolor('white')
    
    y = 0.97
    left = 0.08
    right = 0.92
    content_width = right - left
    footer_y = 0.045
    
    # Header
    ax.text(0.5, y, f'{name_en}  |  {name_cn}', fontsize=22, fontweight='bold', color=PRIMARY, ha='center', va='top')
    y -= 0.034
    
    ax.text(0.5, y, 'Deep Analysis / 深度分析报告', fontsize=17, fontweight='bold', color=SECONDARY, ha='center', va='top')
    y -= 0.032
    
    ax.plot([left, right], [y, y], color=PRIMARY, linewidth=2)
    y -= 0.018
    
    # 认知功能
    ax.text(left, y, 'Cognitive Functions / 认知功能', fontsize=16, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.036
    
    if cognitive:
        funcs = cognitive.get("functions", {})
        
        func_data = [
            ("dominant", "Dominant / 主功能", GREEN),
            ("auxiliary", "Auxiliary / 辅助功能", PRIMARY),
            ("tertiary", "Tertiary / 第三功能", ORANGE),
            ("inferior", "Inferior / 劣势功能", ACCENT),
        ]
        
        box_width = (content_width - 0.04) / 2
        box_height = 0.085
        
        # Row 1
        row1_y = y
        for i, (key, label, color) in enumerate(func_data[:2]):
            func = funcs.get(key, {})
            name = func.get("name", "")
            desc = func.get("description", "")
            
            box_x = left + i * (box_width + 0.04)
            box_y = row1_y - box_height
            
            ax.add_patch(FancyBboxPatch((box_x, box_y), box_width, box_height,
                                          boxstyle="round,pad=0.005,rounding_size=0.01",
                                          facecolor=LIGHT_BG, edgecolor=color, linewidth=2))
            
            ax.text(box_x + 0.015, box_y + box_height - 0.015, f'{label}', 
                    fontsize=16, fontweight='bold', color=color, va='top')
            ax.text(box_x + 0.015, box_y + box_height - 0.04, f'{name}', 
                    fontsize=15, fontweight='bold', color=SECONDARY, va='top')
            ax.text(box_x + 0.015, box_y + box_height - 0.062, desc, 
                    fontsize=13, color='#555', va='top')
        
        # Row 2
        row2_y = row1_y - box_height - 0.018
        for i, (key, label, color) in enumerate(func_data[2:]):
            func = funcs.get(key, {})
            name = func.get("name", "")
            desc = func.get("description", "")
            
            box_x = left + i * (box_width + 0.04)
            box_y = row2_y - box_height
            
            ax.add_patch(FancyBboxPatch((box_x, box_y), box_width, box_height,
                                          boxstyle="round,pad=0.005,rounding_size=0.01",
                                          facecolor=LIGHT_BG, edgecolor=color, linewidth=2))
            
            ax.text(box_x + 0.015, box_y + box_height - 0.015, f'{label}', 
                    fontsize=16, fontweight='bold', color=color, va='top')
            ax.text(box_x + 0.015, box_y + box_height - 0.04, f'{name}', 
                    fontsize=15, fontweight='bold', color=SECONDARY, va='top')
            ax.text(box_x + 0.015, box_y + box_height - 0.062, desc, 
                    fontsize=13, color='#555', va='top')
        
        y = row2_y - box_height - 0.028
    
    # 成长建议 - 内容13pt
    ax.text(left, y, 'Growth Suggestions / 成长建议', fontsize=16, fontweight='bold', color=PRIMARY, va='top')
    y -= 0.032
    
    if cognitive and cognitive.get("growth"):
        for i, tip in enumerate(cognitive["growth"], 1):
            ax.text(left + 0.015, y, f'{i}. {tip}', fontsize=13, color=SECONDARY, va='top')
            y -= 0.026
        y -= 0.015
    
    # 职场 - 内容13pt（与成长建议一致）
    ax.text(left, y, 'Workplace / 职场', fontsize=16, fontweight='bold', color=GREEN, va='top')
    y -= 0.032
    
    workplace = rel_data.get("workplace", {})
    if workplace:
        best_env = workplace.get("best_environment", "")
        team_role = workplace.get("team_role", "")
        tips = workplace.get("collaboration_tips", [])
        
        ax.text(left + 0.015, y, f'Best Environment / 最佳环境:', fontsize=13, fontweight='bold', color=SECONDARY, va='top')
        y -= 0.022
        ax.text(left + 0.015, y, best_env, fontsize=13, color='#555', va='top')
        y -= 0.024
        
        ax.text(left + 0.015, y, f'Team Role / 团队角色:', fontsize=13, fontweight='bold', color=SECONDARY, va='top')
        y -= 0.022
        ax.text(left + 0.015, y, team_role, fontsize=13, color='#555', va='top')
        y -= 0.026
        
        if tips:
            ax.text(left + 0.015, y, f'Collaboration / 协作建议:', fontsize=13, fontweight='bold', color=SECONDARY, va='top')
            y -= 0.022
            for tip in tips[:2]:
                ax.text(left + 0.03, y, f'- {tip}', fontsize=13, color='#666', va='top')
                y -= 0.02
        y -= 0.015
    
    # 情感 - 内容13pt（与成长建议一致）
    ax.text(left, y, 'Romantic / 情感', fontsize=16, fontweight='bold', color=PURPLE, va='top')
    y -= 0.032
    
    romantic = rel_data.get("romantic", {})
    if romantic:
        best_matches = romantic.get("best_match", [])
        challenging = romantic.get("challenging", [])
        rel_tips = romantic.get("relationship_tips", [])
        
        ax.text(left + 0.015, y, f'Best Match / 最佳匹配:', fontsize=13, fontweight='bold', color=SECONDARY, va='top')
        y -= 0.022
        ax.text(left + 0.015, y, ', '.join(best_matches), fontsize=13, color='#555', va='top')
        y -= 0.026
        
        ax.text(left + 0.015, y, f'Challenging / 挑战类型:', fontsize=13, fontweight='bold', color=SECONDARY, va='top')
        y -= 0.022
        ax.text(left + 0.015, y, ', '.join(challenging), fontsize=13, color='#555', va='top')
        y -= 0.026
        
        if rel_tips:
            ax.text(left + 0.015, y, f'Reationship Tips / 关系建议:', fontsize=13, fontweight='bold', color=SECONDARY, va='top')
            y -= 0.022
            for tip in rel_tips[:2]:
                ax.text(left + 0.03, y, f'- {tip}', fontsize=13, color='#666', va='top')
                y -= 0.02
    
    # Footer
    ax.plot([left, right], [footer_y, footer_y], color='#CCC', linewidth=0.8)
    ax.text(0.5, footer_y - 0.015, 'Generated by MBTI Guru', fontsize=8, color='#AAA', ha='center', va='top')
    
    plt.tight_layout()
    fig.savefig(output_path, format='pdf', dpi=150, facecolor='white')
    plt.close()
    
    return output_path


path: lib/question_pool.py
content: #!/usr/bin/env python3
"""
MBTI Guru - 扩展题库系统 v2
支持100+题/维度，支持随机抽取确保多样性
"""

import random
from typing import List, Dict

# ============================================
# EI 维度题库 (Extraversion/Introversion)
# ============================================

EI_BASE = [
    # E 偏向 (50题)
    ("E", "在社交聚会中感到精力充沛", "在独处时感到精力充沛"),
    ("E", "喜欢和很多人交流", "喜欢和少数人深入交流"),
    ("E", "先行动后思考", "先思考后行动"),
    ("E", "喜欢同时处理多个任务", "喜欢专注于一个任务"),
    ("E", "通过交谈来理清思路", "通过思考来理清思路"),
    ("E", "容易在人群中表达意见", "倾向于在纸上表达意见"),
    ("E", "从外部世界获得能量", "从内心世界获得能量"),
    ("E", "喜欢被人包围的感觉", "需要自己的空间"),
    ("E", "喜欢打电话而不是发信息", "喜欢发信息而不是打电话"),
    ("E", "在会议上积极发言", "在会议上倾向于倾听"),
    ("E", "容易结识新朋友", "与熟人会面更自在"),
    ("E", "喜欢尝试新事物", "喜欢熟悉的活动"),
    ("E", "喜欢团队合作", "喜欢独立工作"),
    ("E", "口头表达比书面表达更流畅", "书面表达比口头表达更流畅"),
    ("E", "喜欢被关注的感觉", "不喜欢成为焦点"),
    ("E", "倾向于边做边想", "倾向于想清楚再做"),
    ("E", "在活跃的环境中更有效率", "在安静的环境中更有效率"),
    ("E", "喜欢即兴发挥", "喜欢提前计划"),
    ("E", "闲聊是社交的一部分", "闲聊是浪费时间"),
    ("E", "喜欢多变的日程安排", "喜欢固定的日程安排"),
    ("E", "喜欢参加各种社交活动", "更愿意在家安静地休息"),
    ("E", "陌生人也能轻松交谈", "与陌生人交谈感到不自在"),
    ("E", "乐意分享自己的想法", "倾向于保留自己的想法"),
    ("E", "从对话中获得灵感", "从独处中获得灵感"),
    ("E", "喜欢在人群中工作", "喜欢独自安静地工作"),
    ("E", "新朋友让我兴奋", "新朋友让我有压力"),
    ("E", "喜欢在人群中做决定", "喜欢独自做决定"),
    ("E", "表达情感对我来说很容易", "表达情感对我来说很难"),
    ("E", "喜欢快速回复消息", "需要时间思考后再回复"),
    ("E", "在社交场合感到自在", "在社交场合感到紧张"),
    ("E", "喜欢成为关注的中心", "不喜欢成为关注的中心"),
    ("E", "能轻松地开始对话", "等待别人来开启对话"),
    ("E", "喜欢协作解决问题", "喜欢独立解决问题"),
    ("E", "从他人那里获得能量", "从独处中获得能量"),
    ("E", "喜欢广泛讨论各种话题", "喜欢深入探讨单一话题"),
    ("E", "在人群中更加精力充沛", "独处时更加精力充沛"),
    ("E", "容易在对话中即兴发言", "需要时间整理想法再发言"),
    ("E", "喜欢公开发言", "觉得公开发言有压力"),
    ("E", "愿意主动联系朋友", "等朋友主动联系我"),
    ("E", "在热闹的环境中更开心", "在安静的环境中更开心"),
    ("E", "喜欢与不同类型的人交流", "更喜欢与相似的人交流"),
    ("E", "喜欢组织社交聚会", "更喜欢独自度过时间"),
    ("E", "在对话中思维敏捷", "在对话中需要时间思考"),
    ("E", "喜欢讨论未来的计划", "喜欢思考而非讨论"),
    ("E", "外向让我感到自信", "内向让我感到舒适"),
    ("E", "社交充电让我更有效率", "独处充电让我更有效率"),
    ("E", "喜欢在人群中做演示", "更喜欢书面沟通"),
    # I 偏向 (50题)
    ("I", "独处时思考更清晰", "与人交谈时思考更清晰"),
    ("I", "小群体深度交流更舒适", "大群体社交更愉快"),
    ("I", "需要时间独处来恢复精力", "需要与人互动来恢复精力"),
    ("I", "在安静的环境中更专注", "在热闹的环境中更兴奋"),
    ("I", "善于倾听", "善于表达"),
    ("I", "思考是必要的准备", "行动是更好的准备"),
    ("I", "通常先观察再参与", "通常直接参与"),
    ("I", "喜欢有深度的对话", "喜欢轻松的对话"),
    ("I", "避免成为注意力的中心", "不介意成为注意力的中心"),
    ("I", "私密的事情只想自己知道", "愿意与他人分享经历"),
    ("I", "写下来比说出来更容易", "说出来比写下来更容易"),
    ("I", "一个人工作更有效率", "团队工作更有效率"),
    ("I", "需要时间思考和整理", "需要快速回应和反应"),
    ("I", "有少数几个知心朋友就够了", "有很多朋友更好"),
    ("I", "倾向于内心世界", "倾向于外部世界"),
    ("I", "独处是充电的方式", "社交是充电的方式"),
    ("I", "善于独立完成任务", "善于协调团队合作"),
    ("I", "喜欢深入研究少数话题", "喜欢广泛了解各种话题"),
    ("I", "在压力下倾向于沉默", "在压力下倾向于说话"),
    ("I", "倾向于内心反省", "倾向于外在行动"),
    ("I", "独处时更愿意反思", "社交时更愿意反思"),
    ("I", "深度交流比广泛社交更重要", "广泛社交比深度交流更重要"),
    ("I", "需要时间消化社交经历", "社交后立即恢复精力"),
    ("I", "小圈子让我更自在", "大群体让我更自在"),
    ("I", "更喜欢书面沟通", "更喜欢口头沟通"),
    ("I", "在社交后需要独处时间", "在社交后不需要特别恢复"),
    ("I", "深度友谊比数量重要", "有很多朋友很重要"),
    ("I", "喜欢独自阅读", "喜欢与他人一起学习"),
    ("I", "能长时间独处不感到无聊", "长时间独处让我感到无聊"),
    ("I", "在安静中思考更深入", "在讨论中思考更深入"),
    ("I", "不需要频繁社交", "需要频繁社交"),
    ("I", "善于自我反思", "善于与他人分享"),
    ("I", "内向是力量而非弱点", "外向更有优势"),
    ("I", "独处让我更了解自己", "社交让我更了解自己"),
    ("I", "在社交场合更像观察者", "在社交场合更像个参与者"),
    ("I", "喜欢安静的工作环境", "能接受有点嘈杂的环境"),
    ("I", "小群体让我感到安全", "大群体让我感到安全"),
    ("I", "少数人的深入交流更令我满足", "多数人的轻松交流更令我满足"),
    ("I", "安静的环境让我更有效率", "有点声音让我更有效率"),
    ("I", "需要时间做准备才能发言", "能即兴发言"),
    ("I", "在陌生场合感到不自在", "在陌生场合也能自如"),
    ("I", "独处时思维更清晰", "与人讨论时思维更清晰"),
    ("I", "思考后再说更自在", "边说边想更自在"),
    ("I", "需要时间敞开心扉", "能快速与他人建立信任"),
    ("I", "需要时间消化情绪", "需要倾诉来消化情绪"),
    ("I", "一对一交流更有效率", "小组讨论更有效率"),
    ("I", "喜欢独自享受美食", "喜欢与他人一起用餐"),
]

# ============================================
# SN 维度题库 (Sensing/Intuition)
# ============================================

SN_BASE = [
    # S 偏向 (50题)
    ("S", "注重实际可行的方案", "注重未来的可能性"),
    ("S", "关注具体的事实和细节", "关注整体的概念和意义"),
    ("S", "相信经验和已证实的", "相信直觉和创新的"),
    ("S", "一步一步解决问题", "凭直觉跳跃式解决"),
    ("S", "喜欢具体明确的信息", "喜欢抽象概括的信息"),
    ("S", "脚踏实地更重要", "想象力更重要"),
    ("S", "关注眼前的具体事物", "关注长远的目标"),
    ("S", "喜欢按照指示操作", "喜欢自己摸索方法"),
    ("S", "习惯于使用已有技能", "喜欢学习新技能"),
    ("S", "重视当下的体验", "重视未来的愿景"),
    ("S", "喜欢可预测的环境", "喜欢变化多样的环境"),
    ("S", "依靠五官感受世界", "依靠第六感理解世界"),
    ("S", "注重工作的实际结果", "注重工作的创意过程"),
    ("S", "做事喜欢有明确的规则", "做事喜欢灵活应变"),
    ("S", "相信看得见的证据", "相信潜在的关联"),
    ("S", "喜欢按部就班的生活", "喜欢充满惊喜的生活"),
    ("S", "对细节记忆力强", "对整体 pattern 记忆力强"),
    ("S", "喜欢实在的工作成果", "喜欢概念性的理论"),
    ("S", "用经验判断事物", "用创意判断事物"),
    ("S", "关注现实的问题", "关注抽象的哲理"),
    ("S", "注重可操作的方法", "注重理论的可能性"),
    ("S", "相信实际的经验", "相信抽象的理论"),
    ("S", "关注具体的细节", "关注整体的模式"),
    ("S", "用五官认识世界", "用心去感受世界"),
    ("S", "做事讲究实际", "做事充满想象"),
    ("S", "喜欢已知的安全感", "喜欢未知的刺激"),
    ("S", "重视经验的积累", "重视创新的突破"),
    ("S", "相信已验证的方法", "相信新发现的可能"),
    ("S", "关注眼前的问题", "关注长远的发展"),
    ("S", "喜欢稳定的程序", "喜欢灵活的变化"),
    ("S", "用事实说服他人", "用创意启发他人"),
    ("S", "相信具体的证据", "相信抽象的可能"),
    ("S", "注重做事的步骤", "注重做事的结果"),
    ("S", "喜欢传统的方式", "喜欢新颖的方法"),
    ("S", "相信经验的智慧", "相信直觉的引领"),
    ("S", "关注实际的应用", "关注理论的价值"),
    ("S", "做事有据可依", "做事灵活多变"),
    ("S", "相信眼见为实", "相信想象的空间"),
    ("S", "喜欢可掌控的情况", "喜欢充满变数的挑战"),
    ("S", "用逻辑分析事实", "用直觉把握可能"),
    ("S", "重视已取得的成就", "重视可能的突破"),
    ("S", "相信经过验证的", "相信创新的想法"),
    ("S", "关注具体的执行", "关注整体的战略"),
    # N 偏向 (50题)
    ("N", "喜欢探索新的可能性", "喜欢关注眼前的事实"),
    ("N", "凭直觉把握事物本质", "凭事实分析事物本质"),
    ("N", "对未来充满想象", "对现实有清晰认识"),
    ("N", "善于发现隐藏的模式", "善于注意具体的细节"),
    ("N", "喜欢思考各种可能性", "喜欢关注实际可行"),
    ("N", "相信灵感比经验可靠", "相信经验比灵感可靠"),
    ("N", "喜欢抽象的理论", "喜欢具体的应用"),
    ("N", "善于理解隐喻和类比", "善于理解字面意思"),
    ("N", "关注创意和想象", "关注逻辑和推理"),
    ("N", "喜欢问为什么", "喜欢问怎么做"),
    ("N", "倾向于理论思考", "倾向于实践操作"),
    ("N", "看到森林而不是树木", "看到树木而不是森林"),
    ("N", "对未来有愿景和梦想", "对当下有清晰规划"),
    ("N", "用第六感做决定", "用逻辑分析做决定"),
    ("N", "喜欢暗示和联想", "喜欢字面和直接"),
    ("N", "相信直觉的指引", "相信证据的指引"),
    ("N", "对可能性感到兴奋", "对确定性感到安心"),
    ("N", "喜欢概念和理论", "喜欢事实和数据"),
    ("N", "思考事物的深层含义", "思考事物的表面现象"),
    ("N", "善于从关联中发现规律", "善于从数据中得出结论"),
    ("N", "喜欢探索未知的领域", "喜欢专注已知的范围"),
    ("N", "用想象力拓展可能", "用经验判断可行"),
    ("N", "相信创新的价值", "相信传统的智慧"),
    ("N", "关注未来的趋势", "关注现在的实际"),
    ("N", "善于看到隐藏的机会", "善于看到潜在的风险"),
    ("N", "喜欢思考可能性", "喜欢思考现实性"),
    ("N", "用直觉做预测", "用分析做判断"),
    ("N", "相信创意的力量", "相信数据的力量"),
    ("N", "关注理论的价值", "关注实践的效果"),
    ("N", "善于发现新的联系", "善于深化现有知识"),
    ("N", "喜欢思考开放式的问题", "喜欢思考封闭式的问题"),
    ("N", "用想象力解决问题", "用逻辑分析问题"),
    ("N", "相信直觉的判断", "相信事实的支撑"),
    ("N", "关注创意的可能性", "关注执行的可行性"),
    ("N", "善于看到事物发展的趋势", "善于看到事物存在的现状"),
    ("N", "喜欢探索多种可能性", "喜欢确定一个答案"),
    ("N", "用第六感捕捉机会", "用经验规避风险"),
    ("N", "相信未来的可能性", "相信现在的确定性"),
    ("N", "关注整体的方向", "关注具体的细节"),
    ("N", "善于把握未来的方向", "善于处理当前的问题"),
    ("N", "喜欢思考未来的可能", "喜欢处理现实的问题"),
    ("N", "用直觉引领方向", "用事实支撑决策"),
    ("N", "相信创新的意义", "相信实用的价值"),
    ("N", "关注可能性的探索", "关注实际性的验证"),
]

# ============================================
# TF 维度题库 (Thinking/Feeling)
# ============================================

TF_BASE = [
    # T 偏向 (50题)
    ("T", "用逻辑做决定", "用心感做决定"),
    ("T", "追求客观真理", "追求和谐关系"),
    ("T", "公正比体贴更重要", "体贴比公正更重要"),
    ("T", "善于分析利弊", "善于理解他人感受"),
    ("T", "批评是进步的途径", "赞美是进步的途径"),
    ("T", "相信因果定律", "相信情感的力量"),
    ("T", "对事不对人", "对人不对事"),
    ("T", "原则比人情重要", "人情比原则重要"),
    ("T", "喜欢找出错误和漏洞", "喜欢肯定优点和价值"),
    ("T", "理性地解决问题", "情感地解决问题"),
    ("T", "做决定基于逻辑分析", "做决定基于价值观念"),
    ("T", "讨论观点而不是感受", "讨论感受而不是观点"),
    ("T", "重视能力和效率", "重视和谐和合作"),
    ("T", "喜欢公正公平的系统", "喜欢人性化灵活的安排"),
    ("T", "指出问题是为了解决", "指出问题会伤害感情"),
    ("T", "真相比面子重要", "面子比真相重要"),
    ("T", "合理比受欢迎重要", "受欢迎比合理重要"),
    ("T", "批评诚实但不中听", "赞美温和但可能虚假"),
    ("T", "用理性说服他人", "用情感说服他人"),
    ("T", "注重决策的逻辑性", "注重决策的影响"),
    ("T", "相信公平正义", "相信仁慈善良"),
    ("T", "用分析来做决定", "用感受来做决定"),
    ("T", "追求真理不偏不倚", "追求和谐避免冲突"),
    ("T", "批评对事不对人", "批评伤人感情"),
    ("T", "相信逻辑的力量", "相信情感的力量"),
    ("T", "公正无私", "仁慈宽厚"),
    ("T", "理性分析利弊", "情感体谅难处"),
    ("T", "讲道理不感情用事", "讲感情也讲道理"),
    ("T", "追求效率也追求公平", "追求和谐也追求效率"),
    ("T", "用批判性思维分析", "用同理心理解"),
    ("T", "相信因果报应", "相信人性本善"),
    ("T", "用逻辑验证想法", "用情感体会想法"),
    ("T", "相信客观分析", "相信主观直觉"),
    ("T", "注重事实和证据", "注重感受和体验"),
    ("T", "批评是帮助进步", "批评会伤害自尊"),
    ("T", "相信理性思考", "相信情感智慧"),
    ("T", "用原则指导行动", "用灵活应对情况"),
    ("T", "追求公正不偏袒", "追求和谐不伤感情"),
    ("T", "用理性处理问题", "用心感处理问题"),
    ("T", "相信逻辑分析", "相信第六感"),
    ("T", "注重逻辑的一致性", "注重情感的表达"),
    ("T", "批评对改进有益", "批评会让人难过"),
    ("T", "相信事实胜于雄辩", "相信情感能打动人"),
    ("T", "用分析来理解世界", "用感受来理解世界"),
    ("T", "追求效率和能力", "追求和谐和合作"),
    ("T", "用逻辑思维做决策", "用心直觉做决策"),
    ("T", "相信客观中立", "相信主观情感"),
    # F 偏向 (50题)
    ("F", "用情感做决定", "用逻辑做决定"),
    ("F", "追求人与人之间的和谐", "追求客观事实的真相"),
    ("F", "体贴比公正更重要", "公正比体贴更重要"),
    ("F", "理解他人的感受", "分析利弊得失"),
    ("F", "赞美是友善的表达", "批评是诚实的表现"),
    ("F", "相信情感是真实的", "相信逻辑是可靠的"),
    ("F", "对人不对事", "对事不对人"),
    ("F", "人情比原则重要", "原则比人情重要"),
    ("F", "肯定优点和价值", "找出错误和漏洞"),
    ("F", "情感地解决问题", "理性地解决问题"),
    ("F", "做决定基于价值观念", "做决定基于逻辑分析"),
    ("F", "讨论感受而不是观点", "讨论观点而不是感受"),
    ("F", "重视和谐和合作", "重视能力和效率"),
    ("F", "喜欢人性化灵活的安排", "喜欢公正公平的系统"),
    ("F", "真诚但不中听的话会伤害感情", "真诚但不中听的话是为了帮助"),
    ("F", "面子和感受很重要", "真相和事实更重要"),
    ("F", "受欢迎比合理重要", "合理比受欢迎重要"),
    ("F", "用情感说服他人", "用理性说服他人"),
    ("F", "注重决策对他人的影响", "注重决策的逻辑性"),
    ("F", "相信共鸣比论证更有力", "相信逻辑比情感更可靠"),
    ("F", "重视人的感受", "重视事的逻辑"),
    ("F", "用情感去理解他人", "用分析去理解他人"),
    ("F", "相信和善的力量", "相信原则的力量"),
    ("F", "体贴关怀他人", "分析判断他人"),
    ("F", "重视人际和谐", "重视事实真相"),
    ("F", "用感受做决定", "用分析做决定"),
    ("F", "追求被喜欢而不是被认可", "追求被认可而不是被喜欢"),
    ("F", "相信情感智慧", "相信逻辑智慧"),
    ("F", "用同理心理解他人", "用批判心审视他人"),
    ("F", "关注他人的感受", "关注问题的解决"),
    ("F", "相信善意和好意", "相信能力和实力"),
    ("F", "用情感建立连接", "用逻辑建立框架"),
    ("F", "追求和谐的人际关系", "追求高效的合作关系"),
    ("F", "用理解代替批评", "用分析代替理解"),
    ("F", "相信感性的力量", "相信理性的力量"),
    ("F", "重视他人的需求", "重视自己的原则"),
    ("F", "用关怀来帮助他人", "用建议来帮助他人"),
    ("F", "相信人心的善良", "相信制度的力量"),
    ("F", "用情感体凉他人", "用逻辑评判他人"),
    ("F", "重视感情也重视道理", "重视道理也重视感情"),
    ("F", "相信感受的真实性", "相信数据的客观性"),
    ("F", "用同情心对待他人", "用公平心对待他人"),
    ("F", "追求被人理解", "追求被人尊重"),
    ("F", "用情感影响他人", "用逻辑说服他人"),
    ("F", "相信爱能战胜一切", "相信逻辑能解决一切"),
]

# ============================================
# JP 维度题库 (Judging/Perceiving)
# ============================================

JP_BASE = [
    # J 偏向 (50题)
    ("J", "喜欢有计划的生活", "喜欢灵活自由的生活"),
    ("J", "按计划行事感到安心", "随机应变感到自由"),
    ("J", "喜欢做决定", "喜欢保持开放"),
    ("J", "有截止日期更有效率", "截止日期让我有压力"),
    ("J", "做事喜欢有明确的答案", "做事喜欢留有余地"),
    ("J", "喜欢提前准备", "喜欢临场发挥"),
    ("J", "日程表让我有掌控感", "日程表让我感到束缚"),
    ("J", "完成任务比体验过程重要", "体验过程比完成任务重要"),
    ("J", "喜欢有秩序的环境", "喜欢自然随性的环境"),
    ("J", "有目标才有方向", "随性而为更有趣"),
    ("J", "喜欢把事情安排妥当", "留出变化空间更舒适"),
    ("J", "决定了就不要轻易改变", "计划可以根据情况调整"),
    ("J", "喜欢按清单做事", "喜欢随心所欲"),
    ("J", "秩序和结构让我安心", "秩序和结构让我受限"),
    ("J", "信守承诺很重要", "灵活变通更重要"),
    ("J", "喜欢确定的结果", "喜欢开放的可能性"),
    ("J", "先工作后娱乐", "先娱乐后工作"),
    ("J", "有明确的时间表更好", "没有时间表更自在"),
    ("J", "完成比完美更重要", "完美比完成更重要"),
    ("J", "喜欢掌控局面", "顺其自然更舒适"),
    ("J", "按计划执行让我安心", "灵活应变让我兴奋"),
    ("J", "喜欢井井有条", "喜欢自然发生"),
    ("J", "做事有明确的目标", "做事享受过程"),
    ("J", "准时很重要", "随性也很重要"),
    ("J", "喜欢提前安排", "喜欢临时决定"),
    ("J", "有计划有组织", "随机应变"),
    ("J", "遵守时间约定", "时间约定可以灵活"),
    ("J", "做事有始有终", "享受做事的当下"),
    ("J", "完成目标很重要", "享受过程更重要"),
    ("J", "喜欢可预见的生活", "喜欢充满惊喜的生活"),
    ("J", "按优先级做事", "按直觉做事"),
    ("J", "喜欢有序的环境", "喜欢无拘无束"),
    ("J", "时间管理很重要", "跟随感觉更重要"),
    ("J", "计划让生活更有效率", "计划让生活失去乐趣"),
    ("J", "喜欢按时完成任务", "喜欢按自己节奏来"),
    ("J", "有目标有方向", "自由探索无方向"),
    ("J", "做事有条理", "做事随性"),
    ("J", "遵守规则很重要", "规则可以打破"),
    ("J", "喜欢可预测性", "喜欢可能性"),
    ("J", "计划赶得上变化", "变化不需要计划"),
    ("J", "做事有责任感", "做事享受当下"),
    ("J", "喜欢把事情确定下来", "喜欢保持开放"),
    ("J", "执行计划是重要的", "灵活应变是重要的"),
    ("J", "喜欢做决定", "喜欢探索选项"),
    # P 偏向 (50题)
    ("P", "喜欢灵活自由的生活", "喜欢有计划的生活"),
    ("P", "随机应变感到自由", "按计划行事感到安心"),
    ("P", "喜欢保持开放", "喜欢做决定"),
    ("P", "截止日期让我有压力", "有截止日期更有效率"),
    ("P", "做事喜欢留有余地", "做事喜欢有明确的答案"),
    ("P", "喜欢临场发挥", "喜欢提前准备"),
    ("P", "日程表让我感到束缚", "日程表让我有掌控感"),
    ("P", "体验过程比完成任务重要", "完成任务比体验过程重要"),
    ("P", "喜欢自然随性的环境", "喜欢有秩序的环境"),
    ("P", "随性而为更有趣", "有目标才有方向"),
    ("P", "留出变化空间更舒适", "喜欢把事情安排妥当"),
    ("P", "计划可以根据情况调整", "决定了就不要轻易改变"),
    ("P", "喜欢随心所欲", "喜欢按清单做事"),
    ("P", "秩序和结构让我受限", "秩序和结构让我安心"),
    ("P", "灵活变通更重要", "信守承诺很重要"),
    ("P", "喜欢开放的可能性", "喜欢确定的结果"),
    ("P", "先娱乐后工作", "先工作后娱乐"),
    ("P", "没有时间表更自在", "有明确的时间表更好"),
    ("P", "完美比完成更重要", "完成比完美更重要"),
    ("P", "顺其自然更舒适", "喜欢掌控局面"),
    ("P", "灵活应变让我兴奋", "按计划执行让我安心"),
    ("P", "喜欢自然发生", "喜欢井井有条"),
    ("P", "享受过程更重要", "做事有明确的目标"),
    ("P", "随性也很重要", "准时很重要"),
    ("P", "喜欢临时决定", "喜欢提前安排"),
    ("P", "时间约定可以灵活", "遵守时间约定"),
    ("P", "享受做事的当下", "做事有始有终"),
    ("P", "追求当下的快乐", "追求目标的达成"),
    ("P", "喜欢可能性", "喜欢可预见性"),
    ("P", "跟随感觉更重要", "时间管理很重要"),
    ("P", "计划让生活失去乐趣", "计划让生活更有效率"),
    ("P", "喜欢按自己节奏来", "喜欢按时完成任务"),
    ("P", "自由探索无方向", "有目标有方向"),
    ("P", "做事随性", "做事有条理"),
    ("P", "规则可以打破", "遵守规则很重要"),
    ("P", "喜欢可能性", "喜欢可预测性"),
    ("P", "变化不需要计划", "计划赶得上变化"),
    ("P", "享受当下更重要", "计划目标更重要"),
    ("P", "保持开放很重要", "做决定很重要"),
    ("P", "灵活应变是重要的", "执行计划是重要的"),
    ("P", "喜欢探索选项", "喜欢做决定"),
    ("P", "随性而为", "按计划执行"),
    ("P", "计划只是参考", "计划是必须执行的"),
    ("P", "做事的体验比结果重要", "做事的结果比体验重要"),
    ("P", "可能性比确定性有趣", "确定性比可能性安心"),
    ("P", "开放心态接受变化", "计划心态控制变化"),
]


def generate_question_pool():
    """生成完整题库"""
    all_questions = []
    
    # EI
    for pref, opt_a, opt_b in EI_BASE:
        all_questions.append({
            "dimension": "EI",
            "preference": pref,
            "option_a": opt_a,
            "option_b": opt_b
        })
    
    # SN
    for pref, opt_a, opt_b in SN_BASE:
        all_questions.append({
            "dimension": "SN",
            "preference": pref,
            "option_a": opt_a,
            "option_b": opt_b
        })
    
    # TF
    for pref, opt_a, opt_b in TF_BASE:
        all_questions.append({
            "dimension": "TF",
            "preference": pref,
            "option_a": opt_a,
            "option_b": opt_b
        })
    
    # JP
    for pref, opt_a, opt_b in JP_BASE:
        all_questions.append({
            "dimension": "JP",
            "preference": pref,
            "option_a": opt_a,
            "option_b": opt_b
        })
    
    return all_questions


def get_question_pool() -> Dict[str, List[Dict]]:
    """按维度分组获取题库"""
    all_qs = generate_question_pool()
    
    pool = {"EI": [], "SN": [], "TF": [], "JP": []}
    for q in all_qs:
        pool[q["dimension"]].append(q)
    
    return pool


def sample_questions(version: int, seed: int = None) -> List[Dict]:
    """
    从题库中随机抽取题目
    确保每次测试题目不同
    """
    if seed is not None:
        random.seed(seed)
    
    pool = get_question_pool()
    
    # 每个维度需要抽取的题目数
    dim_counts = {
        70: {"EI": 18, "SN": 18, "TF": 18, "JP": 16},
        93: {"EI": 23, "SN": 24, "TF": 24, "JP": 22},
        144: {"EI": 36, "SN": 36, "TF": 36, "JP": 36},
        200: {"EI": 50, "SN": 50, "TF": 50, "JP": 50},
    }
    
    counts = dim_counts.get(version, dim_counts[70])
    
    selected = []
    for dim, count in counts.items():
        available = pool[dim]
        sampled = random.sample(available, min(count, len(available)))
        selected.extend(sampled)
    
    # 打乱顺序
    random.shuffle(selected)
    
    # 重新编号 (使用整数)
    for i, q in enumerate(selected, 1):
        q["id"] = i
    
    return selected


def get_pool_stats() -> Dict:
    """获取题库统计"""
    pool = get_question_pool()
    return {
        "EI": len(pool["EI"]),
        "SN": len(pool["SN"]),
        "TF": len(pool["TF"]),
        "JP": len(pool["JP"]),
        "total": sum(len(v) for v in pool.values()),
    }


if __name__ == "__main__":
    print("=== 题库统计 ===")
    stats = get_pool_stats()
    for k, v in stats.items():
        print(f"  {k}: {v}")
    
    print("\n=== 70题版本抽样测试 ===")
    qs = sample_questions(70)
    print(f"实际抽取: {len(qs)}题")
    dims = {}
    for q in qs:
        dims[q["dimension"]] = dims.get(q["dimension"], 0) + 1
    print(f"维度分布: {dims}")


path: lib/questions.py
content: #!/usr/bin/env python3
"""
MBTI Questions Bank
Four versions: 70, 93, 144, 200 questions
"""

QUESTIONS = {
    # ========== VERSION 70 (Quick) ==========
    70: [
        # Energy - EI (17 questions)
        {"id": 1, "dimension": "EI", "option_a": "在聚会中，你通常是...", "option_b": "和很多人热烈交谈，从中获得能量", "preference_a": "E"},
        {"id": 2, "dimension": "EI", "option_a": "独处时你感到...", "option_b": "需要社交来充电", "preference_a": "I"},
        {"id": 3, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "和人聊天", "preference_a": "E"},
        {"id": 4, "dimension": "EI", "option_a": "你倾向于...", "option_b": "先行动后思考", "preference_a": "E"},
        {"id": 5, "dimension": "EI", "option_a": "你更愿意...", "option_b": "和一群人一起工作", "preference_a": "E"},
        {"id": 6, "dimension": "EI", "option_a": "社交活动让你...", "option_b": "充满活力", "preference_a": "E"},
        {"id": 7, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "团队讨论", "preference_a": "E"},
        {"id": 8, "dimension": "EI", "option_a": "在空闲时间，你更倾向于...", "option_b": "和朋友出去玩", "preference_a": "E"},
        {"id": 9, "dimension": "EI", "option_a": "你通过...获得能量", "option_b": "与人互动", "preference_a": "E"},
        {"id": 10, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "外出社交", "preference_a": "E"},
        {"id": 11, "dimension": "EI", "option_a": "独处让你...", "option_b": "恢复精力", "preference_a": "I"},
        {"id": 12, "dimension": "EI", "option_a": "你更享受...", "option_b": "一对一的深度对话", "preference_a": "I"},
        {"id": 13, "dimension": "EI", "option_a": "你倾向于...", "option_b": "先思考后行动", "preference_a": "I"},
        {"id": 14, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "独自工作", "preference_a": "I"},
        {"id": 15, "dimension": "EI", "option_a": "社交活动让你...", "option_b": "感到疲惫", "preference_a": "I"},
        {"id": 16, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "安静的环境", "preference_a": "I"},
        {"id": 17, "dimension": "EI", "option_a": "在空闲时间，你更倾向于...", "option_b": "独自阅读或思考", "preference_a": "I"},
        
        # Information - SN (18 questions)
        {"id": 18, "dimension": "SN", "option_a": "你更看重...", "option_b": "实际的数据和事实", "preference_a": "S"},
        {"id": 19, "dimension": "SN", "option_a": "你更相信...", "option_b": "具体的信息", "preference_a": "S"},
        {"id": 20, "dimension": "SN", "option_a": "你更倾向于...", "option_b": "关注细节", "preference_a": "S"},
        {"id": 21, "dimension": "SN", "option_a": "你更喜欢...", "option_b": "已知的方法", "preference_a": "S"},
        {"id": 22, "dimension": "SN", "option_a": "你更关注...", "option_b": "现实情况", "preference_a": "S"},
        {"id": 23, "dimension": "SN", "option_a": "你更愿意...", "option_b": "按照计划执行", "preference_a": "S"},
        {"id": 24, "dimension": "SN", "option_a": "你更喜欢...", "option_b": "处理具体问题", "preference_a": "S"},
        {"id": 25, "dimension": "SN", "option_a": "你更看重...", "option_b": "经验和事实", "preference_a": "S"},
        {"id": 26, "dimension": "SN", "option_a": "你更倾向于...", "option_b": "使用既定的流程", "preference_a": "S"},
        {"id": 27, "dimension": "SN", "option_a": "你更相信...", "option_b": "实际经验", "preference_a": "S"},
        {"id": 28, "dimension": "SN", "option_a": "你更关注...", "option_b": "此时此刻", "preference_a": "S"},
        {"id": 29, "dimension": "SN", "option_a": "你更愿意...", "option_b": "相信看到的", "preference_a": "S"},
        {"id": 30, "dimension": "SN", "option_a": "你更喜欢...", "option_b": "可能性和想象", "preference_a": "N"},
        {"id": 31, "dimension": "SN", "option_a": "你更相信...", "option_b": "直觉", "preference_a": "N"},
        {"id": 32, "dimension": "SN", "option_a": "你更倾向于...", "option_b": "关注整体", "preference_a": "N"},
        {"id": 33, "dimension": "SN", "option_a": "你更喜欢...", "option_b": "新的想法", "preference_a": "N"},
        {"id": 34, "dimension": "SN", "option_a": "你更关注...", "option_b": "未来可能性", "preference_a": "N"},
        {"id": 35, "dimension": "SN", "option_a": "你更愿意...", "option_b": "尝试新方法", "preference_a": "N"},
        
        # Decisions - TF (18 questions)
        {"id": 36, "dimension": "TF", "option_a": "做决定时你更看重...", "option_b": "逻辑和一致性", "preference_a": "T"},
        {"id": 37, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "公平对待每个人", "preference_a": "T"},
        {"id": 38, "dimension": "TF", "option_a": "你更重视...", "option_b": "原则和规则", "preference_a": "T"},
        {"id": 39, "dimension": "TF", "option_a": "你做决定时...", "option_b": "基于事实分析", "preference_a": "T"},
        {"id": 40, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "用理性判断", "preference_a": "T"},
        {"id": 41, "dimension": "TF", "option_a": "你更重视...", "option_b": "正确的分析", "preference_a": "T"},
        {"id": 42, "dimension": "TF", "option_a": "你做决定时...", "option_b": "遵循逻辑", "preference_a": "T"},
        {"id": 43, "dimension": "TF", "option_a": "你更看重...", "option_b": "客观原因", "preference_a": "T"},
        {"id": 44, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "批评错误", "preference_a": "T"},
        {"id": 45, "dimension": "TF", "option_a": "你更重视...", "option_b": "真相", "preference_a": "T"},
        {"id": 46, "dimension": "TF", "option_a": "做决定时你更看重...", "option_b": "对人的影响", "preference_a": "F"},
        {"id": 47, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "考虑他人感受", "preference_a": "F"},
        {"id": 48, "dimension": "TF", "option_a": "你更重视...", "option_b": "人际关系", "preference_a": "F"},
        {"id": 49, "dimension": "TF", "option_a": "你做决定时...", "option_b": "考虑情感因素", "preference_a": "F"},
        {"id": 50, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "体谅他人", "preference_a": "F"},
        {"id": 51, "dimension": "TF", "option_a": "你更重视...", "option_b": "和谐", "preference_a": "F"},
        {"id": 52, "dimension": "TF", "option_a": "你做决定时...", "option_b": "考虑价值观", "preference_a": "F"},
        {"id": 53, "dimension": "TF", "option_a": "你更看重...", "option_b": "情感联系", "preference_a": "F"},
        
        # Structure - JP (17 questions)
        {"id": 54, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有计划的生活", "preference_a": "J"},
        {"id": 55, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "按计划行事", "preference_a": "J"},
        {"id": 56, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "做决定并执行", "preference_a": "J"},
        {"id": 57, "dimension": "JP", "option_a": "你更愿意...", "option_b": "提前规划", "preference_a": "J"},
        {"id": 58, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "整洁有序", "preference_a": "J"},
        {"id": 59, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "完成目标", "preference_a": "J"},
        {"id": 60, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有组织", "preference_a": "J"},
        {"id": 61, "dimension": "JP", "option_a": "你更愿意...", "option_b": "按时完成任务", "preference_a": "J"},
        {"id": 62, "dimension": "JP", "option_a": "你更重视...", "option_b": "计划和安排", "preference_a": "J"},
        {"id": 63, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "遵守截止日期", "preference_a": "J"},
        {"id": 64, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "灵活随性", "preference_a": "P"},
        {"id": 65, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "随机应变", "preference_a": "P"},
        {"id": 66, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "保持开放", "preference_a": "P"},
        {"id": 67, "dimension": "JP", "option_a": "你更愿意...", "option_b": "等待发现", "preference_a": "P"},
        {"id": 68, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "自发行动", "preference_a": "P"},
        {"id": 69, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "适应变化", "preference_a": "P"},
        {"id": 70, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "临时决策", "preference_a": "P"},
    ],
}

# Add more questions for version 93 (extend with 23 more questions)
QUESTIONS[93] = QUESTIONS[70].copy() + [
    {"id": 71, "dimension": "EI", "option_a": "在大型活动中，你通常是...", "option_b": "主动结识新朋友", "preference_a": "E"},
    {"id": 72, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "被人关注", "preference_a": "E"},
    {"id": 73, "dimension": "SN", "option_a": "你更相信...", "option_b": "已证明的方法", "preference_a": "S"},
    {"id": 74, "dimension": "SN", "option_a": "你更倾向于...", "option_b": "关注实际结果", "preference_a": "S"},
    {"id": 75, "dimension": "TF", "option_a": "你做决定时...", "option_b": "先分析利弊", "preference_a": "T"},
    {"id": 76, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "保持客观", "preference_a": "T"},
    {"id": 77, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "按清单办事", "preference_a": "J"},
    {"id": 78, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "事先准备", "preference_a": "J"},
    {"id": 79, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "即兴发挥", "preference_a": "E"},
    {"id": 80, "dimension": "SN", "option_a": "你更关注...", "option_b": "具体例子", "preference_a": "S"},
    {"id": 81, "dimension": "TF", "option_a": "你更重视...", "option_b": "效率和逻辑", "preference_a": "T"},
    {"id": 82, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有明确截止", "preference_a": "J"},
    {"id": 83, "dimension": "EI", "option_a": "在团队中，你...", "option_b": "分享想法", "preference_a": "E"},
    {"id": 84, "dimension": "SN", "option_a": "你更相信...", "option_b": "事实和数据", "preference_a": "S"},
    {"id": 85, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "公平而非怜悯", "preference_a": "T"},
    {"id": 86, "dimension": "JP", "option_a": "你喜欢...", "option_b": "确定计划", "preference_a": "J"},
    {"id": 87, "dimension": "EI", "option_a": "你通过...获得灵感", "option_b": "与头脑风暴", "preference_a": "E"},
    {"id": 88, "dimension": "SN", "option_a": "你更喜欢...", "option_b": "一步一步来", "preference_a": "S"},
    {"id": 89, "dimension": "TF", "option_a": "你做决定时...", "option_b": "优先考虑原则", "preference_a": "T"},
    {"id": 90, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有清晰目标", "preference_a": "J"},
    {"id": 91, "dimension": "SN", "option_a": "你更关注...", "option_b": "当下事实", "preference_a": "S"},
    {"id": 92, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "诚信而非圆滑", "preference_a": "T"},
    {"id": 93, "dimension": "JP", "option_a": "你喜欢...", "option_b": "有期限的压力", "preference_a": "J"},
]

# Add more questions for version 144 (extend with 51 more questions)
QUESTIONS[144] = QUESTIONS[93].copy() + [
    {"id": 94, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "在人群中发言", "preference_a": "E"},
    {"id": 95, "dimension": "SN", "option_a": "你更相信...", "option_b": "亲眼所见", "preference_a": "S"},
    {"id": 96, "dimension": "TF", "option_a": "你做决定时...", "option_b": "考虑对人的影响", "preference_a": "F"},
    {"id": 97, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "做决定后执行", "preference_a": "J"},
    {"id": 98, "dimension": "EI", "option_a": "社交让你...", "option_b": "思维活跃", "preference_a": "E"},
    {"id": 99, "dimension": "SN", "option_a": "你更关注...", "option_b": "具体细节", "preference_a": "S"},
    {"id": 100, "dimension": "TF", "option_a": "你更重视...", "option_b": "逻辑思考", "preference_a": "T"},
    {"id": 101, "dimension": "JP", "option_a": "你喜欢...", "option_b": "井井有条", "preference_a": "J"},
    {"id": 102, "dimension": "EI", "option_a": "你倾向于...", "option_b": "表达自己", "preference_a": "E"},
    {"id": 103, "dimension": "SN", "option_a": "你更喜欢...", "option_b": "已验证的", "preference_a": "S"},
    {"id": 104, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "公正", "preference_a": "T"},
    {"id": 105, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "按计划", "preference_a": "J"},
    {"id": 106, "dimension": "EI", "option_a": "你喜欢...", "option_b": "带领讨论", "preference_a": "E"},
    {"id": 107, "dimension": "SN", "option_a": "你更关注...", "option_b": "现有信息", "preference_a": "S"},
    {"id": 108, "dimension": "TF", "option_a": "你做决定时...", "option_b": "依据客观分析", "preference_a": "T"},
    {"id": 109, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有明确方向", "preference_a": "J"},
    {"id": 110, "dimension": "EI", "option_a": "你通过...获得能量", "option_b": "与他人互动", "preference_a": "E"},
    {"id": 111, "dimension": "SN", "option_a": "你更愿意...", "option_b": "相信事实", "preference_a": "S"},
    {"id": 112, "dimension": "TF", "option_a": "你更重视...", "option_b": "一致性和公平", "preference_a": "T"},
    {"id": 113, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有组织的工作", "preference_a": "J"},
    {"id": 114, "dimension": "EI", "option_a": "你倾向于...", "option_b": "主导对话", "preference_a": "E"},
    {"id": 115, "dimension": "SN", "option_a": "你更关注...", "option_b": "具体事实", "preference_a": "S"},
    {"id": 116, "dimension": "TF", "option_a": "你做决定时...", "option_b": "优先考虑逻辑", "preference_a": "T"},
    {"id": 117, "dimension": "JP", "option_a": "你喜欢...", "option_b": "提前安排", "preference_a": "J"},
    {"id": 118, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "即兴演讲", "preference_a": "E"},
    {"id": 119, "dimension": "SN", "option_a": "你更相信...", "option_b": "实际经验", "preference_a": "S"},
    {"id": 120, "dimension": "TF", "option_a": "你更倾向于...", "option_b": "公平对待", "preference_a": "T"},
    {"id": 121, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "完成再庆祝", "preference_a": "J"},
    {"id": 122, "dimension": "EI", "option_a": "你倾向于...", "option_b": "公开发言", "preference_a": "E"},
    {"id": 123, "dimension": "SN", "option_a": "你更关注...", "option_b": "现实可行", "preference_a": "S"},
    {"id": 124, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "原则", "preference_a": "T"},
    {"id": 125, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "遵守时间", "preference_a": "J"},
    {"id": 126, "dimension": "EI", "option_a": "社交场合中，你...", "option_b": "成为焦点", "preference_a": "E"},
    {"id": 127, "dimension": "SN", "option_a": "你更愿意...", "option_b": "用具体例子", "preference_a": "S"},
    {"id": 128, "dimension": "TF", "option_a": "你做决定时...", "option_b": "考虑客观因素", "preference_a": "T"},
    {"id": 129, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "按部就班", "preference_a": "J"},
    {"id": 130, "dimension": "EI", "option_a": "你喜欢...", "option_b": "主持活动", "preference_a": "E"},
    {"id": 131, "dimension": "SN", "option_a": "你更关注...", "option_b": "真实数据", "preference_a": "S"},
    {"id": 132, "dimension": "TF", "option_a": "你更重视...", "option_b": "理性判断", "preference_a": "T"},
    {"id": 133, "dimension": "JP", "option_a": "你倾向于...", "option_b": "提前规划", "preference_a": "J"},
    {"id": 134, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "多人讨论", "preference_a": "E"},
    {"id": 135, "dimension": "SN", "option_a": "你更相信...", "option_b": "已知事实", "preference_a": "S"},
    {"id": 136, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "正确性", "preference_a": "T"},
    {"id": 137, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "完成任务", "preference_a": "J"},
    {"id": 138, "dimension": "EI", "option_a": "你通过...学习更多", "option_b": "讨论和交流", "preference_a": "E"},
    {"id": 139, "dimension": "SN", "option_a": "你更关注...", "option_b": "现有问题", "preference_a": "S"},
    {"id": 140, "dimension": "TF", "option_a": "你做决定时...", "option_b": "分析所有选项", "preference_a": "T"},
    {"id": 141, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有期限的工作", "preference_a": "J"},
    {"id": 142, "dimension": "EI", "option_a": "你喜欢...", "option_b": "结识新朋友", "preference_a": "E"},
    {"id": 143, "dimension": "SN", "option_a": "你更愿意...", "option_b": "相信经验", "preference_a": "S"},
    {"id": 144, "dimension": "JP", "option_a": "你更倾向于...", "option_b": "有清晰目标", "preference_a": "J"},
]

# Add more questions for version 200 (extend with 56 more questions)
QUESTIONS[200] = QUESTIONS[144].copy() + [
    {"id": 145, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "公开演讲", "preference_a": "E"},
    {"id": 146, "dimension": "SN", "option_a": "你更关注...", "option_b": "实际问题", "preference_a": "S"},
    {"id": 147, "dimension": "TF", "option_a": "你更重视...", "option_b": "客观标准", "preference_a": "T"},
    {"id": 148, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "确定计划", "preference_a": "J"},
    {"id": 149, "dimension": "EI", "option_a": "社交让你感到...", "option_b": "兴奋和活力", "preference_a": "E"},
    {"id": 150, "dimension": "SN", "option_a": "你更相信...", "option_b": "具体证据", "preference_a": "S"},
    {"id": 151, "dimension": "TF", "option_a": "你认为...更能衡量成功", "option_b": "成果和效率", "preference_a": "T"},
    {"id": 152, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "提前准备", "preference_a": "J"},
    {"id": 153, "dimension": "EI", "option_a": "你倾向于...", "option_b": "主动社交", "preference_a": "E"},
    {"id": 154, "dimension": "SN", "option_a": "你更关注...", "option_b": "现实情况", "preference_a": "S"},
    {"id": 155, "dimension": "TF", "option_a": "你做决定时...", "option_b": "考虑因果关系", "preference_a": "T"},
    {"id": 156, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有结构的方式", "preference_a": "J"},
    {"id": 157, "dimension": "EI", "option_a": "你喜欢...", "option_b": "在会议上发言", "preference_a": "E"},
    {"id": 158, "dimension": "SN", "option_a": "你更愿意...", "option_b": "解决具体问题", "preference_a": "S"},
    {"id": 159, "dimension": "TF", "option_a": "你更重视...", "option_b": "一致的标准", "preference_a": "T"},
    {"id": 160, "dimension": "JP", "option_a": "你倾向于...", "option_b": "遵守截止日期", "preference_a": "J"},
    {"id": 161, "dimension": "EI", "option_a": "你通过...获得能量", "option_b": "与朋友聚会", "preference_a": "E"},
    {"id": 162, "dimension": "SN", "option_a": "你更相信...", "option_b": "实际数据", "preference_a": "S"},
    {"id": 163, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "公平的规则", "preference_a": "T"},
    {"id": 164, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "清单和待办", "preference_a": "J"},
    {"id": 165, "dimension": "EI", "option_a": "在聚会中，你...", "option_b": "主导话题", "preference_a": "E"},
    {"id": 166, "dimension": "SN", "option_a": "你更关注...", "option_b": "已知的方法", "preference_a": "S"},
    {"id": 167, "dimension": "TF", "option_a": "你做决定时...", "option_b": "基于事实", "preference_a": "T"},
    {"id": 168, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "按计划进行", "preference_a": "J"},
    {"id": 169, "dimension": "EI", "option_a": "你喜欢...", "option_b": "即兴交流", "preference_a": "E"},
    {"id": 170, "dimension": "SN", "option_a": "你更愿意...", "option_b": "相信经验", "preference_a": "S"},
    {"id": 171, "dimension": "TF", "option_a": "你更重视...", "option_b": "逻辑和证据", "preference_a": "T"},
    {"id": 172, "dimension": "JP", "option_a": "你倾向于...", "option_b": "提前安排", "preference_a": "J"},
    {"id": 173, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "小组头脑风暴", "preference_a": "E"},
    {"id": 174, "dimension": "SN", "option_a": "你更关注...", "option_b": "实际可行", "preference_a": "S"},
    {"id": 175, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "原则和秩序", "preference_a": "T"},
    {"id": 176, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "完成目标", "preference_a": "J"},
    {"id": 177, "dimension": "EI", "option_a": "你倾向于...", "option_b": "发起活动", "preference_a": "E"},
    {"id": 178, "dimension": "SN", "option_a": "你更愿意...", "option_b": "用事实证明", "preference_a": "S"},
    {"id": 179, "dimension": "TF", "option_a": "你做决定时...", "option_b": "优先考虑逻辑", "preference_a": "T"},
    {"id": 180, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有组织的工作", "preference_a": "J"},
    {"id": 181, "dimension": "EI", "option_a": "你喜欢...", "option_b": "在人群中表达", "preference_a": "E"},
    {"id": 182, "dimension": "SN", "option_a": "你更关注...", "option_b": "具体的细节", "preference_a": "S"},
    {"id": 183, "dimension": "TF", "option_a": "你更重视...", "option_b": "客观分析", "preference_a": "T"},
    {"id": 184, "dimension": "JP", "option_a": "你倾向于...", "option_b": "制定计划", "preference_a": "J"},
    {"id": 185, "dimension": "EI", "option_a": "你更喜欢...", "option_b": "即兴发挥", "preference_a": "E"},
    {"id": 186, "dimension": "SN", "option_a": "你更愿意...", "option_b": "相信已知", "preference_a": "S"},
    {"id": 187, "dimension": "TF", "option_a": "你做决定时...", "option_b": "考虑所有因素", "preference_a": "T"},
    {"id": 188, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有清晰的目标", "preference_a": "J"},
    {"id": 189, "dimension": "EI", "option_a": "你喜欢...", "option_b": "主持讨论", "preference_a": "E"},
    {"id": 190, "dimension": "SN", "option_a": "你更关注...", "option_b": "现有数据", "preference_a": "S"},
    {"id": 191, "dimension": "TF", "option_a": "你更重视...", "option_b": "理性和公正", "preference_a": "T"},
    {"id": 192, "dimension": "JP", "option_a": "你倾向于...", "option_b": "遵守时间表", "preference_a": "J"},
    {"id": 193, "dimension": "EI", "option_a": "你通过...获得能量", "option_b": "社交活动", "preference_a": "E"},
    {"id": 194, "dimension": "SN", "option_a": "你更相信...", "option_b": "实际观察", "preference_a": "S"},
    {"id": 195, "dimension": "TF", "option_a": "你认为...更重要", "option_b": "效率和逻辑", "preference_a": "T"},
    {"id": 196, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有结构的工作", "preference_a": "J"},
    {"id": 197, "dimension": "EI", "option_a": "你倾向于...", "option_b": "积极参与社交", "preference_a": "E"},
    {"id": 198, "dimension": "SN", "option_a": "你更关注...", "option_b": "当下的事实", "preference_a": "S"},
    {"id": 199, "dimension": "TF", "option_a": "你做决定时...", "option_b": "优先考虑客观因素", "preference_a": "T"},
    {"id": 200, "dimension": "JP", "option_a": "你更喜欢...", "option_b": "有组织有计划", "preference_a": "J"},
]

def get_questions(version: int = 70) -> list:
    """Get questions for specified version"""
    if version not in QUESTIONS:
        version = 70
    return QUESTIONS[version]

def get_question_count(version: int = 70) -> int:
    """Get question count for version"""
    return len(get_questions(version))


path: lib/relationships.py
content: #!/usr/bin/env python3
"""
MBTI Guru - 职场人际与情侣匹配数据
包含16种MBTI类型的职场建议和关系匹配
"""

WORKPLACE_DATA = {
    "INTJ": {
        "best_environment": "独立工作、有明确目标、能发挥战略思维的环境",
        "worst_environment": "需要频繁社交、缺乏自主性、强调服从的环境",
        "team_role": "战略规划者、问题解决者、创新推动者",
        "leadership_style": "理性领导，注重效率和结果",
        "collaboration_tips": [
            "给他们足够的独立空间和时间",
            "用数据和逻辑说服他们",
            "避免情绪化的讨论",
            "欣赏他们的战略眼光"
        ],
        "stress_response": "退缩到内心世界，需要独处时间来恢复"
    },
    "INTP": {
        "best_environment": "允许深入研究、有弹性工作时间、重视智识的环境",
        "worst_environment": "高压销售环境、过度社交、缺乏自主性的环境",
        "team_role": "分析师、研究者、技术专家",
        "leadership_style": "民主型领导，鼓励独立思考",
        "collaboration_tips": [
            "不要期望他们热情洋溢",
            "给他们时间思考和提出问题",
            "提供清晰的逻辑框架",
            "欣赏他们的创新想法"
        ],
        "stress_response": "过度分析，需要休息大脑"
    },
    "ENTJ": {
        "best_environment": "竞争性环境、有明确晋升通道、能发挥领导才能的环境",
        "worst_environment": "官僚主义严重、缺乏挑战、死气沉沉的环境",
        "team_role": "领导者、决策者、组织者",
        "leadership_style": "命令式领导，目标导向",
        "collaboration_tips": [
            "尊重他们的能力和权威",
            "直接坦诚地沟通",
            "不要浪费他们的时间",
            "提供有挑战性的任务"
        ],
        "stress_response": "更加咄咄逼人，需要掌控局面"
    },
    "ENTP": {
        "best_environment": "创新型企业、多元文化、充满辩论机会的环境",
        "worst_environment": "重复性工作、过度规范、死板的环境",
        "team_role": "创新者、谈判者、问题解决者",
        "leadership_style": "魅力型领导，善于激励团队",
        "collaboration_tips": [
            "欢迎他们的新想法",
            "不要限制他们的讨论",
            "提供智力刺激",
            "欣赏他们的多才多艺"
        ],
        "stress_response": "更加活跃，可能过度承诺"
    },
    "INFJ": {
        "best_environment": "有使命感、能帮助他人、创意与理想并存的非营利或教育机构",
        "worst_environment": "冷漠功利、缺乏价值观、勾心斗角的环境",
        "team_role": "理想主义者、激励者、价值观守护者",
        "leadership_style": "服务型领导，关注成员成长",
        "collaboration_tips": [
            "欣赏他们的理想主义",
            "给他们意义和目标",
            "不要强迫他们做不喜欢的事",
            "保护他们的隐私"
        ],
        "stress_response": "退缩独处，可能对人性失望"
    },
    "INFP": {
        "best_environment": "创意行业、教育、心理咨询、有自我表达空间的非营利组织",
        "worst_environment": "高压销售、过度竞争、扼杀创意的大型企业",
        "team_role": "理想主义者、调解者、创意提供者",
        "leadership_style": "民主型领导，注重价值观",
        "collaboration_tips": [
            "尊重他们的价值观",
            "允许他们按自己的方式工作",
            "给他们创造性的空间",
            "欣赏他们的同理心"
        ],
        "stress_response": "自我怀疑，可能逃避现实"
    },
    "ENFJ": {
        "best_environment": "教育、培训、咨询、团队领导、人力资源管理",
        "worst_environment": "需要独立工作、缺乏人际互动、技术性过强的环境",
        "team_role": "激励者、团队建设者、导师",
        "leadership_style": "魅力型领导，善于激励人心",
        "collaboration_tips": [
            "真诚地认可他们的贡献",
            "给他们带领团队的机会",
            "不要忽视他们的情感需求",
            "提供社交互动的机会"
        ],
        "stress_response": "过度承担责任，可能忽视自己"
    },
    "ENFP": {
        "best_environment": "创意行业、营销、咨询、有灵活性和刺激性的创业环境",
        "worst_environment": "重复性工作、过度规范、缺乏变化的环境",
        "team_role": "激励者、创新者、项目推动者",
        "leadership_style": "魅力型领导，充满热情",
        "collaboration_tips": [
            "给他们表达想法的平台",
            "欢迎他们的热情和创意",
            "不要限制可能性",
            "欣赏他们的正能量"
        ],
        "stress_response": "更加活跃健谈，可能逃避问题"
    },
    "ISTJ": {
        "best_environment": "金融、政府、医疗、传统企业、需要秩序和系统的环境",
        "worst_environment": "高度不确定、经常变化、缺乏结构的创新型创业公司",
        "team_role": "执行者、守护者、质量控制者",
        "leadership_style": "命令型领导，注重责任和传统",
        "collaboration_tips": [
            "尊重他们的专业知识和经验",
            "提供清晰的期望和截止日期",
            "不要指望他们灵活变通",
            "欣赏他们的可靠和稳定"
        ],
        "stress_response": "更加沉默和固执，需要秩序"
    },
    "ISFJ": {
        "best_environment": "医疗、护理、教育、行政、支持性的人际互动环境",
        "worst_environment": "高压销售、竞争激烈、缺乏认可的环境",
        "team_role": "支持者、照顾者、后勤保障",
        "leadership_style": "服务型领导，关注他人需求",
        "collaboration_tips": [
            "真诚感谢他们的付出",
            "给他们安静的工作环境",
            "不要强迫他们在公众场合表现",
            "保护他们不被利用"
        ],
        "stress_response": "过度工作来证明自己价值"
    },
    "ESTJ": {
        "best_environment": "企业管理、法律、执法、政府、传统行业管理岗位",
        "worst_environment": "缺乏结构、需要创意、情绪敏感的工作环境",
        "team_role": "管理者、组织者、质量控制者",
        "leadership_style": "命令型领导，注重效率和结果",
        "collaboration_tips": [
            "尊重他们的经验和能力",
            "直接坦诚地沟通",
            "不要质疑他们的权威",
            "欣赏他们的组织能力"
        ],
        "stress_response": "更加控制欲，可能对他人苛刻"
    },
    "ESFJ": {
        "best_environment": "客户服务、销售、医疗、教育、人际密集型服务行业",
        "worst_environment": "需要独立工作、缺乏认可、技术冷冰冰的环境",
        "team_role": "协调者、激励者、关系维护者",
        "leadership_style": "服务型领导，营造和谐氛围",
        "collaboration_tips": [
            "真诚感谢他们的帮助",
            "给他们社交互动的机会",
            "不要忽视他们的情感需求",
            "认可他们的努力和付出"
        ],
        "stress_response": "过度在意他人看法，可能迎合他人"
    },
    "ISTP": {
        "best_environment": "工程、技术、机械、军事、户外、动手操作的环境",
        "worst_environment": "需要频繁社交、过度规范、创意表演的环境",
        "team_role": "技术专家、问题解决者、实用主义者",
        "leadership_style": "放任型领导，尊重个人能力",
        "collaboration_tips": [
            "给他们动手操作的机会",
            "用事实和逻辑沟通",
            "不要过度干预他们的工作方式",
            "欣赏他们的技术能力"
        ],
        "stress_response": "退缩到内心，需要独处"
    },
    "ISFP": {
        "best_environment": "艺术、设计、手工艺、时尚、医疗保健支持",
        "worst_environment": "高压竞争、过度规范、缺乏美感的机械环境",
        "team_role": "艺术家、创作者、调解者",
        "leadership_style": "服务型领导，注重个人价值",
        "collaboration_tips": [
            "欣赏他们的审美和独特视角",
            "给他们创造性的空间",
            "不要给他们太大压力",
            "认可他们的艺术贡献"
        ],
        "stress_response": "逃避冲突，可能消极怠工"
    },
    "ESTP": {
        "best_environment": "销售、商务、体育、创业、危机管理、行动导向",
        "worst_environment": "长期规划、理论研究、缺乏刺激的环境",
        "team_role": "推动者、冒险家、危机处理者",
        "leadership_style": "命令型领导，注重即时结果",
        "collaboration_tips": [
            "给他们行动的机会",
            "直接简洁地沟通",
            "不要过度规划或理论化",
            "欣赏他们的活力和务实"
        ],
        "stress_response": "更加冲动，可能冒险"
    },
    "ESFP": {
        "best_environment": "娱乐、旅游、销售、表演艺术、公共关系",
        "worst_environment": "长期规划、重复性工作、缺乏变化的机械环境",
        "team_role": "激励者、氛围制造者、团队粘合剂",
        "leadership_style": "魅力型领导，营造愉快氛围",
        "collaboration_tips": [
            "给他们展示自己的舞台",
            "欢迎他们的热情和活力",
            "不要限制他们的社交",
            "认可他们的表现力"
        ],
        "stress_response": "更加活跃健谈，可能冲动"
    },
}

# 情侣匹配数据
ROMANTIC_COMPATIBILITY = {
    "INTJ": {
        "best_match": ["INFJ", "ENFJ", "INTP"],
        "challenging": ["ESFP", "ISFP", "ESTP"],
        "relationship_tips": [
            "学习表达情感，而不只是分析",
            "给伴侣足够的个人空间",
            "不要总是追求完美",
            "学会欣赏伴侣的感受"
        ],
        "communication": "直接但温柔，表达想法时加入情感元素"
    },
    "INTP": {
        "best_match": ["INFJ", "ENFJ", "INTJ"],
        "challenging": ["ESFJ", "ESTJ", "ISFP"],
        "relationship_tips": [
            "主动表达欣赏和爱意",
            "不要过度分析伴侣的意图",
            "多花时间陪伴而不只是思考",
            "学会处理冲突"
        ],
        "communication": "开放讨论，但注意伴侣需要情感支持"
    },
    "ENTJ": {
        "best_match": ["INTJ", "INFJ", "INFP"],
        "challenging": ["ISFP", "ESFP", "ISTP"],
        "relationship_tips": [
            "倾听而不只是指挥",
            "学会欣赏伴侣的付出",
            "不要总是争强好胜",
            "给伴侣表达意见的空间"
        ],
        "communication": "直接沟通，但注意语气不要太过命令式"
    },
    "ENTP": {
        "best_match": ["INFJ", "INTJ", "ENFJ"],
        "challenging": ["ISFJ", "ISTJ", "ESFJ"],
        "relationship_tips": [
            "专注而不要三心二意",
            "学会承诺并坚持",
            "不要只用智力吸引伴侣",
            "多关注伴侣的情感需求"
        ],
        "communication": "保持辩论的乐趣，但学会妥协"
    },
    "INFJ": {
        "best_match": ["INTJ", "ENFP", "INFP", "ENTJ"],
        "challenging": ["ESTP", "ESFP"],
        "relationship_tips": [
            "不要为了和谐而牺牲自己需求",
            "学会接受冲突",
            "表达自己的真实感受",
            "相信自己的直觉"
        ],
        "communication": "温柔但坦诚，表达深层感受"
    },
    "INFP": {
        "best_match": ["ENFJ", "INFJ", "INTJ"],
        "challenging": ["ESTJ", "ISTJ", "ESFJ"],
        "relationship_tips": [
            "坚持自己的价值观但保持灵活",
            "学会说“不”",
            "不要过度自我批评",
            "行动比完美主义更重要"
        ],
        "communication": "真诚表达，但也学会处理现实问题"
    },
    "ENFJ": {
        "best_match": ["INFP", "ISFP", "INTJ"],
        "challenging": ["ISTP", "ESTP", "INTP"],
        "relationship_tips": [
            "不要为他人牺牲自己",
            "学会独处而不感到内疚",
            "接受不是所有人都会喜欢你",
            "给自己空间满足自己的需求"
        ],
        "communication": "热情关怀，但学会倾听自己"
    },
    "ENFP": {
        "best_match": ["INFJ", "ENFJ", "INTJ"],
        "challenging": ["ISTJ", "ESTJ", "ISFJ"],
        "relationship_tips": [
            "学会承诺并坚持",
            "不要逃避困难的话题",
            "练习专注而不只是跳跃性思维",
            "给伴侣安全感"
        ],
        "communication": "保持热情和创意，但学会深度对话"
    },
    "ISTJ": {
        "best_match": ["ESFJ", "ESTJ", "ISFJ"],
        "challenging": ["ENFP", "ENTP", "INFP"],
        "relationship_tips": [
            "接受变化和新想法",
            "学会表达感情",
            "不要过于批评伴侣",
            "理解伴侣有不同的做事方式"
        ],
        "communication": "可靠稳定，但加入情感表达"
    },
    "ISFJ": {
        "best_match": ["ESFP", "ESTP", "ISFP"],
        "challenging": ["ENTP", "INTP", "ENTJ"],
        "relationship_tips": [
            "为自己站出来说话",
            "学会接受赞美",
            "不要把伴侣的问题都揽过来",
            "培养自己的兴趣和社交"
        ],
        "communication": "关怀体贴，但也表达自己的需求"
    },
    "ESTJ": {
        "best_match": ["ISTP", "ISFP", "ESTP"],
        "challenging": ["NF", "INFP", "ENFP"],
        "relationship_tips": [
            "倾听而不只是命令",
            "学会欣赏不同的做事方式",
            "注意伴侣的感受",
            "不要过于挑剔"
        ],
        "communication": "直接明了，但加入柔和语气"
    },
    "ESFJ": {
        "best_match": ["ISFP", "ISTP", "ESFP"],
        "challenging": ["INTP", "ENTP", "INTJ"],
        "relationship_tips": [
            "不要太在意他人的看法",
            "学会拒绝过分的要求",
            "给伴侣个人空间",
            "不要总是照顾他人而忽视自己"
        ],
        "communication": "温暖热情，但也表达自己的界限"
    },
    "ISTP": {
        "best_match": ["ESTJ", "ESFJ", "ESTP"],
        "challenging": ["NF", "ENFJ", "INFJ"],
        "relationship_tips": [
            "表达情感而不只是分析",
            "学会承诺和投入",
            "不要回避冲突",
            "多花时间陪伴伴侣"
        ],
        "communication": "实际直接，但也表达内心感受"
    },
    "ISFP": {
        "best_match": ["ESFJ", "ENFJ", "ESFP"],
        "challenging": ["ENTJ", "INTJ", "ENTP"],
        "relationship_tips": [
            "勇敢表达自己的立场",
            "学会坚持而不只是退让",
            "不要过度自我怀疑",
            "培养自信"
        ],
        "communication": "温和但真诚，表达独特视角"
    },
    "ESTP": {
        "best_match": ["ISTJ", "ISFJ", "ESTJ"],
        "challenging": ["INFJ", "INFP", "INTJ"],
        "relationship_tips": [
            "学会考虑后果",
            "练习倾听而不是只是说话",
            "不要只用刺激来维持关系",
            "培养深度对话的能力"
        ],
        "communication": "活泼直接，但也学会深度交流"
    },
    "ESFP": {
        "best_match": ["ISFJ", "ISTJ", "ESTJ"],
        "challenging": ["INTJ", "INFJ", "INTP"],
        "relationship_tips": [
            "学会承诺并坚持",
            "不要太在意他人目光",
            "发展深度而非只是广度",
            "练习在安静中享受相处"
        ],
        "communication": "活泼热情，但也学会倾听"
    },
}

def get_workplace_info(type_code: str) -> dict:
    """获取类型的职场信息"""
    return WORKPLACE_DATA.get(type_code, {})

def get_romantic_info(type_code: str) -> dict:
    """获取类型的情侣匹配信息"""
    return ROMANTIC_COMPATIBILITY.get(type_code, {})

def get_relationship_data(type_code: str) -> dict:
    """获取完整的关系数据"""
    return {
        "workplace": get_workplace_info(type_code),
        "romantic": get_romantic_info(type_code)
    }


path: lib/reports.py
content: #!/usr/bin/env python3
"""
MBTI Report Generator - Bilingual reports
"""

from typing import Dict, List

def generate_report(type_code: str, scores: Dict, clarity_levels: Dict) -> str:
    """
    Generate comprehensive bilingual report
    
    Args:
        type_code: MBTI type like "INFP"
        scores: Dict of dimension scores
        clarity_levels: Dict of clarity for each dimension
    """
    from lib.mbti_types import get_type
    
    type_data = get_type(type_code)
    if not type_data:
        return "Type not found"
    
    name_en = type_data.get("name_en", "")
    name_cn = type_data.get("name_cn", "")
    summary = type_data.get("summary", "")
    summary_cn = type_data.get("summary_cn", "")
    strengths = type_data.get("strengths", [])
    weaknesses = type_data.get("weaknesses", [])
    careers = type_data.get("careers", [])
    relationships = type_data.get("relationships", {})
    
    # Calculate overall clarity
    total_clarity = sum(c[0] for c in clarity_levels.values()) / len(clarity_levels)
    
    # Build report
    report = f"""
╔══════════════════════════════════════════════════════════════════╗
║                                                                  ║
║              MBTI PERSONALITY ANALYSIS REPORT                   ║
║                  MBTI 人格分析报告                             ║
║                                                                  ║
╠══════════════════════════════════════════════════════════════════╣
║                                                                  ║
║   🧠 {type_code}                                            ║
║      {name_en}                                         ║
║      {name_cn}                                            ║
║                                                                  ║
║   ─────────────────────────────────────────────────────────   ║
║                                                                  ║
║   Overall Clarity / 综合清晰度: {total_clarity:.0f}%                          ║
║                                                                  ║
║   Dimension Scores / 维度得分:                                   ║
║     • Energy / 能量:      {scores.get('EI', 50):.0f}% → {'E' if scores.get('EI', 50) > 50 else 'I'}                                     ║
║     • Information / 信息: {scores.get('SN', 50):.0f}% → {'S' if scores.get('SN', 50) > 50 else 'N'}                                     ║
║     • Decisions / 决策: {scores.get('TF', 50):.0f}% → {'T' if scores.get('TF', 50) > 50 else 'F'}                                     ║
║     • Structure / 结构: {scores.get('JP', 50):.0f}% → {'J' if scores.get('JP', 50) > 50 else 'P'}                                     ║
║                                                                  ║
║   ─────────────────────────────────────────────────────────   ║
║                                                                  ║
║   Brief Summary / 简介:                                        ║
║   {summary[:60]}                                               ║
║   {summary_cn[:50]}                                            ║
║                                                                  ║
╚══════════════════════════════════════════════════════════════════╝
"""
    
    report += f"""

╔══════════════════════════════════════════════════════════════════╗
║                   STRENGTHS / 优势                           ║
╠══════════════════════════════════════════════════════════════════╣"""

    for i, s in enumerate(strengths[:5], 1):
        report += f"""
║   {i}. {s[:60]:<60} ║"""

    report += """

╚══════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════╗
║                   WEAKNESSES / 劣势                         ║
╠══════════════════════════════════════════════════════════════════╣"""

    for i, w in enumerate(weaknesses[:5], 1):
        report += f"""
║   {i}. {w[:60]:<60} ║"""

    report += """

╚══════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════╗
║                   CAREERS / 职业建议                       ║
╠══════════════════════════════════════════════════════════════════╣"""

    for i, c in enumerate(careers[:5], 1):
        report += f"""
║   {i}. {c[:60]:<60} ║"""

    # Best matches
    best_matches = relationships.get("best", [])
    challenging = relationships.get("challenging", [])
    
    report += """

╚══════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════╗
║              COMPATIBLE TYPES / 性格匹配                    ║
╠══════════════════════════════════════════════════════════════════╣
║                                                                  ║
║   🌟 Best Match / 最佳匹配:                                  ║
"""

    for m in best_matches[:3]:
        report += f"""║      {m}                                                     ║
"""

    report += """║                                                                  ║
║   ⚠️ Challenging / 挑战类型:                                  ║"""

    for m in challenging[:2]:
        report += f"""║      {m}                                                     ║
"""

    report += """
╚══════════════════════════════════════════════════════════════════╝
"""
    
    return report


def generate_summary_report(type_code: str, scores: Dict) -> str:
    """Generate brief summary report"""
    from lib.mbti_types import get_type
    
    type_data = get_type(type_code)
    if not type_data:
        return "Type not found"
    
    name_en = type_data.get("name_en", "")
    name_cn = type_data.get("name_cn", "")
    summary = type_data.get("summary", "")[:80]
    summary_cn = type_data.get("summary_cn", "")[:60]
    
    return f"""
╔══════════════════════════════════════════════════════════════════╗
║                  YOUR PERSONALITY TYPE                      ║
║                    您的性格类型                          ║
╠══════════════════════════════════════════════════════════════════╣
║                                                                  ║
║   🧠 {type_code}                                             ║
║      {name_en}                                          ║
║      {name_cn}                                             ║
║                                                                  ║
║   Summary / 简介:                                            ║
║   {summary[:65]}                                  ║
║   {summary_cn[:50]}                                           ║
║                                                                  ║
║   Dimension Breakdown / 维度分析:                               ║
║     EI: {scores.get('EI', 50):.0f}% → {'Extraversion' if scores.get('EI', 50) > 50 else 'Introversion'}                          ║
║     SN: {scores.get('SN', 50):.0f}% → {'Sensing' if scores.get('SN', 50) > 50 else 'Intuition'}                              ║
║     TF: {scores.get('TF', 50):.0f}% → {'Thinking' if scores.get('TF', 50) > 50 else 'Feeling'}                              ║
║     JP: {scores.get('JP', 50):.0f}% → {'Judging' if scores.get('JP', 50) > 50 else 'Perceiving'}                            ║
║                                                                  ║
╚══════════════════════════════════════════════════════════════════╝
"""


path: lib/scorer.py
content: #!/usr/bin/env python3
"""
MBTI Scorer - Calculate personality type from answers
"""

from typing import Dict, Tuple

def calculate_type(answers: list) -> Tuple[str, Dict]:
    """
    Calculate MBTI type from answers
    """
    from lib.questions import get_questions
    
    if not answers:
        return "UNKNOWN", {}
    
    # Get question count
    q_count = len(answers)
    if q_count >= 200:
        version = 200
    elif q_count >= 144:
        version = 144
    elif q_count >= 93:
        version = 93
    else:
        version = 70
    
    questions = get_questions(version)
    
    # Count preferences for each dimension
    dim_counts = {
        "EI": {"E": 0, "I": 0},
        "SN": {"S": 0, "N": 0},
        "TF": {"T": 0, "F": 0},
        "JP": {"J": 0, "P": 0}
    }
    
    dim_totals = {"EI": 0, "SN": 0, "TF": 0, "JP": 0}
    
    # Build question lookup
    q_map = {q["id"]: q for q in questions}
    
    for q_id, selected in answers:
        if q_id not in q_map:
            continue
        q = q_map[q_id]
        dim = q["dimension"]
        
        # Get preference based on answer choice
        if selected.upper() == "A":
            pref = q["preference_a"]
        else:
            # The other preference
            first_pref = q["preference_a"]
            all_prefs = {"EI": ["E", "I"], "SN": ["S", "N"], "TF": ["T", "F"], "JP": ["J", "P"]}
            prefs = all_prefs[dim]
            pref = prefs[1] if first_pref == prefs[0] else prefs[0]
        
        dim_counts[dim][pref] += 1
        dim_totals[dim] += 1
    
    # Calculate indices
    scores = {}
    type_letters = []
    
    for dim in ["EI", "SN", "TF", "JP"]:
        first_count = dim_counts[dim][dim[0]]
        total = dim_totals[dim]
        
        if total == 0:
            scores[dim] = 50
            type_letters.append(dim[0])
            continue
        
        # Index for first letter
        index_first = (first_count / total) * 100
        
        scores[dim] = round(index_first, 1)
        
        # Determine preference
        if index_first > 50:
            type_letters.append(dim[0])
        else:
            type_letters.append(dim[1])
    
    type_code = "".join(type_letters)
    
    return type_code, scores


def calculate_clarity(score: float) -> Tuple[float, str]:
    """
    Calculate clarity level from score
    """
    clarity = abs(score - 50) * 2
    
    if clarity <= 25:
        level = "Slight / 轻微"
    elif clarity <= 50:
        level = "Moderate / 中等"
    elif clarity <= 75:
        level = "Clear / 清晰"
    else:
        level = "Very Clear / 非常清晰"
    
    return round(clarity, 1), level


def format_scores(scores: Dict) -> str:
    """Format scores for display"""
    result = []
    for dim, score in scores.items():
        clarity, level = calculate_clarity(score)
        pref = dim[0] if score > 50 else dim[1]
        result.append(f"{dim}: {pref} {score}% ({level})")
    return "\n".join(result)


path: lib/session.py
content: #!/usr/bin/env python3
"""
MBTI Guru - 会话管理模块
支持测试进度保存和恢复
"""

import os
import json
import time
from datetime import datetime
from typing import Optional, Dict, List

DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data", "sessions")

def ensure_data_dir():
    """确保数据目录存在"""
    os.makedirs(DATA_DIR, exist_ok=True)

def save_session(chat_id: int, version: int, current_index: int, answers: List[tuple], questions_total: int) -> str:
    """
    保存测试进度
    
    Args:
        chat_id: 用户ID
        version: 测试版本(70/93/144/200)
        current_index: 当前题目索引(从1开始)
        answers: 已完成的答案 [(question_id, choice), ...]
        questions_total: 总题数
    
    Returns:
        session_id: 会话ID，用于恢复
    """
    ensure_data_dir()
    
    timestamp = int(time.time())
    session_id = f"{chat_id}_{timestamp}"
    
    session_data = {
        "session_id": session_id,
        "chat_id": chat_id,
        "version": version,
        "current_index": current_index,
        "answers": answers,
        "questions_total": questions_total,
        "timestamp": timestamp,
        "created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }
    
    filepath = os.path.join(DATA_DIR, f"{session_id}.json")
    with open(filepath, 'w', encoding='utf-8') as f:
        json.dump(session_data, f, ensure_ascii=False, indent=2)
    
    return session_id

def load_session(session_id: str) -> Optional[Dict]:
    """
    加载会话数据
    
    Args:
        session_id: 会话ID
    
    Returns:
        session数据或None
    """
    filepath = os.path.join(DATA_DIR, f"{session_id}.json")
    if not os.path.exists(filepath):
        return None
    
    with open(filepath, 'r', encoding='utf-8') as f:
        return json.load(f)

def get_incomplete_session(chat_id: int) -> Optional[Dict]:
    """
    获取用户未完成的会话
    
    Args:
        chat_id: 用户ID
    
    Returns:
        最新的未完成会话或None
    """
    ensure_data_dir()
    
    sessions = []
    for filename in os.listdir(DATA_DIR):
        if filename.endswith('.json') and filename.startswith(str(chat_id)):
            filepath = os.path.join(DATA_DIR, filename)
            with open(filepath, 'r', encoding='utf-8') as f:
                session = json.load(f)
                sessions.append(session)
    
    if not sessions:
        return None
    
    # 返回最新的未完成会话
    sessions.sort(key=lambda x: x['timestamp'], reverse=True)
    latest = sessions[0]
    
    # 检查是否真的未完成
    if latest['current_index'] < latest['questions_total']:
        return latest
    
    return None

def delete_session(session_id: str) -> bool:
    """
    删除会话
    
    Args:
        session_id: 会话ID
    
    Returns:
        是否删除成功
    """
    filepath = os.path.join(DATA_DIR, f"{session_id}.json")
    if os.path.exists(filepath):
        os.remove(filepath)
        return True
    return False

def list_user_sessions(chat_id: int, include_complete: bool = False) -> List[Dict]:
    """
    列出用户的所有会话
    
    Args:
        chat_id: 用户ID
        include_complete: 是否包含已完成的
    
    Returns:
        会话列表
    """
    ensure_data_dir()
    
    sessions = []
    for filename in os.listdir(DATA_DIR):
        if filename.endswith('.json') and filename.startswith(str(chat_id)):
            filepath = os.path.join(DATA_DIR, filename)
            with open(filepath, 'r', encoding='utf-8') as f:
                session = json.load(f)
                if include_complete or session['current_index'] < session['questions_total']:
                    sessions.append(session)
    
    sessions.sort(key=lambda x: x['timestamp'], reverse=True)
    return sessions


path: lib/telegram_handler.py
content: #!/usr/bin/env python3
"""
MBTI Guru - Telegram Handler
基于 mbti_core 的 Telegram 适配器
支持所有 OpenClaw 渠道
"""

import sys
import os

sys.path.insert(0, os.path.dirname(__file__))

# 导入核心处理模块
from lib.mbti_core import (
    process_message,
    process_start,
    process_resume,
    process_history,
    process_status,
    process_cancel,
    process_answer,
    process_version_select,
    get_version_selection_text,
    get_progress_bar,
    get_history_text,
    VERSIONS
)

# 兼容旧接口
def handle_message(chat_id: int, text: str):
    """Telegram 消息处理"""
    return process_message(chat_id, text)

def handle_callback(chat_id: int, data: str):
    """Telegram 回调处理"""
    if data == "resume":
        return process_resume(chat_id)
    elif data.startswith("version_"):
        version_key = data.split("_")[1]
        return process_version_select(chat_id, version_key)
    elif data == "history":
        return process_history(chat_id)
    return {"action": "send", "message": "未知操作"}

# 导出
__all__ = [
    'handle_message',
    'handle_callback',
    'process_message',
    'process_start',
    'process_resume',
    'process_history',
    'process_status',
    'process_cancel',
    'get_version_selection_text',
    'get_progress_bar',
    'VERSIONS'
]


path: mbti.py
content: #!/usr/bin/env python3
"""
MBTI Personality Test - Main Entry Point
"""
import sys
import os

# Add lib to path
sys.path.insert(0, os.path.dirname(__file__))

from lib.questions import get_questions, get_question_count
from lib.scorer import calculate_type, calculate_clarity
from lib.reports import generate_report, generate_summary_report
from lib.types import get_type

def main():
    print("=" * 60)
    print("MBTI Personality Test")
    print("MBTI 人格测试")
    print("=" * 60)
    
    print("""
请选择测试版本 / Select test version:
1. 快速版 (70题 ~10分钟) Quick
2. 标准版 (93题 ~15分钟) Standard  
3. 扩展版 (144题 ~25分钟) Extended
4. 专业版 (200题 ~35分钟) Professional
""")
    
    version_map = {"1": 70, "2": 93, "3": 144, "4": 200}
    
    version = input("输入数字选择 / Enter number: ").strip()
    
    if version not in version_map:
        print("无效选择，使用快速版")
        version = 70
    else:
        version = version_map[version]
    
    questions = get_questions(version)
    total = len(questions)
    
    print(f"\n开始测试，共{total}题")
    print(f"每个问题请输入 A 或 B\n")
    
    answers = []
    
    for i, q in enumerate(questions, 1):
        print(f"\n问题 {i}/{total}")
        print(f"{q['option_a']}")
        print(f"A) {q['option_a']}")
        print(f"B) {q['option_b']}")
        
        while True:
            choice = input("选择 / Choice (A/B): ").strip().upper()
            if choice in ["A", "B"]:
                break
            print("请输入 A 或 B")
        
        answers.append((q["id"], choice))
        
        # Progress every 10 questions
        if i % 10 == 0:
            print(f"进度: {i}/{total} ({i*100//total}%)")
    
    # Calculate results
    print("\n计算中...")
    type_code, scores = calculate_type(answers)
    
    # Generate report
    print("\n" + "=" * 60)
    print("测试完成！")
    print("=" * 60)
    
    report = generate_summary_report(type_code, scores)
    print(report)
    
    # Full report
    print("\n输入任意键查看完整报告...")
    input()
    full_report = generate_report(type_code, scores, 
                                  {dim: calculate_clarity(score) 
                                   for dim, score in scores.items()})
    print(full_report)

if __name__ == "__main__":
    main()


path: test_all_features.py
content: #!/usr/bin/env python3
"""
MBTI Guru v1.4 - 全面功能测试
测试：恢复、中断、保存、记录、比较
"""

import sys
import os
import time

sys.path.insert(0, os.path.dirname(__file__))

from lib.telegram_handler import (
    handle_message, handle_start, handle_resume, handle_history,
    handle_status, handle_cancel, handle_callback,
    get_user_state, clear_user_state,
    save_session, load_session, get_incomplete_session, delete_session
)
from lib.history import save_test_result, get_test_history, compare_tests
from lib.scorer import calculate_type, calculate_clarity
from datetime import datetime

CHAT_ID = 123456  # 测试用chat_id

def print_test(name):
    print(f"\n{'='*50}")
    print(f"测试: {name}")
    print('='*50)

def cleanup():
    """清理测试数据"""
    clear_user_state(CHAT_ID)
    # 删除测试会话文件
    import glob
    for f in glob.glob(f"data/sessions/{CHAT_ID}_*.json"):
        os.remove(f)
    for f in glob.glob(f"data/history/{CHAT_ID}/*.json"):
        os.remove(f)

def test_1_save_and_resume():
    """测试1: 保存和恢复"""
    print_test("保存和恢复")
    
    cleanup()
    
    # 1. 开始测试
    print("\n[1] 开始测试...")
    result = handle_start(CHAT_ID)
    print(f"状态: {result.get('state')}")
    assert result['state'] == 'selecting_version', "应该进入版本选择状态"
    
    # 2. 选择版本
    print("\n[2] 选择版本 70题...")
    result = handle_message(CHAT_ID, "1")
    print(f"状态: {result.get('state')}")
    assert result['state'] == 'testing', "应该进入测试状态"
    
    # 3. 回答几题
    print("\n[3] 回答10题...")
    for i in range(10):
        result = handle_message(CHAT_ID, "A" if i % 2 == 0 else "B")
        if i == 9:
            print(f"已答: {i+1}题")
    
    # 4. 检查状态
    state = get_user_state(CHAT_ID)
    print(f"\n[4] 当前进度: {state.current_index}/{len(state.questions)}")
    print(f"    已保存session_id: {state.session_id}")
    assert state.current_index == 10, "应该已回答10题"
    
    # 5. 模拟中断（不清除状态）
    print("\n[5] 模拟中断...")
    session_id = state.session_id
    
    # 6. 重新开始
    print("\n[6] 重新开始并恢复...")
    clear_user_state(CHAT_ID)
    
    # 手动设置一个未完成的会话
    if session_id:
        # 验证会话存在
        session_data = load_session(session_id)
        print(f"    Session存在: {session_data is not None}")
        print(f"    进度: {session_data['current_index']}/{session_data['questions_total']}")
    
    # 使用resume恢复
    result = handle_resume(CHAT_ID)
    print(f"恢复后状态: {result['state']}")
    
    state = get_user_state(CHAT_ID)
    print(f"恢复后进度: {state.current_index}/{len(state.questions)}")
    assert state.current_index == 10, "恢复后应该从第10题开始"
    
    print("\n✅ 保存和恢复测试通过!")
    return True

def test_2_interrupt():
    """测试2: 中断"""
    print_test("中断")
    
    cleanup()
    
    # 1. 开始测试
    print("\n[1] 开始测试...")
    handle_message(CHAT_ID, "1")
    
    # 2. 回答几题
    print("\n[2] 回答5题...")
    for i in range(5):
        handle_message(CHAT_ID, "A")
    
    # 3. 取消
    print("\n[3] 取消测试...")
    result = handle_cancel(CHAT_ID)
    print(f"消息: {result['message'][:50]}...")
    
    # 4. 验证状态已清除
    state = get_user_state(CHAT_ID)
    print(f"状态: {state.state}")
    assert state.state == "idle", "状态应该重置为idle"
    
    # 5. 尝试恢复（应该失败）
    print("\n[4] 尝试恢复已取消的测试...")
    result = handle_resume(CHAT_ID)
    # 应该返回版本选择界面
    print(f"结果: {result['message'][:50]}...")
    
    print("\n✅ 中断测试通过!")
    return True

def test_3_auto_save():
    """测试3: 自动保存"""
    print_test("自动保存")
    
    cleanup()
    
    # 1. 开始测试
    print("\n[1] 开始测试...")
    handle_message(CHAT_ID, "1")
    
    # 2. 回答10题（应该触发保存）
    print("\n[2] 回答10题（触发自动保存）...")
    for i in range(10):
        result = handle_message(CHAT_ID, "A" if i % 2 == 0 else "B")
    
    state = get_user_state(CHAT_ID)
    print(f"Session ID: {state.session_id}")
    assert state.session_id is not None, "应该有session_id"
    
    # 3. 验证session文件存在
    if state.session_id:
        session_data = load_session(state.session_id)
        print(f"Session文件存在: {session_data is not None}")
        print(f"保存的进度: {session_data['current_index']}/{session_data['questions_total']}")
        assert session_data['current_index'] == 10, "保存的进度应该是10"
    
    print("\n✅ 自动保存测试通过!")
    return True

def test_4_history():
    """测试4: 历史记录"""
    print_test("历史记录")
    
    cleanup()
    
    # 1. 完成一个测试
    print("\n[1] 完成一个测试...")
    handle_message(CHAT_ID, "1")  # 70题版本
    
    # 回答所有70题
    for i in range(70):
        result = handle_message(CHAT_ID, "A" if i % 2 == 0 else "B")
        if result['action'] == 'complete':
            print(f"测试完成! 类型: {result.get('type_code')}")
            break
    
    # 2. 检查历史
    print("\n[2] 查看历史...")
    result = handle_history(CHAT_ID)
    print(f"历史消息: {result['message'][:100]}...")
    
    history = get_test_history(CHAT_ID)
    print(f"历史记录数: {len(history)}")
    assert len(history) >= 1, "应该有至少1条历史"
    
    print("\n✅ 历史记录测试通过!")
    return True

def test_5_compare():
    """测试5: 比较功能"""
    print_test("比较功能")
    
    # 创建两个测试记录
    test1 = {
        "test_id": "test1",
        "type_code": "INFP",
        "scores": {"EI": 70, "SN": 65, "TF": 75, "JP": 60},
        "date": "2026-03-25"
    }
    
    test2 = {
        "test_id": "test2", 
        "type_code": "INFJ",
        "scores": {"EI": 80, "SN": 70, "TF": 65, "JP": 55},
        "date": "2026-03-27"
    }
    
    # 测试比较
    result = compare_tests(test1, test2)
    print(f"类型变化: {result['type_change']}")
    print(f"EI变化: {result['dimension_changes']['EI']['label']}")
    print(f"SN变化: {result['dimension_changes']['SN']['label']}")
    
    assert result['type_change'] == "INFP → INFJ", "类型应该变化"
    
    print("\n✅ 比较功能测试通过!")
    return True

def test_6_status():
    """测试6: 状态查询"""
    print_test("状态查询")
    
    cleanup()
    
    # 1. 空闲状态
    print("\n[1] 空闲状态...")
    result = handle_status(CHAT_ID)
    print(f"消息: {result['message']}")
    assert "空闲" in result['message'], "应该显示空闲"
    
    # 2. 测试中状态
    print("\n[2] 测试中状态...")
    handle_message(CHAT_ID, "1")
    for i in range(5):
        handle_message(CHAT_ID, "A")
    
    result = handle_status(CHAT_ID)
    print(f"消息: {result['message']}")
    assert "测试进行中" in result['message'], "应该显示测试进行中"
    
    # 3. 清理
    handle_cancel(CHAT_ID)
    
    print("\n✅ 状态查询测试通过!")
    return True

def test_7_version_selection():
    """测试7: 版本选择"""
    print_test("版本选择")
    
    cleanup()
    
    versions = {"1": 70, "2": 93, "3": 144, "4": 200}
    
    for key, expected_count in versions.items():
        clear_user_state(CHAT_ID)
        print(f"\n测试版本 {key} ({expected_count}题)...")
        result = handle_message(CHAT_ID, key)
        state = get_user_state(CHAT_ID)
        actual_count = len(state.questions)
        print(f"  题目数: {actual_count}")
        assert actual_count == expected_count, f"应该是{expected_count}题"
    
    handle_cancel(CHAT_ID)
    
    print("\n✅ 版本选择测试通过!")
    return True

def run_all_tests():
    """运行所有测试"""
    print("\n" + "="*60)
    print("MBTI Guru v1.4 - 全面功能测试")
    print("="*60)
    
    tests = [
        ("版本选择", test_7_version_selection),
        ("自动保存", test_3_auto_save),
        ("保存和恢复", test_1_save_and_resume),
        ("中断", test_2_interrupt),
        ("状态查询", test_6_status),
        ("历史记录", test_4_history),
        ("比较功能", test_5_compare),
    ]
    
    results = []
    for name, test_func in tests:
        try:
            success = test_func()
            results.append((name, success))
        except Exception as e:
            print(f"\n❌ {name} 测试失败: {e}")
            results.append((name, False))
    
    # 清理
    cleanup()
    
    # 打印结果
    print("\n" + "="*60)
    print("测试结果汇总")
    print("="*60)
    
    for name, success in results:
        status = "✅ 通过" if success else "❌ 失败"
        print(f"  {name}: {status}")
    
    all_passed = all(r[1] for r in results)
    print("\n" + ("全部测试通过! 🎉" if all_passed else "有测试失败"))
    
    return all_passed

if __name__ == "__main__":
    success = run_all_tests()
    sys.exit(0 if success else 1)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)