refactor(exam): SKILL 中文化,增加用户确认阶段,默认子代理分批处理
This commit is contained in:
+244
-86
@@ -1,20 +1,21 @@
|
||||
---
|
||||
name: exam
|
||||
description: "Generate, validate, and manage CS exam questions. Trigger: '生成题目', 'generate questions', 'validate', '添加题目', '/exam'"
|
||||
---
|
||||
|
||||
# CS 知识应试强化 — 题目生成 Skill
|
||||
|
||||
为 `topics/` 目录生成符合 JSON Schema 的题目文件。
|
||||
从 `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/ # 各题型示例
|
||||
│ ├── prompt-template.md # 题目生成提示词模板
|
||||
│ └── templates/ # 各题型示例 JSON
|
||||
├── topics/
|
||||
│ ├── index.json # 主题索引
|
||||
│ └── {group}/{subtopic}/
|
||||
@@ -23,111 +24,268 @@ examination/
|
||||
│ └── single_choice.json # 单选题
|
||||
└── exam/ # 本 skill 目录
|
||||
├── SKILL.md
|
||||
├── validate.py # Python 校验脚本
|
||||
└── validate.mjs # Node.js 校验脚本
|
||||
├── schema/
|
||||
│ └── question.schema.json
|
||||
└── scripts/
|
||||
├── validate.py
|
||||
├── validate.mjs
|
||||
├── merge_questions.py
|
||||
├── update_meta.py
|
||||
├── read_context.json
|
||||
└── validate_and_merge.json
|
||||
```
|
||||
|
||||
## 工作流
|
||||
## 执行流程
|
||||
|
||||
### 1. 确定目标
|
||||
> **重要:默认使用子代理(SubAgent)分批次处理。** 生成题目是 token 密集型任务,为避免主会话上下文爆炸,应将每批题目的生成工作委派给子代理执行。主会话负责规划、确认和汇总。
|
||||
|
||||
用户会说类似「生成填空题」「给 gc-jvm 加10道选择题」。你需要:
|
||||
### 阶段 0:与用户确认方案
|
||||
|
||||
1. 确认 **子主题**:读 `topics/index.json` 找到对应 slug 和 path
|
||||
2. 确认 **题型**:fill_blank / single_choice / multiple_choice / true_false / short_answer / code_reading / scenario
|
||||
3. 确认 **数量**:默认 10
|
||||
**在执行任何操作之前,必须先与用户确认以下信息。** 不要假设默认值。
|
||||
|
||||
如果用户没指定,主动询问。
|
||||
1. **子主题(subtopic)**:读取仓库中的 `topics/index.json`,列出可用的子主题供用户选择。如果用户已经指定了子主题,验证其是否存在。
|
||||
2. **题型与数量**:询问用户需要生成哪些题型、每种多少道。可以一次性给出建议(如「建议先生成 10 道单选题 + 5 道判断题」),但最终必须等用户确认。
|
||||
3. **展示确认摘要**:在开始生成前,向用户展示如下摘要并等待确认:
|
||||
|
||||
### 2. 读取上下文
|
||||
```
|
||||
📋 生成方案确认
|
||||
━━━━━━━━━━━━━━
|
||||
子主题:gc-jvm(JVM 垃圾回收)
|
||||
分组: qunar-ai-fullstack
|
||||
题型: 单选 × 10、判断 × 5
|
||||
━━━━━━━━━━━━━━
|
||||
确认后开始生成 ✅
|
||||
```
|
||||
|
||||
用户确认后才进入下一阶段。
|
||||
|
||||
### 阶段 1:克隆仓库
|
||||
|
||||
```bash
|
||||
# 查看子主题已有题目,避免重复
|
||||
cat topics/{group}/{subtopic}/meta.json
|
||||
cat topics/{group}/{subtopic}/{type}.json # 如果存在
|
||||
REPO_DIR=$(mktemp -d)
|
||||
git clone --depth 1 ssh://git@47.121.181.112:222/wonder/examination.git "$REPO_DIR"
|
||||
```
|
||||
|
||||
### 3. 生成题目
|
||||
### 阶段 2:读取上下文(子代理)
|
||||
|
||||
你(Claude)直接生成 JSON。遵循以下规则:
|
||||
派发一个子代理,执行以下任务:
|
||||
- 读取 `topics/index.json` 确认子主题存在
|
||||
- 读取该子主题的 `meta.json`(获取 tags、已有题目信息)
|
||||
- 读取同类型已有的题目文件(获取已有 ID,避免重复)
|
||||
- 将汇总信息返回给主会话
|
||||
|
||||
- 输出格式严格匹配 `schema/question.schema.json`
|
||||
- ID 格式:`{type_short}-{seq}`,如 `fb-001`,序号从已有最大值 +1 开始
|
||||
- type_short 映射:sc=single_choice, mc=multiple_choice, tf=true_false, fb=fill_blank, sa=short_answer, cr=code_reading, sn=scenario
|
||||
- difficulty 1-5,根据主题上下文合理分布
|
||||
- tags 使用子主题 meta.json 中的 tags 作为参考
|
||||
使用本 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 写入临时文件进行校验:
|
||||
|
||||
```bash
|
||||
# 写入临时文件
|
||||
cat > /tmp/exam_gen.json << 'ENDJSON'
|
||||
{ ...生成的JSON... }
|
||||
ENDJSON
|
||||
```
|
||||
|
||||
### 4. 校验
|
||||
|
||||
根据可用运行时选择其一:
|
||||
|
||||
```bash
|
||||
# Python (优先)
|
||||
python3 exam/validate.py /tmp/exam_gen.json
|
||||
|
||||
# Node.js (备选)
|
||||
node exam/validate.mjs /tmp/exam_gen.json
|
||||
```
|
||||
|
||||
- 校验通过 → 继续写入
|
||||
- 校验失败 → 修正 JSON 后重新校验,直到通过
|
||||
|
||||
### 5. 写入文件
|
||||
|
||||
校验通过后:
|
||||
|
||||
1. 如果目标题型文件已存在,读取现有 questions 数组,追加新题目(不覆盖旧题)
|
||||
2. 如果不存在,创建新文件
|
||||
3. 更新 `meta.json` 的 `question_files` 和 `stats`
|
||||
4. 更新 `topics/index.json` 中对应子主题的 `stats`
|
||||
|
||||
文件格式:
|
||||
|
||||
```json
|
||||
【输出】
|
||||
将完整 JSON 写入文件 /tmp/exam_gen_{type}_{batch}.json,格式:
|
||||
{
|
||||
"topic": "{subtopic-slug}",
|
||||
"topic": "{subtopic_slug}",
|
||||
"type": "{question_type}",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601}",
|
||||
"generated": "{ISO 8601 时间戳}",
|
||||
"questions": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 确认
|
||||
**如果题目数量较多(如 > 15 道),建议拆分成多个批次**,每批 5-10 道,分别派发子代理并行处理。
|
||||
|
||||
报告生成结果:题型、数量、文件路径。
|
||||
### 阶段 4:校验、合并、更新元数据(子代理)
|
||||
|
||||
提示用户在线查看:
|
||||
所有批次的题目生成完成后,派发子代理执行校验与合并:
|
||||
|
||||
> ✅ 已完成!前往 http://47.121.181.112:30000/ 查看新题目。
|
||||
```
|
||||
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:提交推送
|
||||
|
||||
- `exam/validate.py` — 优先用 `jsonschema`,不可用时回退到基础校验
|
||||
- `exam/validate.mjs` — 纯 Node.js 内置模块,无外部依赖
|
||||
```bash
|
||||
cd "$REPO_DIR"
|
||||
git add -A
|
||||
git commit -m "feat: add {count} {type} questions for {subtopic}"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
退出码:0=通过,1=有错误,2=用法错误。
|
||||
### 阶段 6:汇报结果
|
||||
|
||||
## 7 种题型速查
|
||||
向用户展示生成结果摘要:
|
||||
|
||||
| 题型 | type | 关键字段 |
|
||||
|------|------|----------|
|
||||
| 填空 | fill_blank | answer: string[], answer_rule: "any"/"all"/"ordered" |
|
||||
| 单选 | single_choice | options: {A-D}, answer: string |
|
||||
| 多选 | multiple_choice | options: {A-D}, answer: string[] |
|
||||
| 判断 | true_false | answer: boolean |
|
||||
| 简答 | short_answer | answer: string, keywords: string[], scoring_rubric: string |
|
||||
| 代码阅读 | code_reading | code, language, sub_questions[] |
|
||||
| 场景分析 | scenario | context, sub_questions[] |
|
||||
```
|
||||
✅ 题目生成完成
|
||||
━━━━━━━━━━━━━━━━━━━━━━
|
||||
子主题: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`,与仓库版本一致
|
||||
|
||||
Reference in New Issue
Block a user