292 lines
9.4 KiB
Markdown
292 lines
9.4 KiB
Markdown
# CS 知识应试强化 — 题目生成 Skill
|
||
|
||
从 `examination` 仓库生成符合 JSON Schema 的题目文件,自动校验、合并、更新元数据,推送到远程。
|
||
|
||
## 前置条件
|
||
|
||
- Python 3.8+(用于校验和合并脚本)
|
||
- SSH 访问 `git@47.121.181.112:222`(用于 clone/push)
|
||
- `topics/index.json` 包含子主题索引
|
||
|
||
## 仓库结构
|
||
|
||
```
|
||
examination/
|
||
├── schema/
|
||
│ ├── question.schema.json # 题目 JSON Schema (Draft-07)
|
||
│ ├── prompt-template.md # 题目生成提示词模板
|
||
│ └── templates/ # 各题型示例 JSON
|
||
├── topics/
|
||
│ ├── index.json # 主题索引
|
||
│ └── {group}/{subtopic}/
|
||
│ ├── meta.json # 子主题元信息
|
||
│ ├── fill_blank.json # 填空题
|
||
│ └── single_choice.json # 单选题
|
||
└── exam/ # 本 skill 目录
|
||
├── SKILL.md
|
||
├── schema/
|
||
│ └── question.schema.json
|
||
└── scripts/
|
||
├── validate.py
|
||
├── validate.mjs
|
||
├── merge_questions.py
|
||
├── update_meta.py
|
||
├── read_context.json
|
||
└── validate_and_merge.json
|
||
```
|
||
|
||
## 执行流程
|
||
|
||
> **重要:默认使用子代理(SubAgent)分批次处理。** 生成题目是 token 密集型任务,为避免主会话上下文爆炸,应将每批题目的生成工作委派给子代理执行。主会话负责规划、确认和汇总。
|
||
|
||
### 阶段 0:与用户确认方案
|
||
|
||
**在执行任何操作之前,必须先与用户确认以下信息。** 不要假设默认值。
|
||
|
||
1. **子主题(subtopic)**:读取仓库中的 `topics/index.json`,列出可用的子主题供用户选择。如果用户已经指定了子主题,验证其是否存在。
|
||
2. **题型与数量**:询问用户需要生成哪些题型、每种多少道。可以一次性给出建议(如「建议先生成 10 道单选题 + 5 道判断题」),但最终必须等用户确认。
|
||
3. **展示确认摘要**:在开始生成前,向用户展示如下摘要并等待确认:
|
||
|
||
```
|
||
📋 生成方案确认
|
||
━━━━━━━━━━━━━━
|
||
子主题:gc-jvm(JVM 垃圾回收)
|
||
分组: qunar-ai-fullstack
|
||
题型: 单选 × 10、判断 × 5
|
||
━━━━━━━━━━━━━━
|
||
确认后开始生成 ✅
|
||
```
|
||
|
||
用户确认后才进入下一阶段。
|
||
|
||
### 阶段 1:克隆仓库
|
||
|
||
```bash
|
||
REPO_DIR=$(mktemp -d)
|
||
git clone --depth 1 ssh://git@47.121.181.112:222/wonder/examination.git "$REPO_DIR"
|
||
```
|
||
|
||
### 阶段 2:读取上下文(子代理)
|
||
|
||
派发一个子代理,执行以下任务:
|
||
- 读取 `topics/index.json` 确认子主题存在
|
||
- 读取该子主题的 `meta.json`(获取 tags、已有题目信息)
|
||
- 读取同类型已有的题目文件(获取已有 ID,避免重复)
|
||
- 将汇总信息返回给主会话
|
||
|
||
使用本 skill 自带的批量脚本 `scripts/read_context.json` 可以一次完成上述读取:
|
||
|
||
```
|
||
run_tool_batch(
|
||
file_path="<skill_dir>/scripts/read_context.json",
|
||
args={
|
||
"topics_dir": "<REPO_DIR>/topics",
|
||
"group": "<用户确认的分组>",
|
||
"subtopic": "<用户确认的子主题>",
|
||
"question_type": "<用户确认的题型>"
|
||
}
|
||
)
|
||
```
|
||
|
||
### 阶段 3:生成题目(子代理)
|
||
|
||
> **这是 token 消耗最大的阶段,务必使用子代理处理。**
|
||
|
||
根据阶段 2 返回的上下文,**为每种题型分别派发子代理**来生成题目。每个子代理的任务是:
|
||
|
||
1. 根据子主题内容和已有题目,生成指定数量的新题目
|
||
2. 严格遵循 Schema 格式(见下方「题目格式规范」)
|
||
3. 将生成的 JSON 写入临时文件(如 `/tmp/exam_gen_{type}_{batch}.json`)
|
||
|
||
**子代理提示词模板:**
|
||
|
||
```
|
||
你是题目生成专家。请为子主题 "{subtopic_name}" 生成 {count} 道 {type_name} 题。
|
||
|
||
【上下文】
|
||
{从阶段 2 获取的 meta.json 和已有题目摘要}
|
||
|
||
【要求】
|
||
- 严格遵循 JSON Schema(见下方规范)
|
||
- ID 从 {next_id} 开始递增(如 fb-001, fb-002...)
|
||
- 难度分布在 1-5 之间,合理分布
|
||
- tags 参考子主题已有的 tags
|
||
- explanation 必须详细,解释为什么对/错
|
||
- source 填 null,related 填空数组
|
||
|
||
【输出】
|
||
将完整 JSON 写入文件 /tmp/exam_gen_{type}_{batch}.json,格式:
|
||
{
|
||
"topic": "{subtopic_slug}",
|
||
"type": "{question_type}",
|
||
"schema_version": "1.0.0",
|
||
"generated": "{ISO 8601 时间戳}",
|
||
"questions": [ ... ]
|
||
}
|
||
```
|
||
|
||
**如果题目数量较多(如 > 15 道),建议拆分成多个批次**,每批 5-10 道,分别派发子代理并行处理。
|
||
|
||
### 阶段 4:校验、合并、更新元数据(子代理)
|
||
|
||
所有批次的题目生成完成后,派发子代理执行校验与合并:
|
||
|
||
```
|
||
run_tool_batch(
|
||
file_path="<skill_dir>/scripts/validate_and_merge.json",
|
||
args={
|
||
"skill_dir": "<skill_dir>",
|
||
"topics_dir": "<REPO_DIR>/topics",
|
||
"group": "<group>",
|
||
"subtopic": "<subtopic>",
|
||
"question_type": "<question_type>",
|
||
"generated_file": "/tmp/exam_gen_{type}_{batch}.json"
|
||
}
|
||
)
|
||
```
|
||
|
||
如果是多批次生成,需要对每个批次依次执行合并,或者先合并各批次为一个临时文件,再统一合并到已有文件。
|
||
|
||
### 阶段 5:提交推送
|
||
|
||
```bash
|
||
cd "$REPO_DIR"
|
||
git add -A
|
||
git commit -m "feat: add {count} {type} questions for {subtopic}"
|
||
git push origin main
|
||
```
|
||
|
||
### 阶段 6:汇报结果
|
||
|
||
向用户展示生成结果摘要:
|
||
|
||
```
|
||
✅ 题目生成完成
|
||
━━━━━━━━━━━━━━━━━━━━━━
|
||
子主题:gc-jvm
|
||
新增: 单选 × 10、判断 × 5
|
||
累计: 35 题(单选 20、判断 10、填空 5)
|
||
提交: feat: add 15 questions for gc-jvm
|
||
━━━━━━━━━━━━━━━━━━━━━━
|
||
```
|
||
|
||
---
|
||
|
||
## 批量脚本参数
|
||
|
||
### read_context.json
|
||
|
||
| 参数 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| `topics_dir` | 仓库内 `topics/` 目录的绝对路径 | `/tmp/xxxxx/topics` |
|
||
| `group` | 主题分组名 | `qunar-ai-fullstack` |
|
||
| `subtopic` | 子主题 slug | `gc-jvm` |
|
||
| `question_type` | 题型 | `single_choice` |
|
||
|
||
### validate_and_merge.json
|
||
|
||
| 参数 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| `skill_dir` | 本 skill 目录的绝对路径 | `/home/.../skills/exam` |
|
||
| `topics_dir` | 同上 | 同上 |
|
||
| `group` | 同上 | 同上 |
|
||
| `subtopic` | 同上 | 同上 |
|
||
| `question_type` | 同上 | 同上 |
|
||
| `generated_file` | 生成的题目 JSON 文件绝对路径 | `/tmp/exam_gen_sc_1.json` |
|
||
|
||
## 批量执行失败处理
|
||
|
||
如果批量脚本执行失败:
|
||
|
||
1. 首先检查所有参数是否正确传入,尤其是 `args` 不能为空
|
||
2. 根据错误信息修正参数后重试
|
||
3. 如果脚本方式持续失败,回退到手动逐步执行(见下方「手动执行参考」)
|
||
4. 完成任务后告知用户:「批量执行遇到了问题,已手动完成。是否需要调整 skill 的批量脚本以便下次正常使用?」
|
||
|
||
---
|
||
|
||
## 手动执行参考
|
||
|
||
### 读取上下文
|
||
|
||
```bash
|
||
cat "$REPO_DIR/topics/index.json"
|
||
cat "$REPO_DIR/topics/{group}/{subtopic}/meta.json"
|
||
cat "$REPO_DIR/topics/{group}/{subtopic}/{type}.json" # 如果存在
|
||
```
|
||
|
||
### 校验
|
||
|
||
```bash
|
||
python3 <skill_dir>/scripts/validate.py /tmp/exam_generated.json
|
||
```
|
||
|
||
### 合并
|
||
|
||
```bash
|
||
python3 <skill_dir>/scripts/merge_questions.py \
|
||
"$REPO_DIR/topics/{group}/{subtopic}/{type}.json" \
|
||
/tmp/exam_generated.json \
|
||
/tmp/exam_merged.json
|
||
|
||
cp /tmp/exam_merged.json "$REPO_DIR/topics/{group}/{subtopic}/{type}.json"
|
||
```
|
||
|
||
### 更新元数据
|
||
|
||
```bash
|
||
python3 <skill_dir>/scripts/update_meta.py \
|
||
"$REPO_DIR/topics" {group} {subtopic} {type}
|
||
```
|
||
|
||
---
|
||
|
||
## 题目格式规范
|
||
|
||
ID 格式:`{type_short}-{seq}`,序号从已有最大值 +1 开始,三位数补零。
|
||
|
||
| 题型 | type | type_short | 关键字段 |
|
||
|------|------|------------|----------|
|
||
| 填空 | `fill_blank` | `fb` | `answer: string[]`, `answer_rule: "any"/"all"/"ordered"` |
|
||
| 单选 | `single_choice` | `sc` | `options: {A-D}`, `answer: string` |
|
||
| 多选 | `multiple_choice` | `mc` | `options: {A-D}`, `answer: string[]` |
|
||
| 判断 | `true_false` | `tf` | `answer: boolean` |
|
||
| 简答 | `short_answer` | `sa` | `answer: string`, `keywords: string[]`, `scoring_rubric: string` |
|
||
| 代码阅读 | `code_reading` | `cr` | `code`, `language`, `sub_questions[]` |
|
||
| 场景分析 | `scenario` | `sn` | `context`, `sub_questions[]` |
|
||
|
||
### 通用字段
|
||
|
||
每道题必须包含:
|
||
|
||
- `id` — 唯一标识,格式 `type_short-NNN`
|
||
- `type` — 题型枚举值
|
||
- `difficulty` — 1 到 5 的整数
|
||
- `tags` — 标签数组,参考子主题 meta.json
|
||
- `question` — 题目文本
|
||
- `explanation` — 详细解析
|
||
- `source` — 填 `null`
|
||
- `related` — 填空数组 `[]`
|
||
|
||
### sub_questions 规范
|
||
|
||
`code_reading` 和 `scenario` 题型使用 `sub_questions` 数组,每个子问题需要:
|
||
|
||
- `index` — 从 1 开始的序号
|
||
- `type` — 子问题的题型(通常是 `single_choice` 或 `short_answer`)
|
||
- `question` — 子问题文本
|
||
- `answer` — 答案
|
||
- `explanation` — 解析
|
||
- 选择题类子问题还需 `options` 字段
|
||
|
||
---
|
||
|
||
## 备注
|
||
|
||
- 校验脚本优先使用 `jsonschema` 库(Python),不可用时回退到基础校验
|
||
- Node.js 校验脚本(`validate.mjs`)为纯内置模块,无外部依赖,可作为备选
|
||
- 合并脚本自动去重(基于 question ID),不会覆盖已有题目
|
||
- 元数据更新脚本自动计算 `question_files` 和 `stats.by_type` 统计信息
|
||
- 所有脚本退出码:0=成功,1=有错误,2=用法错误
|
||
- Schema 文件已内置于 `<skill_dir>/schema/question.schema.json`,与仓库版本一致
|