Files
examination/docs/architecture.md
T
wonder 10a464eea4 feat: 添加 Topic-SubTopic 层级结构与 Accordion 侧边栏
- topics/index.json 重构为嵌套模型(topic → subtopics)
- 侧边栏改为 Accordion 交互:默认折叠、同时只展开一个
- 展开/折叠状态通过 localStorage 持久化
- 更新架构文档同步数据模型变更

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-02 19:10:14 +08:00

14 KiB
Raw Blame History

架构文档

1. 系统总览

┌─────────────────────────────────────────────────────┐
│                    用户浏览器                         │
│  ┌───────────────────────────────────────────────┐  │
│  │  index.html  (单文件 SPA)                     │  │
│  │  ┌─────────┐  ┌──────────┐  ┌─────────────┐  │  │
│  │  │ 导航栏   │  │ 做题区域  │  │ 导入模态框   │  │  │
│  │  └────┬────┘  └─────┬────┘  └──────┬──────┘  │  │
│  └───────┼─────────────┼──────────────┼──────────┘  │
└──────────┼─────────────┼──────────────┼─────────────┘
           │ fetch       │ fetch        │ JSON.parse
           ▼             ▼              ▼
┌─────────────────────────────────────────────────────┐
│  topics/  (数据层)                                   │
│  ├── index.json          ← 主题索引                  │
│  └── {topic-slug}/                                   │
│      ├── meta.json        ← 主题元信息(稳定)        │
│      ├── fill_blank.json  ← 填空题集                  │
│      └── single_choice.json ← 单选题集               │
└─────────────────────────────────────────────────────┘
           ▲
           │ git push / pull
           ▼
┌─────────────────────────────────────────────────────┐
│  Agent / LLM  (生成层)                               │
│  ├── Claude Code Skill (generate / validate)         │
│  ├── LLM + 提示词模板 (schema/prompt-template.md)    │
│  └── JSON Schema 校验 (schema/question.schema.json)  │
└─────────────────────────────────────────────────────┘

2. 目录结构

examination/
├── index.html                      # 前端入口(单文件 SPA)
├── topics/                         # 题目数据
│   ├── index.json                  # 主题索引
│   └── {topic-slug}/               # 主题目录
│       ├── meta.json               # 主题元信息
│       ├── fill_blank.json         # 填空题
│       ├── single_choice.json      # 单选题
│       ├── multiple_choice.json    # 多选题(可选)
│       ├── true_false.json         # 判断题(可选)
│       ├── short_answer.json       # 简答题(可选)
│       ├── code_reading.json       # 代码阅读题(可选)
│       └── scenario.json           # 场景分析题(可选)
├── schema/                         # Schema 与模板
│   ├── question.schema.json        # 题目 JSON Schema(Draft-07)
│   ├── prompt-template.md          # LLM 生成提示词模板
│   └── templates/                  # 各题型示例
│       ├── single_choice.json
│       ├── multiple_choice.json
│       ├── true_false.json
│       ├── fill_blank.json
│       ├── short_answer.json
│       ├── code_reading.json
│       └── scenario.json
├── docs/                           # 文档
│   ├── requirements.md             # 需求文档
│   └── architecture.md             # 架构文档(本文件)
├── .gitignore                      # 排除 .ref/
└── README.md

3. 数据模型

3.1 主题索引 — topics/index.json

{
  "version": "1.0.0",
  "updated": "2026-09-02",
  "topics": [
    {
      "slug": "qunar-ai-fullstack",
      "name": "去哪儿 AI 面 — AI 全栈方向",
      "description": "覆盖 JVM/GC、AI Agent 架构、工具链、幻觉治理等核心考察领域",
      "subtopics": [
        {
          "slug": "gc-jvm",
          "name": "GC / JVM / 三色标记",
          "description": "...",
          "path": "topics/gc-jvm",
          "stats": { "total": 20, "by_type": { "fill_blank": 10, "single_choice": 10 } }
        }
      ]
    }
  ]
}
  • topics[] — 顶层主题列表,每个主题是一个手风琴(Accordion)分区
    • slug / name / description — 主题标识与展示信息
    • subtopics[] — 子主题列表,隶属于该顶层主题
      • path — 子主题数据目录路径
      • stats — 题目数量统计
  • 前端以 Accordion 形式渲染:默认全部折叠,同时只展开一个

3.2 主题元信息 — topics/{slug}/meta.json

{
  "slug": "gc-jvm",
  "name": "GC / JVM / 三色标记",
  "description": "JVM 堆内存模型、GC 算法、三色标记法、垃圾收集器",
  "tags": ["jvm", "gc", "heap"],
  "difficulty_range": [1, 5],
  "schema_version": "1.0.0",
  "question_files": ["fill_blank", "single_choice"],
  "stats": {
    "total": 20,
    "by_type": { "fill_blank": 10, "single_choice": 10 }
  }
}
  • question_files 列出该主题包含的题型文件名(不含 .json 后缀)
  • 前端根据此列表动态加载对应的题目文件
  • stats 在每次生成/追加题目后由 Agent 更新

3.3 题目文件 — topics/{slug}/{type}.json

{
  "topic": "gc-jvm",
  "type": "fill_blank",
  "schema_version": "1.0.0",
  "generated": "2026-09-02T00:00:00Z",
  "questions": [ /* 题目数组 */ ]
}

每种题型一个文件,支持多 Agent 并行写入不同题型文件而无冲突。

3.4 题目对象

公共字段(所有题型必有)

字段 类型 必填 说明
id string ✅ 格式 {type_short}-{seq},如 fb-001
type enum ✅ 题型标识
difficulty integer ✅ 1-5
tags string[] ✅ 细粒度知识点标签
question string ✅ 题干
explanation string ✅ 解析
source string ❌ 来源
related string[] ❌ 关联题目 id

填空题 (fill_blank)

{
  "answer": ["老", "Old"],
  "answer_rule": "any"
}
  • answer: string[],可接受的答案列表
  • answer_rule: "any"(任一匹配) | "all"(全部匹配) | "ordered"(按序匹配)

单选题 (single_choice)

{
  "options": { "A": "选项A", "B": "选项B", "C": "选项C", "D": "选项D" },
  "answer": "B"
}

多选题 (multiple_choice)

{
  "options": { "A": "...", "B": "...", "C": "...", "D": "..." },
  "answer": ["A", "C"]
}

判断题 (true_false)

{ "answer": true }

简答题 (short_answer)

{
  "answer": "参考答案文本",
  "keywords": ["关键词1", "关键词2"],
  "scoring_rubric": "评分标准说明"
}

代码阅读题 (code_reading)

{
  "code": "int x = 1;\nprintf(\"%d\", x);",
  "language": "c",
  "sub_questions": [
    { "index": 1, "type": "single_choice", "question": "...", "options": {...}, "answer": "A", "explanation": "..." }
  ]
}

场景分析题 (scenario)

{
  "context": "一个电商系统在高峰期出现超卖问题...",
  "code": null,
  "sub_questions": [
    { "index": 1, "type": "short_answer", "question": "...", "answer": "...", "keywords": [...], "explanation": "..." }
  ]
}

3.5 ID 编码规则

{type_short}-{sequence}
   fb     -   001
type_short 题型
sc single_choice
mc multiple_choice
tf true_false
fb fill_blank
sa short_answer
cr code_reading
sn scenario

4. 前端架构

4.1 页面结构

┌──────────────────────────────────────────┐
│  Header: 🎯 CS 知识应试强化      [🌙/☀️] │
├────────────┬─────────────────────────────┤
│  Sidebar   │  Main Content               │
│            │                             │
│ ▼ 去哪儿.. │  ┌─ Tabs ─────────────────┐ │
│   · gc-jvm │  │ 全部 | 填空 | 选择 |.. │ │
│   · ai-so  │  └────────────────────────┘ │
│   · ai-mm  │  ┌─ Toolbar ──────────────┐ │
│ ▶ 其他主题 │  │ 重置 全部显示 提交全部  │ │
│            │  └────────────────────────┘ │
│            │  ┌─ Question Card ────────┐ │
│ ────────   │  │ #1  ★★★               │ │
│ 📥 导入    │  │ 题干 [____]            │ │
│            │  │ [检查] [显示答案]       │ │
│            │  │ 💡 解析...              │ │
│            │  └────────────────────────┘ │
└────────────┴─────────────────────────────┘
  ▼ = 展开中  ▶ = 折叠中(Accordion 模式)

4.2 数据流

页面加载
  │
  ▼
fetch topics/index.json ──→ 渲染侧边栏 Accordion(默认全折叠)
  │
  │ 用户展开某主题 + 点击子主题
  ▼
fetch topics/{slug}/meta.json ──→ 获取 question_files 列表
  │
  ▼
fetch topics/{slug}/{type}.json (并行) ──→ 合并 questions 数组
  │
  ▼
renderQuiz() ──→ 渲染 Tab 栏 + 按当前 Tab 过滤题目卡片
  │
  │ 用户交互(选择/输入/检查/显示答案/切换 Tab)
  ▼
answers / activeTab 状态更新 ──→ re-render

4.3 状态管理

// 题型配置(常量)
const QUESTION_TYPES = {
  fill_blank:      { label: '填空', color: '--type-fill_blank' },
  single_choice:   { label: '选择', color: '--type-single_choice' },
  // ...
};

// 应用状态
let topicGroups = [];        // 顶层主题列表(含 subtopics)
let expandedGroup = null;    // 当前展开的顶层主题 slug(Accordion)
let currentTopic = null;     // 当前选中的子主题 slug
let currentQuestions = [];   // 当前子主题的所有题目
let answers = {};            // qId → { value, correct, revealed }
let customQuestions = [];    // 通过导入功能临时添加的题目
let activeTab = 'all';      // 当前 Tab:'all' 或题型名称

4.4 题型配置与渲染

题型颜色通过 CSS 变量定义在 :root 中,新增题型只需:

  1. 在 QUESTION_TYPES 添加配置
  2. 在 :root 添加 --type-{name} 变量
  3. CSS 自动生效(通过 .q-card.type-{name} 规则)
题型 渲染方式 判定逻辑 边框颜色变量
fill_blank 题干中的 ______ 替换为 <input> matchFillAnswer() 根据 answer_rule 匹配 --type-fill_blank
single_choice 选项列表,radio 样式 value === answer --type-single_choice
multiple_choice 选项列表,checkbox 样式 数组比较 --type-multiple_choice
true_false 两个大按钮 value === answer --type-true_false
short_answer <textarea> 关键词匹配(辅助) --type-short_answer
code_reading 代码块 + 子题列表 按子题类型分别判定 --type-code_reading
scenario 场景描述 + 子题列表 按子题类型分别判定 --type-scenario

5. 生成工作流

5.1 非 Agent 环境(手动 LLM 对话)

  1. 打开 schema/prompt-template.md,复制提示词模板
  2. 替换 {topic_name}、{question_type}、{count} 等变量
  3. 将提示词发送给 LLM(ChatGPT / Claude 等)
  4. 获取 JSON 输出后,粘贴到对应题型文件中
  5. 更新 meta.json 的 stats 字段
  6. 刷新前端页面

5.2 Agent 环境(Claude Code Skill)

/examination generate --topic gc-jvm --type fill_blank --count 10
/examination validate --file topics/gc-jvm/fill_blank.json
/examination batch-generate --topic gc-jvm --types fill_blank,single_choice

Skill 内部流程:

  1. 读取 meta.json 获取主题上下文
  2. 调用 LLM 生成题目(使用 schema 约束)
  3. 校验输出是否符合 question.schema.json
  4. 写入对应题型文件
  5. 更新 meta.json 和 index.json 的 stats

5.3 多 Agent 并行生成

Agent 1 ──→ topics/gc-jvm/fill_blank.json
Agent 2 ──→ topics/gc-jvm/single_choice.json
Agent 3 ──→ topics/gc-jvm/multiple_choice.json

由于每种题型写入独立文件,多个 Agent 可并行生成同一主题的不同题型而无写冲突。

6. 扩展指南

新增主题

  1. 创建 topics/{new-slug}/ 目录
  2. 创建 meta.json
  3. 按题型创建题目 JSON 文件
  4. 更新 topics/index.json

新增题型

  1. 在 schema/question.schema.json 的 definitions.question.type.enum 中添加新类型
  2. 添加对应的 allOf 条件分支定义字段约束
  3. 在前端 typeLabel() 中添加映射
  4. 在前端 renderQuestionCard() 中添加渲染逻辑
  5. 在 schema/templates/ 中添加示例模板