commit 050c05437e7792b5d86003779fd7b4ba0cd110a7 Author: xiaoyan-bot Date: Fri Apr 10 15:24:57 2026 +0800 auto-sync: listening-drag 2026-04-10_15:24 diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..a410f1c --- /dev/null +++ b/SKILL.md @@ -0,0 +1,111 @@ +--- +name: listening-drag +description: K12英语听力拖拽题(Listening Drag & Drop)标准化设计、生产、审校工具。别名:听力Part1图片连线、听力Part1人物匹配,三者为同一题型。使用场景:(1) 根据给定知识点、难度等级、人名列表生成符合教研规范的听力拖拽题;(2) 校验听力拖拽题的格式、难度匹配、内容合规性;(3) 批量生产听力拖拽题内容。触发关键词:听力拖拽题、listening drag、拖拽题生产、听力Part1图片连线、听力Part1人物匹配、人物匹配题生产、图片连线题设计 +--- +# Listening Drag 听力拖拽题生产技能 +## 核心功能 +标准化生成符合L1 6个阶段要求的听力拖拽题【阶段归属:L1专属】,自动绑定对应能力标签,支持批量生产与合规校验。 +## 难度对应关系(已适配新的阶段划分) +| 原难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | +|------------|------------|------------------|----------| +| A级 | L1 第1-2阶段 | Starters 1-2 | 强视觉特征,简单句,低干扰 | +| B级 | L1 第3-4阶段 | Starters 3 | 细节特征,复合句,中干扰 | +| C级 | L1 第5-6阶段 | Movers 1 | 状态动作,描述性语言,高干扰 | +| D级 | L2 第1-2阶段 | Movers 2-3 | 情境推断,隐含逻辑,极高干扰 | +## 题型别名说明 +听力拖拽题 = 听力Part1图片连线 = 听力Part1人物匹配,三者为同一题型,规则完全通用。 +## 能力标签绑定规则 +1. 题型固定绑定通用能力标签:基础语境理解(场景/物品/动作识别)、多模态信息匹配、注意力分配 +2. 每道小题对应专属能力标签(与教研部统一定义的45个能力tag一一对应): + - Q1:显性事实理解(关键词识别) + - Q2:显性事实理解(关键词识别) + - Q3:多信息整合 + - Q4:干扰抑制 / 语用推断(否定与纠错) +3. 生产时自动根据题目类型填充对应能力标签,无需手动配置 +## 生产流程 +1. 接收输入参数:{knowledge_module}(知识点列表)、{difficulty}(难度等级A/B/C/D)、{name_list}(人名列表)、{name_list_shuffle}(打乱后的人名列表) +2. 读取共享L1词库(路径:`business_knowledge/L1_word_list.json`),优先使用库中词汇,超纲词汇自动拦截提示 +3. 按难度对应关系校验阶段匹配性,超纲内容自动提示 +4. 生成符合难度要求的听力文本、图片描述、任务描述、答案、能力项 +5. 自动执行4层校验:词汇超纲校验、格式合规性校验、难度匹配校验、内容逻辑校验 +6. 输出标准格式的题目内容 +## 脚本使用说明 +执行`scripts/generate_listening_drag.py`即可完成自动化生产: +```bash +python scripts/generate_listening_drag.py \ + --knowledge "blue adj. 蓝色的;red adj. 红色的;green adj. 绿色的" \ + --difficulty "A" \ + --names "Peter,Sue,Julia,Dan,May" \ + --shuffled_names "Julia,Sue,May,Peter,Dan" +``` +> 🔘 词汇校验规则:脚本会自动校验所有知识点是否在共享L1词库中,禁止使用超纲词汇,无需单独传入或保存词表文件。 +## 输出格式规范 +严格按照以下格式输出,禁止添加额外解释内容: +``` +【听力文本】 +(对话要求自然日常,避免统一使用祈使句,可使用陈述句、疑问句等多种形式,示例参考:) +One - +Girl: Look! The girl with the red shirt is happy. +Boy: Yes. That’s Julia. +Two - +Girl: Who is the girl in the yellow skirt? +Boy: That’s Sue. +Three - +Girl: The girl with the blue watch is pretty. +Boy: That’s May. +Four - +Girl: Is the boy in the white shirt Dan? +Boy: No, sorry. That’s Peter. + +(通用规则:Q4必须包含纠错/否定逻辑,如No, sorry/I mean等表述) + +【图片描述】 +风格: 卡通,简单线条 +场景:[场景名称] + [人物描述] - [动作/特征] ; + [人物描述] - [动作/特征] ; + [人物描述] - [动作/特征] ; + [人物描述] - [动作/特征] ; + [人物描述] - [动作/特征] 。 +【人物顺序】 +[按图片P1-P5顺序排列的人名列表,例如:['Kim', 'Bill', 'Sue', 'Nick', 'Mary']] +【选项排序】 +[打乱后的选项人名列表,与输入的shuffled_names一致,例如:['Bill', 'Mary', 'Nick', 'Kim', 'Sue']] +【任务描述】 +You will hear [角色1] and [角色2] talking about xxx. Drag the names to the correct people. + +【选项数量】 +5 + +【答案】 +[4个答案索引,逗号分隔,例如03,01,00,04] + +【能力项】 +题目1 - 显性事实理解(关键词识别)- [该题考察的核心知识点关键词,例如:blue T-shirt] +题目2 - 显性事实理解(关键词识别)- [该题考察的核心知识点关键词,例如:red dress] +题目3 - 多信息整合 - [该题考察的组合知识点,例如:pink hairband + white skirt] +题目4 - 干扰抑制 / 语用推断(否定与纠错)- [该题考察的核心知识点,例如:green cap] +``` +## 校验规则 +自动校验不通过时直接返回错误原因: +1. 难度匹配错误:知识点超出对应阶段大纲范围 +2. 格式错误:缺少必填字段或格式不符合规范 +3. 逻辑错误:听力文本与图片描述不对应、答案错误 +## 题目解析规则(标准化输出) +### 通用规范 +1. 结构固定:能力点说明 + 做题技巧 + 正向鼓励 +2. 长度控制:80-100字,中文为主,核心关键词可标注英文 +3. 语气正向:避免负面表述,突出技巧可复制性 +### 结构模板 +> ✅ 本题考察【对应能力标签】能力。听的时候抓住人物的关键特征「核心关键词」,对应到正确人名即可。记住这个小技巧,下次碰到这类题就能快速做对哦😉 +### 题型示例 +``` +【题目1】 +【听力文本】 +Daisy: Look! The boy in the blue shirt is eating cake. +Jim: Yes. That’s Peter. +【能力项】 +题目1 - 显性事实理解(关键词识别)- blue shirt +【解析】 +✅ 本题考察【显性事实理解(关键词识别)】能力。听的时候抓住关键特征`blue shirt`,对应穿蓝色衬衫的男孩就是Peter,直接匹配即可。记住抓关键特征就能快速做对哦😉 +``` diff --git a/references/ability_tags.md b/references/ability_tags.md new file mode 100644 index 0000000..d5fe6ea --- /dev/null +++ b/references/ability_tags.md @@ -0,0 +1,33 @@ +# 听力拖拽题能力标签对应表 +## 题型所属分类 +听力拖拽题 = 听力Part1图片连线 = 听力Part1人物匹配 = L1 - Movers - 听力 Part1 人物匹配,三者为完全等价的同一题型,规则通用。 +## 题型固定绑定核心能力标签(与45个能力tag一一对应) +| 能力标签ID(预留) | 能力标签名称 | 能力描述 | +|--------------------|--------------|----------| +| TAG_LISTEN_001 | 基础语境理解 | 场景/物品/动作识别,结合听觉和视觉信息完成人物匹配 | +| TAG_LISTEN_002 | 多模态信息匹配 | 同步处理音频输入与图片视觉信息 | +| TAG_LISTEN_003 | 注意力分配 | 同时处理多组人物特征信息,排除无关干扰 | +## 小题专属能力标签(与教研统一定义对齐) +| 题号 | 能力标签ID(预留) | 能力标签名称 | 能力描述 | +|------|--------------------|--------------|----------| +| Q1 | TAG_LISTEN_004 | 显性事实理解 | 关键词识别,直接抓取听力中的核心名词/形容词 | +| Q2 | TAG_LISTEN_004 | 显性事实理解 | 关键词识别,直接抓取听力中的核心名词/形容词 | +| Q3 | TAG_LISTEN_005 | 多信息整合 | 结合2条及以上特征信息从干扰组中筛选正确答案 | +| Q4 | TAG_LISTEN_006 / TAG_LISTEN_007 | 干扰抑制 / 语用推断 | 多信息筛选排除干扰项/理解对话中的否定、纠错逻辑 | +--- +## 全题型能力标签映射参考表(官方统一) +| 题型分类 | 能力标签 | +|----------|----------| +| L1 - Starters - 听力 Part1 图片连线 | 显性事实理解(关键词识别);基础语境理解(场景/物品/动作识别) | +| L1 - Starters - 听力 Part2 信息填写 | 显性细节理解(数字/时间/地点) | +| L1 - Starters - 听力 Part3 三选一图片选择 | 显性事实理解(单句信息点抓取) | +| L1 - Starters - 听力 Part4 涂色+位置 | 显性事实理解(关键词识别) | +| L1 - Movers - 听力 Part1 人物匹配(本技能对应题型) | 基础语境理解(场景/物品/动作识别) | +| L1 - Movers - 听力 Part2 表格填写 | 显性细节理解(数字/时间/地点);干扰抑制(多信息筛选) | +| L1 - Movers - 听力 Part3 星期活动匹配 | 目的/偏好识别(显性to/for/like) | +| L1 - Movers - 听力 Part4 三选一 | 同义替换识别(词/短语级);目的/偏好识别(显性to/for/like) | +| L1 - Movers(高阶) - 听力 Flyers Part2 表格填写 | 干扰抑制(多信息筛选) | +| L1 - Movers(高阶) - 听力 Flyers Part3 长对话选择 | 多句保持(信息整合) | +| L1 - Movers(高阶) - 听力 Flyers Part4 三选一 | 语用推断(否定与纠错) | +| L1 - Movers(高阶) - 听力 Flyers Part5 信息匹配 | 目的/偏好识别(显性to/for/like) | +| L1 - Movers(高阶) - 听力 KET Part3 长对话选择 | 情绪/态度理解 | diff --git a/references/difficulty_mapping.md b/references/difficulty_mapping.md new file mode 100644 index 0000000..765afa0 --- /dev/null +++ b/references/difficulty_mapping.md @@ -0,0 +1,15 @@ +# 听力拖拽题难度对应表 +## ABCD等级与L1阶段、剑桥考试对应关系 +| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 适用学生水平 | +|----------|------------|------------------|--------------| +| A级 | L1 第1-2阶段 | Starters 1-2 | 入门级,接触英语1-2个月 | +| B级 | L1 第3-4阶段 | Starters 3 | 入门级,接触英语3-4个月 | +| C级 | L1 第5-6阶段 | Movers 1 | 进阶级,接触英语5-6个月 | +| D级 | L2 第1-2阶段 | Movers 2-3 | 进阶级,接触英语7-8个月 | +## 各难度生产约束规则 +| 难度等级 | 单句最大单词数 | 词汇范围限制 | 干扰项差异要求 | +|----------|----------------|--------------|----------------| +| A级 | ≤5个单词 | 仅允许L1第1-2单元大纲词汇 | 差异非常明显(颜色/大类别区分) | +| B级 | ≤8个单词 | 仅允许L1第3-4单元大纲词汇 | 差异为细节(花纹/方位区分) | +| C级 | ≤12个单词 | 允许L1全阶段大纲词汇 | 差异为动作/状态(表情/动作区分) | +| D级 | ≤15个单词 | 允许L1+L2前2单元大纲词汇 | 差异为逻辑推断(因果/情境区分) | diff --git a/references/l1_word_list.json b/references/l1_word_list.json new file mode 100644 index 0000000..bb68948 --- /dev/null +++ b/references/l1_word_list.json @@ -0,0 +1 @@ +{"update_time": "2026-03-26", "total_count": 980, "words": [{"word": "red", "pos": "adj", "meaning": "红色的", "level": "LV1(上)", "unit": "S0-U0-L1", "difficulty": 1, "enabled": true}, {"word": "hat", "pos": "n", "meaning": "帽子", "level": "LV1(上)", "unit": "S0-U0-L1", "difficulty": 1, "enabled": true}, {"word": "blue", "pos": "adj", "meaning": "蓝的", "level": "LV1(上)", "unit": "S0-U0-L1", "difficulty": 1, "enabled": true}, {"word": "bag", "pos": "n", "meaning": "袋,包", "level": "LV1(上)", "unit": "S0-U0-L1", "difficulty": 1, "enabled": true}, {"word": "orange", "pos": "adj", "meaning": "橙色的", "level": "LV1(上)", "unit": "S0-U0-L3", "difficulty": 1, "enabled": true}, {"word": "green", "pos": "adj", "meaning": "绿色的", "level": "LV1(上)", "unit": "S0-U0-L2", "difficulty": 1, "enabled": true}, {"word": "dress", "pos": "n", "meaning": "连衣裙", "level": "LV1(上)", "unit": "S0-U0-L2", "difficulty": 1, "enabled": true}, {"word": "there", "pos": "adv", "meaning": "那里", "level": "LV1(上)", "unit": "S0-U0-L3", "difficulty": 1, "enabled": true}, {"word": "put", "pos": "v", "meaning": "放", "level": "LV1(上)", "unit": "S0-U0-L3", "difficulty": 1, "enabled": true}, {"word": "purple", "pos": "adj", "meaning": "紫色的", "level": "LV1(上)", "unit": "S0-U0-L5", "difficulty": 1, "enabled": true}, {"word": "give", "pos": "v", "meaning": "给", "level": "LV1(上)", "unit": "S0-U0-L3", "difficulty": 1, "enabled": true}, {"word": "water", "pos": "n", "meaning": "水", "level": "LV1(上)", "unit": "S0-U0-L4", "difficulty": 1, "enabled": true}, {"word": "here", "pos": "adv", "meaning": "在这里", "level": "LV1(上)", "unit": "S0-U0-L4", "difficulty": 1, "enabled": true}, {"word": "dirty", "pos": "adj", "meaning": "肮脏的", "level": "LV1(上)", "unit": "S0-U0-L4", "difficulty": 1, "enabled": true}, {"word": "clean", "pos": "v", "meaning": "清洁", "level": "LV1(上)", "unit": "S0-U0-L4", "difficulty": 1, "enabled": true}, {"word": "pink", "pos": "adj", "meaning": "粉红色的", "level": "LV1(上)", "unit": "S0-U0-L2", "difficulty": 1, "enabled": true}, {"word": "get", "pos": "v", "meaning": "收到", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "wall", "pos": "n", "meaning": "墙", "level": "LV1(上)", "unit": "S1-U1-L1", "difficulty": 1, "enabled": true}, {"word": "table", "pos": "n", "meaning": "桌子", "level": "LV1(上)", "unit": "S1-U1-L1", "difficulty": 1, "enabled": true}, {"word": "floor", "pos": "n", "meaning": "地板", "level": "LV1(上)", "unit": "S1-U1-L1", "difficulty": 1, "enabled": true}, {"word": "hand", "pos": "n", "meaning": "手", "level": "LV1(上)", "unit": "S1-U1-L2", "difficulty": 1, "enabled": true}, {"word": "foot", "pos": "n", "meaning": "脚", "level": "LV1(上)", "unit": "S1-U1-L2", "difficulty": 1, "enabled": true}, {"word": "black", "pos": "adj", "meaning": "黑的", "level": "LV1(上)", "unit": "S1-U1-L2", "difficulty": 1, "enabled": true}, {"word": "tomato", "pos": "n", "meaning": "西红柿", "level": "LV1(上)", "unit": "S1-U1-L3", "difficulty": 1, "enabled": true}, {"word": "fish", "pos": "n", "meaning": "鱼", "level": "LV1(上)", "unit": "S1-U1-L3", "difficulty": 1, "enabled": true}, {"word": "chicken", "pos": "n", "meaning": "鸡肉", "level": "LV1(上)", "unit": "S1-U1-L3", "difficulty": 1, "enabled": true}, {"word": "nose", "pos": "n", "meaning": "鼻子", "level": "LV1(上)", "unit": "S1-U1-L4", "difficulty": 1, "enabled": true}, {"word": "eye", "pos": "n", "meaning": "眼睛", "level": "LV1(上)", "unit": "S1-U1-L4", "difficulty": 1, "enabled": true}, {"word": "brown", "pos": "adj", "meaning": "棕色的", "level": "LV1(上)", "unit": "S1-U1-L4", "difficulty": 1, "enabled": true}, {"word": "yellow", "pos": "adj", "meaning": "黄色的", "level": "LV1(上)", "unit": "S1-U1-L5", "difficulty": 1, "enabled": true}, {"word": "white", "pos": "adj", "meaning": "白色的", "level": "LV1(上)", "unit": "S1-U1-L5", "difficulty": 1, "enabled": true}, {"word": "go", "pos": "v", "meaning": "去,走", "level": "LV1(上)", "unit": "S1-U2-L1", "difficulty": 1, "enabled": true}, {"word": "dog", "pos": "n", "meaning": "狗", "level": "LV1(上)", "unit": "S1-U2-L1", "difficulty": 1, "enabled": true}, {"word": "come", "pos": "v", "meaning": "来", "level": "LV1(上)", "unit": "S1-U2-L1", "difficulty": 1, "enabled": true}, {"word": "cat", "pos": "n", "meaning": "猫", "level": "LV1(上)", "unit": "S1-U2-L1", "difficulty": 1, "enabled": true}, {"word": "pie", "pos": "n", "meaning": "馅饼", "level": "LV1(上)", "unit": "S1-U2-L2", "difficulty": 1, "enabled": true}, {"word": "can", "pos": "v", "meaning": "能,会", "level": "LV1(上)", "unit": "S1-U2-L4", "difficulty": 1, "enabled": true}, {"word": "open", "pos": "v", "meaning": "打开", "level": "LV1(上)", "unit": "S1-U2-L5", "difficulty": 1, "enabled": true}, {"word": "happy", "pos": "adj", "meaning": "快乐的", "level": "LV1(上)", "unit": "S1-U2-L5", "difficulty": 1, "enabled": true}, {"word": "see", "pos": "v", "meaning": "看见", "level": "LV1(上)", "unit": "S1-U3-L1", "difficulty": 1, "enabled": true}, {"word": "1", "pos": "num", "meaning": "1", "level": "LV1(上)", "unit": "S1-U3-L1", "difficulty": 1, "enabled": true}, {"word": "paper", "pos": "n", "meaning": "纸", "level": "LV1(上)", "unit": "S1-U3-L2", "difficulty": 1, "enabled": true}, {"word": "book", "pos": "n", "meaning": "书", "level": "LV1(上)", "unit": "S1-U3-L2", "difficulty": 1, "enabled": true}, {"word": "3", "pos": "num", "meaning": "3", "level": "LV1(上)", "unit": "S1-U3-L2", "difficulty": 1, "enabled": true}, {"word": "2", "pos": "num", "meaning": "2", "level": "LV1(上)", "unit": "S1-U3-L2", "difficulty": 1, "enabled": true}, {"word": "pen", "pos": "n", "meaning": "钢笔", "level": "LV1(上)", "unit": "S1-U3-L3", "difficulty": 1, "enabled": true}, {"word": "box", "pos": "n", "meaning": "盒子", "level": "LV1(上)", "unit": "S1-U3-L3", "difficulty": 1, "enabled": true}, {"word": "4", "pos": "num", "meaning": "4", "level": "LV1(上)", "unit": "S1-U3-L3", "difficulty": 1, "enabled": true}, {"word": "6", "pos": "num", "meaning": "6", "level": "LV1(上)", "unit": "S1-U3-L4", "difficulty": 1, "enabled": true}, {"word": "5", "pos": "num", "meaning": "5", "level": "LV1(上)", "unit": "S1-U3-L4", "difficulty": 1, "enabled": true}, {"word": "sing", "pos": "v", "meaning": "唱", "level": "LV1(上)", "unit": "S1-U4-L1", "difficulty": 1, "enabled": true}, {"word": "eat", "pos": "v", "meaning": "吃", "level": "LV1(上)", "unit": "S1-U4-L2", "difficulty": 1, "enabled": true}, {"word": "7", "pos": "num", "meaning": "7", "level": "LV1(上)", "unit": "S1-U4-L2", "difficulty": 1, "enabled": true}, {"word": "rug", "pos": "n", "meaning": "小地毯", "level": "LV1(上)", "unit": "S1-U4-L3", "difficulty": 1, "enabled": true}, {"word": "door", "pos": "n", "meaning": " 门", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "bed", "pos": "n", "meaning": "床", "level": "LV1(上)", "unit": "S1-U4-L3", "difficulty": 1, "enabled": true}, {"word": "9", "pos": "num", "meaning": "9", "level": "LV1(上)", "unit": "S1-U4-L4", "difficulty": 1, "enabled": true}, {"word": "8", "pos": "num", "meaning": "8", "level": "LV1(上)", "unit": "S1-U4-L4", "difficulty": 1, "enabled": true}, {"word": "10", "pos": "num", "meaning": "10", "level": "LV1(上)", "unit": "S1-U4-L5", "difficulty": 1, "enabled": true}, {"word": "zoo", "pos": "n", "meaning": "动物园", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "zebra", "pos": "n", "meaning": "斑马", "level": "LV1(上)", "unit": "S1-U7-L2", "difficulty": 1, "enabled": true}, {"word": "young", "pos": "adj", "meaning": "年轻的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "year", "pos": "n", "meaning": "年", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "write", "pos": "v", "meaning": "写", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "woman", "pos": "n", "meaning": "女人", "level": "LV1(上)", "unit": "S1-U5-L1", "difficulty": 1, "enabled": true}, {"word": "window", "pos": "n", "meaning": "窗户", "level": "LV1(上)", "unit": "S1-U12-L4", "difficulty": 1, "enabled": true}, {"word": "warm", "pos": "adj", "meaning": "温暖的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "want", "pos": "v", "meaning": "想要", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "walk", "pos": "v", "meaning": "走", "level": "LV1(上)", "unit": "S1-U7-L2", "difficulty": 1, "enabled": true}, {"word": "very", "pos": "adv", "meaning": "非常", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "under", "pos": "prep", "meaning": "在...下面", "level": "LV1(上)", "unit": "S2-U14-L5", "difficulty": 1, "enabled": true}, {"word": "try", "pos": "n", "meaning": "尝试", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "try", "pos": "v", "meaning": "尝试", "level": "LV1(上)", "unit": "S1-U12-L5", "difficulty": 1, "enabled": true}, {"word": "tree", "pos": "n", "meaning": "树", "level": "LV1(上)", "unit": "S1-U7-L1", "difficulty": 1, "enabled": true}, {"word": "train", "pos": "n", "meaning": "火车", "level": "LV1(上)", "unit": "S1-U8-L1", "difficulty": 1, "enabled": true}, {"word": "too", "pos": "adv", "meaning": "也", "level": "LV1(上)", "unit": "S1-U12-L2", "difficulty": 1, "enabled": true}, {"word": "tomorrow", "pos": "adv", "meaning": "明天", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "tomorrow", "pos": "n", "meaning": "明天", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "today", "pos": "adv", "meaning": "今天", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "today", "pos": "n", "meaning": "今天", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "tiger", "pos": "n", "meaning": "老虎", "level": "LV1(上)", "unit": "S1-U5-L3", "difficulty": 1, "enabled": true}, {"word": "those", "pos": "det", "meaning": "那些", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "those", "pos": "pron", "meaning": "那些", "level": "LV1(上)", "unit": "S1-U12-L1", "difficulty": 1, "enabled": true}, {"word": "this", "pos": "det", "meaning": "这个", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "this", "pos": "pron", "meaning": "这个", "level": "LV1(上)", "unit": "S1-U12-L1", "difficulty": 1, "enabled": true}, {"word": "these", "pos": "det", "meaning": "这些", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "these", "pos": "pron", "meaning": "这些", "level": "LV1(上)", "unit": "S1-U12-L1", "difficulty": 1, "enabled": true}, {"word": "that", "pos": "det", "meaning": "那个", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "that", "pos": "pron", "meaning": "那个", "level": "LV1(上)", "unit": "S1-U12-L1", "difficulty": 1, "enabled": true}, {"word": "tell", "pos": "v", "meaning": "告诉", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "teacher", "pos": "n", "meaning": "老师", "level": "LV1(上)", "unit": "S2-U14-L2", "difficulty": 1, "enabled": true}, {"word": "talk", "pos": "v", "meaning": "说话", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "swim", "pos": "v", "meaning": "游泳", "level": "LV1(上)", "unit": "S1-U11-L5", "difficulty": 1, "enabled": true}, {"word": "sun", "pos": "n", "meaning": "太阳", "level": "LV1(上)", "unit": "S1-U8-L2", "difficulty": 1, "enabled": true}, {"word": "study", "pos": "v", "meaning": "学习", "level": "LV1(上)", "unit": "S2-U14-L1", "difficulty": 1, "enabled": true}, {"word": "student", "pos": "n", "meaning": "学生", "level": "LV1(上)", "unit": "S2-U14-L1", "difficulty": 1, "enabled": true}, {"word": "speak", "pos": "v", "meaning": "说", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "some", "pos": "det", "meaning": "一些", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "sofa", "pos": "n", "meaning": "沙发", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "sock", "pos": "n", "meaning": "袜子", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "small", "pos": "adj", "meaning": "小的", "level": "LV1(上)", "unit": "S1-U9-L1", "difficulty": 1, "enabled": true}, {"word": "sleep", "pos": "v", "meaning": "睡觉", "level": "LV1(上)", "unit": "S1-U7-L4", "difficulty": 1, "enabled": true}, {"word": "skirt", "pos": "n", "meaning": "裙子", "level": "LV1(上)", "unit": "S1-U9-L4", "difficulty": 1, "enabled": true}, {"word": "sit", "pos": "v", "meaning": "坐下", "level": "LV1(上)", "unit": "S1-U8-L1", "difficulty": 1, "enabled": true}, {"word": "sister", "pos": "n", "meaning": "姐姐、妹妹", "level": "LV1(上)", "unit": "S1-U8-L1", "difficulty": 1, "enabled": true}, {"word": "short", "pos": "adj", "meaning": "短的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "shoe", "pos": "n", "meaning": "鞋", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "shirt", "pos": "n", "meaning": "衬衫", "level": "LV1(上)", "unit": "S1-U9-L1", "difficulty": 1, "enabled": true}, {"word": "ship", "pos": "n", "meaning": "船", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "sheep", "pos": "n", "meaning": "绵羊", "level": "LV1(上)", "unit": "S1-U5-L3", "difficulty": 1, "enabled": true}, {"word": "school", "pos": "n", "meaning": "学校", "level": "LV1(上)", "unit": "S2-U14-L1", "difficulty": 1, "enabled": true}, {"word": "say", "pos": "v", "meaning": "说", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "sad", "pos": "adj", "meaning": "悲伤的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "run", "pos": "v", "meaning": "跑", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "ruler", "pos": "n", "meaning": "尺子", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "room", "pos": "n", "meaning": "房间", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "rice", "pos": "n", "meaning": "米饭", "level": "LV1(上)", "unit": "S1-U6-L4", "difficulty": 1, "enabled": true}, {"word": "read", "pos": "v", "meaning": "阅读", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "radio", "pos": "n", "meaning": "收音机", "level": "LV1(上)", "unit": "S1-U9-L2", "difficulty": 1, "enabled": true}, {"word": "potato", "pos": "n", "meaning": "土豆", "level": "LV1(上)", "unit": "S1-U7-L3", "difficulty": 1, "enabled": true}, {"word": "play", "pos": "v", "meaning": "玩", "level": "LV1(上)", "unit": "S2-U14-L1", "difficulty": 1, "enabled": true}, {"word": "plane", "pos": "n", "meaning": "飞机", "level": "LV1(上)", "unit": "S1-U8-L3", "difficulty": 1, "enabled": true}, {"word": "pig", "pos": "n", "meaning": "猪", "level": "LV1(上)", "unit": "S1-U7-L4", "difficulty": 1, "enabled": true}, {"word": "phone", "pos": "n", "meaning": "电话", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "phone", "pos": "v", "meaning": "给…...打电话", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "people", "pos": "n", "meaning": "人", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "pencil", "pos": "n", "meaning": "铅笔", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "pea", "pos": "n", "meaning": "豌豆", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "PE", "pos": "n", "meaning": "体育", "level": "LV1(上)", "unit": "S1-U10-L4", "difficulty": 1, "enabled": true}, {"word": "paper", "pos": "adj", "meaning": "纸质的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "paint", "pos": "n", "meaning": "绘画颜料", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "paint", "pos": "v", "meaning": "(用颜料)绘画", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "orange", "pos": "n", "meaning": "橙子", "level": "LV1(上)", "unit": "S1-U10-L3", "difficulty": 1, "enabled": true}, {"word": "open", "pos": "adj", "meaning": "开着的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "one", "pos": "det", "meaning": "一个", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "one", "pos": "pron", "meaning": "一个人(或物)", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "old", "pos": "adj", "meaning": "年老的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "now", "pos": "adv", "meaning": "现在", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "night", "pos": "n", "meaning": "夜晚", "level": "LV1(上)", "unit": "S1-U12-L5", "difficulty": 1, "enabled": true}, {"word": "nice", "pos": "adj", "meaning": "好的", "level": "LV1(上)", "unit": "S1-U6-L3", "difficulty": 1, "enabled": true}, {"word": "new", "pos": "adj", "meaning": "新的", "level": "LV1(上)", "unit": "S1-U9-L2", "difficulty": 1, "enabled": true}, {"word": "mum", "pos": "n", "meaning": "妈妈", "level": "LV1(上)", "unit": "S1-U8-L4", "difficulty": 1, "enabled": true}, {"word": "mouth", "pos": "n", "meaning": "嘴巴", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "mother", "pos": "n", "meaning": "母亲", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "morning", "pos": "n", "meaning": "早上", "level": "LV1(上)", "unit": "S1-U12-L5", "difficulty": 1, "enabled": true}, {"word": "month", "pos": "n", "meaning": "月", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "monkey", "pos": "n", "meaning": "猴子", "level": "LV1(上)", "unit": "S1-U7-L1", "difficulty": 1, "enabled": true}, {"word": "minute", "pos": "n", "meaning": "分钟", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "mine", "pos": "pron", "meaning": "我的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "milk", "pos": "n", "meaning": "牛奶", "level": "LV1(上)", "unit": "S1-U5-L2", "difficulty": 1, "enabled": true}, {"word": "men", "pos": "n", "meaning": "男人(man 的复数)", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "meat", "pos": "n", "meaning": "肉类", "level": "LV1(上)", "unit": "S1-U10-L3", "difficulty": 1, "enabled": true}, {"word": "me", "pos": "pron", "meaning": "我", "level": "LV1(上)", "unit": "S1-U12-L2", "difficulty": 1, "enabled": true}, {"word": "mat", "pos": "n", "meaning": "地垫", "level": "LV1(上)", "unit": "S2-U14-L4", "difficulty": 1, "enabled": true}, {"word": "many", "pos": "det", "meaning": "许多", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "man", "pos": "n", "meaning": "男人", "level": "LV1(上)", "unit": "S1-U5-L1", "difficulty": 1, "enabled": true}, {"word": "make", "pos": "v", "meaning": "制造", "level": "LV1(上)", "unit": "S1-U6-L3", "difficulty": 1, "enabled": true}, {"word": "love", "pos": "v", "meaning": "爱", "level": "LV1(上)", "unit": "S1-U12-L2", "difficulty": 1, "enabled": true}, {"word": "lots of", "pos": "det", "meaning": "大量的", "level": "LV1(上)", "unit": "", "difficulty": 1, "enabled": true}, {"word": "long", "pos": "adj", "meaning": "长的", "level": "LV1(上)", "unit": "S1-U7-L5 ", "difficulty": 1, "enabled": true}]} diff --git a/scripts/generate_listening_drag.py b/scripts/generate_listening_drag.py new file mode 100755 index 0000000..cf3ef3c --- /dev/null +++ b/scripts/generate_listening_drag.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +import sys +import os +from typing import List, Dict + +# 角色名称库 +CHARACTERS = ["Otis", "Ben", "Skylar", "Brainy", "Daisy", "Jim", "Paul", "Fred"] + +# 单词库路径 +WORD_LIST_PATH = os.path.join(os.path.dirname(__file__), "../references/l1_word_list.json") + +# 难度对应规则 +DIFFICULTY_RULES = { + "A": { + "visual": "强视觉特征,依赖大色块、明显大物品区分", + "audio": "简单句,语速慢,SVO结构,关键词后置", + "interference": "低,干扰组差异非常明显,仅需识别基础名词或颜色形容词" + }, + "B": { + "visual": "细节特征,依赖花纹、衣着款式、具体方位", + "audio": "复合句,包含介词短语、连词and/but", + "interference": "中,干扰组差异在于细节,需分辨修饰性短语" + }, + "C": { + "visual": "状态与动作,依赖面部表情、身体姿态、动作细节", + "audio": "描述性语言,使用丰富的形容词、副词,语速正常", + "interference": "高,干扰组动作极其相似,需分辨程度副词或具体形容词" + }, + "D": { + "visual": "情境推断,依赖抽象线索或因果状态", + "audio": "隐含逻辑,包含推测、因果关系,语速较快", + "interference": "极高,干扰组无法直接通过视觉匹配,需结合上下文逻辑推理排除" + } +} + +# 能力标签对应(与官方统一定义对齐) +ABILITY_TAGS = [ + "显性事实理解(关键词识别)", + "显性事实理解(关键词识别)", + "多信息整合", + "干扰抑制 / 语用推断(否定与纠错)" +] + +def parse_args(): + parser = argparse.ArgumentParser(description="Generate Listening Drag & Drop questions") + parser.add_argument("--knowledge", required=True, help="知识点列表,分号分隔") + parser.add_argument("--difficulty", required=True, choices=["A", "B", "C", "D"], help="难度等级") + parser.add_argument("--names", required=True, help="人名列表,逗号分隔") + parser.add_argument("--shuffled_names", required=True, help="打乱后的人名列表,逗号分隔") + return parser.parse_args() + +def load_word_list() -> List[str]: + """加载L1单词库""" + try: + with open(WORD_LIST_PATH, "r", encoding="utf-8") as f: + data = json.load(f) + return [word["word"].lower() for word in data["words"]] + except Exception as e: + print(f"警告:加载单词库失败,跳过超纲校验:{str(e)}", file=sys.stderr) + return [] + +def validate_knowledge_with_wordlist(knowledge: str, word_list: List[str]) -> bool: + """校验知识点是否在单词库中""" + if not word_list: + return True + knowledge_words = [item.split()[0].lower() for item in knowledge.split(";") if item.strip()] + out_of_range = [] + for word in knowledge_words: + if word not in word_list: + out_of_range.append(word) + if out_of_range: + print(f"错误:以下知识点超纲,不在L1单词库中:{','.join(out_of_range)}", file=sys.stderr) + return False + return True + +def validate_input(args) -> bool: + """校验输入参数合法性""" + names = args.names.split(",") + shuffled_names = args.shuffled_names.split(",") + if len(names) != 5 or len(shuffled_names) !=5: + print("错误:人名列表必须包含5个名字", file=sys.stderr) + return False + if set(names) != set(shuffled_names): + print("错误:打乱后的人名列表与原列表不一致", file=sys.stderr) + return False + if not args.knowledge.strip(): + print("错误:知识点不能为空", file=sys.stderr) + return False + # 超纲校验 + word_list = load_word_list() + if not validate_knowledge_with_wordlist(args.knowledge, word_list): + return False + return True + +def generate_prompt(args) -> str: + """生成生产prompt""" + difficulty_rule = DIFFICULTY_RULES[args.difficulty] + return f""" +你是资深K12英语教研专家,严格按照以下要求生成听力拖拽题: +## 难度要求 +{difficulty_rule} +## 输入参数 +知识点:{args.knowledge} +人名列表:{args.names} +打乱后人名列表:{args.shuffled_names} +## 题目要求 +1. 共4道小题,对应P1-P4,P5为干扰项 +2. P3-P5为干扰组,共享一个核心特征,仅通过细节区分 +3. Q4必须包含纠错逻辑(如No, sorry/I mean)或否定逻辑 +4. 角色从{CHARACTERS}中选择2个不同的角色 +5. 对话要求自然日常,避免统一使用祈使句,可使用陈述句、疑问句等多种形式,参考示例: +One - +Girl: Look! The girl with the red shirt is happy. +Boy: Yes. That’s Julia. +Two - +Girl: Who is the girl in the yellow skirt? +Boy: That’s Sue. +Three - +Girl: The girl with the blue watch is pretty. +Boy: That’s May. +Four - +Girl: Is the boy in the white shirt Dan? +Boy: No, sorry. That’s Peter. +## 输出格式 +严格按照以下格式输出,不要添加任何额外内容: +【听力文本】 +One - +[角色1]: [对话指向P1] +[角色2]: Yes. That’s [Name1]. +Two - +[角色1]: [对话指向P2] +[角色2]: That’s [Name2]. +Three - +[角色1]: [对话指向P3,从干扰组筛选] +[角色2]: That’s [Name3]. +Four - +[角色1]: [对话指向P4,包含纠错逻辑] +[角色2]: Oh! That’s [Name4]. +【图片描述】 +风格: 卡通,简单线条 +场景:[符合知识点的场景] + [人物1描述] - [动作/特征] ; + [人物2描述] - [动作/特征] ; + [人物3描述] - [动作/特征] ; + [人物4描述] - [动作/特征] ; + [人物5描述] - [动作/特征] 。 +【人物顺序】 +[{args.names.split(',')}] +【选项排序】 +[{args.shuffled_names.split(',')}] +【任务描述】 +You will hear [角色1] and [角色2] talking about [场景相关内容]. Drag the names to the correct people. +【选项数量】 +5 +【答案】 +[Name1在打乱列表中的索引,Name2索引,Name3索引,Name4索引],例如03,01,00,04 +【能力项】 +题目1 - 显性事实理解(关键词识别)- [Q1考察的核心知识点关键词,例如:blue T-shirt] +题目2 - 显性事实理解(关键词识别)- [Q2考察的核心知识点关键词,例如:red dress] +题目3 - 多信息整合 - [Q3考察的组合知识点关键词,例如:pink hairband + white skirt] +题目4 - 干扰抑制 / 语用推断(否定与纠错)- [Q4考察的核心知识点关键词,例如:green cap] +""" + +def call_llm(prompt: str) -> str: + """调用大模型生成内容(此处留空,实际使用时替换为对应模型调用逻辑)""" + # 实际生产环境替换为模型API调用 + print("请输入模型生成的内容:") + return sys.stdin.read() + +def validate_output(output: str, original_names: List[str], shuffled_names: List[str]) -> bool: + """校验输出格式合法性""" + # 检查必填字段是否存在 + required_sections = ["【听力文本】", "【图片描述】", "【人物顺序】", "【选项排序】", "【任务描述】", "【选项数量】", "【答案】", "【能力项】"] + for section in required_sections: + if section not in output: + print(f"错误:缺少必填字段{section}", file=sys.stderr) + return False + # 校验人物顺序格式 + person_order_match = re.search(r"【人物顺序】\s*\[(.*)\]", output) + if not person_order_match: + print("错误:人物顺序格式不正确", file=sys.stderr) + return False + person_order = [name.strip().strip("'\"") for name in person_order_match.group(1).split(",")] + if person_order != original_names: + print(f"错误:人物顺序与输入人名列表不一致,预期:{original_names},实际:{person_order}", file=sys.stderr) + return False + # 校验选项排序格式 + option_order_match = re.search(r"【选项排序】\s*\[(.*)\]", output) + if not option_order_match: + print("错误:选项排序格式不正确", file=sys.stderr) + return False + option_order = [name.strip().strip("'\"") for name in option_order_match.group(1).split(",")] + if option_order != shuffled_names: + print(f"错误:选项排序与输入打乱后的人名列表不一致,预期:{shuffled_names},实际:{option_order}", file=sys.stderr) + return False + # 校验答案格式 + answer_match = re.search(r"【答案】\s*([\d,]+)", output) + if not answer_match: + print("错误:答案格式不正确", file=sys.stderr) + return False + answers = answer_match.group(1).split(",") + if len(answers) !=4: + print("错误:答案必须包含4个索引", file=sys.stderr) + return False + for ans in answers: + if not ans.isdigit() or int(ans) <0 or int(ans)>=5: + print(f"错误:答案索引{ans}不合法", file=sys.stderr) + return False + # 校验答案对应的人名是否在列表中 + for ans in answers: + idx = int(ans) + if idx >= len(shuffled_names): + print(f"错误:答案索引{idx}超出范围", file=sys.stderr) + return False + # 校验能力项格式 + ability_pattern = r"【能力项】\s*题目1 - .* - .*\s*题目2 - .* - .*\s*题目3 - .* - .*\s*题目4 - .* - .*" + if not re.search(ability_pattern, output, re.DOTALL): + print("错误:能力项格式不正确,必须按以下格式分行列出:题目N - 能力标签 - 考察知识点", file=sys.stderr) + return False + return True + +def main(): + args = parse_args() + if not validate_input(args): + sys.exit(1) + prompt = generate_prompt(args) + output = call_llm(prompt) + original_names = args.names.split(",") + shuffled_names = args.shuffled_names.split(",") + if validate_output(output, original_names, shuffled_names): + print(output) + sys.exit(0) + else: + sys.exit(1) + +if __name__ == "__main__": + main()