Files
examination/docs/architecture.md
T
wonder 890d75cf00
Deploy Examination / deploy (push) Successful in 22s
feat: 添加复制题目提示词与导入页 AI 生成提示词模板
- 题目卡片新增「📋 复制题目」按钮,按题型组织提示词复制到剪贴板
- 导入弹窗新增可折叠的 AI 生成提示词区域,支持 7 种题型切换与复制
- 更新 architecture.md 和 requirements.md 文档

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-02 20:12:32 +08:00

402 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构文档
## 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`
```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`
```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`
```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`)
```json
{
"answer": ["老", "Old"],
"answer_rule": "any"
}
```
- `answer`: string[],可接受的答案列表
- `answer_rule`: `"any"`(任一匹配) | `"all"`(全部匹配) | `"ordered"`(按序匹配)
#### 单选题 (`single_choice`)
```json
{
"options": { "A": "选项A", "B": "选项B", "C": "选项C", "D": "选项D" },
"answer": "B"
}
```
#### 多选题 (`multiple_choice`)
```json
{
"options": { "A": "...", "B": "...", "C": "...", "D": "..." },
"answer": ["A", "C"]
}
```
#### 判断题 (`true_false`)
```json
{ "answer": true }
```
#### 简答题 (`short_answer`)
```json
{
"answer": "参考答案文本",
"keywords": ["关键词1", "关键词2"],
"scoring_rubric": "评分标准说明"
}
```
#### 代码阅读题 (`code_reading`)
```json
{
"code": "int x = 1;\nprintf(\"%d\", x);",
"language": "c",
"sub_questions": [
{ "index": 1, "type": "single_choice", "question": "...", "options": {...}, "answer": "A", "explanation": "..." }
]
}
```
#### 场景分析题 (`scenario`)
```json
{
"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
复制题目流程:
用户点击 [📋 复制题目] ──→ copyQuestionPrompt(qId)
──→ 按题型组织提示词(题干 + 选项/代码/子问题)
──→ navigator.clipboard.writeText() ──→ 按钮变为 ✅ 已复制
导入提示词流程:
用户打开导入弹窗 ──→ 点击 [📋 复制 AI 生成提示词]
──→ 选择题型 ──→ 预览提示词模板 ──→ 复制到剪贴板
──→ 粘贴到外部 AI ──→ 获取 JSON ──→ 粘贴回导入框
```
### 4.3 状态管理
```javascript
// 题型配置(常量)
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. 点击侧边栏底部「📥 导入题目」按钮
2. 展开「📋 复制 AI 生成提示词」区域,选择目标题型
3. 点击「复制提示词」,将包含完整 JSON Schema 的提示词复制到剪贴板
4. 将提示词粘贴到外部 AI(ChatGPT / Claude 等),按需修改主题、数量等占位符
5. 获取 AI 返回的 JSON 后,粘贴回导入弹窗的文本框
6. 点击「导入」,题目将临时加载到当前会话
**方式二:通过 schema 模板文件**
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/` 中添加示例模板