From e251630966d88a0ed964a8aa23ffc195997d0a82 Mon Sep 17 00:00:00 2001 From: ai_member_only Date: Tue, 12 May 2026 08:10:01 +0800 Subject: [PATCH] auto backup: 2026-05-12 08:10:01 --- AGENTS.md | 12 - .../unit_challenge_all_types.md | 149 +++ .../listening/L2/listening-choicePic/SKILL.md | 13 +- .../listening/L2/listening_form_fill/SKILL.md | 13 +- .../L2/listening_info_match/SKILL.md | 13 +- .../L2/listening_long_conversation/SKILL.md | 13 +- .../L2/listening_short_conversation/SKILL.md | 13 +- .../reading/common/reading_pic_judge/SKILL.md | 8 +- .../reading/common/reading_pic_qa/SKILL.md | 8 +- .../questions/reading/reading_cloze/SKILL.md | 13 +- .../reading/reading_info_match/SKILL.md | 13 +- .../reading/reading_long_passage/SKILL.md | 13 +- .../reading/reading_open_fill/SKILL.md | 13 +- .../reading/reading_paragraph_match/SKILL.md | 13 +- .../L2/speaking_topic_discussion/SKILL.md | 13 +- .../common/speaking_daily_qa/SKILL.md | 8 +- .../writing/common/writing_pic_qa/SKILL.md | 33 +- .../writing/writing_email_reply/SKILL.md | 13 +- .../writing/writing_picture_writing/SKILL.md | 13 +- memory/.dreams/events.jsonl | 4 + memory/.dreams/short-term-recall.json | 134 ++- memory/2026-05-11-0956.md | 109 +++ memory/2026-05-11-1129.md | 10 + memory/2026-05-11-1131.md | 14 + memory/2026-05-11.md | 88 ++ .../单元挑战_审校规范_2026-05-11.md | 889 ++++++++++++++++++ .../batch_reading_pic_qa.cpython-312.pyc | Bin 0 -> 19097 bytes scripts/batch_create_delayed.py | 113 +++ scripts/batch_create_safe.py | 94 ++ scripts/batch_create_two_step.py | 115 +++ scripts/batch_reading_matchInfo.py | 509 ++++++++++ scripts/batch_reading_pic_judge.py | 265 ++++++ scripts/batch_reading_pic_qa.py | 600 ++++++++++++ scripts/batch_reading_pic_qa_2.py | 141 +++ scripts/test_one_record.py | 80 ++ scripts/update_image_descriptions.py | 185 ++++ scripts/update_l2_difficulty_skills.py | 193 ++++ scripts/update_pic_judge_descriptions.py | 285 ++++++ scripts/update_speaking_qa_ability.py | 173 ++++ scripts/verify_record.py | 38 + 40 files changed, 4294 insertions(+), 132 deletions(-) create mode 100644 business_knowledge/unit_challenge_all_types.md create mode 100644 memory/2026-05-11-0956.md create mode 100644 memory/2026-05-11-1129.md create mode 100644 memory/2026-05-11-1131.md create mode 100644 memory/2026-05-11.md create mode 100644 output/单元挑战_审校规范_2026-05-11.md create mode 100644 scripts/__pycache__/batch_reading_pic_qa.cpython-312.pyc create mode 100644 scripts/batch_create_delayed.py create mode 100644 scripts/batch_create_safe.py create mode 100644 scripts/batch_create_two_step.py create mode 100644 scripts/batch_reading_matchInfo.py create mode 100644 scripts/batch_reading_pic_judge.py create mode 100644 scripts/batch_reading_pic_qa.py create mode 100644 scripts/batch_reading_pic_qa_2.py create mode 100644 scripts/test_one_record.py create mode 100644 scripts/update_image_descriptions.py create mode 100644 scripts/update_l2_difficulty_skills.py create mode 100644 scripts/update_pic_judge_descriptions.py create mode 100644 scripts/update_speaking_qa_ability.py create mode 100644 scripts/verify_record.py diff --git a/AGENTS.md b/AGENTS.md index f9e97e5..376962e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -125,18 +125,6 @@ System: [...] Feishu[xiaobian] group oc_xxx | 发送者 (ou_xxx) [msg:om_xxx] 2. **无 `GroupSystemPrompt` 或无特殊任务时:** 不执行任何操作 3. ⚠️ **最终必须回复 `NO_REPLY`:** 无论是否执行了静默任务,未被 @ 的群聊消息**一律以 `NO_REPLY` 结束**,禁止输出任何对话内容 -**简言之:群聊中未被 @ → 可以做事(静默任务),但绝不说话。** - -#### 何时发言(仅在被 @ 的前提下) - -即使被 @,以下情况仍应保持沉默(NO_REPLY): - -- 同事之间的闲聊,@ 你只是无意的 -- 已经有人回答了问题 -- 你的回复只是"是的"或"收到",没有实质价值 -- 对话在没有你的情况下进展顺利 - -参与,而非主导。质量 > 数量。 ## 工作区目录规范(强制执行) diff --git a/business_knowledge/unit_challenge_all_types.md b/business_knowledge/unit_challenge_all_types.md new file mode 100644 index 0000000..deeb31b --- /dev/null +++ b/business_knowledge/unit_challenge_all_types.md @@ -0,0 +1,149 @@ +# 单元挑战全部题型 · 生产工作流总览 + +> 数据源:飞书 Wiki [【最新】单元挑战全部题型JSON结构说明](https://makee-interactive.feishu.cn/wiki/UUKtwRWQRiPw1Gko3R8cLVfUnCe) +> Bitable App Token: `CMHSbUUjka3TrUsaxxEc297ongf` + +## 一、全部题型映射(20种) + +### 听力类(6种) + +| # | 题型 | type | 阶段 | Bitable表 | table_id | 技能文件 | +|---|------|------|------|-----------|----------|---------| +| 1 | 图片选择题 | `listening_choicePic` | L2 | 听力-P1-图片选择题 | `tbliZAhcc9C43B23` | `L2/listening-choicePic/SKILL.md` | +| 2 | 表格填空题 | `listening_tableCloze` | L2 | 听力-P2-表格填空题 | `tblzTLNH7f13uWQN` | `L2/listening_form_fill/SKILL.md` | +| 3 | 长对话选择 | `listening_choiceLong` | L2 | 听力-P3-长对话选择 | `tblgxsDn25oSq7WS` | `L2/listening_long_conversation/SKILL.md` | +| 4 | 短对话选择题 | `listening_choiceShort` | L2 | 听力-P4-短对话选择题 | `tblVmeDtBDKsAEfz` | `L2/listening_short_conversation/SKILL.md` | +| 5 | 信息匹配题 | `listening_matchInfo` | L2 | 听力-P5-信息匹配题 | `tblDssVmhGzc3UKd` | `L1+L2/listening_info_match/SKILL.md` | +| 6 | 听力拖拽 | `listening_drag` | L1 | 听力-P7-听力拖拽 | `tbly9SvPEa44k3yX` | `L1/listening-drag/SKILL.md` | + +### 阅读类(7种) + +| # | 题型 | type | 阶段 | Bitable表 | table_id | 技能文件 | +|---|------|------|------|-----------|----------|---------| +| 7 | 信息匹配题 | `reading_matchInfo` | L2 | 阅读-P1-信息匹配题 | `tblCgfYDnnqwLfgH` | `reading_info_match/SKILL.md` | +| 8 | 段落匹配题 | `reading_matchPara` | L2 | 阅读-P2-段落匹配题 | `tblEp820dnatNYbb` | `reading_paragraph_match/SKILL.md` | +| 9 | 长文选择题 | `reading_choiceLong` | L2 | 阅读-P3-长文选择题 | `tbl4q0ZUV3HB54t1` | `reading_long_passage/SKILL.md` | +| 10 | 完形填空题 | `reading_cloze` | L2 | 阅读-P4-完形填空题 | `tblzKVm1FEukPgnN` | `reading_cloze/SKILL.md` | +| 11 | 开放填空题 | `reading_openCloze` | L2 | 阅读-P5-开放填空题 | `tblLmUxzzUDe0QAJ` | `reading_open_fill/SKILL.md` | +| 12 | 看图判断题 | `reading_pic_judge` | L1&L2 | 阅读-P6-看图判断题 | `tblJc60aO0T163MJ` | `common/reading_pic_judge/SKILL.md` | +| 13 | 看图回答题 | `reading_pic_qa` | L1&L2 | 阅读-P7-看图回答题 | `tblweY65jGBiwSdt` | `common/reading_pic_qa/SKILL.md` | + +### 写作类(3种) + +| # | 题型 | type | 阶段 | Bitable表 | table_id | 技能文件 | +|---|------|------|------|-----------|----------|---------| +| 14 | 邮件回复 | `writing_email` | L2 | 写作-P1-邮件回复 | `tblszuk1TeToofBF` | `writing_email_reply/SKILL.md` | +| 15 | 看图写作 | `writing_picWrite` | L2 | 写作-P2-看图写作 | `tblSAwlMumKoyjws` | `writing_picture_writing/SKILL.md` | +| 16 | 看图回答 | `writing_pic_qa` | L1&L2 | 写作-P3-看图回答题 | `tblFc9TVl2PeM2tg` | `common/writing_pic_qa/SKILL.md` | + +### 口语类(4种) + +| # | 题型 | type | 阶段 | Bitable表 | table_id | 技能文件 | +|---|------|------|------|-----------|----------|---------| +| 17 | 日常问答 | `speaking_qa` | L1&L2 | 口语-P1-日常回答 | `tblRGv7k4WH58Jgq` | `common/speaking_daily_qa/SKILL.md` | +| 18 | 看图回答 | `speaking_pic_qa` | L1 | 口语-P3-看图回答 | `tblOHgNkNer2hGEp` | `L1/speaking_pic_qa/SKILL.md` | +| 19 | 话题讨论 | `speaking_topic` | L2 | 口语-P2-话题讨论 | `tblGoWYBmVI0IrvQ` | `L1+L2/speaking_topic/SKILL.md` | +| 20 | 看图识物 | `speaking_pic_recognize` | L1 | 口语-P4-看图识物 | `tblsD2dxaRpLmkXD` | `L1/speaking_pic_recognize/SKILL.md` | + +## 二、通用生产流程 + +每类题型遵循统一流程: + +``` +用户输入(含触发关键词)→ 解析(题型type/知识点/难度/ID/题量/能力标签) + → 读取对应 SKILL.md 确认JSON结构和规范 + → 设计题目(校核词汇大纲/句型库/难度匹配) + → LLM生成完整jsonData + → 写入多维表格(两阶段延迟策略:先空记录等30s → 再PUT jsonData) + → 回读验证(题数+抽样Q1内容) + → 生成图片描述(如题型需要)→ 回填 图片描述 列 + → 汇总交付 +``` + +## 三、触发关键词体系 + +### 按题型 type 触发(精确匹配) +| type | 关键词 | +|------|--------| +| `listening_choicePic` | 听力图片选择 / listening choicePic / 听力P1 | +| `listening_tableCloze` | 听力表格填空 / listening tableCloze / 听力P2 | +| `listening_choiceLong` | 听力长对话 / listening choiceLong / 听力P3 | +| `listening_choiceShort` | 听力短对话 / listening choiceShort / 听力P4 | +| `listening_matchInfo` | 听力信息匹配 / listening matchInfo / 听力P5 | +| `listening_drag` | 听力拖拽 / listening drag / 听力P7 | +| `reading_matchInfo` | 阅读信息匹配 / reading matchInfo / 阅读P1 | +| `reading_matchPara` | 阅读段落匹配 / reading matchPara / 阅读P2 | +| `reading_choiceLong` | 阅读长文选择 / reading choiceLong / 阅读P3 | +| `reading_cloze` | 阅读完形填空 / reading cloze / 阅读P4 | +| `reading_openCloze` | 阅读开放填空 / reading openCloze / 阅读P5 | +| `reading_pic_judge` | 阅读看图判断 / reading pic judge / 看图判断题 / 阅读P6 | +| `reading_pic_qa` | 阅读看图回答 / reading pic qa / 看图回答题 / 阅读P7 | +| `writing_email` | 写作邮件 / writing email / 邮件回复 / 写作P1 | +| `writing_picWrite` | 写作看图写作 / writing picWrite / 看图写作 / 写作P2 | +| `writing_pic_qa` | 写作看图回答 / writing pic qa / 写作P3 | +| `speaking_qa` | 口语日常问答 / speaking qa / 口语P1 | +| `speaking_pic_qa` | 口语看图回答 / speaking pic qa / 口语P3 | +| `speaking_topic` | 口语话题讨论 / speaking topic / 口语P2 | +| `speaking_pic_recognize` | 口语看图识物 / speaking pic recognize / 口语P4 | + +## 四、生产请求标准格式 + +用户提供以下信息即可触发自动生产: + +``` +题型:{type 或 中文名} +ID:{questionSetID} +级别:L1/L2 +难度:A/B/C/D +能力标签:{逗号分隔} +题组1知识点:{逗号分隔} +题组2知识点:{逗号分隔}(如适用) +``` + +## 五、Bitable 写入策略(通用) + +所有表均存在自动化覆盖问题,统一使用两阶段延迟写入: + +1. **删除旧记录** → 清理同ID的损坏记录 +2. **创建空记录** → 仅写入 `题目集合ID` + `dataStatus` +3. **等待30秒** → 让自动化完成模板覆写 +4. **PUT jsonData** → 写入完整 JSON +5. **等待 >10秒** → 最终回读验证 +6. **抽样检查** → Q1 内容是否匹配(确认未被自动化替换为空壳) + +## 六、参考:各题型JSON结构特点 + +### 听力类 +- **choicePic**: questionSet[].question + questionAudio + optionsImage[] + answer[] +- **tableCloze**: textTitle + textBody(含[0][1]占位) + textDesc + textAudio + questionSet[].options[] + answer[] +- **choiceLong**: textDesc + textAudio + questionSet[].question + options[] + answer[] +- **choiceShort**: questionSet[].question + questionAudio + questionDesc + options[] + answer[] +- **matchInfo**: textTitle + textBody(含[0][1]占位) + textDesc + textAudio + optionSetTitle + optionSetList[] + answerSet[] +- **drag**: textDesc + textAudio + questionSet[].answer[] + imageInfo + optionList[] + +### 阅读类 +- **matchInfo**: questionSet[].questionImage + question + options[] + answer[] +- **matchPara**: textTitle + textBody + questionSet[].question + options[] + answer[] +- **choiceLong**: textTitle + textSubtitle + textBody + questionSet[].question + options[] + answer[] +- **cloze**: textTitle + textBody(含[0][1]占位) + questionSet[].options[] + answer[] +- **openCloze**: textTitle + textBody(含[0][1]占位) + questionSet[].options[] + answer[] +- **pic_judge**: 使用 first/second 包裹,每组含 textTitle + textImage + questionSet[].question + answerText(Yes/No) +- **pic_qa**: 使用 first/second 包裹,每组含 textTitle + textImage + questionSet[].question + answerText(Yes/No) + +### 写作类 +- **email**: textDesc + optionSetList[] + answerSet[] +- **picWrite**: textDesc + textImage + optionSetList[] + answerSet[] +- **pic_qa**: questionSet[].questionImage + questionList[].question + questionDesc + answerText + +### 口语类 +- **qa**: asrPrompt + textDesc + questionSet[].content +- **pic_qa**: asrPrompt + textDesc + textImage + imageDesc + questionSet[].content +- **topic**: asrPrompt + textDesc + textImage + imageDesc + questionSet[].content +- **pic_recognize**: asrPrompt + textTitle + questionSet[].question + questionImage + imageDesc + +## 七、已生产记录 + +### reading_pic_qa(2026-05-11) +- 000001, 010101, 010201, 010301, 010401, 010501, 010601(7套 L2 A级) +- 含图片描述 +- 脚本:`scripts/batch_reading_pic_qa.py`、`scripts/batch_create_delayed.py`、`scripts/update_image_descriptions.py` diff --git a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening-choicePic/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening-choicePic/SKILL.md index 13592d8..28d9063 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening-choicePic/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening-choicePic/SKILL.md @@ -6,12 +6,11 @@ description: K12英语听力三选一图片选择题标准化设计、生产、 ## 题型说明 本技能对应官方题型:**L2 - Movers - 听力 Part3 三选一图片选择【阶段归属:L2专属】**,考察核心能力为**显性事实理解(单句信息点抓取)**:学生听单句听力内容,从3张图片选项中选出与听力内容匹配的正确答案。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第1-2阶段 | Starters 1-2 | 选项差异极大(类别完全不同),听力为3-5词简单句,关键词后置 | -| B级 | L1 第3-4阶段 | Starters 3 | 选项为同类物品差异(颜色/大小不同),听力为5-8词简单句 | -| C级 | L1 第5-6阶段 | Movers 1 | 选项为动作/状态差异,听力为8-12词带介词短语的句子 | -| D级 | L2 第1-2阶段 | Movers 2-3 | 选项为场景/逻辑差异,听力为12-15词带简单从句的句子 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 选项差异极大(类别完全不同),听力为3-5词简单句,关键词后置 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 选项为同类物品差异(颜色/大小不同),听力为5-8词简单句 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 选项为动作/状态差异,听力为8-12词带介词短语的句子 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 选项为场景/逻辑差异,听力为12-15词带简单从句的句子 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:听力理解、图片信息识别、单句信息抓取 2. 每道题专属能力标签(与官方45个能力tag对齐): @@ -30,7 +29,7 @@ description: K12英语听力三选一图片选择题标准化设计、生产、 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为家庭厨房` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_form_fill/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_form_fill/SKILL.md index 0888128..7050bf6 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_form_fill/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_form_fill/SKILL.md @@ -6,12 +6,11 @@ description: K12英语听力表格填空题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 听力 Part2 表格填空【阶段归属:L2专属】**,考察核心能力为**信息抓取(关键词识别+拼写准确性)**:学生听1-2段短对话/独白,提取关键信息填写表格中空缺的内容,考察词汇拼写、数字识别、信息匹配能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第1-2阶段 | Starters 1-2 | 表格仅2-3个空缺,答案为1-2个字母/数字,听力直接念出答案 | -| B级 | L1 第3-4阶段 | Starters 3 | 表格3-4个空缺,答案为3-5个字母的单词,听力直接念出答案 | -| C级 | L1 第5-6阶段 | Movers 1 | 表格4-5个空缺,答案为单词/短语,听力包含简单干扰信息 | -| D级 | L2 第1-2阶段 | Movers 2-3 | 表格5-6个空缺,答案为短语/短句,听力包含较多干扰信息 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 表格仅2-3个空缺,答案为1-2个字母/数字,听力直接念出答案 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 表格3-4个空缺,答案为3-5个字母的单词,听力直接念出答案 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 表格4-5个空缺,答案为单词/短语,听力包含简单干扰信息 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 表格5-6个空缺,答案为短语/短句,听力包含较多干扰信息 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:听力理解、信息抓取、拼写准确性 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语听力表格填空题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为学校报名` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_info_match/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_info_match/SKILL.md index c5ee18c..65d0e6b 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_info_match/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_info_match/SKILL.md @@ -6,12 +6,11 @@ description: K12英语听力信息匹配题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 听力 Part5 信息匹配【阶段归属:L2专属】**,考察核心能力为**信息关联(多任务处理+干扰抑制)**:学生听1段长对话/独白,将左右两栏的相关信息进行匹配,考察对多个信息点的关联、记忆和区分能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 匹配项5组,信息直接对应,无干扰 | -| B级 | L2 第1-2阶段 | Movers 2 | 匹配项5-6组,信息需要简单关联,1个干扰项 | -| C级 | L2 第3-4阶段 | Movers 3 | 匹配项6组,信息需要整合关联,2个干扰项 | -| D级 | L2 第5-6阶段 | Flyers 1 | 匹配项6-7组,信息需要推断关联,3个干扰项 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 匹配项5组,信息直接对应,无干扰 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 匹配项5-6组,信息需要简单关联,1个干扰项 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 匹配项6组,信息需要整合关联,2个干扰项 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 匹配项6-7组,信息需要推断关联,3个干扰项 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:听力理解、信息关联、干扰抑制 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语听力信息匹配题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为生日派对` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_long_conversation/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_long_conversation/SKILL.md index 0882383..eb01bfa 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_long_conversation/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_long_conversation/SKILL.md @@ -6,12 +6,11 @@ description: K12英语听力长对话选择题标准化设计、生产、审校 ## 题型说明 本技能对应官方题型:**L2 - Movers - 听力 Part3 长对话选择【阶段归属:L2专属】**,考察核心能力为**逻辑理解(信息整合+干扰抑制)**:学生听1段3-5轮的长对话,回答3-4道选择题,考察对对话整体逻辑、隐含信息、因果关系的理解能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 对话3轮,3道题,答案直接对应对话内容,无干扰 | -| B级 | L2 第1-2阶段 | Movers 2 | 对话4轮,3道题,答案需要简单推理,1个干扰项 | -| C级 | L2 第3-4阶段 | Movers 3 | 对话5轮,4道题,答案需要逻辑整合,2个干扰项 | -| D级 | L2 第5-6阶段 | Flyers 1 | 对话6轮,4道题,答案需要隐含信息推断,3个干扰项 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 对话3轮,3道题,答案直接对应对话内容,无干扰 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 对话4轮,3道题,答案需要简单推理,1个干扰项 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 对话5轮,4道题,答案需要逻辑整合,2个干扰项 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 对话6轮,4道题,答案需要隐含信息推断,3个干扰项 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:听力理解、逻辑推理、信息整合 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语听力长对话选择题标准化设计、生产、审校 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为周末购物` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_short_conversation/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_short_conversation/SKILL.md index 63313f7..9929172 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_short_conversation/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/listening/L2/listening_short_conversation/SKILL.md @@ -6,12 +6,11 @@ description: K12英语听力短对话选择题标准化设计、生产、审校 ## 题型说明 本技能对应官方题型:**L2 - Movers - 听力 Part4 短对话选择【阶段归属:L2专属】**,考察核心能力为**场景理解(快速信息抓取+反应)**:学生听多段1-2轮的短对话,每段对话对应1道选择题,考察对常见场景、日常用语的理解能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第1-2阶段 | Starters 1-2 | 每段对话1轮,问题直接问物品/场景,答案直接明确 | -| B级 | L1 第3-4阶段 | Starters 3 | 每段对话1-2轮,问题问物品属性/人物动作,答案简单明确 | -| C级 | L1 第5-6阶段 | Movers 1 | 每段对话2轮,问题问人物意图/事件结果,需要简单推理 | -| D级 | L2 第1-2阶段 | Movers 2-3 | 每段对话2轮,问题问隐含观点/未来计划,需要推理 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 每段对话1轮,问题直接问物品/场景,答案直接明确 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 每段对话1-2轮,问题问物品属性/人物动作,答案简单明确 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 每段对话2轮,问题问人物意图/事件结果,需要简单推理 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 每段对话2轮,问题问隐含观点/未来计划,需要推理 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:听力理解、场景识别、快速反应 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语听力短对话选择题标准化设计、生产、审校 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1套(5段对话) | | 场景要求 | 🟢 | `场景为家庭日常` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_judge/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_judge/SKILL.md index 836ea30..36e4985 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_judge/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_judge/SKILL.md @@ -11,6 +11,12 @@ description: K12英语阅读看图判断题标准化设计、生产、审校工 - 答案统一为"Yes"或"No" ## 难度对应关系(与教研标准对齐) | 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | + +> **L2 难度维度(对标 Flyers/KET,每2阶段为1个难度等级):** +> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础 +> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门 +> - C级 = U25-U36(阶段5-6):KET基础→KET强化 +> - D级 = U37-U48(阶段7-8):KET高位→KET达标 |----------|------------|------------------|----------| | A级 | L1 第1-2阶段 | Starters 1-2 | 句子为1-3词极简陈述句(如"These are grapes."),图片为单一物品,判断直接 | | B级 | L1 第3-4阶段 | Starters 3 | 句子为4-6词简单陈述句,图片为单一物品+简单背景,需辨识颜色/数量属性 | @@ -37,7 +43,7 @@ description: K12英语阅读看图判断题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `grape n.葡萄;house n.房子;helicopter n.直升机` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级(L1用Starters/Movers,L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认5道 | | 场景要求 | 🟢 | `场景为日常物品` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范(JSON) diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_qa/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_qa/SKILL.md index bcdac77..5cf9711 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_qa/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/common/reading_pic_qa/SKILL.md @@ -11,6 +11,12 @@ description: K12英语阅读看图回答题标准化设计、生产、审校工 - 答案统一为"Yes"或"No" ## 难度对应关系(与教研标准对齐) | 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | + +> **L2 难度维度(对标 Flyers/KET,每2阶段为1个难度等级):** +> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础 +> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门 +> - C级 = U25-U36(阶段5-6):KET基础→KET强化 +> - D级 = U37-U48(阶段7-8):KET高位→KET达标 |----------|------------|------------------|----------| | A级 | L1 第1-2阶段 | Starters 1-2 | 句子为3-5词简单陈述句,考察单个名词/属性匹配,图片元素差异明显 | | B级 | L1 第3-4阶段 | Starters 3 | 句子为5-8词简单陈述句,考察多个名词/属性匹配,图片包含多人/多物但干扰少 | @@ -37,7 +43,7 @@ description: K12英语阅读看图回答题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `hat n.帽子;beach n.海滩;book n.书;shell n.贝壳` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级(L1用Starters/Movers,L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量(小题数),默认5道 | | 场景要求 | 🟢 | `场景为阳光海滩` | 指定场景内容,不指定则自动匹配知识点 | ## 输出格式规范(JSON) diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_cloze/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_cloze/SKILL.md index 9e79e33..4653623 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_cloze/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_cloze/SKILL.md @@ -6,12 +6,11 @@ description: K12英语阅读完形填空题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 阅读 Part4 完形填空【阶段归属:L2专属】**,考察核心能力为**词汇运用(语法判断+上下文逻辑理解)**:学生阅读1篇80-150词的短文,文中有3-5个空缺,每个空缺从3个选项中选择正确的单词填入,考察词汇、语法和上下文理解能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 短文80词左右,3个空,考察名词/形容词等实词,上下文提示明显 | -| B级 | L2 第1-2阶段 | Movers 2 | 短文100词左右,4个空,考察实词+简单介词/动词,上下文有提示 | -| C级 | L2 第3-4阶段 | Movers 3 | 短文120词左右,5个空,考察实词+虚词,需要简单逻辑判断 | -| D级 | L2 第5-6阶段 | Flyers 1 | 短文150词左右,5个空,考察词汇+语法+逻辑,需要上下文推断 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 短文80词左右,3个空,考察名词/形容词等实词,上下文提示明显 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 短文100词左右,4个空,考察实词+简单介词/动词,上下文有提示 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 短文120词左右,5个空,考察实词+虚词,需要简单逻辑判断 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 短文150词左右,5个空,考察词汇+语法+逻辑,需要上下文推断 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:阅读理解、词汇运用、逻辑推理 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语阅读完形填空题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为生日派对` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_info_match/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_info_match/SKILL.md index fc15c23..7287a47 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_info_match/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_info_match/SKILL.md @@ -6,12 +6,11 @@ description: K12英语阅读信息匹配题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 阅读 Part1 信息匹配【阶段归属:L2专属】**,考察核心能力为**信息抓取(快速阅读+图文匹配)**:学生阅读5-6个简短文本(标识/告示/短句),与对应的图片/标题进行匹配,考察快速抓取关键信息和图文匹配能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第1-2阶段 | Starters 1-2 | 文本为1-2个单词,图片差异极大,直接对应 | -| B级 | L1 第3-4阶段 | Starters 3 | 文本为3-5词短句,图片为同类差异,简单对应 | -| C级 | L1 第5-6阶段 | Movers 1 | 文本为5-8词短句,需要提取关键词匹配 | -| D级 | L2 第1-2阶段 | Movers 2-3 | 文本为8-10词短句,包含干扰信息,需要筛选匹配 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 文本为1-2个单词,图片差异极大,直接对应 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 文本为3-5词短句,图片为同类差异,简单对应 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 文本为5-8词短句,需要提取关键词匹配 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 文本为8-10词短句,包含干扰信息,需要筛选匹配 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:阅读理解、信息抓取、快速阅读 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语阅读信息匹配题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1套(6小题) | | 场景要求 | 🟢 | `场景为公共场所标识` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_long_passage/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_long_passage/SKILL.md index 0022df6..9571a61 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_long_passage/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_long_passage/SKILL.md @@ -6,12 +6,11 @@ description: K12英语阅读长文选择题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 阅读 Part3 长文选择【阶段归属:L2专属】**,考察核心能力为**篇章理解(细节定位+推理判断)**:学生阅读1篇100-200词的短文,回答3-4道选择题,考察对篇章整体内容、细节信息、推理判断的能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 短文100词左右,3道题,答案直接在原文中出现 | -| B级 | L2 第1-2阶段 | Movers 2 | 短文120-150词,3道题,答案需要简单定位 | -| C级 | L2 第3-4阶段 | Movers 3 | 短文150-180词,4道题,答案需要简单推理 | -| D级 | L2 第5-6阶段 | Flyers 1 | 短文180-200词,4道题,答案需要逻辑推断 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 短文100词左右,3道题,答案直接在原文中出现 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 短文120-150词,3道题,答案需要简单定位 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 短文150-180词,4道题,答案需要简单推理 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 短文180-200词,4道题,答案需要逻辑推断 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:阅读理解、细节定位、推理判断 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语阅读长文选择题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为周末活动` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_open_fill/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_open_fill/SKILL.md index ec8e993..278edfe 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_open_fill/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_open_fill/SKILL.md @@ -6,12 +6,11 @@ description: K12英语阅读开放填空题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 阅读 Part5 开放填空【阶段归属:L2专属】**,考察核心能力为**信息提取(词汇拼写+语法正确性)**:学生阅读1篇短文/通知/表格,根据要求填写3-5个空缺的单词,无选项提示,考察信息提取、词汇拼写和语法正确性。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 空缺3个,答案为1-4个字母的简单名词,原文直接给出 | -| B级 | L2 第1-2阶段 | Movers 2 | 空缺4个,答案为名词/形容词,原文直接给出,需要简单变形 | -| C级 | L2 第3-4阶段 | Movers 3 | 空缺5个,答案为动词/短语,需要从原文提取后变形 | -| D级 | L2 第5-6阶段 | Flyers 1 | 空缺5个,答案需要根据原文信息归纳总结,符合语法要求 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 空缺3个,答案为1-4个字母的简单名词,原文直接给出 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 空缺4个,答案为名词/形容词,原文直接给出,需要简单变形 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 空缺5个,答案为动词/短语,需要从原文提取后变形 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 空缺5个,答案需要根据原文信息归纳总结,符合语法要求 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:阅读理解、信息提取、词汇拼写 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语阅读开放填空题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为活动通知` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_paragraph_match/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_paragraph_match/SKILL.md index d482e4e..4bdc8b6 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_paragraph_match/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/reading/reading_paragraph_match/SKILL.md @@ -6,12 +6,11 @@ description: K12英语阅读段落匹配题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 阅读 Part2 段落匹配【阶段归属:L2专属】**,考察核心能力为**主旨理解(信息归纳+快速阅读)**:学生阅读4-5个简短段落,与对应的段落标题/主旨句进行匹配,考察归纳段落主旨和抓取核心信息的能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 段落3-4句话,主旨直接明确,无干扰 | -| B级 | L2 第1-2阶段 | Movers 2 | 段落4-5句话,主旨需要简单归纳,1个干扰项 | -| C级 | L2 第3-4阶段 | Movers 3 | 段落5-6句话,主旨需要整合信息,2个干扰项 | -| D级 | L2 第5-6阶段 | Flyers 1 | 段落6-7句话,主旨需要隐含推断,3个干扰项 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 段落3-4句话,主旨直接明确,无干扰 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 段落4-5句话,主旨需要简单归纳,1个干扰项 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 段落5-6句话,主旨需要整合信息,2个干扰项 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 段落6-7句话,主旨需要隐含推断,3个干扰项 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:阅读理解、主旨归纳、快速阅读 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语阅读段落匹配题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为校园生活` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/speaking/L2/speaking_topic_discussion/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/speaking/L2/speaking_topic_discussion/SKILL.md index dc0511f..f4796fc 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/speaking/L2/speaking_topic_discussion/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/speaking/L2/speaking_topic_discussion/SKILL.md @@ -6,12 +6,11 @@ description: K12英语口语话题讨论题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 口语 Part2 话题讨论【阶段归属:L2专属】**,考察核心能力为**口语表达(逻辑组织+观点阐述)**:学生根据给定话题,进行1-2分钟的口头阐述,考察词汇运用、逻辑连贯、观点表达能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 话题为具体物品/活动,回答3-4句话即可,无需复杂逻辑 | -| B级 | L2 第1-2阶段 | Movers 2 | 话题为喜好/习惯,回答4-5句话,有简单逻辑顺序 | -| C级 | L2 第3-4阶段 | Movers 3 | 话题为观点/经历,回答5-6句话,有清晰逻辑层次 | -| D级 | L2 第5-6阶段 | Flyers 1 | 话题为抽象感受/计划,回答6-8句话,逻辑连贯有支撑 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 话题为具体物品/活动,回答3-4句话即可,无需复杂逻辑 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 话题为喜好/习惯,回答4-5句话,有简单逻辑顺序 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 话题为观点/经历,回答5-6句话,有清晰逻辑层次 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 话题为抽象感受/计划,回答6-8句话,逻辑连贯有支撑 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:口语表达、逻辑组织、观点阐述 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语口语话题讨论题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `话题为食物` | 指定话题范围,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/speaking/common/speaking_daily_qa/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/speaking/common/speaking_daily_qa/SKILL.md index a22d5ad..87cb0d7 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/speaking/common/speaking_daily_qa/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/speaking/common/speaking_daily_qa/SKILL.md @@ -7,6 +7,12 @@ description: K12英语口语日常回答题标准化设计、生产、审校工 本技能对应官方题型:**口语 Part1 日常回答【阶段归属:L1&L2共用,通过难度参数区分】**,考察核心能力为**口语表达(日常用语运用+快速反应)**:学生回答3-4个日常问题,每个问题回答1-3句话,考察日常词汇、句式的运用能力和表达流畅度。 ## 难度对应关系(与教研标准对齐) | 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 | + +> **L2 难度维度(对标 Flyers/KET,每2阶段为1个难度等级):** +> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础 +> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门 +> - C级 = U25-U36(阶段5-6):KET基础→KET强化 +> - D级 = U37-U48(阶段7-8):KET高位→KET达标 |----------|------------|------------------|----------| | A级 | L1 第1-2阶段 | Starters 1-2 | 问题为个人基本信息类,答案为1-2个单词/短语即可 | | B级 | L1 第3-4阶段 | Starters 3 | 问题为喜好/日常活动类,答案为1个简单句即可 | @@ -31,7 +37,7 @@ description: K12英语口语日常回答题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `L1第2阶段` / `Starters 2` | 难度等级(L1用Starters/Movers,L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1套(4个问题) | | 场景要求 | 🟢 | `场景为个人介绍` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/writing/common/writing_pic_qa/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/writing/common/writing_pic_qa/SKILL.md index 02d9f05..4f5ac07 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/writing/common/writing_pic_qa/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/writing/common/writing_pic_qa/SKILL.md @@ -73,20 +73,27 @@ description: K12英语写作看图回答题标准化设计、生产、审校工 ### 3.3 语言难度 = 欧标 A1-A2 -- 句型控制在 Starters/Movers 级别(A1-A2) +- 句型控制在 Starters(L1)/ Flyers-KET(L2)级别 +- L1 对标 A1(Starters/Movers),L2 对标 A1-A2(Flyers/KET) - 题干用词优先使用 L1 词库词汇(`business_knowledge/L1_word_list.json`) - 基础功能词(代词、be动词、介词)当前 L1/L2 词库暂不收录,写题时绕过词汇校验硬性约束 ### 3.4 阶段变化原则 -当前规格适用 **L2 第一阶段(Unit0-6)**,后续阶段规则将随难度递增而变化: +当前规格适用 **L2 U1-U6(阶段1,A级,Flyers入门)**,后续阶段规则将随难度递增而变化: -| 维度 | L2 Unit0-6(当前) | 后续阶段 | -|------|-------------------|---------| +| 维度 | L2 U1-U6(阶段1,A级) | 后续阶段 | +|------|----------------------|---------| | 挖空单位 | 1 个单词 | 短语 / 完整句子 | | 题组小题数 | 2 小题 / 题组 | 4 小题 / 题组 | | 能力标签 | 简短作答 / 一词答案 | (待补充) | +> **L2 8阶段难度体系(每2阶段为1个难度等级):** +> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础 +> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门 +> - C级 = U25-U36(阶段5-6):KET基础→KET强化 +> - D级 = U37-U48(阶段7-8):KET高位→KET达标 + ### 3.5 题干多样化 同一题包内的 question 不能全部相同,应随语境微调: @@ -239,7 +246,7 @@ description: K12英语写作看图回答题标准化设计、生产、审校工 | questionList[].question | ✅ | string | 问题(问角色说了什么,口语化,各题微调) | | questionList[].questionDesc | ✅ | string | 提示答案开头,含字母数标记如 `(5)` | | questionList[].answerText | ✅ | string | 标准答案(1个单词) | -| questionList[].ability | ✅ | array | L2 Unit0-6 阶段固定 `["简短作答", "一词答案"]` | +| questionList[].ability | ✅ | array | L2 U1-U6(阶段1,A级)固定 `["简短作答", "一词答案"]` | | questionList[].explanation | ✅ | string | 解析:图片内容 + 考点 + 字母数 | --- @@ -352,13 +359,19 @@ description: K12英语写作看图回答题标准化设计、生产、审校工 ## 九、阶段与难度对应 -当前首个阶段为 **L2 Unit0-6**,后续阶段规则将随难度递增: +当前首个阶段为 **L2 U1-U6(阶段1,A级,Flyers入门)**,后续阶段规则将随难度递增: -| 阶段 | 难度 | 剑桥对标 | 挖空单位 | 小题/题组 | 能力标签 | -|------|------|---------|---------|----------|---------| -| L2 Unit0-6(当前) | A级 | Starters 1-2 | 1个核心词 | 2 小题 | 简短作答 / 一词答案 | +| 阶段 | 难度 | 对标 | 挖空单位 | 小题/题组 | 能力标签 | +|------|------|------|---------|----------|---------| +| L2 U1-U6(阶段1) | A级 | Flyers入门 | 1个核心词 | 2 小题 | 简短作答 / 一词答案 | | L2 后续阶段 | B/C/D | (待补充) | 短语 / 句子 | 4 小题 | (待补充) | +> **L2 完整 8 阶段难度体系(每2阶段为1个难度等级):** +> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础 +> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门 +> - C级 = U25-U36(阶段5-6):KET基础→KET强化 +> - D级 = U37-U48(阶段7-8):KET高位→KET达标 + > **历史 L1 规则(供参考):** > > | 难度等级 | 对应阶段 | 剑桥对标 | 答案要求 | @@ -372,7 +385,7 @@ description: K12英语写作看图回答题标准化设计、生产、审校工 ## 十、能力标签 -- **L2 Unit0-6 阶段:** `["简短作答", "一词答案"]` +- **L2 U1-U6(阶段1,A级):** `["简短作答", "一词答案"]` - 后续阶段的能力标签将在交付时逐个补充 - 所有同阶段产出的题目,能力标签必须统一 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_email_reply/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_email_reply/SKILL.md index 5a54629..fe86c63 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_email_reply/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_email_reply/SKILL.md @@ -6,12 +6,11 @@ description: K12英语写作邮件回复题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 写作 Part1 邮件回复【阶段归属:L2专属】**,考察核心能力为**书面表达(格式规范+信息提取)**:学生阅读一封简短邮件,根据要求写20-50词的回复邮件,考察邮件格式、信息完整性、语言正确性。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 回复要求明确,只需回答2个简单问题,20-30词,格式简单 | -| B级 | L2 第1-2阶段 | Movers 2 | 回复要求回答3个问题,30-40词,包含问候和结尾 | -| C级 | L2 第3-4阶段 | Movers 3 | 回复要求回答3个问题+1个附加要求,40-50词,格式完整 | -| D级 | L2 第5-6阶段 | Flyers 1 | 回复要求回答3个问题+表达个人观点,50-60词,语言连贯 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 回复要求明确,只需回答2个简单问题,20-30词,格式简单 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 回复要求回答3个问题,30-40词,包含问候和结尾 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 回复要求回答3个问题+1个附加要求,40-50词,格式完整 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 回复要求回答3个问题+表达个人观点,50-60词,语言连贯 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:书面表达、格式规范、信息提取 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语写作邮件回复题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为生日派对邀请` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_picture_writing/SKILL.md b/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_picture_writing/SKILL.md index 55a5d7c..13cbc8a 100644 --- a/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_picture_writing/SKILL.md +++ b/business_production/单元挑战/skills/unit_challenge/questions/writing/writing_picture_writing/SKILL.md @@ -6,12 +6,11 @@ description: K12英语写作看图写作题标准化设计、生产、审校工 ## 题型说明 本技能对应官方题型:**L2 - Movers - 写作 Part2 看图写作【阶段归属:L2专属】**,考察核心能力为**书面表达(图片观察+逻辑组织)**:学生根据1-3张图片的提示,写3-5句话的短文,考察图片信息提取、语言组织、逻辑连贯能力。 ## 难度对应关系(与教研标准对齐) -| 难度等级 | 对应L1/L2阶段 | 对应剑桥考试等级 | 难度描述 | -|----------|------------|------------------|----------| -| A级 | L1 第5-6阶段 | Movers 1 | 1张简单图片,写3句话,描述图片内容即可 | -| B级 | L2 第1-2阶段 | Movers 2 | 2张连续图片,写4句话,描述事件过程 | -| C级 | L2 第3-4阶段 | Movers 3 | 3张连续图片,写5句话,描述完整事件 | -| D级 | L2 第5-6阶段 | Flyers 1 | 3张连续图片,写6句话,描述事件+表达感受 | +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +| A级 | U1-U12(阶段1-2) | Flyers入门→基础 | 1张简单图片,写3句话,描述图片内容即可 | +| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 | 2张连续图片,写4句话,描述事件过程 | +| C级 | U25-U36(阶段5-6) | KET基础→强化 | 3张连续图片,写5句话,描述完整事件 | +| D级 | U37-U48(阶段7-8) | KET高位→达标 | 3张连续图片,写6句话,描述事件+表达感受 | ## 能力标签绑定规则 1. 题型固定绑定通用能力标签:书面表达、图片观察、逻辑组织 2. 能力标签分级规则: @@ -31,7 +30,7 @@ description: K12英语写作看图写作题标准化设计、生产、审校工 | 参数 | 必填 | 格式示例 | 说明 | |------|------|----------|------| | 知识点 | ✅ | `apple n.苹果;red adj.红色;eat v.吃` | 考察的核心知识点列表 | -| 难度等级 | ✅ | `A级` / `L2第2阶段` / `Movers 2` | 难度等级或对应阶段 | +| 难度等级 | ✅ | `A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础` | 难度等级(L2用Unit范围) | | 题量 | 🟢 | `5` | 生成题目数量,默认1道 | | 场景要求 | 🟢 | `场景为公园玩耍` | 指定题目场景,不指定则自动匹配知识点 | ## 输出格式规范 diff --git a/memory/.dreams/events.jsonl b/memory/.dreams/events.jsonl index 177ba61..f058d1e 100644 --- a/memory/.dreams/events.jsonl +++ b/memory/.dreams/events.jsonl @@ -1,3 +1,7 @@ {"type":"memory.recall.recorded","timestamp":"2026-05-07T08:48:55.437Z","query":"王璐辰 user_id permissions","resultCount":1,"results":[{"path":"memory/2026-05-07.md","startLine":57,"endLine":74,"score":0.6621249966506227}]} {"type":"memory.recall.recorded","timestamp":"2026-05-08T01:18:18.412Z","query":"writing_pic_qa 写作看图回答题 生产规则","resultCount":3,"results":[{"path":"memory/2026-05-07.md","startLine":215,"endLine":235,"score":1},{"path":"memory/2026-05-07.md","startLine":199,"endLine":219,"score":1},{"path":"memory/2026-05-07.md","startLine":1,"endLine":20,"score":1}]} {"type":"memory.recall.recorded","timestamp":"2026-05-09T02:03:53.176Z","query":"reading_pic_judge bitable 多维表格 table_id 121301","resultCount":1,"results":[{"path":"memory/2026-05-07.md","startLine":1,"endLine":20,"score":1}]} +{"type":"memory.recall.recorded","timestamp":"2026-05-11T02:19:37.985Z","query":"单元挑战 unit challenge 题型 审校规范 互动组件 样题","resultCount":2,"results":[{"path":"memory/2026-04-13.md","startLine":1,"endLine":5,"score":1},{"path":"memory/2026-05-08.md","startLine":33,"endLine":55,"score":1}]} +{"type":"memory.recall.recorded","timestamp":"2026-05-11T02:37:23.203Z","query":"writing_pic_qa 看图回答题 生产流程 bitable writing","resultCount":5,"results":[{"path":"memory/2026-05-07.md","startLine":215,"endLine":235,"score":1},{"path":"memory/2026-05-08.md","startLine":33,"endLine":55,"score":1},{"path":"memory/2026-05-07.md","startLine":199,"endLine":219,"score":1},{"path":"memory/2026-05-08.md","startLine":1,"endLine":23,"score":1},{"path":"memory/2026-05-07.md","startLine":1,"endLine":20,"score":1}]} +{"type":"memory.recall.recorded","timestamp":"2026-05-11T02:37:23.204Z","query":"reading_pic_qa 看图回答题 口语看图回答题 speaking_pic_qa","resultCount":1,"results":[{"path":"memory/2026-05-07.md","startLine":1,"endLine":20,"score":1}]} +{"type":"memory.recall.recorded","timestamp":"2026-05-11T02:51:47.112Z","query":"reading_pic_qa bitable table_id 单元挑战 阅读","resultCount":1,"results":[{"path":"memory/2026-05-07.md","startLine":1,"endLine":20,"score":1}]} diff --git a/memory/.dreams/short-term-recall.json b/memory/.dreams/short-term-recall.json index 7a9fb23..3507e99 100644 --- a/memory/.dreams/short-term-recall.json +++ b/memory/.dreams/short-term-recall.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-05-09T02:03:53.176Z", + "updatedAt": "2026-05-11T02:51:47.112Z", "entries": { "memory:memory/2026-05-07.md:57:74": { "key": "memory:memory/2026-05-07.md:57:74", @@ -40,18 +40,20 @@ "endLine": 235, "source": "memory", "snippet": "3. He is (4) Japan. → from(男孩穿和服,背景富士山) 4. She is a (7). → student(女孩背书包进校门) - **JSON 结构:** questionSetID=020102, category=writing, type=writing_pic_qa - **关键教训:** - 初版设计错误:让学手写 Anna/China 等非知识点词,被用户纠正 - 初版 questionDesc \"She is a (7) girl.\" 不自然,修正为 \"She is a (7).\" - writing_pic_qa 题型核心考察点:知识点词本身(am/from),而非题干已知信息 - 挖空字母数标记(如(4))必须与答案单词字母数一致 - **词库缺口发现:** L1和L2词库均缺失人称代词(I/you/he/she)、be动词(am/is/are)、介词(from)、国家名等基础功能词,写题时先绕过词汇校验硬性约束 ### 刘彦江 (ou_5af74c1fb96042e33cc0f16b5ca02cf4) — 单元挑战新增3个题型(已完结) - 详见上文 11:19-11:28 记录 ### 刘彦江 (ou_5af74c1fb96042e33cc0f16b5ca02cf4) — 写作看图回答题 \"I am ready / Thank you\" 样题生产 - **时间:** 15:53 ~ 15:55 - **需求:** 使用知识点 \"I am ready! / Thank you for...\" 再生产一套", - "recallCount": 1, + "recallCount": 2, "dailyCount": 0, "groundedCount": 0, - "totalScore": 1, + "totalScore": 2, "maxScore": 1, "firstRecalledAt": "2026-05-08T01:18:18.412Z", - "lastRecalledAt": "2026-05-08T01:18:18.412Z", + "lastRecalledAt": "2026-05-11T02:37:23.203Z", "queryHashes": [ - "390d35f8d143" + "390d35f8d143", + "be437071312f" ], "recallDays": [ - "2026-05-08" + "2026-05-08", + "2026-05-11" ], "conceptTags": [ "writing-pic-qa", @@ -71,18 +73,20 @@ "endLine": 219, "source": "memory", "snippet": "### 刘彦江 (ou_5af74c1fb96042e33cc0f16b5ca02cf4) — 写作看图回答题 \"I am... / I am from...\" 样题生产 - **时间:** 15:38 ~ 15:52 - **需求:** 使用知识点 \"I am... / I am from...\" 生产一套写作看图回答题(writing_pic_qa) - **状态:** ✅ 已完成,用户确认 - **题型级别:** L2(因L1词库缺少所有代词/be动词/介词/国家名) - **难度:** A级 - **题量:** 4小题 - **核心原则(用户明确):** 1. 挖空必须是考察知识点本身的单词(am/from/student),不是手写姓名/国名等附带信息 2. 只能为1个单词挖空,不能两个及以上 3. 正确模式示例:`I (2) Anna.` → 挖空am,数字标记字母数 4. 图片提供语境线索(名牌→am,地标→from国家,书包→student),学生填写核心知识点词 - **输出文件:** `output/writing_pic_qa_I_am_from_020102.json` - **题目清单:** 1. I (2) Anna. → am(女孩指着名牌,图片语境提示姓名已给出) 2. I am (4) China. → from(女孩站在长城前,国家名已给出) 3. He is (4) Japan. → from(男孩穿和服,背景富士山) 4. She is a (7). → student(女孩背书包进校门) - **JSON", - "recallCount": 1, + "recallCount": 2, "dailyCount": 0, "groundedCount": 0, - "totalScore": 1, + "totalScore": 2, "maxScore": 1, "firstRecalledAt": "2026-05-08T01:18:18.412Z", - "lastRecalledAt": "2026-05-08T01:18:18.412Z", + "lastRecalledAt": "2026-05-11T02:37:23.203Z", "queryHashes": [ - "390d35f8d143" + "390d35f8d143", + "be437071312f" ], "recallDays": [ - "2026-05-08" + "2026-05-08", + "2026-05-11" ], "conceptTags": [ "writing-pic-qa", @@ -102,20 +106,24 @@ "endLine": 20, "source": "memory", "snippet": "# 2026-05-07 工作日志 ## 会话记录 ### 刘彦江 (ou_5af74c1fb96042e33cc0f16b5ca02cf4) — 单元挑战新增3个题型 - **时间:** 11:19 ~ 11:28 - **需求:** 单元挑战新增3个题型:阅读看图回答题(reading_pic_qa)、阅读看图判断题(reading_pic_judge)、写作看图回答题(writing_pic_qa) - **状态:** ✅ 已完成题型规范落地 - **交付内容:** 1. 创建3个题型SKILL.md: - `reading/common/reading_pic_qa/SKILL.md` — 阅读看图回答题(共享大图+多题Yes/No判断) - `reading/common/reading_pic_judge/SKILL.md` — 阅读看图判断题(每题独立配图+Yes/No判断) - `writing/common/writing_pic_qa/SKILL.md` — 写作看图回答题(看图+提示答案开头+填空) 2. 更新 `MEMORY.md` 全题型阶段归属规则 3. 明确题型JSON结构、难度等级(A/B/C/D四级)、能力标签、校验规则 - **关键决策:** 3个题型均为 L1&L2 共用,通过难度参数区分阶段;reading_pic_qa 共享大图,reading_pic_judge 每题独立配图 ### 李应瑛 (ou_1bd7317ae2ccfeb57e1132028847279e) — 单词表对比请求", - "recallCount": 2, + "recallCount": 5, "dailyCount": 0, "groundedCount": 0, - "totalScore": 2, + "totalScore": 5, "maxScore": 1, "firstRecalledAt": "2026-05-08T01:18:18.412Z", - "lastRecalledAt": "2026-05-09T02:03:53.176Z", + "lastRecalledAt": "2026-05-11T02:51:47.112Z", "queryHashes": [ "390d35f8d143", - "fd4c9b7de37b" + "fd4c9b7de37b", + "be437071312f", + "0e27779653c1", + "5c08c6f8788a" ], "recallDays": [ "2026-05-08", - "2026-05-09" + "2026-05-09", + "2026-05-11" ], "conceptTags": [ "reading-pic-qa", @@ -127,6 +135,100 @@ "memory.md", "a/b/c/d四级" ] + }, + "memory:memory/2026-04-13.md:1:5": { + "key": "memory:memory/2026-04-13.md:1:5", + "path": "memory/2026-04-13.md", + "startLine": 1, + "endLine": 5, + "source": "memory", + "snippet": "## 2026-04-13 12:28 [童瑶需求] 读取飞书文档https://makee-interactive.feishu.cn/wiki/QkY5wkcZYindgzkv5O0cpApRnzh内L1S1_Unit 11【其他需求】中所有的图片,并根据【描述】列的ID重命名为\"ID.png\" [执行结果] 完成35张图片的提取和重命名,保存到unit11_final_images/目录下,包含L1-L5全级别教研图、选项图,全部符合命名规则。", + "recallCount": 1, + "dailyCount": 0, + "groundedCount": 0, + "totalScore": 1, + "maxScore": 1, + "firstRecalledAt": "2026-05-11T02:19:37.985Z", + "lastRecalledAt": "2026-05-11T02:19:37.985Z", + "queryHashes": [ + "6f88450e9f9b" + ], + "recallDays": [ + "2026-05-11" + ], + "conceptTags": [ + "id.png", + "保存到unit11-final-images/目录下", + "包含l1-l5全级别教研图", + "需求", + "https", + "makee", + "interactive.feishu.cn", + "wiki" + ] + }, + "memory:memory/2026-05-08.md:33:55": { + "key": "memory:memory/2026-05-08.md:33:55", + "path": "memory/2026-05-08.md", + "startLine": 33, + "endLine": 55, + "source": "memory", + "snippet": "**图片资源新规:** - 每个题组共享一张图(非每题独立配图) - 产出 JSON 同时输出图片描述 prompt(英文,卡通简线条风格) - prompt 写入 bitable `图片描述` 列,格式 `{\"first\": \"...\", \"second\": \"...\"}` **通用化写入流程:** - 单元挑战全题型适用:定位 bitable 表 → 创建/更新记录 → 写入 `题目集合 ID` + `jsonData` + `图片描述` - 各题型仅 jsonData 结构不同,写入流程统一 **重写文件:** - `business_production/单元挑战/skills/unit_challenge/questions/writing/common/writing_pic_qa/SKILL.md`(11节完整规范) - `business_production/单元挑战/skills/unit_challenge/questions/writing/writing_pic_qa/SKILL.md`(简化为快速入口) **bitable 更新:** 已将新 JSON(每题组1图2题)+ 图片描述写入 `recviZlAxxXlAb` ### 刘彦江 (ou_5af74c1fb96042e33cc0f16b5ca02cf4) — 图片描述格式修正 - **时间:** 09:46 ~ 09:51 - **修正:** 用户给出中文样例格式,要求图片描述从英文 prompt 改为纯中文结构化列表 - **新格式:** 编号 `1.` / `2.` 自上而下,每段", + "recallCount": 2, + "dailyCount": 0, + "groundedCount": 0, + "totalScore": 2, + "maxScore": 1, + "firstRecalledAt": "2026-05-11T02:19:37.985Z", + "lastRecalledAt": "2026-05-11T02:37:23.203Z", + "queryHashes": [ + "6f88450e9f9b", + "be437071312f" + ], + "recallDays": [ + "2026-05-11" + ], + "conceptTags": [ + "创建/更新记录", + "图片", + "资源", + "共享", + "一张", + "每题", + "独立", + "同时" + ] + }, + "memory:memory/2026-05-08.md:1:23": { + "key": "memory:memory/2026-05-08.md:1:23", + "path": "memory/2026-05-08.md", + "startLine": 1, + "endLine": 23, + "source": "memory", + "snippet": "### 刘彦江 (ou_5af74c1fb96042e33cc0f16b5ca02cf4) — writing_pic_qa 样题生产 \"I am / I am going to / I am ready / Thank you for\" - **时间:** 09:26 ~ 09:34 - **需求:** 生产 writing_pic_qa A级样题,ID=000001,2题组各2小题 - **状态:** ✅ 已完成,已写入飞书多维表格 - **目标表格:** 单元挑战 → 写作-P3-看图回答题 (tblFc9TVl2PeM2tg),record_id=recviZlAxxXlAb - **题目清单:** 题组1 (first): 1. I (2) a student. → am(女孩穿校服举手,考察be动词am) 2. I am (5) to school. → going(男孩背书包走向校门,考察going) 题组2 (second): 3. I am (5)! → ready(男孩穿泳衣戴泳镜,考察状态词ready) 4. Thank you (3) the book. → for(女孩接书微笑,考察介词for) - **核心修正(用户反馈):** 1. 挖空必须考察知识点核心词,非附带信息(you → for) 2. 不能挖空题干已有信息(如姓名/国家名),只挖知识点词本身 3. 只能为1个单词挖空 4. question与questionDesc人称必须一致(统一改为 What does [角色] say? → 第一人称直接", + "recallCount": 1, + "dailyCount": 0, + "groundedCount": 0, + "totalScore": 1, + "maxScore": 1, + "firstRecalledAt": "2026-05-11T02:37:23.203Z", + "lastRecalledAt": "2026-05-11T02:37:23.203Z", + "queryHashes": [ + "be437071312f" + ], + "recallDays": [ + "2026-05-11" + ], + "conceptTags": [ + "writing-pic-qa", + "写作-p3-看图回答题", + "record-id", + "如姓名/国家名", + "彦江", + "生产", + "going", + "ready" + ] } } } diff --git a/memory/2026-05-11-0956.md b/memory/2026-05-11-0956.md new file mode 100644 index 0000000..d56737d --- /dev/null +++ b/memory/2026-05-11-0956.md @@ -0,0 +1,109 @@ +# Session: 2026-05-11 09:56:30 GMT+8 + +- **Session Key**: agent:xiaoyan:feishu:direct:ou_5af74c1fb96042e33cc0f16b5ca02cf4 +- **Session ID**: 986e3170-1f81-48b5-a6b9-4154e280140d +- **Source**: feishu + +## Conversation Summary + +user: 看图判断题,产出填入飞书多维表格 + +ID:121301 +L1 A级难度 +能力tag:图文判断 | 句图一致性 +题组1: +home +house +sofa +cupboard +bedroom + +题组2: +zoo +hippo +bear +jeans +sock + +ID:121401 +L1 A级难度 +能力tag:图文判断 | 句图一致性 +题组1: +student +school +teacher +crayon +board + +题组2: +mat +letter +desk +schoolbag +handbag + +ID:121501 +L1 A级难度 +能力tag:图文判断 | 句图一致性 +题组1: +body +head +mouth +ear +friend + + +题组2: +camera +boy +girl +skateboarding +skateboard + + +ID:121601 +L1 A级难度 +能力tag:图文判断 | 句图一致性 +题组1: +summer +autumn +winter +cold +spring + + +题组2: +bus +taxi +trousers +shorts +snow +assistant: 先确认身份和资源,再动手。 +assistant: 先读之前的同类任务记忆和校验词库。 +assistant: 找到了目标多维表格。现在获取表格结构和题型规范。 +assistant: 找到了!现在获取表格字段结构。 +assistant: 表格结构已确认。现在开始生产 4 套看图判断题并写入。 +assistant: 写入成功,现在回读验证。 +assistant: 问题在于 shell 脚本处理嵌套 JSON 时有转义问题。直接用 Python + curl 写入。 +assistant: 全部写入并验证通过。现在更新记忆并向用户汇报。 +assistant: 所有4套题目已完成并写入飞书多维表格。向刘彦江汇报: + +--- + +## 📝 reading_pic_judge 看图判断题 — 4套产出完成 + +**目标表格:** 阅读-P6-看图判断题 +**难度:** L1 A级 | **能力标签:** 图文判断 + 句图一致性 + +| ID | 题组1 | 题组2 | 判断 | record_id | +|---|---|---|---|---| +| **121301** | home, house, sofa, cupboard, bedroom | zoo, hippo, bear, jeans, sock | **6Y/4N** | `recvj5nIAxF3XG` | +| **121401** | student, school, teacher, crayon, board | mat, letter, desk, schoolbag, handbag | **6Y/4N** | `recvj5nJG33bBE` | +| **121501** | body, head, mouth, ear, friend | camera, boy, girl, skateboarding, skateboard | **6Y/4N** | `recvj5nKxhm8X6` | +| **121601** | summer, autumn, winter, cold, spring | bus, taxi, trousers, shorts, snow | **6Y/4N** | `recvj5nLUpubaU` | + +每题配有独立图片描述(卡通简单线条,白色背景/简单场景),已全量写入+回读验证通过。 + +### ⚠️ 词汇校验提示 + +40个知识点中,仅 **8个** 在 L1 词库内(sofa / zoo / sock / student / school / teacher / mat / mouth),**32个** 不在词库。已按您直接提供的词汇产出,未禁用超纲词汇。需不需要后续调整? diff --git a/memory/2026-05-11-1129.md b/memory/2026-05-11-1129.md new file mode 100644 index 0000000..ab59e35 --- /dev/null +++ b/memory/2026-05-11-1129.md @@ -0,0 +1,10 @@ +# Session: 2026-05-11 11:29:24 GMT+8 + +- **Session Key**: agent:xiaoyan:feishu:direct:ou_270924c22bf8d63cf356dbd37614055f +- **Session ID**: e37475eb-584d-497c-bc3c-7fb5de189903 +- **Source**: feishu + +## Conversation Summary + +user: hi +assistant: 你好,若松。有什么需要处理的? diff --git a/memory/2026-05-11-1131.md b/memory/2026-05-11-1131.md new file mode 100644 index 0000000..eaacd97 --- /dev/null +++ b/memory/2026-05-11-1131.md @@ -0,0 +1,14 @@ +# Session: 2026-05-11 11:31:12 GMT+8 + +- **Session Key**: agent:xiaoyan:feishu:group:oc_5c9f0e631266b9ef6e05a95daa80fbda +- **Session ID**: b1a2ee39-373d-4068-994f-57fc9205ec12 +- **Source**: feishu + +## Conversation Summary + +user: 你在吗 +assistant: 在的,若松。有什么需要处理的? +user: 你在吗 +assistant: 在的,若松。有什么需要处理的? +user: @小研 /reset +assistant: 好的,已重置。有什么需要处理的随时说。 diff --git a/memory/2026-05-11.md b/memory/2026-05-11.md new file mode 100644 index 0000000..940ea67 --- /dev/null +++ b/memory/2026-05-11.md @@ -0,0 +1,88 @@ + +### 刘彦江 — reading_pic_judge 看图判断题批量生产(4套,17:28 ~ 17:35) +- **需求:** 4套 reading_pic_judge,L1 A级,能力标签 图文匹配 +- **题型特点:** 每题独立配图(questionImage),first/second双题组包裹 +- **状态:** ✅ 已完成,4套全部入库+图片描述回填验证通过 +- **脚本:** `scripts/batch_reading_pic_judge.py`、`scripts/update_pic_judge_descriptions.py` + +| ID | 题组1 | 题组2 | Y/N | 图片描述 | +|----|-------|-------|-----|---------| +| 121301 | home/house/sofa/cupboard/bedroom | zoo/hippo/bear/jeans/sock | 3Y2N | ✅ 596chars | +| 121401 | student/school/teacher/crayon/board | mat/letter/desk/schoolbag/handbag | 3Y2N | ✅ 562chars | +| 121501 | body/head/mouth/ear/friend | camera/boy/girl/skateboarding/skateboard | 3Y2N | ✅ 553chars | +| 121601 | summer/autumn/winter/cold/spring | bus/taxi/trousers/shorts/snow | 3Y2N | ✅ 565chars | + +- **L1 A级**:3-4词极简陈述句,使用单物品/场景图 +- record_ids: recvj5nIAxF3XG, recvj5nJG33bBE, recvj5nKxhm8X6, recvj5nLUpubaU + + +### 刘彦江 — reading_pic_qa 看图回答题(2套: 010701/010801,18:00 ~ 18:10) +- **需求:** 2套 reading_pic_qa,L2 A级,能力标签 图文判断|句图一致性 +- 每套5+5道 Y/N 判断,3Y2N平衡,4-7词陈述句 +- **状态:** ✅ 已完成,2套全部入库 + 图片描述回填验证通过 +- **脚本:** `scripts/batch_reading_pic_qa_2.py` + +| ID | 题组1 | 题组2 | record_id | +|----|-------|-------|-----------| +| 010701 | notebook/star/folder/diary/contact | friendly/crazy/brilliant/kind/afraid | recvjiQuGtsVp8 | +| 010801 | uncle/aunt/grandma/neighbour/pet | expensive/entrance/upstairs/downstairs/toast | recvjiQvc8EsB8 | + +- 表:阅读-P7-看图回答题(tblweY65jGBiwSdt) +- 当前该表累计:11条记录(000001~010801,含reading_pic_judge类型 4 条及其他) + +### 刘彦江 — 口语-P1-日常回答 ability 标签填充(18:15 ~ 18:25) +- **需求:** 从能力标签名称图片中,选择与已生成题目契合的标签填入 jsonData +- 表:口语-P1-日常回答(tblRGv7k4WH58Jgq),26条有效记录,185道题 +- **状态:** ✅ 全部完成 +- **脚本:** `scripts/update_speaking_qa_ability.py` +- **标签映射(5个适用标签):** + - `基础信息表达|个人信息问答` — 姓名/年龄/家庭/外貌类 + - `表达喜好与理由` — 偏好/观点/意愿类 + - `过去经历描述|Past Activities` — 过去事件类 + - `互动应答|问答交流` — 社交场景类 + - `信息交换|双向问答` — 物品归属/需求类 +- 未使用标签(属看图/发音题型专用):图片描述类(3个)、图片对比、故事讲述、发音与语调 +- **分类逻辑**:基于关键词匹配(what do you say → 互动应答; what did you/last → 过去经历; favorite/like/want → 喜好; name/age/from/family → 个人信息; whose/need → 信息交换) + +### 刘彦江 — reading_matchInfo 阅读信息匹配题(4套: 010901/011001/011101/011201,18:55 ~ 19:15) +- **需求:** 4套 reading_matchInfo,L2 A级,能力标签 标识与通知理解|标识 & 通知 +- 每套 first=5q + second=5q,每题含 questionImage/question/options/answer/ability/explanation +- **表:** 阅读-P1-信息匹配题(tblCgfYDnnqwLfgH) +- **自动化问题:** 该表存在异步自动化,创建后会将 jsonData 改写为模板壳(减少题量+覆盖 ability) +- **解决方案:** 两阶段延迟写入(先创建空记录→等35s让自动化完成→PUT jsonData),010901/011201 修复后验证通过 + +| ID | 题组1 | 题组2 | record_id | +|----|-------|-------|-----------| +| 010901 | north/east/south/west/exit(方向标识) | businessman/job/letter/call/guard(职场) | recvjiYAHtp05c | +| 011001 | beat/dancer/drum/player/work(音乐体育) | aged/good-looking/mistake/weakness/be good at(人物描述) | recvjiXLdt5itq | +| 011101 | dangerous/badly/worst/steal/run away(危险) | minute/close/kilometre/second/away(时间距离) | recvjiXLOOWMsq | +| 011201 | summer/winter/Earth/holiday/term(季节) | examination/plus/minus/biology/study(学校) | recvjiYBd8Mejs | + +- **脚本:** `scripts/batch_reading_matchInfo.py` +- **关键教训:** reading_matchInfo 表同样需要延迟写策略,直接创建+jsonData会被自动化覆盖 + +### 刘彦江 — reading_matchInfo 图片描述+题目重写(19:20 ~ 19:30) +- **反馈问题:** + 1. 图片应是**真实标识/通知**(非标签式),如零售楼层导览那种 + 2. 图片上的文字**至少为一句话**,L2 A级难度,不能只有一个词 +- **处理:** 4 套 reading_matchInfo 全部重新设计(题目+图片描述),两阶段延迟写入 +- **新record_ids:** recvjj2Nuw9MJd / recvjj2NZz75zb / recvjj2Ov8Stks / recvjj2OX0Dmo5 +- **改进要点:** + - 每张图改为真实通知/标识,如 "TRAINS to the NORTH. Go to Platform 3." + - 图片文字至少 1 句完整陈述,含足够信息供阅读提取 + - 题目围绕通知/标识内容提问,考察学生从标识中抓取关键信息的能力 + +### 刘彦江 — reading_matchInfo 阅读信息匹配题(6套L2 B级: 021301-021801,19:30 ~ 19:45) +- **需求:** 6套 reading_matchInfo,L2 B级,能力标签 标识与通知理解|标识 & 通知 +- **B级规范:** 文本3-5词短句,图片为同类差异,简单对应;每张图是真实的标识/通知(非标签式) +- **状态:** ✅ 全部完成(两阶段延迟写入策略,避免自动化覆盖) +- **脚本:** `scripts/batch_reading_matchInfo.py` + +| ID | 题组1 | 题组2 | record_id | +|----|-------|-------|-----------| +| 021301 | actor/beard/different/brave/neighbour(人物) | dinner/cooking/fire/album/diary(生活) | recvjjk7oAT1YN | +| 021401 | plan/repair/newspaper/backpack/seat(日程) | garden/melon/water/plant/summer(自然) | recvjjk7TU4ZFs | +| 021501 | visitor/welcome/invite/laugh/guest(社交) | mistake/instruction/reason/technology/brush(学习) | recvjjk8mju8ou | +| 021601 | record/event/enter/return/sort(管理) | handbag/camera/gate/wheel/entrance(场所) | recvjjk8Q5ZMIu | +| 021701 | driver/street/market/guidebook/shopping(出行) | machine/draw/basketball/bookcase/invention(活动) | recvjjk9m5zhMS | +| 021801 | library/magazine/cartoon/test/carefully(学校) | battery/corner/theatre/climb/midday(场所时间) | recvjjk9OVmASC | diff --git a/output/单元挑战_审校规范_2026-05-11.md b/output/单元挑战_审校规范_2026-05-11.md new file mode 100644 index 0000000..e59d9e7 --- /dev/null +++ b/output/单元挑战_审校规范_2026-05-11.md @@ -0,0 +1,889 @@ +# 单元挑战(Unit Challenge)生成规范与审校标准 + +> 最后更新:2026-05-11 | 基于 skills/unit_challenge 全部题型 SKILL.md 汇总整理 + +--- + +## 一、产品定位 + +Vala 是一款按照 KET 考试要求设计的英语学习产品。**单元挑战** 是 Unit 级别的综合评估环节,核心要求: + +- **与考试题型一致性:** 在单元挑战上追求与 KET 考试真题样式一致 +- **以 Unit 为单位交付效果**,每个 Unit 包含 30个单词 + 20个句型 + 1个语法点 + 1个发音规则 +- **形式:** 模仿 KET 考试真题样式,时间目标 20 分钟(约 1/5 个 KET 考试) +- **出题逻辑:** 根据回顾和总结中的错误情况,智能出题;每个题目绑定对应知识模块标签 +- **发音:** 单元挑战中不出题(发音模块在总结中完成) + +--- + +## 二、难度等级体系 + +**L1 和 L2 使用两套不同的难度对标体系**,不可混用: + +### L1 难度等级(对标 Starters/Movers) + +| 难度等级 | 剑桥对标 | 通用说明 | +|---------|---------|---------| +| A级 | Starters 1-2 | 基础入门,简单直接,强视觉特征,低干扰 | +| B级 | Starters 3 | 进阶入门,简单复合,细节特征,中干扰 | +| C级 | Movers 1 | 初级提升,需要推理整合,高干扰 | +| D级 | Movers 2-3 / Flyers 1 | 中级能力,隐含推断,极高干扰 | + +### L2 难度等级(对标 Flyers/KET,每 2 个阶段为 1 个难度等级) + +L2 共 48 个 Unit,分 8 个阶段,每 2 个阶段合并为 1 个难度等级(A/B/C/D): + +| 难度等级 | 阶段 | Unit 范围 | 对标说明 | +|---------|------|----------|---------| +| **A级** | 阶段1-2 | U1-U12 | Flyers 入门 → Flyers 基础 | +| **B级** | 阶段3-4 | U13-U24 | Flyers 达标 → KET 入门 | +| **C级** | 阶段5-6 | U25-U36 | KET 基础 → KET 强化 | +| **D级** | 阶段7-8 | U37-U48 | KET 高位 → KET 达标 | + +**8 个阶段明细:** + +| 阶段 | Unit 范围 | 说明 | +|------|----------|------| +| 1 | U1-U6 | Flyers 入门 | +| 2 | U7-U12 | Flyers 基础 | +| 3 | U13-U18 | Flyers 达标 | +| 4 | U19-U24 | KET 入门 | +| 5 | U25-U30 | KET 基础 | +| 6 | U31-U36 | KET 强化 | +| 7 | U37-U42 | KET 高位 | +| 8 | U43-U48 | KET 达标 | + +--- + +## 三、阶段归属总表 + +### 听力类 + +| 题型 | SKILL 名称 | 剑桥对应 | 阶段归属 | +|------|-----------|---------|---------| +| 听力拖拽题 | listening-drag | 听力 Part1 图片连线/人物匹配 | **L1 专属** | +| 听力选图题 | listening_picture_selection | 听力 Part6 听力选图 | **L1 专属** | +| 听力图片选择题 | listening-choicePic | — | **L2 专属** | +| 听力表格填空题 | listening_form_fill | 听力 Part2 表格填空 | **L2 专属** | +| 听力长对话选择题 | listening_long_conversation | 听力 Part3 长对话选择 | **L2 专属** | +| 听力短对话选择题 | listening_short_conversation | 听力 Part4 短对话选择 | **L2 专属** | +| 听力信息匹配题 | listening_info_match | — | **L2 专属** | + +### 阅读类 + +| 题型 | SKILL 名称 | 剑桥对应 | 阶段归属 | +|------|-----------|---------|---------| +| 阅读看图回答题 | reading_pic_qa | — | **L1&L2 共用**(通过难度参数区分) | +| 阅读看图判断题 | reading_pic_judge | — | **L1&L2 共用**(通过难度参数区分) | +| 阅读信息匹配题 | reading_info_match | 阅读 Part1 信息匹配 | **L2 专属** | +| 阅读段落匹配题 | reading_paragraph_match | 阅读 Part2 段落匹配 | **L2 专属** | +| 阅读长文选择题 | reading_long_passage | 阅读 Part3 长文选择 | **L2 专属** | +| 阅读完形填空题 | reading_cloze | 阅读 Part4 完形填空 | **L2 专属** | +| 阅读开放填空题 | reading_open_fill | 阅读 Part5 开放填空 | **L2 专属** | + +### 写作类 + +| 题型 | SKILL 名称 | 剑桥对应 | 阶段归属 | +|------|-----------|---------|---------| +| 写作看图回答题 | writing_pic_qa | 写作 Part3 看图回答 | **L1&L2 共用**(通过难度参数区分) | +| 写作邮件回复题 | writing_email_reply | 写作 Part1 邮件回复 | **L2 专属** | +| 写作看图写作题 | writing_picture_writing | — | **L2 专属** | + +### 口语类 + +| 题型 | SKILL 名称 | 剑桥对应 | 阶段归属 | +|------|-----------|---------|---------| +| 口语看图回答题 | speaking_pic_qa | 口语 Part2 看图问答 | **L1 专属** | +| 口语看图识物题 | speaking_pic_recognize | — | **L1 专属** | +| 口语日常回答题 | speaking_daily_qa / speaking_qa | 口语 Part1 日常回答 | **L1&L2 共用**(通过难度参数区分) | +| 口语话题讨论题 | speaking_topic_discussion | 口语 Part2 话题讨论 | **L2 专属** | + +--- + +## 四、全局通用规则 + +### 4.1 词汇管控(强制执行) + +- **L1 级别题目:** 仅限使用 L1 词库(980词),路径:`business_knowledge/L1_word_list.json` +- **L2 级别题目:** 可使用 L1 + L2 词库(1470词),路径:`business_knowledge/L2_word_list.json` +- **超纲拦截:** 任何不在对应词库内的词汇禁止出现,基础功能词(代词/be动词/介词)暂豁免 +- **句型管控:** L1 题目句型必须在 L1 句型库(490个)范围内;L2 题目句型在 L2 句型库(380个)范围内 + +### 4.2 知识点去重规则 + +- 同一题包内同一知识点最多出现 2 次 +- 同一题包内不得存在语义高度重复的题目 + +### 4.3 逐题解析要求 + +所有题目必须附带标准化解析,结构固定:**能力点说明 + 做题技巧 + 正向鼓励**,80-100字,中文为主,核心关键词可标注英文。 + +### 4.4 能力标签规则 + +每道题必须绑定能力标签,格式: +- 1级标签:题型通用大标签(如「阅读理解」「听力理解」「口语表达」「书面表达」「图文匹配」) +- 2级标签:根据考察点动态匹配(如「细节定位」「词汇运用」「信息提取」等) +- 口语/写作题型需绑定语言输出标签(如「简短作答」「一词答案」「信息回复」「格式应用」) + +### 4.5 价值观导向 + +- 避免使用负面词汇评价自己或他人 +- 优先使用正向引导表述 +- 内容贴近学生生活,情节积极正向 + +--- + +## 五、各题型的生成规范与审校要点 + +--- + +### 5.1 听力拖拽题(listening-drag)★ L1 专属 + +**剑桥对应:** Starters 听力 Part1 图片连线/人物匹配 + +**核心能力:** 基础语境理解(场景/物品/动作识别)、多模态信息匹配、注意力分配 + +**题型结构:** +- 1张场景大图(含5个角色),5段听力对话 +- 学生听对话后,将人名拖拽到对应角色 + +**题目结构(5小题):** +- Q1:显性事实理解(关键词识别) +- Q2:显性事实理解(关键词识别) +- Q3:多信息整合 +- Q4:干扰抑制 / 语用推断(否定与纠错) ← **Q4 强制要求:必须包含否定/纠错逻辑(如 No, sorry / I mean 等表述)** + +**难度细则:** + +| 等级 | 阶段 | 特征 | +|------|------|------| +| A级 | L1 1-2阶段 | 强视觉特征,听力为3-5词简单句,低干扰 | +| B级 | L1 3-4阶段 | 细节特征,听力为5-8词复合句,中干扰 | +| C级 | L1 5-6阶段 | 状态动作,听力为8-10词描述性语言,高干扰 | +| D级 | L2 1-2阶段 | 情境推断,听力为10-12词隐含逻辑,极高干扰 | + +**审校要点:** +- [ ] Q4 是否包含「No, sorry/I mean」等否定/纠错表述 +- [ ] 听力对话是否自然日常(不用统一祈使句,用陈述句/疑问句等多种形式) +- [ ] 图片描述中人物特征是否清晰可辨 +- [ ] 能力标签:Q1/Q2=显性事实理解(关键词识别),Q3=多信息整合,Q4=干扰抑制/语用推断 + +**输出关键字段:** 听力文本、图片描述、人物顺序、选项排序(打乱)、答案(4个索引)、能力项 + +--- + +### 5.2 听力选图题(listening_picture_selection)★ L1 专属 + +**剑桥对应:** Starters 听力 Part6 听力选图 + +**核心能力:** 图片信息识别(听力信息匹配)、快速反应 + +**题型结构:** +- 5-6个听力单句/短对话 +- 每小题对应一组3张图片,选出与听力内容匹配的图片 + +**难度细则:** + +| 等级 | 阶段 | 特征 | +|------|------|------| +| A级 | L1 1-2阶段 | 图片3张类别完全不同,听力为3-5词简单句 | +| B级 | L1 3-4阶段 | 图片同类物品不同属性(颜色/大小),听力为5-8词 | +| C级 | L1 5-6阶段 | 同一场景不同动作,听力为8-10词 | +| D级 | L2 1-2阶段 | 同一场景不同逻辑,听力为10-12词 | + +**审校要点:** +- [ ] 图片3张,差异度匹配对应难度(A级差异极大,D级需甄别细节) +- [ ] 听力句子长度符合难度要求 +- [ ] 答案与听力内容严格匹配 + +--- + +### 5.3 听力短对话选择题(listening_short_conversation)★ L2 专属 + +**剑桥对应:** Movers 听力 Part4 短对话选择 + +**核心能力:** 场景理解(快速信息抓取+反应) + +**题型结构:** +- 4-5段1-2轮短对话(A/B角色),每段对应1道选择题(3个选项) + +**难度细则:** + +| 等级 | Unit 范围 | 对话轮次 | 考察特征 | +|------|----------|---------|---------| +| A级 | U1-U12 | 1轮 | Flyers入门~基础,直接问物品/场景,答案明确 | +| B级 | U13-U24 | 1-2轮 | Flyers达标~KET入门,问属性/动作,同类选项差异 | +| C级 | U25-U36 | 2轮 | KET基础~强化,问意图/结果,需要简单推理 | +| D级 | U37-U48 | 2轮 | KET高位~达标,问隐含观点/计划,需要推理 | + +**审校要点:** +- [ ] 对话简短自然,符合日常交流场景 +- [ ] 干扰项为常见易混淆的场景内容(非无关内容) +- [ ] 每段对话有明确语境和角色区分 + +--- + +### 5.4 听力长对话选择题(listening_long_conversation)★ L2 专属 + +**剑桥对应:** Movers 听力 Part3 长对话选择 + +**核心能力:** 逻辑理解(信息整合+干扰抑制) + +**题型结构:** +- 1段3-6轮长对话(A/B角色),3-4道选择题(每道3个选项) + +**难度细则:** + +| 等级 | Unit 范围 | 对话轮次 | 题目数 | 干扰特征 | +|------|----------|---------|--------|---------| +| A级 | U1-U12 | 3轮 | 3题 | Flyers入门~基础,答案直接对应,无干扰 | +| B级 | U13-U24 | 4轮 | 3题 | Flyers达标~KET入门,需简单推理,1干扰 | +| C级 | U25-U36 | 5轮 | 4题 | KET基础~强化,需逻辑整合,2干扰 | +| D级 | U37-U48 | 6轮 | 4题 | KET高位~达标,隐含推断,3干扰 | + +**审校要点:** +- [ ] 对话自然流畅,符合日常交流逻辑,角色明确 +- [ ] 干扰项设置合理(对话中出现的无关信息/部分正确信息/看似合理但错误的信息) +- [ ] 对话长度/轮次/干扰项数量严格匹配难度等级 + +--- + +### 5.5 听力表格填空题(listening_form_fill)★ L2 专属 + +**剑桥对应:** Movers 听力 Part2 表格填空 + +**核心能力:** 信息抓取(关键词识别+拼写准确性) + +**题型结构:** +- 1-2段短对话/独白 + 1份表格模板(含空缺位) + +**难度细则:** + +| 等级 | Unit 范围 | 空缺数 | 答案类型 | 干扰 | +|------|----------|--------|---------|------| +| A级 | U1-U12 | 2-3空 | Flyers入门~基础,1-2字母/数字,直接念出 | 无 | +| B级 | U13-U24 | 3-4空 | Flyers达标~KET入门,3-5字母单词 | 无 | +| C级 | U25-U36 | 4-5空 | KET基础~强化,单词/短语 | 1个干扰 | +| D级 | U37-U48 | 5-6空 | KET高位~达标,短语/短句 | 2-3个干扰 | + +**审校要点:** +- [ ] 表格结构清晰,空缺位标注明确(用 `___`) +- [ ] 表头信息完整 +- [ ] 答案在听力文本中有明确依据 + +--- + +### 5.6 听力图片选择题(listening-choicePic)★ L2 专属 + +**核心能力:** 图片信息提取与听力信息匹配 + +**备注:** 具体规格详见对应 SKILL.md,此处列核心框架。 + +--- + +### 5.7 听力信息匹配题(listening_info_match)★ L2 专属 + +**核心能力:** 多信息点匹配与筛选 + +**备注:** 具体规格详见对应 SKILL.md,此处列核心框架。 + +--- + +### 5.8 阅读看图回答题(reading_pic_qa)★ L1&L2 共用 + +**核心能力:** 图文匹配(阅读理解+图片信息提取) + +**题型结构:** +- 1张场景大图(textImage),N道 Yes/No 判断题(3-6题) +- 所有小题共享同一张大图 + +**JSON 结构关键字段:** +```json +{ + "category": "reading", + "type": "reading_pic_qa", + "questionSetID": "阶段-U编号-自增", + "textTitle": "Look and read. Put a tick (Yes) or a cross (No) in the box.", + "textImage": "{ID}-00.png", + "questionSet": [ + { + "question": "描述性句子", + "answerText": "Yes | No", + "ability": ["图文匹配"], + "explanation": "判断依据说明" + } + ] +} +``` + +**难度细则:** + +L1 维度(对标 Starters/Movers): + +| 等级 | 阶段 | 句子长度 | 图片特征 | +|------|------|---------|---------| +| A级 | L1 1-2阶段 | 3-5词简单陈述句 | 元素差异明显 | +| B级 | L1 3-4阶段 | 5-8词简单陈述句 | 多人/多物但干扰少 | +| C级 | L1 5-6阶段 | 8-12词复合陈述句 | 场景较复杂,多人物多动作 | +| D级 | L2 1-2阶段 | 12-15词复合陈述句 | 密集场景,需仔细甄别 | + +L2 维度(对标 Flyers/KET): + +| 等级 | Unit 范围 | 句子长度 | 图片特征 | +|------|----------|---------|---------| +| A级 | U1-U12 | 3-5词简单陈述句 | Flyers入门~基础,元素差异明显 | +| B级 | U13-U24 | 5-8词简单陈述句 | Flyers达标~KET入门,多人/多物干扰少 | +| C级 | U25-U36 | 8-12词复合陈述句 | KET基础~强化,场景复杂多人物动作 | +| D级 | U37-U48 | 12-15词复合陈述句 | KET高位~达标,密集场景需甄别 | + +**审校要点:** +- [ ] Yes/No 题数量大致各半(偏差不超过60%) +- [ ] No题的描述不应过于明显,需引导学生仔细甄别 +- [ ] 所有题干描述对象必须存在于图片场景中 +- [ ] textTitle 固定为 `Look and read. Put a tick (Yes) or a cross (No) in the box.` +- [ ] A/B级单句不超过8个单词,C/D级不超过15个单词 + +--- + +### 5.9 阅读看图判断题(reading_pic_judge)★ L1&L2 共用 + +**核心能力:** 图文匹配(阅读理解+图片信息提取) + +**题型结构:** +- 每题独立配1张图(与 reading_pic_qa 不同,非共享大图) +- N道 Yes/No 判断题(通常5题) + +**JSON 结构关键字段:** +```json +{ + "category": "reading", + "type": "reading_pic_judge", + "questionSetID": "阶段-U编号-自增", + "textTitle": "Look and read. Put a tick (Yes) or a cross (No) in the box.", + "questionSet": [ + { + "questionImage": "{ID}-{序号}.png", + "question": "描述性句子", + "answerText": "Yes | No", + "ability": ["图文匹配"], + "explanation": "判断依据说明" + } + ] +} +``` + +**难度细则:** + +L1 维度(对标 Starters/Movers): + +| 等级 | 阶段 | 句子长度 | 图片特征 | +|------|------|---------|---------| +| A级 | L1 1-2阶段 | 1-3词极简陈述句 | 单一物品,判断直接 | +| B级 | L1 3-4阶段 | 4-6词简单陈述句 | 单一物品+简单背景,需辨识颜色/数量 | +| C级 | L1 5-6阶段 | 6-10词复合陈述句 | 包含物品属性/动作/方位等多维信息 | +| D级 | L2 1-2阶段 | 10-15词复合描述句 | 复杂场景缩略图,需甄别细节差异 | + +L2 维度(对标 Flyers/KET): + +| 等级 | Unit 范围 | 句子长度 | 图片特征 | +|------|----------|---------|---------| +| A级 | U1-U12 | 1-3词极简陈述句 | Flyers入门~基础,单物品直接判断 | +| B级 | U13-U24 | 4-6词简单陈述句 | Flyers达标~KET入门,辨识颜色/数量 | +| C级 | U25-U36 | 6-10词复合陈述句 | KET基础~强化,属性/动作/方位多维 | +| D级 | U37-U48 | 10-15词复合描述句 | KET高位~达标,复杂场景甄别细节 | + +**与 reading_pic_qa 的区别:** + +| 维度 | reading_pic_qa | reading_pic_judge | +|------|----------------|-------------------| +| 配图方式 | 1张大图共享 | 每题独立配图 | +| 考察点 | 大图中定位信息并判断 | 单图直接匹配判断 | +| 图片规模 | 复杂场景图 | 简单单物品/单场景图 | +| L1适配 | 适合L1中期起步 | 适合L1早期入门 | + +**审校要点:** +- [ ] 每道题独立配图,命名格式 `{ID}-{序号}.png`(序号从00开始连续递增) +- [ ] Yes/No 题数量大致各半(偏差不超过60%) +- [ ] No题的描述指代物品应与图片内容不同 +- [ ] 句子长度匹配难度等级 + +--- + +### 5.10 阅读信息匹配题(reading_info_match)★ L2 专属 + +**剑桥对应:** Movers 阅读 Part1 信息匹配 + +**核心能力:** 信息抓取(快速阅读+图文匹配) + +**题型结构:** +- 5-6个简短文本(标识/告示/短句)+ 对应匹配项(图片/标题) + +**难度细则:** + +| 等级 | Unit 范围 | 文本长度 | 匹配特征 | +|------|----------|---------|---------| +| A级 | U1-U12 | 1-2个单词 | Flyers入门~基础,直接对应图片内容 | +| B级 | U13-U24 | 3-5词短句 | Flyers达标~KET入门,对应图片属性 | +| C级 | U25-U36 | 5-8词短句 | KET基础~强化,提取关键词匹配 | +| D级 | U37-U48 | 8-10词短句 | KET高位~达标,含干扰信息需筛选 | + +**审校要点:** +- [ ] 干扰项为常见易混淆内容,避免过于明显的错误 +- [ ] 文本简洁清晰,符合对应难度词汇要求 +- [ ] 匹配关系有明确对应依据 + +--- + +### 5.11 阅读段落匹配题(reading_paragraph_match)★ L2 专属 + +**剑桥对应:** Movers 阅读 Part2 段落匹配 + +**核心能力:** 主旨理解(信息归纳+快速阅读) + +**题型结构:** +- 4-5个简短段落 + 对应标题选项 + +**难度细则:** + +| 等级 | Unit 范围 | 段落长度 | 干扰项数 | 特征 | +|------|----------|---------|---------|------| +| A级 | U1-U12 | 3-4句话 | 无 | Flyers入门~基础,主旨直接明确 | +| B级 | U13-U24 | 4-5句话 | 1个 | Flyers达标~KET入门,需简单归纳 | +| C级 | U25-U36 | 5-6句话 | 2个 | KET基础~强化,需整合信息 | +| D级 | U37-U48 | 6-7句话 | 3个 | KET高位~达标,需隐含推断 | + +**审校要点:** +- [ ] 标题为段落主旨概括,非细节复述 +- [ ] 干扰项为段落中出现的细节信息(非完全无关内容) +- [ ] 段落内容贴近学生生活 + +--- + +### 5.12 阅读长文选择题(reading_long_passage)★ L2 专属 + +**剑桥对应:** Movers 阅读 Part3 长文选择 + +**核心能力:** 篇章理解(细节定位+推理判断) + +**题型结构:** +- 1篇短文 + 3-4道选择题(每道3个选项) + +**难度细则:** + +| 等级 | Unit 范围 | 短文长度 | 题目数 | 考察特征 | +|------|----------|---------|--------|---------| +| A级 | U1-U12 | 100词左右 | 3题 | Flyers入门~基础,答案直接出现在原文 | +| B级 | U13-U24 | 120-150词 | 3题 | Flyers达标~KET入门,需简单定位 | +| C级 | U25-U36 | 150-180词 | 4题 | KET基础~强化,需简单推理 | +| D级 | U37-U48 | 180-200词 | 4题 | KET高位~达标,需逻辑推断 | + +**审校要点:** +- [ ] 短文内容贴近学生生活,情节积极正向 +- [ ] 干扰项为原文出现的无关信息、部分正确信息、看似合理的错误信息 +- [ ] 短文长度和问题复杂度匹配难度等级 + +--- + +### 5.13 阅读完形填空题(reading_cloze)★ L2 专属 + +**剑桥对应:** Movers 阅读 Part4 完形填空 + +**核心能力:** 词汇运用(语法判断+上下文逻辑理解) + +**题型结构:** +- 1篇带空缺的短文 + 每个空缺3个选项 + +**难度细则:** + +| 等级 | Unit 范围 | 短文长度 | 空缺数 | 考察特征 | +|------|----------|---------|--------|---------| +| A级 | U1-U12 | 80词左右 | 3个空 | Flyers入门~基础,实词,上下文提示明显 | +| B级 | U13-U24 | 100词左右 | 4个空 | Flyers达标~KET入门,实词+介词/动词 | +| C级 | U25-U36 | 120词左右 | 5个空 | KET基础~强化,需前后句逻辑判断 | +| D级 | U37-U48 | 150词左右 | 5个空 | KET高位~达标,需全文主旨推断 | + +**审校要点:** +- [ ] 短文内容连贯,情节完整 +- [ ] 干扰项为常见易混淆词汇/语法形式错误的选项/不符合上下文逻辑的选项 +- [ ] 空缺单词需符合句子意思和语法 + +--- + +### 5.14 阅读开放填空题(reading_open_fill)★ L2 专属 + +**剑桥对应:** Movers 阅读 Part5 开放填空 + +**核心能力:** 信息提取(词汇拼写+语法正确性) + +**题型结构:** +- 1篇阅读材料(短文/通知/表格)+ 3-5个填空问题,无选项提示 + +**难度细则:** + +| 等级 | Unit 范围 | 空缺数 | 答案特征 | +|------|----------|--------|---------| +| A级 | U1-U12 | 3个 | Flyers入门~基础,1-4字母简单名词,原文直接给出 | +| B级 | U13-U24 | 4个 | Flyers达标~KET入门,名词/形容词,需简单变形 | +| C级 | U25-U36 | 5个 | KET基础~强化,动词/短语,需提取多信息点后变形 | +| D级 | U37-U48 | 5个 | KET高位~达标,需归纳总结,非原文原词 | + +**审校要点:** +- [ ] 评分标准明确:拼写错误扣分规则、大小写/单复数处理规则 +- [ ] 答案在原文中有明确对应依据 +- [ ] 阅读材料信息点清晰 + +--- + +### 5.15 写作看图回答题(writing_pic_qa)★ L1&L2 共用 + +**这是最详细的题型规范,有完整的样板和规则沉淀。** + +**核心能力:** 图文匹配(图片信息提取+书写表达) + +**题型结构:** +- 2个题组(first/second),每个题组 1张共享图 + 2-3小题(L2 U1-U6阶段1为2小题) +- 学生看图,根据 questionDesc(含挖空标记)写出答案 + +**JSON 结构(bitable 写入格式):** +```json +{ + "first": { + "category": "writing", + "type": "writing_pic_qa", + "questionSetID": "000001", + "textDesc": "Look and write.", + "questionSet": [ + { + "questionImage": "000001-00.png", + "questionList": [ + { + "question": "问题(What does the girl say about herself?)", + "questionDesc": "完整句子骨架,含挖空标记如 I (2) a student.", + "answerText": "答案(1个单词)", + "ability": ["简短作答", "一词答案"], + "explanation": "图片内容+考点+字母数说明" + } + ] + } + ] + }, + "second": { "... 同上结构 ..." } +} +``` + +**🔴 核心原则(强制执行,来自彦江明确反馈):** + +1. **挖空 = 考察知识点本身。** 挖空必须是句型结构中的核心知识词(be动词、介词、状态词等),不能是题干已给出的附带信息。 + + | ❌ 错误 | ✅ 正确 | 原因 | + |--------|--------|------| + | `Thank you (3) the book.` → you | `Thank you (3) the book.` → **for** | for 是介词知识点 | + | `I (2) Anna.` → I | `I (2) Anna.` → **am** | am 是 be动词知识点 | + | `I am from (5).` → China | `I am (4) China.` → **from** | from 是介词知识点 | + +2. **只能为 1 个单词挖空。** 禁止多词填空。 + +3. **考察点多样化。** 同一句型结构应覆盖多种搭配用法: + - `Thank you for...` → for + 名词 / for + 动名词 + - `I am...` → be + 状态词 / be + 身份 + - `I am going to...` → 动名词 going + +4. **题干多样化。** question 不能全部相同,应随语境微调: + - ❌ 全部 `What does the girl say?` + - ✅ `What does the girl say about herself?` / `Where does the girl say she is going?` / `What does the girl say to her friend?` + +5. **question 与 questionDesc 人称必须一致。** question 问角色说了什么 → questionDesc 用第一人称直接引语。 + +6. **问句型知识点挖空方向。** 当知识点是问句时,挖空在问句上,答句完整给出。 + - ✅ `"What (6) we do? We should go home now."` → 挖空 should + - ❌ `"What should we do? We should (2) home."` → 挖空了答句的 go + +**图片资源规则:** +- 每个题组 = 1张共享图(非每题独立配图) +- 命名:`{questionSetID}-{序号}.png`,序号跨全题组连续递增(00起始) +- 图片描述为纯中文文本(非 JSON),格式: + +``` +1. + +风格: 卡通,简单线条 +场景:{场景名称} +{角色特征} - {动作描述} ; +{角色特征} - {动作描述} 。 + +2. + +风格: 卡通,简单线条 +场景:{场景名称} +... +``` + +**阶段变化原则:** + +| 维度 | L2 U1-U6(阶段1,A级) | 后续阶段 | +|------|----------------------|---------| +| 挖空单位 | 1 个单词 | 短语 / 完整句子 | +| 题组小题数 | 2 小题 / 题组 | 4 小题 / 题组 | +| 能力标签 | ["简短作答", "一词答案"] | (待补充) | + +**历史 L1 规则(供参考):** + +| 等级 | 阶段 | 答案要求 | +|------|------|---------| +| A级 | L1 1-2阶段 | 1个核心词(功能词/名词),questionDesc提供完整句子骨架 | +| B级 | L1 3-4阶段 | 1-2词短语 | +| C级 | L1 5-6阶段 | 2-3词动作描述 | +| D级 | L2 1-2阶段 | 完整简单句或3-5词短语 | + +**审校要点:** +- [ ] 挖空词是否为知识点核心词?(不是题干已知信息) +- [ ] 每空仅1个单词? +- [ ] questionDesc 中数字标记 = 答案字母数? +- [ ] question 与 questionDesc 人称是否一致? +- [ ] 同题包内 question 是否多样化? +- [ ] 同句型是否覆盖多种搭配? +- [ ] 图片描述格式是否正确?(中文、编号、风格/场景/角色) +- [ ] questionImage 序号从 00 开始连续递增? +- [ ] L2 U1-U6(阶段1,A级)ability 固定为 `["简短作答", "一词答案"]`? + +**完整样题(questionSetID = 000001):** + +题组1(first)— 校门口场景: +1. What does the girl say about herself? → `I (2) a student.` → am(女孩穿校服举手,考察be动词am) +2. Where does the girl say she is going? → `I am (5) to school.` → going(男孩背书包走向校门,考察going) + +题组2(second)— 生日派对场景: +3. What does the boy say at the party? → `I am (5)!` → ready(男孩穿泳衣戴泳镜,考察状态词ready) +4. What does the girl say to her friend? → `Thank you (3) the book.` → for(女孩接书微笑,考察介词for) + +--- + +### 5.16 写作邮件回复题(writing_email_reply)★ L2 专属 + +**剑桥对应:** Movers 写作 Part1 邮件回复 + +**核心能力:** 书面表达(格式规范+信息提取) + +**题型结构:** +- 1封收到的邮件 + 回复要求 + 参考范文 + 评分标准 + +**难度细则:** + +| 等级 | Unit 范围 | 词数要求 | 考察内容 | +|------|----------|---------|---------| +| A级 | U1-U12 | 20-30词 | Flyers入门~基础,回答2个简单问题,格式简单 | +| B级 | U13-U24 | 30-40词 | Flyers达标~KET入门,3个问题+问候结尾 | +| C级 | U25-U36 | 40-50词 | KET基础~强化,3问题+1附加要求,格式完整 | +| D级 | U37-U48 | 50-60词 | KET高位~达标,3问题+个人观点,语言连贯 | + +**审校要点:** +- [ ] 邮件格式规范(问候、正文、结尾署名) +- [ ] 参考范文符合字数要求,信息完整 +- [ ] 评分标准分三级(优秀/合格/不合格),判断依据明确 + +--- + +### 5.17 写作看图写作题(writing_picture_writing)★ L2 专属 + +**核心能力:** 独立书面表达 + +**备注:** 具体规格详见对应 SKILL.md。 + +--- + +### 5.18 口语看图回答题(speaking_pic_qa)★ L1 专属 + +**剑桥对应:** Starters 口语 Part2 看图问答 + +**核心能力:** 口语表达(图片信息提取+场景词汇运用) + +**题型结构:** +- 1张图片描述 + 1个引导问题 + 1-3个参考回答 + 三级评分标准 + +**难度细则:** + +| 等级 | 阶段 | 图片/问题特征 | 答案要求 | +|------|------|-------------|---------| +| A级 | L1 1-2阶段 | 1个核心物品,无干扰,问"What's this?" | 1-2个核心名词,无语法要求 | +| B级 | L1 3-4阶段 | 核心物品+1个属性,问属性 | 3-5词短语(名词+属性词) | +| C级 | L1 5-6阶段 | 核心人物+1个动作,问动作 | 完整简单句(主谓宾),6-10词 | +| D级 | L2 1-2阶段 | 完整场景(物品+人物+动作),问场景 | 2-3句连贯表达,15-20词 | + +**题型示例(A级):** +``` +图片描述:白色背景下的红色苹果,无其他干扰元素 +引导问题:What's this? +满分回答:Apple. +合格回答:Red apple. +``` + +**审校要点:** +- [ ] 引导问题复杂度匹配难度等级 +- [ ] 参考回答至少1个满分回答 +- [ ] 评分标准明确标注「优秀/合格/不合格」三个等级判断依据 + +--- + +### 5.19 口语日常回答题(speaking_daily_qa / speaking_qa)★ L1&L2 共用 + +**剑桥对应:** 口语 Part1 日常回答 + +**核心能力:** 口语表达(日常用语运用+快速反应) + +**题型结构:** +- 3-4个日常问题 + 每题满分/合格参考回答 + 评分标准 + +**难度细则(L1 维度,对标 Starters/Movers):** + +| 等级 | 阶段 | 问题类型 | 答案要求 | +|------|------|---------|---------| +| A级 | L1 1-2阶段 | 个人基本信息类(What's your name?) | 1-2个单词/短语 | +| B级 | L1 3-4阶段 | 喜好/日常活动类(What color do you like?) | 1个简单句 | +| C级 | L1 5-6阶段 | 习惯/能力类(What do you do every day?) | 2个简单句 | +| D级 | L2 1-2阶段 | 经历/计划类(What did you do yesterday?) | 2-3个连贯句子 | + +**难度细则(L2 维度,对标 Flyers/KET):** + +| 等级 | Unit 范围 | 问题类型 | 答案要求 | +|------|----------|---------|---------| +| A级 | U1-U12 | Flyers入门~基础,个人基本信息/喜好类 | 2个简单句 | +| B级 | U13-U24 | Flyers达标~KET入门,喜好/日常活动类 | 2-3个连贯句 | +| C级 | U25-U36 | KET基础~强化,习惯/能力类 | 3-4个连贯句 | +| D级 | U37-U48 | KET高位~达标,经历/计划类 | 4-5个连贯句 | + +**JSON 结构(speaking_qa):** +```json +{ + "category": "speaking", + "type": "speaking_qa", + "asrPrompt": "ASR提示词列表(逗号分隔)", + "questionSetID": "111101", + "textDesc": "Answer these questions.", + "questionSet": [ + { + "content": "问题内容", + "ability": ["能力标签数组"], + "explanation": "回答问题时使用完整句子。" + } + ] +} +``` + +**审校要点:** +- [ ] 问题符合日常交流场景,贴近学生生活 +- [ ] 至少提供1个满分回答+1个合格回答 +- [ ] ASR 提示词覆盖问题中所有核心词汇 +- [ ] 评分标准分三级 + +--- + +### 5.20 口语话题讨论题(speaking_topic_discussion)★ L2 专属 + +**剑桥对应:** Movers 口语 Part2 话题讨论 + +**核心能力:** 口语表达(逻辑组织+观点阐述) + +**题型结构:** +- 1个话题 + 1-2个引导提示 + 参考回答 + 评分标准 + +**难度细则:** + +| 等级 | Unit 范围 | 话题类型 | 要求 | +|------|----------|---------|------| +| A级 | U1-U12 | 具体物品/活动(Describe your favorite toy) | Flyers入门~基础,3-4句话,无复杂逻辑 | +| B级 | U13-U24 | 喜好/习惯(Talk about your favorite food) | Flyers达标~KET入门,4-5句话,简单逻辑 | +| C级 | U25-U36 | 观点/经历(Talk about your last birthday) | KET基础~强化,5-6句话,清晰逻辑层次 | +| D级 | U37-U48 | 抽象感受/计划(Talk about your weekend plan) | KET高位~达标,6-8句话,逻辑连贯 | + +**审校要点:** +- [ ] 话题贴近学生生活,符合对应年龄段认知 +- [ ] 至少提供1个满分参考回答,结构清晰 +- [ ] 评分标准分三级 + +--- + +### 5.21 口语看图识物题(speaking_pic_recognize)★ L1 专属 + +**核心能力:** 图片辨识与口语输出 + +**备注:** 具体规格详见对应 SKILL.md。 + +--- + +## 六、飞书多维表格写入规范(通用流程) + +### 6.1 目标表格 + +- App Token:`CMHSbUUjka3TrUsaxxEc297ongf` +- 表格按题型分表(如 写作-P3-看图回答题、阅读-P1-看图回答题 等) +- Bot 身份操作(App ID: `cli_a931175d41799cc7`) + +### 6.2 通用写入字段 + +| bitable 列名 | 写入内容 | 格式 | +|-------------|---------|------| +| 题目集合 ID | questionSetID | 纯文本 | +| jsonData | 完整 JSON | JSON 字符串 | +| 图片描述 | 图片描述(如需配图) | 纯中文文本 | + +### 6.3 写入流程图 + +``` +定位 bitable 表 → 创建/更新记录 → 写入三字段 → 回读验证 +``` + +### 6.4 注意事项 +- 嵌套 JSON 写入时避免 Shell 变量展开截断(用 Python subprocess 直接调用 curl) +- 写入后必须回读验证 jsonData 内容完整性 + +--- + +## 七、全局审校检查清单 + +### 7.1 词汇与阶段匹配 +- [ ] 所有词汇在对应用别的词库(L1用L1词库,L2用L1+L2词库) +- [ ] 句型在对应阶段句型库范围内 +- [ ] 题型阶段归属正确(L1专属题型不出现在L2,反之亦然) + +### 7.2 难度匹配 +- [ ] 对话/短文长度符合等级要求 +- [ ] 选项/干扰项复杂度符合等级要求 +- [ ] 问题难度符合等级要求 + +### 7.3 格式合规 +- [ ] JSON 结构完整,必填字段齐全 +- [ ] 图片命名格式正确(`{questionSetID}-{序号}.png`) +- [ ] 标准化解析包含(80-100字,中文为主) + +### 7.4 逻辑合规 +- [ ] 答案有明确依据(听力文本/阅读原文/图片内容) +- [ ] 干扰项合理,不出现明显错误选项 +- [ ] 同一题包内题目不重复(知识盲点不重复考察) + +### 7.5 能力标签 +- [ ] 每道题绑定1级+2级能力标签 +- [ ] 能力标签与题型匹配 +- [ ] 口语/写作题型绑定语言输出标签 + +### 7.6 价值观 +- [ ] 无负面评价词汇 +- [ ] 情节积极正向 +- [ ] 内容贴近学生生活,符合年龄段认知 + +--- + +## 八、资源路径速查 + +| 资源 | 路径 | +|------|------| +| L1 词库(980词) | `business_knowledge/L1_word_list.json` | +| L2 词库(1470词) | `business_knowledge/L2_word_list.json` | +| L1 句型库(490个) | `business_knowledge/L1_pattern_list.json` | +| L2 句型库(380个) | `business_knowledge/L2_pattern_list.json` | +| 单元挑战 skills | `business_production/单元挑战/skills/unit_challenge/` | +| 单元挑战记忆 | `business_production/单元挑战/memory/` | +| 单元挑战 bitable | App Token: `CMHSbUUjka3TrUsaxxEc297ongf` | + +--- + +*本文档依据全部题型 SKILL.md(共 18 个题型规范文件)及近期生产实践经验教训整理,可作为审校工作的一站式参照标准。* diff --git a/scripts/__pycache__/batch_reading_pic_qa.cpython-312.pyc b/scripts/__pycache__/batch_reading_pic_qa.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc23da4b863c25585fcc6455dd479e1bae7c6956 GIT binary patch literal 19097 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 literal 0 HcmV?d00001 diff --git a/scripts/batch_create_delayed.py b/scripts/batch_create_delayed.py new file mode 100644 index 0000000..7eef7cd --- /dev/null +++ b/scripts/batch_create_delayed.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""Create empty records, wait for automation, then update with real jsonData.""" +import json, requests, sys, time + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" + +sys.path.insert(0, '/root/.openclaw/workspace-xiaoyan') +from scripts.batch_reading_pic_qa import ALL_QUESTIONS, build_json + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +def get_token(): + resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, + ) + return resp.json()["tenant_access_token"] + +token = get_token() + +# Step 1: Delete any existing corrupted records +print("=== Step 1: Clean up ===") +resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records?page_size=20", + headers={"Authorization": f"Bearer {token}"}, +) +for item in resp.json()["data"]["items"]: + fid = item["fields"].get("题目集合 ID", "") + if fid in ALL_QUESTIONS: + rid = item["record_id"] + requests.delete( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}"}, + ) + print(f" Deleted {fid}") + +time.sleep(3) + +# Step 2: Create ALL empty records first +print("\n=== Step 2: Create empty records ===") +record_ids = {} +for qid in sorted(ALL_QUESTIONS.keys()): + fields = {"题目集合 ID": qid, "dataStatus": "0"} + resp = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": fields}, + ) + rid = resp.json()["data"]["record"]["record_id"] + record_ids[qid] = rid + print(f" {qid}: {rid}") + time.sleep(0.5) + +# Step 3: Wait for automation to finish +print("\n=== Step 3: Waiting 30s for automation... ===") +for i in range(30, 0, -5): + print(f" {i}s remaining...") + time.sleep(5) + +# Step 4: Update all with real jsonData +print("\n=== Step 4: Update with real jsonData ===") +token = get_token() # refresh token +results = {} +for qid in sorted(record_ids.keys()): + rid = record_ids[qid] + json_data = build_json(qid, ALL_QUESTIONS[qid]) + json_str = json.dumps(json_data, ensure_ascii=False) + print(f" Updating {qid} ({len(json_str)} chars)...") + + update_resp = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"jsonData": json_str}}, + ) + print(f" update code: {update_resp.json().get('code')}") + time.sleep(0.5) + +# Step 5: Verify after delay +print("\n=== Step 5: Wait 30s then verify ===") +for i in range(30, 0, -5): + print(f" {i}s remaining...") + time.sleep(5) + +token = get_token() +print("\n=== Step 6: Final verification ===") +all_ok = True +for qid, rid in sorted(record_ids.items()): + resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}"}, + ) + jd = resp.json()["data"]["record"]["fields"].get("jsonData", "") + try: + j = json.loads(jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + if fq == 5 and sq == 5: + q1 = j["first"]["questionSet"][0]["question"][:50] + print(f" ✅ {qid}: {fq}q/{sq}q | Q1=\"{q1}\"") + else: + print(f" ❌ {qid}: {fq}q/{sq}q | jd={len(jd)}chars") + print(f" jd preview: {jd[:200]}") + all_ok = False + except Exception as e: + print(f" ❌ {qid}: {e}") + all_ok = False + +print(f"\n{'✅ ALL INTACT' if all_ok else '❌ SOME CORRUPTED'}") diff --git a/scripts/batch_create_safe.py b/scripts/batch_create_safe.py new file mode 100644 index 0000000..824dbab --- /dev/null +++ b/scripts/batch_create_safe.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Batch create reading_pic_qa records with delay and immediate verification.""" +import json, requests, sys, time + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" + +sys.path.insert(0, '/root/.openclaw/workspace-xiaoyan') +from scripts.batch_reading_pic_qa import ALL_QUESTIONS, build_json + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +results = [] +for qid in sorted(ALL_QUESTIONS.keys()): + print(f"\n{'='*50}") + print(f"ID: {qid}") + + json_data = build_json(qid, ALL_QUESTIONS[qid]) + json_str = json.dumps(json_data, ensure_ascii=False) + print(f" jsonData: {len(json_str)} chars") + + # Verify Y/N balance + for grp in ('first', 'second'): + qs = json_data[grp]['questionSet'] + yc = sum(1 for q in qs if q['answerText']=='Yes') + nc = sum(1 for q in qs if q['answerText']=='No') + print(f" {grp}: {yc}Y/{nc}N") + + # Create + fields = { + "题目集合 ID": qid, + "jsonData": json_str, + "dataStatus": "0", + } + resp = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": fields}, + ) + result = resp.json() + + if result.get("code") != 0: + print(f" ❌ Create failed: {result.get('msg', result)}") + results.append((qid, "FAIL")) + continue + + record_id = result["data"]["record"]["record_id"] + print(f" record_id: {record_id}") + + # Verify immediately + time.sleep(0.5) + resp2 = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, + ) + data2 = resp2.json() + stored_jd = data2["data"]["record"]["fields"].get("jsonData", "") + + if len(stored_jd) < 100: + print(f" ❌ TRUNCATED ({len(stored_jd)} chars)! Deleting...") + requests.delete( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, + ) + results.append((qid, "TRUNCATED")) + continue + + try: + j = json.loads(stored_jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + print(f" ✅ Verified: first={fq}q, second={sq}q") + results.append((qid, "OK", record_id)) + except Exception as e: + print(f" ❌ Parse error: {e}") + results.append((qid, "PARSE_ERROR")) + + time.sleep(1.0) + +print(f"\n{'='*50}") +print("SUMMARY") +print(f"{'='*50}") +for r in results: + print(f" {r}") diff --git a/scripts/batch_create_two_step.py b/scripts/batch_create_two_step.py new file mode 100644 index 0000000..408f71c --- /dev/null +++ b/scripts/batch_create_two_step.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Create records first, then update jsonData separately.""" +import json, requests, sys, time + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" + +sys.path.insert(0, '/root/.openclaw/workspace-xiaoyan') +from scripts.batch_reading_pic_qa import ALL_QUESTIONS, build_json + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +# First delete all corrupted records +print("=== Cleaning corrupted records ===") +resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records?page_size=20", + headers={"Authorization": f"Bearer {token}"}, +) +for item in resp.json()["data"]["items"]: + fid = item["fields"].get("题目集合 ID", "") + if fid in ("000001", "010101", "010201", "010301", "010401", "010501", "010601"): + rid = item["record_id"] + del_resp = requests.delete( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}"}, + ) + print(f" Deleted {fid} ({rid}): {del_resp.json().get('code')}") + +time.sleep(2) + +results = {} +for qid in sorted(ALL_QUESTIONS.keys()): + print(f"\n{'='*50}") + print(f"ID: {qid}") + + # Step 1: Create with minimal fields (no jsonData) + fields = { + "题目集合 ID": qid, + "dataStatus": "0", + } + resp = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": fields}, + ) + result = resp.json() + if result.get("code") != 0: + print(f" ❌ Create failed: {result.get('msg')}") + results[qid] = "FAIL_CREATE" + continue + + record_id = result["data"]["record"]["record_id"] + print(f" Created: {record_id}") + + # Step 2: Update with jsonData + json_data = build_json(qid, ALL_QUESTIONS[qid]) + json_str = json.dumps(json_data, ensure_ascii=False) + print(f" Updating jsonData ({len(json_str)} chars)...") + + update_resp = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"jsonData": json_str}}, + ) + up_result = update_resp.json() + print(f" Update: code={up_result.get('code')}") + + if up_result.get("code") != 0: + print(f" ❌ Update failed: {up_result.get('msg')}") + results[qid] = "FAIL_UPDATE" + continue + + # Wait for any automation to process + time.sleep(1.0) + + # Step 3: Read back and verify + resp3 = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, + ) + data3 = resp3.json() + stored_jd = data3["data"]["record"]["fields"].get("jsonData", "") + + try: + j = json.loads(stored_jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + if fq == 5 and sq == 5: + first_q = j["first"]["questionSet"][0]["question"][:50] + print(f" ✅ VERIFIED: first={fq}q, second={sq}q, Q1='{first_q}'") + results[qid] = ("OK", record_id) + else: + print(f" ⚠️ PARTIAL: first={fq}q, second={sq}q") + results[qid] = ("PARTIAL", record_id) + except Exception as e: + print(f" ❌ CORRUPTED: {e} | jd_len={len(stored_jd)}") + results[qid] = ("CORRUPTED", record_id) + + time.sleep(1.0) + +print(f"\n{'='*50}") +print("FINAL SUMMARY") +print(f"{'='*50}") +for qid, val in sorted(results.items()): + print(f" {qid}: {val}") diff --git a/scripts/batch_reading_matchInfo.py b/scripts/batch_reading_matchInfo.py new file mode 100644 index 0000000..f980eaa --- /dev/null +++ b/scripts/batch_reading_matchInfo.py @@ -0,0 +1,509 @@ +#!/usr/bin/env python3 +"""reading_matchInfo 021301-021801 (6套), L2 B级 3-5词短句通知""" +import json, requests, time + +CRED = "/root/.openclaw/credentials/xiaoyan/config.json" +TOKEN_APP = "CMHSbUUjka3TrUsaxxEc297ongf" +TBL = "tblCgfYDnnqwLfgH" +AB = ["标识与通知理解|标识 & 通知"] + +with open(CRED) as f: + cred = json.load(f) +aid = cred["apps"][0]["appId"] +asecret = cred["apps"][0]["appSecret"] + +def get_token(): + r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": aid, "app_secret": asecret}) + return r.json()["tenant_access_token"] + +# ==================== QUESTION DATA ==================== +# Each entry: (questionImage, question, [options], [answer_index], explanation) +# Format: image text is 3-5 word real notice/sign + +I = lambda img, q, opts, ans, exp: { + "questionImage": img, "question": q, "options": opts, + "answer": ans, "explanation": exp +} + +DATA = { + "021301": { + "first": [ + I("021301-00.png","What does this poster say?", + ["The singer is here","The actor is on stage","The dancer is late"],[1], + "海报上写着 The ACTOR is on stage,演员在舞台上。"), + I("021301-01.png","What does the man have?", + ["White hair","Black beard","Red hat"],[1], + "通缉令上写着 Man with black BEARD,男子有黑胡子。"), + I("021301-02.png","What does the label say?", + ["The pictures are the same","The pictures are different","The pictures are old"],[1], + "标签上写着 These pictures are DIFFERENT,两幅画不一样。"), + I("021301-03.png","What was Lily today?", + ["Brave","Sad","Tired"],[0], + "奖状上写着 Lily was very BRAVE today,Lily 今天很勇敢。"), + I("021301-04.png","Who just moved in?", + ["A new teacher","A new neighbour","A new student"],[1], + "卡片上写着 A new NEIGHBOUR moved in,新邻居搬来了。"), + ], + "second": [ + I("021301-05.png","What time is dinner?", + ["At five","At six","At seven"],[1], + "便条上写着 DINNER at six tonight,晚饭六点吃。"), + I("021301-06.png","What is Mum doing?", + ["Sleeping","Cooking","Reading"],[1], + "门牌上写着 Mum is COOKING right now,妈妈在做饭。"), + I("021301-07.png","What kind of exit is this?", + ["Fire exit","Park exit","Shop exit"],[0], + "标志上写着 FIRE exit this way,这是消防出口。"), + I("021301-08.png","What is on show Friday?", + ["A family album","A film","A play"],[0], + "海报上写着 Family ALBUM on show Friday,周五展示家庭相册。"), + I("021301-09.png","What does this note ask?", + ["Read my diary","Do not read my diary","Buy a new diary"],[1], + "便条上写着 Do not read my DIARY,不要看我的日记。"), + ], + "desc": """1. +图片材料文本: +The ACTOR is on stage. + +图片材料文本: +Man with black BEARD wanted. + +图片材料文本: +These pictures are DIFFERENT. + +图片材料文本: +Lily was very BRAVE today. + +图片材料文本: +A new NEIGHBOUR moved in. + +2. +图片材料文本: +DINNER at six tonight. + +图片材料文本: +Mum is COOKING right now. + +图片材料文本: +FIRE exit this way → + +图片材料文本: +Family ALBUM on show Friday. + +图片材料文本: +Do not read my DIARY.""", + }, + "021401": { + "first": [ + I("021401-00.png","What should you plan?", + ["Your weekend","Your lesson","Your dinner"],[0], + "便签上写着 PLAN your weekend now,现在就计划周末吧。"), + I("021401-01.png","How fast is the bike repair?", + ["In one day","In one hour","In one week"],[1], + "店招牌上写着 Bike REPAIR in one hour,一小时修好自行车。"), + I("021401-02.png","Where is today's newspaper?", + ["On the shelf","On the desk","In the bag"],[1], + "图书馆标牌上写着 Today's NEWSPAPER on the desk,报纸在桌上。"), + I("021401-03.png","What is lost?", + ["A blue backpack","A red pen","A white book"],[0], + "失物招领上写着 Lost: Blue BACKPACK in Room 3,蓝色背包丢了。"), + I("021401-04.png","What should you do on the bus?", + ["Stand up","Give your seat to old people","Sing songs"],[1], + "公交标牌上写着 Give your SEAT to old people,给老人让座。"), + ], + "second": [ + I("021401-05.png","When is the garden open?", + ["In the morning","After class","At lunch"],[1], + "公告上写着 School GARDEN open after class,花园放学后开放。"), + I("021401-06.png","What fruit is on sale?", + ["Apple","Banana","Melon"],[2], + "市场标牌上写着 Sweet MELON for sale today,今天甜瓜热卖。"), + I("021401-07.png","What does this lake sign say?", + ["Drink lake water","Do not drink lake water","Swim in the lake"],[1], + "警示牌上写着 Do not drink lake WATER,不要喝湖水。"), + I("021401-08.png","When should you plant a tree?", + ["On Monday","On Friday","On Sunday"],[1], + "公告上写着 PLANT a tree on Friday,周五植树。"), + I("021401-09.png","What starts next week?", + ["Summer camp","Winter camp","Spring class"],[0], + "校园通知上写着 SUMMER camp starts next week,夏令营下周开始。"), + ], + "desc": """1. +图片材料文本: +PLAN your weekend now. + +图片材料文本: +Bike REPAIR in one hour. + +图片材料文本: +Today's NEWSPAPER on the desk. + +图片材料文本: +Lost: Blue BACKPACK in Room 3. + +图片材料文本: +Give your SEAT to old people. + +2. +图片材料文本: +School GARDEN open after class. + +图片材料文本: +Sweet MELON for sale today. + +图片材料文本: +Do not drink lake WATER. + +图片材料文本: +PLANT a tree on Friday. + +图片材料文本: +SUMMER camp starts next week.""", + }, + "021501": { + "first": [ + I("021501-00.png","Where should visitors sign in?", + ["At the gate","At the office","At the shop"],[1], + "校门标识上写着 VISITORs sign in at the office,访客去办公室签到。"), + I("021501-01.png","What kind of party is it?", + ["Birthday party","School party","Garden party"],[0], + "横幅上写着 WELCOME to the birthday party,生日派对欢迎你。"), + I("021501-02.png","Whose party are you invited to?", + ["Lily's","Tom's","Mum's"],[1], + "邀请函上写着 You are INVITEd to Tom's party,你被邀请参加Tom的派对。"), + I("021501-03.png","What can you do here?", + ["Sleep","Laugh","Cry"],[1], + "海报上写着 Come and LAUGH with us,来和我们一起大笑。"), + I("021501-04.png","What time does breakfast end?", + ["At 7 a.m.","At 8 a.m.","At 9 a.m."],[2], + "酒店通知上写着 GUESTs: breakfast 7 to 9 a.m.,早餐9点结束。"), + ], + "second": [ + I("021501-05.png","What should you check for?", + ["Mistakes","Stars","Pictures"],[0], + "作业提醒上写着 Check for MISTAKEs first,先检查有没有错误。"), + I("021501-06.png","What should you read carefully?", + ["The story","The instruction","The picture"],[1], + "考试通知上写着 Read the INSTRUCTION carefully,仔细读说明。"), + I("021501-07.png","What must you give if late?", + ["A reason","A gift","A book"],[0], + "办公室告知上写着 Give a REASON if late,迟到要说明原因。"), + I("021501-08.png","Where must you not bring drinks?", + ["Near the library","Near technology room","Near the garden"],[1], + "电脑室标识上写着 No drinks near TECHNOLOGY room,科技室附近不准带饮料。"), + I("021501-09.png","Where should you wash your brush?", + ["At home","Here","Outside"],[1], + "美术室标识上写着 Wash your paint BRUSH here,在这里洗画笔。"), + ], + "desc": """1. +图片材料文本: +VISITORs sign in at the office. + +图片材料文本: +WELCOME to the birthday party! + +图片材料文本: +You are INVITEd to Tom's party. + +图片材料文本: +Come and LAUGH with us! + +图片材料文本: +GUESTs: breakfast 7 to 9 a.m. + +2. +图片材料文本: +Check for MISTAKEs first. + +图片材料文本: +Read the INSTRUCTION carefully. + +图片材料文本: +Give a REASON if late. + +图片材料文本: +No drinks near TECHNOLOGY room. + +图片材料文本: +Wash your paint BRUSH here.""", + }, + "021601": { + "first": [ + I("021601-00.png","What is the new record in?", + ["Run","Jump","Swim"],[1], + "体育公告上写着 New school RECORD in jump,跳高破了新纪录。"), + I("021601-01.png","What is the big event?", + ["Sports day","Exam day","Book day"],[0], + "公告上写着 Sports day — a big EVENT,运动会是件大事。"), + I("021601-02.png","Who must you have to enter?", + ["A friend","A teacher","A parent"],[1], + "门上写着 Do not ENTER without teacher,没有老师不能进。"), + I("021601-03.png","When should you return books?", + ["By Monday","By Friday","By Sunday"],[1], + "图书馆通知上写着 RETURN books by this Friday,周五前还书。"), + I("021601-04.png","What should you sort?", + ["Paper and food","Paper and glass","Glass and water"],[1], + "回收标识上写着 Please SORT paper and glass,请把纸和玻璃分开。"), + ], + "second": [ + I("021601-05.png","What colour is the handbag?", + ["Blue","Red","Green"],[1], + "失物招领上写着 Lost: red HANDBAG near playground,丢了红色手提包。"), + I("021601-06.png","What is not allowed here?", + ["Camera","Books","Shoes"],[0], + "展馆标识上写着 No CAMERA in this room,此房间不许拍照。"), + I("021601-07.png","When does the park gate close?", + ["At seven","At eight","At nine"],[1], + "公园标牌上写着 Park GATE closes at eight,大门8点关闭。"), + I("021601-08.png","How long to fix a wheel?", + ["10 minutes","30 minutes","60 minutes"],[1], + "车店标牌上写着 Fix your WHEEL in 30 minutes,30分钟修好轮子。"), + I("021601-09.png","Where is the main entrance?", + ["South side","East side","North side"],[2], + "大楼标识上写着 Main ENTRANCE on north side,主入口在北边。"), + ], + "desc": """1. +图片材料文本: +New school RECORD in jump! + +图片材料文本: +Sports day — a big EVENT. + +图片材料文本: +Do not ENTER without teacher. + +图片材料文本: +RETURN books by this Friday. + +图片材料文本: +Please SORT paper and glass. + +2. +图片材料文本: +Lost: red HANDBAG near playground. + +图片材料文本: +No CAMERA in this room. + +图片材料文本: +Park GATE closes at eight. + +图片材料文本: +Fix your WHEEL in 30 minutes. + +图片材料文本: +Main ENTRANCE on north side.""", + }, + "021701": { + "first": [ + I("021701-00.png","Who should you not talk to?", + ["The driver","The guard","The teacher"],[0], + "公交标牌上写着 Do not talk to the DRIVER,不要和司机说话。"), + I("021701-01.png","Where should you look both ways?", + ["At school","On the street","At home"],[1], + "安全海报上写着 Look both ways on the STREET,过街道要左右看。"), + I("021701-02.png","When is the farmers' market?", + ["Every Monday","Every Saturday","Every Sunday"],[1], + "市场标牌上写着 Farmers' MARKET every Saturday,每周六有农夫市集。"), + I("021701-03.png","Where can you get a guidebook?", + ["At this desk","At the gate","In the shop"],[0], + "咨询台标牌上写着 Free GUIDEBOOK at this desk,此处免费领指南。"), + I("021701-04.png","What time does shopping start?", + ["At 10 a.m.","At 11 a.m.","At 12 p.m."],[0], + "商场标牌上写着 SHOPPING hours: 10 a.m. to 8 p.m.,购物10点开始。"), + ], + "second": [ + I("021701-05.png","Where is the washing machine?", + ["On first floor","On second floor","Outside"],[0], + "洗衣房标识上写着 Washing MACHINE on first floor,洗衣机在一楼。"), + I("021701-06.png","What should you draw?", + ["Your school","Your family","Your pet"],[1], + "美术社通知上写着 DRAW your family for the show,画你的家人参展。"), + I("021701-07.png","When is basketball practice?", + ["At three","At four","At five"],[1], + "体育通知上写着 BASKETBALL practice at four today,今天四点篮球训练。"), + I("021701-08.png","Where should you put the books?", + ["On the desk","On the bookcase","On the floor"],[1], + "图书馆标识上写着 Put books on the BOOKCASE,把书放回书柜。"), + I("021701-09.png","When is the science fair?", + ["On Friday","On Monday","On Wednesday"],[1], + "科学展标识上写着 Show your INVENTION on Monday,周一展示你的发明。"), + ], + "desc": """1. +图片材料文本: +Do not talk to the DRIVER. + +图片材料文本: +Look both ways on the STREET. + +图片材料文本: +Farmers' MARKET every Saturday. + +图片材料文本: +Free GUIDEBOOK at this desk. + +图片材料文本: +SHOPPING hours: 10 a.m. to 8 p.m. + +2. +图片材料文本: +Washing MACHINE on first floor. + +图片材料文本: +DRAW your family for the show. + +图片材料文本: +BASKETBALL practice at four today. + +图片材料文本: +Put books on the BOOKCASE. + +图片材料文本: +Show your INVENTION on Monday.""", + }, + "021801": { + "first": [ + I("021801-00.png","What time does the library close?", + ["At 2 p.m.","At 4 p.m.","At 6 p.m."],[1], + "图书馆通知上写着 LIBRARY open 8 a.m. to 4 p.m.,下午4点闭馆。"), + I("021801-01.png","Where can you read the magazines?", + ["At home","Here only","In the park"],[1], + "阅览室标牌上写着 New MAGAZINEs — read here only,杂志仅供此处阅读。"), + I("021801-02.png","What is on at three?", + ["A film","A cartoon","A play"],[1], + "TV室标牌上写着 CARTOON at three this afternoon,下午三点播动画片。"), + I("021801-03.png","When is the English test?", + ["Friday morning","Monday afternoon","Wednesday evening"],[0], + "考试通知上写着 English TEST on Friday morning,周五上午英语考试。"), + I("021801-04.png","How should you read the instructions?", + ["Quickly","Carefully","Slowly"],[1], + "实验室标牌上写着 Read CAREFULLY before you start,开始前仔细阅读。"), + ], + "second": [ + I("021801-05.png","What does the toy need?", + ["A new book","A new battery","A new bag"],[1], + "玩具上贴着 This toy needs a new BATTERY,玩具需要新电池。"), + I("021801-06.png","Where is the reading corner?", + ["On the right","On the left","In the middle"],[1], + "图书馆标牌上写着 Reading CORNER on the left side,阅读角在左边。"), + I("021801-07.png","When is the school play?", + ["On Friday","On Saturday","On Sunday"],[1], + "校公告上写着 Play at the THEATRE this Saturday,周六剧院有演出。"), + I("021801-08.png","What should you not climb?", + ["The wall","The stairs","The park trees"],[2], + "公园警示牌上写着 Do not CLIMB the park trees,不要爬公园的树。"), + I("021801-09.png","When is lunch today?", + ["At midday","At one","At two"],[0], + "食堂通知上写着 Lunch is at MIDDAY today,今天中午十二点午饭。"), + ], + "desc": """1. +图片材料文本: +LIBRARY open 8 a.m. to 4 p.m. + +图片材料文本: +New MAGAZINEs — read here only. + +图片材料文本: +CARTOON at three this afternoon. + +图片材料文本: +English TEST on Friday morning. + +图片材料文本: +Read CAREFULLY before you start. + +2. +图片材料文本: +This toy needs a new BATTERY. + +图片材料文本: +Reading CORNER on the left side. + +图片材料文本: +Play at the THEATRE this Saturday. + +图片材料文本: +Do not CLIMB the park trees. + +图片材料文本: +Lunch is at MIDDAY today.""", + }, +} + +# ==================== BUILD & WRITE ==================== +def build(qid): + def qset(qs): + return [{ + "questionImage": q["questionImage"], "question": q["question"], + "options": q["options"], "answer": q["answer"], + "ability": AB, "explanation": q["explanation"], + } for q in qs] + return { + "first": {"category": "reading", "type": "reading_matchInfo", "questionSetID": qid, "questionSet": qset(DATA[qid]["first"])}, + "second": {"category": "reading", "type": "reading_matchInfo", "questionSetID": qid, "questionSet": qset(DATA[qid]["second"])}, + } + +# === STEP 1: Create empty records === +token = get_token() +print("=== Step 1: Create empty records ===") +new_rids = {} +for qid in sorted(DATA): + r = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"题目集合 ID": qid, "dataStatus": "0"}}) + new_rids[qid] = r.json()["data"]["record"]["record_id"] + print(f" {qid}: {new_rids[qid]}") + time.sleep(0.5) + +# === STEP 2: Wait for automation === +print("\n=== Step 2: Wait 35s ===") +time.sleep(35) + +# === STEP 3: PUT jsonData === +print("\n=== Step 3: PUT jsonData ===") +token = get_token() +for qid in sorted(new_rids): + j = build(qid) + js = json.dumps(j, ensure_ascii=False) + r = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records/{new_rids[qid]}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"jsonData": js}}) + print(f" {qid}: json={len(js)}chars code={r.json().get('code')}") + time.sleep(0.5) + +# === STEP 4: Wait then PUT 图片描述 === +print("\n=== Step 4: Wait 15s + PUT 图片描述 ===") +time.sleep(15) +token = get_token() +for qid in sorted(new_rids): + r = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records/{new_rids[qid]}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"图片描述": DATA[qid]["desc"]}}) + print(f" {qid}: desc code={r.json().get('code')} len={len(DATA[qid]['desc'])}") + time.sleep(0.5) + +# === STEP 5: Final verify === +print("\n=== Step 5: Wait 20s + Verify ===") +time.sleep(20) +token = get_token() +for qid in sorted(new_rids): + r = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records/{new_rids[qid]}", + headers={"Authorization": f"Bearer {token}"}) + fields = r.json()["data"]["record"]["fields"] + jd = fields.get("jsonData", "") + desc = fields.get("图片描述", "") or "" + try: + j = json.loads(jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + ok = (fq == 5 and sq == 5 and len(desc) > 100) + print(f" {'✅' if ok else '❌'} {qid} ({new_rids[qid]}): {fq}/{sq}q desc={len(desc)}chars") + except Exception as e: + print(f" ❌ {qid}: {e}") + +print("\nDone.") diff --git a/scripts/batch_reading_pic_judge.py b/scripts/batch_reading_pic_judge.py new file mode 100644 index 0000000..d40e61b --- /dev/null +++ b/scripts/batch_reading_pic_judge.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +"""Produce reading_pic_judge for 4 IDs (121301-121601), L1 A级.""" +import json, requests, time + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblJc60aO0T163MJ" + +# ============================================================ +# ALL QUESTIONS — L1 A级, 3-4词极简句, L1词汇 +# ============================================================ +ALL_DATA = {} + +# ---- 121301 ---- +ALL_DATA["121301"] = { + "first": [ + {"qi": "121301-00.png", "q": "These are houses.", "a": "Yes", + "x": "图上有三栋房子,句子说「这些是房子」,匹配。"}, + {"qi": "121301-01.png", "q": "This is a home.", "a": "No", + "x": "图上是一个橱柜,不是家(home),句子与图片不匹配。"}, + {"qi": "121301-02.png", "q": "This is a sofa.", "a": "Yes", + "x": "图上是一张沙发,句子说「这是一张沙发」,匹配。"}, + {"qi": "121301-03.png", "q": "These are cupboards.", "a": "No", + "x": "图上是一间卧室放着一张床,句子说「这些是橱柜」,与图片不匹配。"}, + {"qi": "121301-04.png", "q": "This is a bedroom.", "a": "Yes", + "x": "图上是一间有床的卧室,句子说「这是一间卧室」,匹配。"}, + ], + "second": [ + {"qi": "121301-05.png", "q": "This is a zoo.", "a": "Yes", + "x": "图上是动物园入口,能看到动物,句子说「这是一个动物园」,匹配。"}, + {"qi": "121301-06.png", "q": "This is a hippo.", "a": "Yes", + "x": "图上是一只河马在水里,句子说「这是一只河马」,匹配。"}, + {"qi": "121301-07.png", "q": "This is a bear.", "a": "No", + "x": "图上是一条牛仔裤,不是熊(bear),句子与图片不匹配。"}, + {"qi": "121301-08.png", "q": "These are jeans.", "a": "Yes", + "x": "图上是一条牛仔裤,句子说「这些是牛仔裤」,匹配。"}, + {"qi": "121301-09.png", "q": "This is a sock.", "a": "No", + "x": "图上是一只大棕熊在森林里,不是袜子(sock),句子与图片不匹配。"}, + ], +} + +# ---- 121401 ---- +ALL_DATA["121401"] = { + "first": [ + {"qi": "121401-00.png", "q": "These are students.", "a": "Yes", + "x": "图上是几个学生坐在教室里,句子说「这些是学生」,匹配。"}, + {"qi": "121401-01.png", "q": "This is a school.", "a": "Yes", + "x": "图上是一所学校建筑,句子说「这是一所学校」,匹配。"}, + {"qi": "121401-02.png", "q": "This is a teacher.", "a": "No", + "x": "图上是几支蜡笔,不是老师(teacher),句子与图片不匹配。"}, + {"qi": "121401-03.png", "q": "These are crayons.", "a": "Yes", + "x": "图上是几支彩色蜡笔放在桌上,句子说「这些是蜡笔」,匹配。"}, + {"qi": "121401-04.png", "q": "These are boards.", "a": "No", + "x": "图上是一位老师在讲桌前,不是黑板(board),句子与图片不匹配。"}, + ], + "second": [ + {"qi": "121401-05.png", "q": "This is a mat.", "a": "Yes", + "x": "图上是一张地板上的垫子,句子说「这是一张垫子」,匹配。"}, + {"qi": "121401-06.png", "q": "This is a letter.", "a": "No", + "x": "图上是一张课桌上面有台灯,不是信(letter),句子与图片不匹配。"}, + {"qi": "121401-07.png", "q": "This is a desk.", "a": "Yes", + "x": "图上是一张课桌,句子说「这是一张课桌」,匹配。"}, + {"qi": "121401-08.png", "q": "This is a schoolbag.", "a": "No", + "x": "图上是一个女士手提包,不是书包(schoolbag),句子与图片不匹配。"}, + {"qi": "121401-09.png", "q": "This is a handbag.", "a": "Yes", + "x": "图上是一个手提包,句子说「这是一个手提包」,匹配。"}, + ], +} + +# ---- 121501 ---- +ALL_DATA["121501"] = { + "first": [ + {"qi": "121501-00.png", "q": "This is a body.", "a": "Yes", + "x": "图上是一个孩子的全身,句子说「这是一个身体」,匹配。"}, + {"qi": "121501-01.png", "q": "This is a head.", "a": "No", + "x": "图上是一张嘴的特写,不是头(head),句子与图片不匹配。"}, + {"qi": "121501-02.png", "q": "These are mouths.", "a": "No", + "x": "图上是两个孩子在玩耍(朋友),不是嘴(mouth),句子与图片不匹配。"}, + {"qi": "121501-03.png", "q": "This is an ear.", "a": "Yes", + "x": "图上是一只耳朵的特写,句子说「这是一只耳朵」,匹配。"}, + {"qi": "121501-04.png", "q": "These are friends.", "a": "Yes", + "x": "图上是两个小朋友在一起玩,句子说「这些是朋友」,匹配。"}, + ], + "second": [ + {"qi": "121501-05.png", "q": "This is a camera.", "a": "Yes", + "x": "图上是一台相机,句子说「这是一台相机」,匹配。"}, + {"qi": "121501-06.png", "q": "This is a boy.", "a": "Yes", + "x": "图上是一个小男孩,句子说「这是一个男孩」,匹配。"}, + {"qi": "121501-07.png", "q": "This is a girl.", "a": "No", + "x": "图上是一个滑板,不是女孩(girl),句子与图片不匹配。"}, + {"qi": "121501-08.png", "q": "These are skateboards.", "a": "No", + "x": "图上是一个女孩在滑滑板,句子说「这些是滑板」但图中只有一个,且重点是人在滑板动作,与句子描述不符。"}, + {"qi": "121501-09.png", "q": "This is a skateboard.", "a": "Yes", + "x": "图上是一个滑板,句子说「这是一个滑板」,匹配。"}, + ], +} + +# ---- 121601 ---- +ALL_DATA["121601"] = { + "first": [ + {"qi": "121601-00.png", "q": "It is summer.", "a": "Yes", + "x": "图上是阳光海滩夏天场景,句子说「现在是夏天」,匹配。"}, + {"qi": "121601-01.png", "q": "It is autumn.", "a": "No", + "x": "图上是冬天白雪覆盖的场景,不是秋天(autumn),句子与图片不匹配。"}, + {"qi": "121601-02.png", "q": "It is winter.", "a": "Yes", + "x": "图上是一片雪景,句子说「现在是冬天」,匹配。"}, + {"qi": "121601-03.png", "q": "It is cold.", "a": "Yes", + "x": "图上是一个孩子穿着厚棉衣冬天场景,句子说「天气冷」,匹配。"}, + {"qi": "121601-04.png", "q": "It is spring.", "a": "No", + "x": "图上是秋天落叶满地,不是春天(spring),句子与图片不匹配。"}, + ], + "second": [ + {"qi": "121601-05.png", "q": "This is a bus.", "a": "Yes", + "x": "图上是一辆公交车,句子说「这是一辆公交车」,匹配。"}, + {"qi": "121601-06.png", "q": "This is a taxi.", "a": "No", + "x": "图上是一条长裤,不是出租车(taxi),句子与图片不匹配。"}, + {"qi": "121601-07.png", "q": "These are trousers.", "a": "Yes", + "x": "图上是一条长裤,句子说「这些是长裤」,匹配。"}, + {"qi": "121601-08.png", "q": "These are shorts.", "a": "No", + "x": "图上是白雪皑皑的下雪场景,不是短裤(shorts),句子与图片不匹配。"}, + {"qi": "121601-09.png", "q": "This is snow.", "a": "Yes", + "x": "图上是一片雪景,句子说「这是雪」,匹配。"}, + ], +} + +# ============================================================ +# Build JSON +# ============================================================ +def build_json(qid, groups): + return { + "first": { + "category": "reading", + "type": "reading_pic_judge", + "questionSetID": qid, + "textTitle": "Look and read. Put a tick (Yes) or a cross (No) in the box.", + "questionSet": [ + { + "questionImage": item["qi"], + "question": item["q"], + "answerText": item["a"], + "ability": ["图文匹配"], + "explanation": item["x"], + } + for item in groups["first"] + ], + }, + "second": { + "category": "reading", + "type": "reading_pic_judge", + "questionSetID": qid, + "textTitle": "Look and read. Put a tick (Yes) or a cross (No) in the box.", + "questionSet": [ + { + "questionImage": item["qi"], + "question": item["q"], + "answerText": item["a"], + "ability": ["图文匹配"], + "explanation": item["x"], + } + for item in groups["second"] + ], + }, + } + +# ============================================================ +# Get token & find existing records +# ============================================================ +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +# Find existing records +resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records?page_size=20", + headers={"Authorization": f"Bearer {token}"}, +) +record_map = {} +for item in resp.json()["data"]["items"]: + fid = item["fields"].get("题目集合 ID", "") + if fid in ALL_DATA: + record_map[fid] = item["record_id"] + print(f"Found: {fid} → {item['record_id']}") + +# ============================================================ +# Update each record with real jsonData +# ============================================================ +for qid in sorted(ALL_DATA.keys()): + rid = record_map.get(qid) + if not rid: + print(f"❌ {qid}: no existing record, creating new...") + resp = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"题目集合 ID": qid, "dataStatus": "0"}}, + ) + rid = resp.json()["data"]["record"]["record_id"] + print(f" Created: {rid}") + time.sleep(1.0) + + json_data = build_json(qid, ALL_DATA[qid]) + json_str = json.dumps(json_data, ensure_ascii=False) + + # Verify balance + for grp in ("first", "second"): + qs = json_data[grp]["questionSet"] + yc = sum(1 for q in qs if q["answerText"] == "Yes") + nc = sum(1 for q in qs if q["answerText"] == "No") + print(f" {qid} {grp}: {yc}Y/{nc}N, json={len(json_str)}chars") + + # Update + resp = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"jsonData": json_str}}, + ) + code = resp.json().get("code") + print(f" Update: code={code}") + time.sleep(0.5) + +# ============================================================ +# Wait & Verify +# ============================================================ +print("\nWaiting 15s for verification...") +time.sleep(15) + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +print("\n=== Final Verification ===") +all_ok = True +for qid in sorted(ALL_DATA.keys()): + rid = record_map.get(qid) + resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}"}, + ) + jd = resp.json()["data"]["record"]["fields"].get("jsonData", "") + try: + j = json.loads(jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + if fq == 5 and sq == 5: + fq1 = j["first"]["questionSet"][0]["question"] + sq1 = j["second"]["questionSet"][0]["question"] + fy = sum(1 for q in j["first"]["questionSet"] if q["answerText"] == "Yes") + fn = sum(1 for q in j["first"]["questionSet"] if q["answerText"] == "No") + print(f"✅ {qid}: first={fq}q({fy}Y{fn}N) second={sq}q | Q1='{fq1}' | SQ1='{sq1}'") + else: + print(f"❌ {qid}: first={fq}q second={sq}q | jd={len(jd)}chars") + all_ok = False + except Exception as e: + print(f"❌ {qid}: PARSE_ERROR {e} | jd={len(jd)}chars") + all_ok = False + +print(f"\n{'✅ ALL VERIFIED' if all_ok else '❌ SOME CORRUPTED'}") diff --git a/scripts/batch_reading_pic_qa.py b/scripts/batch_reading_pic_qa.py new file mode 100644 index 0000000..ace0b33 --- /dev/null +++ b/scripts/batch_reading_pic_qa.py @@ -0,0 +1,600 @@ +#!/usr/bin/env python3 +"""Batch produce reading_pic_qa questions and write to bitable.""" + +import json +import subprocess +import sys +import os + +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" # 阅读-P7-看图回答题 +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" + +TEXT_TITLE = "Look and read. Put a tick (Yes) or a cross (No) in the box." + +# ============================================================ +# All question data +# ============================================================ + +ALL_QUESTIONS = { + "000001": { + "first": { + "textImage": "000001-00.png", + "questions": [ + { + "question": "A boy looks out of the window.", + "answerText": "Yes", + "explanation": "图中男孩正站在窗前往外看,与句子描述一致。考察知识点:look out of(向外看)。" + }, + { + "question": "The boy finds a red ball.", + "answerText": "Yes", + "explanation": "图中男孩从床底下找到一只红色的皮球,与句子描述一致。考察知识点:find(找到)。" + }, + { + "question": "A girl picks up a sock.", + "answerText": "No", + "explanation": "图中是男孩捡起一只袜子,并非女孩,与句子描述不符「女孩捡起袜子」。考察知识点:pick up(捡起)。" + }, + { + "question": "The boy puts on a hat.", + "answerText": "No", + "explanation": "图中男孩正在穿鞋,并非戴帽子。put on 的对象是 shoe 而非 hat,与句子描述不符。考察知识点:put on(穿上/戴上)。" + }, + { + "question": "The boy stops at the door.", + "answerText": "Yes", + "explanation": "图中男孩在门口停下脚步回头看,与句子描述一致。考察知识点:stop(停下)。" + }, + ] + }, + "second": { + "textImage": "000001-01.png", + "questions": [ + { + "question": "A boy fastens his helmet.", + "answerText": "Yes", + "explanation": "图中男孩正在系紧头盔的带子,与句子描述一致。考察知识点:fasten(系紧/扣紧)。" + }, + { + "question": "A girl unfastens the rope.", + "answerText": "Yes", + "explanation": "图中女孩在攀爬架上正在解开安全绳,与句子描述一致。考察知识点:unfasten(解开)。" + }, + { + "question": "The playground is dangerous.", + "answerText": "No", + "explanation": "图中游乐场有安全绳、防护垫等安全设施,是安全的游戏区域,并非危险。考察知识点:dangerous(危险的)。" + }, + { + "question": "A woman guides the boy.", + "answerText": "Yes", + "explanation": "图中一位女士正在指导男孩如何攀爬,与句子描述一致。考察知识点:guide(指导/引导)。" + }, + { + "question": "The pirate is on a ship.", + "answerText": "No", + "explanation": "图中穿海盗服装的男孩在游乐场攀爬架上玩耍,并不在船上。考察知识点:pirate(海盗)。" + }, + ] + } + }, + "010101": { + "first": { + "textImage": "010101-00.png", + "questions": [ + { + "question": "The chicken is on the plate.", + "answerText": "Yes", + "explanation": "图中盘子里放着鸡肉,与句子描述一致。考察知识点:chicken(鸡;鸡肉)。" + }, + { + "question": "The girl reads a menu.", + "answerText": "Yes", + "explanation": "图中女孩正在看菜单,与句子描述一致。考察知识点:menu(菜单)。" + }, + { + "question": "The boy cooks the steak.", + "answerText": "No", + "explanation": "图中是厨师在烹饪牛排,并非男孩在烹饪,与句子描述不符。考察知识点:cook(烹饪)。" + }, + { + "question": "The steak is on the table.", + "answerText": "Yes", + "explanation": "图中桌上放着一块牛排,与句子描述一致。考察知识点:steak(牛排)。" + }, + { + "question": "A chef has a red hat.", + "answerText": "No", + "explanation": "图中厨师帽是白色的,并非红色,与句子描述不符。考察知识点:chef(厨师)。" + }, + ] + }, + "second": { + "textImage": "010101-01.png", + "questions": [ + { + "question": "The onion is white.", + "answerText": "Yes", + "explanation": "图中摊位上放着白色的洋葱,与句子描述一致。考察知识点:onion(洋葱)。" + }, + { + "question": "The curry is yellow.", + "answerText": "Yes", + "explanation": "图中碗里的咖喱是黄色的,与句子描述一致。考察知识点:curry(咖喱)。" + }, + { + "question": "The mushroom is blue.", + "answerText": "No", + "explanation": "图中蘑菇是棕色/白色的,并非蓝色。自然界没有蓝色的蘑菇,与句子描述不符。考察知识点:mushroom(蘑菇)。" + }, + { + "question": "The garlic is green.", + "answerText": "No", + "explanation": "图中大蒜是白色的,并非绿色,与句子描述不符。考察知识点:garlic(大蒜)。" + }, + { + "question": "The chilli is red.", + "answerText": "Yes", + "explanation": "图中摊位上放着红色的辣椒,与句子描述一致。考察知识点:chilli(辣椒)。" + }, + ] + } + }, + "010201": { + "first": { + "textImage": "010201-00.png", + "questions": [ + { + "question": "A robot is in the room.", + "answerText": "Yes", + "explanation": "图中房间中央站着一个机器人,与句子描述一致。考察知识点:robot(机器人)。" + }, + { + "question": "The robot has a big head.", + "answerText": "Yes", + "explanation": "图中机器人的头部很大,与句子描述一致。考察知识点:head(头)。" + }, + { + "question": "The robot has four legs.", + "answerText": "No", + "explanation": "图中机器人只有两条腿,并非四条,与句子描述不符。考察知识点:leg(腿)。" + }, + { + "question": "The robot has two arms.", + "answerText": "Yes", + "explanation": "图中机器人有两条手臂,与句子描述一致。考察知识点:arm(手臂)。" + }, + { + "question": "The robot's body is green.", + "answerText": "No", + "explanation": "图中机器人身体是银灰色的,并非绿色,与句子描述不符。考察知识点:body(身体)。" + }, + ] + }, + "second": { + "textImage": "010201-01.png", + "questions": [ + { + "question": "A story is on the wall.", + "answerText": "Yes", + "explanation": "图中布告栏上贴着一篇故事,与句子描述一致。考察知识点:story(故事)。" + }, + { + "question": "A poster is on the door.", + "answerText": "No", + "explanation": "图中海报贴在布告栏上,并非贴在门上,与句子描述不符。考察知识点:poster(海报)。" + }, + { + "question": "A report is on the desk.", + "answerText": "Yes", + "explanation": "图中桌上放着一份报告,与句子描述一致。考察知识点:report(报告)。" + }, + { + "question": "The news is on TV.", + "answerText": "No", + "explanation": "图中场景是教室布告栏,没有电视。新闻印在纸上贴在布告栏上,并非在电视上播放。考察知识点:news(新闻)。" + }, + { + "question": "A picture is on the board.", + "answerText": "Yes", + "explanation": "图中布告板上贴着一张图片,与句子描述一致。考察知识点:picture(图片)。" + }, + ] + } + }, + "010301": { + "first": { + "textImage": "010301-00.png", + "questions": [ + { + "question": "The lion has a mane.", + "answerText": "Yes", + "explanation": "图中狮子有一圈鬃毛,与句子描述一致。考察知识点:lion(狮子)。" + }, + { + "question": "The animal is a dog.", + "answerText": "No", + "explanation": "图中动物是狮子,并非狗,与句子描述不符。考察知识点:animal(动物)。" + }, + { + "question": "A bear is in the tree.", + "answerText": "Yes", + "explanation": "图中树洞里有一只熊,与句子描述一致。考察知识点:bear(熊)。" + }, + { + "question": "The butterfly is pink.", + "answerText": "No", + "explanation": "图中蝴蝶是黄色和黑色的,并非粉色,与句子描述不符。考察知识点:butterfly(蝴蝶)。" + }, + { + "question": "The fish is in water.", + "answerText": "Yes", + "explanation": "图中鱼在水中游动,与句子描述一致。考察知识点:fish(鱼)。" + }, + ] + }, + "second": { + "textImage": "010301-01.png", + "questions": [ + { + "question": "The pool is blue.", + "answerText": "Yes", + "explanation": "图中游泳池的水是蓝色的,与句子描述一致。考察知识点:pool(水池/游泳池)。" + }, + { + "question": "A storm is in the sky.", + "answerText": "No", + "explanation": "图中天空晴朗有太阳,并没有暴风雨,与句子描述不符。考察知识点:storm(暴风雨)。" + }, + { + "question": "The lake is big.", + "answerText": "Yes", + "explanation": "图中的湖泊很大,与句子描述一致。考察知识点:lake(湖泊)。" + }, + { + "question": "The grass is brown.", + "answerText": "No", + "explanation": "图中草地是绿色的,并非棕色,与句子描述不符。考察知识点:grass(草地)。" + }, + { + "question": "A forest has many trees.", + "answerText": "Yes", + "explanation": "图中森林里有很多树,与句子描述一致。考察知识点:forest(森林)。" + }, + ] + } + }, + "010401": { + "first": { + "textImage": "010401-00.png", + "questions": [ + { + "question": "A purple hat is on the chair.", + "answerText": "No", + "explanation": "图中紫色帽子在桌上,并不在椅子上,与句子描述不符。考察知识点:purple(紫色的)。" + }, + { + "question": "The pink flower is big.", + "answerText": "Yes", + "explanation": "图中有朵粉色的花很大,与句子描述一致。考察知识点:pink(粉色的)。" + }, + { + "question": "The picture is colourful.", + "answerText": "Yes", + "explanation": "图中的画色彩丰富,与句子描述一致。考察知识点:colourful(色彩丰富的)。" + }, + { + "question": "The spoon is silver.", + "answerText": "No", + "explanation": "图中勺子为木色的,并非银色,与句子描述不符。考察知识点:silver(银色的)。" + }, + { + "question": "A special cake is on the desk.", + "answerText": "Yes", + "explanation": "图中桌中央有一个装饰特别的生日蛋糕,与其他物品不同。考察知识点:special(特别的)。" + }, + ] + }, + "second": { + "textImage": "010401-01.png", + "questions": [ + { + "question": "A sour lemon is on the table.", + "answerText": "Yes", + "explanation": "图中桌上放着一个柠檬(柠檬是酸的),与句子描述一致。考察知识点:sour(酸的)。" + }, + { + "question": "The mango is yellow.", + "answerText": "Yes", + "explanation": "图中芒果是黄色的,与句子描述一致。考察知识点:mango(芒果)。" + }, + { + "question": "The grape is purple.", + "answerText": "Yes", + "explanation": "图中葡萄是紫色的,与句子描述一致。考察知识点:grape(葡萄)。" + }, + { + "question": "The lemon is sweet.", + "answerText": "No", + "explanation": "柠檬是酸的,并非甜的,与句子描述不符。考察知识点:lemon(柠檬)。" + }, + { + "question": "The juice is orange.", + "answerText": "No", + "explanation": "图中果汁是紫色的葡萄汁,并非橙色的橙汁,与句子描述不符。考察知识点:juice(果汁)。" + }, + ] + } + }, + "010501": { + "first": { + "textImage": "010501-00.png", + "questions": [ + { + "question": "The company has an office.", + "answerText": "Yes", + "explanation": "图中公司设有办公室,与句子描述一致。考察知识点:company(公司)。" + }, + { + "question": "A card is on the desk.", + "answerText": "Yes", + "explanation": "图中桌面上放着一张卡片,与句子描述一致。考察知识点:card(卡片)。" + }, + { + "question": "The receptionist is a man.", + "answerText": "No", + "explanation": "图中前台接待员是女性,并非男性,与句子描述不符。考察知识点:receptionist(接待员)。" + }, + { + "question": "The office is small.", + "answerText": "Yes", + "explanation": "图中的办公室不大/是小的,与句子描述一致。考察知识点:office(办公室)。" + }, + { + "question": "A meeting is in the park.", + "answerText": "No", + "explanation": "图中会议在办公室会议室举行,不在公园里,与句子描述不符。考察知识点:meeting(会议)。" + }, + ] + }, + "second": { + "textImage": "010501-01.png", + "questions": [ + { + "question": "The bathroom is clean.", + "answerText": "Yes", + "explanation": "图中卫生间干净整洁,与句子描述一致。考察知识点:bathroom(卫生间)。" + }, + { + "question": "A bookshelf is on the wall.", + "answerText": "Yes", + "explanation": "图中书柜靠墙摆放,与句子描述一致。考察知识点:bookshelf(书架)。" + }, + { + "question": "The toilet is in the kitchen.", + "answerText": "No", + "explanation": "图中马桶在卫生间里,不在厨房里,与句子描述不符。考察知识点:toilet(马桶)。" + }, + { + "question": "The lamp is on the table.", + "answerText": "Yes", + "explanation": "图中台灯放在桌上,与句子描述一致。考察知识点:lamp(台灯)。" + }, + { + "question": "A laptop is on the bed.", + "answerText": "No", + "explanation": "图中笔记本电脑在书桌上,不在床上,与句子描述不符。考察知识点:laptop(笔记本电脑)。" + }, + ] + } + }, + "010601": { + "first": { + "textImage": "010601-00.png", + "questions": [ + { + "question": "A big stone is on the ground.", + "answerText": "Yes", + "explanation": "图中地上一块大石头,与句子描述一致。考察知识点:stone(石头)。" + }, + { + "question": "A dinosaur has big bones.", + "answerText": "Yes", + "explanation": "图中恐龙骨架有巨大的骨头,与句子描述一致。考察知识点:dinosaur(恐龙)。" + }, + { + "question": "The sky is gray.", + "answerText": "No", + "explanation": "图中天空是蓝色的,并非灰色,与句子描述不符。考察知识点:gray(灰色的)。" + }, + { + "question": "The rock is very big.", + "answerText": "Yes", + "explanation": "图中的岩石非常大,与句子描述一致。考察知识点:rock(岩石)。" + }, + { + "question": "The flower is fresh.", + "answerText": "No", + "explanation": "图中场景是岩石挖掘区,没有鲜花。花朵不存在于场景中,与句子描述不符。考察知识点:fresh(新鲜的)。" + }, + ] + }, + "second": { + "textImage": "010601-01.png", + "questions": [ + { + "question": "A tool is on the desk.", + "answerText": "Yes", + "explanation": "图中桌上放着一件工具,与句子描述一致。考察知识点:tool(工具)。" + }, + { + "question": "A scientist has a white coat.", + "answerText": "Yes", + "explanation": "图中科学家穿着白色实验服,与句子描述一致。考察知识点:scientist(科学家)。" + }, + { + "question": "A brain is in the jar.", + "answerText": "Yes", + "explanation": "图中罐子里放着一个大脑模型,与句子描述一致。考察知识点:brain(大脑)。" + }, + { + "question": "The boy dives in the sea.", + "answerText": "No", + "explanation": "图中男孩在实验室里看潜水设备,并没有在海里潜水,与句子描述不符。考察知识点:dive(潜水)。" + }, + { + "question": "The adventure is on TV.", + "answerText": "No", + "explanation": "图中实验室里没有电视,探险装备是实物放在桌上,不在电视上。考察知识点:adventure(探险/冒险)。" + }, + ] + } + }, +} + + +def build_json(question_set_id, data): + """Build the full JSON structure for one questionSetID.""" + result = { + "first": { + "category": "reading", + "type": "reading_pic_qa", + "questionSetID": question_set_id, + "textTitle": TEXT_TITLE, + "textImage": data["first"]["textImage"], + "questionSet": [ + { + "question": q["question"], + "answerText": q["answerText"], + "ability": ["图文判断", "句图一致性"], + "explanation": q["explanation"], + } + for q in data["first"]["questions"] + ] + }, + "second": { + "category": "reading", + "type": "reading_pic_qa", + "questionSetID": question_set_id, + "textTitle": TEXT_TITLE, + "textImage": data["second"]["textImage"], + "questionSet": [ + { + "question": q["question"], + "answerText": q["answerText"], + "ability": ["图文判断", "句图一致性"], + "explanation": q["explanation"], + } + for q in data["second"]["questions"] + ] + } + } + return result + + +def get_token(): + """Get tenant_access_token from credentials.""" + with open(CRED_FILE) as f: + cred = json.load(f) + app_id = cred["apps"][0]["appId"] + app_secret = cred["apps"][0]["appSecret"] + + import requests + resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, + ) + return resp.json()["tenant_access_token"] + + +def create_record(token, question_set_id, json_data): + """Create a record in the bitable.""" + import requests + + fields = { + "题目集合 ID": question_set_id, + "jsonData": json.dumps(json_data, ensure_ascii=False), + "dataStatus": "0", + } + + resp = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records", + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + }, + json={"fields": fields}, + ) + return resp.json() + + +def verify_record(token, record_id): + """Verify a record by reading it back.""" + import requests + + resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, + ) + return resp.json() + + +def main(): + token = get_token() + + results = [] + for qid in sorted(ALL_QUESTIONS.keys()): + print(f"\n{'='*60}") + print(f"Processing ID: {qid}") + print(f"{'='*60}") + + data = ALL_QUESTIONS[qid] + json_data = build_json(qid, data) + + # Count Yes/No per 题组 + for grp_name in ("first", "second"): + qs = json_data[grp_name]["questionSet"] + yes_count = sum(1 for q in qs if q["answerText"] == "Yes") + no_count = sum(1 for q in qs if q["answerText"] == "No") + print(f" {grp_name}: {yes_count}Y / {no_count}N") + + # Create record + resp = create_record(token, qid, json_data) + if resp.get("code") != 0: + print(f" ❌ 创建失败: {resp.get('msg', resp)}") + results.append((qid, "FAIL", resp.get("msg", ""))) + continue + + record_id = resp["data"]["record"]["record_id"] + print(f" ✅ 创建成功, record_id: {record_id}") + + # Verify + verify_resp = verify_record(token, record_id) + if verify_resp.get("code") != 0: + print(f" ⚠️ 回读失败: {verify_resp.get('msg', verify_resp)}") + results.append((qid, "CREATED_NO_VERIFY", record_id)) + continue + + # Check jsonData完整性 + stored_fields = verify_resp["data"]["record"]["fields"] + stored_json = stored_fields.get("jsonData", "") + try: + parsed = json.loads(stored_json) + f_count = len(parsed.get("first", {}).get("questionSet", [])) + s_count = len(parsed.get("second", {}).get("questionSet", [])) + print(f" ✅ 回读验证: first={f_count}题, second={s_count}题, jsonData完整") + except Exception as e: + print(f" ⚠️ 回读JSON解析失败: {e}") + + results.append((qid, "OK", record_id)) + + print(f"\n{'='*60}") + print(f"汇总") + print(f"{'='*60}") + for qid, status, detail in results: + print(f" {qid}: {status} ({detail})") + + +if __name__ == "__main__": + main() diff --git a/scripts/batch_reading_pic_qa_2.py b/scripts/batch_reading_pic_qa_2.py new file mode 100644 index 0000000..bc61868 --- /dev/null +++ b/scripts/batch_reading_pic_qa_2.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""reading_pic_qa 010701 + 010801, L2 A级""" +import json, requests, time + +CRED = "/root/.openclaw/credentials/xiaoyan/config.json" +TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TBL = "tblweY65jGBiwSdt" + +with open(CRED) as f: + cred = json.load(f) +aid = cred["apps"][0]["appId"] +asecret = cred["apps"][0]["appSecret"] + +def get_token(): + r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": aid, "app_secret": asecret}) + return r.json()["tenant_access_token"] + +token = get_token() + +DATA = { + "010701": { + "first": [ + ("A notebook is on the desk.", "Yes", "桌子上放着一本打开的笔记本,句子描述正确。"), + ("A star is on the wall.", "Yes", "墙上贴着一颗黄色的星星贴纸,句子描述正确。"), + ("A folder is under the chair.", "No", "文件夹在桌上,不在椅子下面,句子与图片不匹配。"), + ("The girl reads a diary.", "Yes", "女孩坐在桌前翻开一本日记在阅读,句子描述正确。"), + ("The contact list is on the wall.", "No", "联系人列表在电脑屏幕上,不在墙上贴着,句子与图片不匹配。"), + ], + "second": [ + ("A friendly girl gives you a flower.", "Yes", "一个面带微笑的女孩伸手递出一朵花,她很友好,句子描述正确。"), + ("A crazy boy is jumping on the desk.", "No", "男孩安静地坐在桌前看书,没有在桌上跳,他不疯狂,句子与图片不匹配。"), + ("A brilliant student has a gold star.", "Yes", "一个学生拿着A+试卷,上面贴着金色星星,她非常优秀,句子描述正确。"), + ("A kind boy helps his friend.", "Yes", "一个男孩蹲下帮另一个孩子捡起掉在地上的书,他很善良,句子描述正确。"), + ("The little girl is afraid of the dog.", "No", "小女孩开心地摸着小狗的头,她并不害怕,句子与图片不匹配。"), + ], + }, + "010801": { + "first": [ + ("Uncle is reading a newspaper.", "Yes", "叔叔坐在沙发上展开一份报纸在读,句子描述正确。"), + ("Aunt is cooking in the kitchen.", "No", "阿姨坐在沙发上织毛衣,不在厨房做饭,句子与图片不匹配。"), + ("Grandma is sitting in an armchair.", "Yes", "奶奶坐在一张扶手椅上,腿上盖着毯子,句子描述正确。"), + ("The neighbour is at the door.", "No", "门口没有邻居,只有一只宠物狗趴在地板上,句子与图片不匹配。"), + ("A pet dog is sleeping on the floor.", "Yes", "一只宠物狗趴在客厅地板上闭着眼睛睡觉,句子描述正确。"), + ], + "second": [ + ("The house looks expensive.", "Yes", "房子外观很大气,花园整洁,有雕花栏杆,看起来很贵,句子描述正确。"), + ("A boy is at the entrance.", "Yes", "男孩站在房子大门口(入口处),正按门铃,句子描述正确。"), + ("A cat is walking upstairs.", "No", "小猫正在沿着楼梯往下走(downstairs),不是往上走(upstairs),句子与图片不匹配。"), + ("A girl is running downstairs.", "Yes", "一个女孩正从楼梯上跑下来,头发飘起,句子描述正确。"), + ("Mum is making toast in the kitchen.", "No", "妈妈在客厅窗边看书,不在厨房做吐司,句子与图片不匹配。"), + ], + }, +} + +TT = "Look and read. Put a tick (Yes) or a cross (No) in the box." +AB = ["图文判断", "句图一致性"] + +def build(qid, grp_name, questions): + return { + "category": "reading", + "type": "reading_pic_qa", + "questionSetID": qid, + "textTitle": TT, + "textImage": f"{qid}-{'00' if grp_name=='first' else '01'}.png", + "questionSet": [ + {"question": q, "answerText": a, "ability": AB, "explanation": x} + for q, a, x in questions + ], + } + +def build_full(qid): + return { + "first": build(qid, "first", DATA[qid]["first"]), + "second": build(qid, "second", DATA[qid]["second"]), + } + +# Delete existing +print("=== Clean ===") +resp = requests.get(f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN}/tables/{TBL}/records?page_size=20", + headers={"Authorization": f"Bearer {token}"}) +for item in resp.json()["data"]["items"]: + fid = item["fields"].get("题目集合 ID", "") + if fid in DATA: + requests.delete( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN}/tables/{TBL}/records/{item['record_id']}", + headers={"Authorization": f"Bearer {token}"}) + print(f" Deleted {fid}") + +time.sleep(2) + +# Create empty +print("\n=== Create ===") +rids = {} +for qid in sorted(DATA): + r = requests.post(f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN}/tables/{TBL}/records", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"题目集合 ID": qid, "dataStatus": "0"}}) + rid = r.json()["data"]["record"]["record_id"] + rids[qid] = rid + print(f" {qid}: {rid}") + time.sleep(0.5) + +# Wait for automation +print("\n=== Wait 30s ===") +time.sleep(30) + +# Update with jsonData +print("\n=== Update ===") +token = get_token() +for qid in sorted(rids): + j = build_full(qid) + js = json.dumps(j, ensure_ascii=False) + fy = sum(1 for q in j["first"]["questionSet"] if q["answerText"] == "Yes") + fn = 5 - fy + print(f" {qid}: first={fy}Y/{fn}N, json={len(js)}chars") + r = requests.put(f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN}/tables/{TBL}/records/{rids[qid]}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"jsonData": js}}) + print(f" update code={r.json().get('code')}") + time.sleep(0.5) + +# Wait & verify +print("\n=== Wait 30s ===") +time.sleep(30) + +token = get_token() +print("\n=== Verify ===") +for qid in sorted(rids): + r = requests.get(f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN}/tables/{TBL}/records/{rids[qid]}", + headers={"Authorization": f"Bearer {token}"}) + jd = r.json()["data"]["record"]["fields"].get("jsonData", "") + try: + j = json.loads(jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + q1 = j["first"]["questionSet"][0]["question"][:50] + print(f" {'✅' if fq==5 and sq==5 else '❌'} {qid}: {fq}/{sq}q Q1='{q1}'") + except Exception as e: + print(f" ❌ {qid}: {e}") +print("Done.") diff --git a/scripts/test_one_record.py b/scripts/test_one_record.py new file mode 100644 index 0000000..124e090 --- /dev/null +++ b/scripts/test_one_record.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Create ONE reading_pic_qa record with immediate verification.""" +import json, requests, sys + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" + +sys.path.insert(0, '/root/.openclaw/workspace-xiaoyan') +from scripts.batch_reading_pic_qa import ALL_QUESTIONS, build_json + +qid = "000001" +json_data = build_json(qid, ALL_QUESTIONS[qid]) +json_str = json.dumps(json_data, ensure_ascii=False) +print(f"jsonData size: {len(json_str)} chars") + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +# Create record +fields = { + "题目集合 ID": qid, + "jsonData": json_str, + "dataStatus": "0", +} + +print(f"\n=== Creating record ===") +resp = requests.post( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records", + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + }, + json={"fields": fields}, +) +result = resp.json() +print(f"Create response code: {result.get('code')}") + +if result.get("code") != 0: + print(f"ERROR: {result}") + sys.exit(1) + +record_id = result["data"]["record"]["record_id"] +print(f"record_id: {record_id}") + +# Read back immediately +print(f"\n=== Reading back ===") +resp2 = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, +) +data2 = resp2.json() +stored_jd = data2["data"]["record"]["fields"].get("jsonData", "") +print(f"Stored jsonData size: {len(stored_jd)} chars") + +if len(stored_jd) < 100: + print(f"TRUNCATED! Content: {repr(stored_jd)}") + # Delete it + requests.delete( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, + ) +else: + # Verify content + try: + j = json.loads(stored_jd) + fq = len(j["first"]["questionSet"]) + sq = len(j["second"]["questionSet"]) + print(f"✅ OK: first={fq} questions, second={sq} questions") + except Exception as e: + print(f"Parse error: {e}") + print(f"First 200 chars: {stored_jd[:200]}") diff --git a/scripts/update_image_descriptions.py b/scripts/update_image_descriptions.py new file mode 100644 index 0000000..46e4f94 --- /dev/null +++ b/scripts/update_image_descriptions.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +"""Update 图片描述 field for reading_pic_qa records.""" +import json, requests, time + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" + +IMAGE_DESCRIPTIONS = { + "recvjhhtWLApoW": """1. + +风格: 卡通,简单线条 +场景:儿童卧室早晨,阳光从窗户照进来 +穿睡衣的小男孩 - 站在窗前往外看,窗外有树和小鸟 ; +同一个穿睡衣的小男孩 - 跪在床前地板上,从床底下伸手找到一只红色皮球,脸上表情惊喜 ; +同一个男孩 - 从地板上弯腰捡起一只蓝白条纹袜子 ; +穿好外出衣服的小男孩 - 坐在床边正穿一只蓝色运动鞋,另一只已穿好 ; +背书包的小男孩 - 在房间门口停下脚步回头看向房间,面带微笑。 + +2. + +风格: 卡通,简单线条 +场景:户外游乐场攀爬区,有彩色攀爬架、滑梯、秋千 +戴蓝色头盔的小男孩 - 双手系紧头盔下巴带子,站在攀爬架旁 ; +扎马尾穿粉色运动服的女孩 - 在攀爬架高处双手解开安全绳 ; +攀爬架旁立着黄色安全警示牌(感叹号标志),地面铺彩色防护软垫 ; +穿红色外套的女教练 - 蹲在攀爬架旁指导男孩攀爬 ; +戴海盗帽和黑色眼罩的男孩 - 举着玩具剑站在攀爬架平台,做夸张海盗表情。""", + + "recvjhhurtvJRc": """1. + +风格: 卡通,简单线条 +场景:温馨餐厅内,靠窗的餐桌,阳光明媚 +桌上白色盘子里 - 放着一块金黄烤鸡肉 ; +扎双马尾的小女孩 - 坐在餐桌旁,双手捧着一本打开的菜单在看 ; +戴白色高厨师帽的厨师 - 在开放式厨房灶台前翻动平底锅里的牛排,穿着白色围裙 ; +同一张餐桌上 - 另一个盘子里放着一块煎好的牛排,旁边配蔬菜 ; +戴白色高厨师帽的厨师 - 头戴白色厨师帽,面带微笑。 + +2. + +风格: 卡通,简单线条 +场景:菜市场蔬菜摊位,木制摊位上摆满各种蔬菜 +白色洋葱 - 放在摊位左侧的木篮里 ; +黄色咖喱 - 盛在摊位前的小碗里,旁边有标签 ; +棕白色蘑菇 - 整齐摆放在摊位的竹篮中 ; +白色大蒜 - 串成一串挂在摊位上方 ; +红色辣椒 - 堆放在摊位右侧的红色小筐里。""", + + "recvjhhv24p2Pu": """1. + +风格: 卡通,简单线条 +场景:儿童游戏室,地板上铺着彩色地毯,墙上有时钟和画 +银灰色卡通机器人 - 站在房间中央,有大大的方脑袋,两只圆圆的眼睛,两条手臂,两条腿 ; +同一机器人 - 脑袋特别大,占据了身体三分之一的高度 ; +同一机器人 - 两条银色金属腿,每条腿有三个关节 ; +同一机器人 - 两条手臂可以弯曲,末端是钳子状抓手 ; +同一机器人 - 身体是银灰色的长方体,胸前有闪烁的小灯。 + +2. + +风格: 卡通,简单线条 +场景:教室布告栏,绿色布告板挂在墙上,旁边有老师的讲台和学生的课桌 +墙上布告栏 - 贴着一篇手写的故事,标题彩色大字 ; +布告栏上 - 贴着一张彩色大海报,上面画着动物图案 ; +教师讲台上 - 放着一份装订好的报告,封面写着"Report" ; +墙上布告栏下方 - 贴着一张打印的校园新闻,标题可见"School News" ; +布告栏中央 - 贴着一张彩色图画,画的是太阳花。""", + + "recvjhhvB36ro1": """1. + +风格: 卡通,简单线条 +场景:动物园/野生动物园,有草地、树木、水池 +一头雄狮 - 金色鬃毛浓密蓬松,站在草地上 ; +同一头狮子 - 站在草地中央,周围有其他小动物 ; +一棵大树 - 树洞里探出一只棕熊的头和爪子 ; +黄色和黑色的蝴蝶 - 停在花丛中的一朵红花上 ; +蓝色水池里 - 一条橙色金鱼在游动,水面有波纹。 + +2. + +风格: 卡通,简单线条 +场景:自然风景区,有游泳池、湖泊、草地、远处森林 +蓝色游泳池 - 池水清澈蔚蓝,池边有躺椅和遮阳伞 ; +天空晴朗 - 几朵白云,大太阳,空中没有乌云和闪电 ; +湖泊 - 在游泳池后方,湖面宽阔,远处有小船 ; +大片绿色草地 - 从游泳池边延伸到湖边,草地上有几朵小野花 ; +远景 - 大片深绿色森林,树木茂密成片。""", + + "recvjhhwbepReL": """1. + +风格: 卡通,简单线条 +场景:美术教室,桌上和墙上展示着各种色彩鲜艳的物品 +桌上放着一顶紫色帽子 - 帽子上有彩色波点装饰 ; +桌上花瓶里 - 插着一朵粉色大花,花瓣层层叠叠 ; +墙上贴着一幅画 - 画面色彩非常丰富,有红黄蓝绿紫多种颜色 ; +桌边放着几把勺子 - 其中一把是木色/米色的,旁边是彩色笔筒 ; +桌子中央 - 放着一个装饰特别的生日蛋糕,插着蜡烛,蛋糕上有彩色糖粒。 + +2. + +风格: 卡通,简单线条 +场景:果汁店/厨房操作台,桌上摆满各种水果 +桌上 - 放着一个黄色柠檬,旁边有切开的半个柠檬 ; +黄色芒果 - 放在水果篮里,表皮光滑 ; +紫色葡萄 - 一串放在白色盘子里 ; +同一个柠檬 - 旁边贴着"酸"的标签/符号 ; +玻璃杯里 - 装着紫色葡萄汁,不是橙色橙汁,旁边有榨汁机。""", + + "recvjhhwIKni7F": """1. + +风格: 卡通,简单线条 +场景:公司办公室前台区域,玻璃大门,接待台 +墙上挂着公司名称牌 - 上面印着公司logo和名称 ; +办公桌上 - 放着一张访客登记卡片 ; +女性前台接待员 - 坐在接待台后,穿着职业装,戴着耳麦,微笑面对访客 ; +小小的单间办公室 - 里面有一张办公桌和电脑,门半开 ; +办公室里的会议室 - 椭圆长桌周围坐满了人,有人在白板前讲解。 + +2. + +风格: 卡通,简单线条 +场景:现代家庭/公寓内部,多个房间 +干净整洁的卫生间 - 白色瓷砖墙面,洗手台和镜子 ; +墙上 - 靠着一个大书柜,上面整齐排列着各种颜色的书 ; +白色马桶 - 在卫生间角落里,旁边有卫生纸架 ; +书桌上 - 放着一盏亮着的台灯,灯光照在桌面上 ; +书桌上 - 放着一台打开的笔记本电脑,屏幕亮着,鼠标在旁,不是在床上。""", + + "recvjhhxe6QPYd": """1. + +风格: 卡通,简单线条 +场景:考古挖掘现场/自然博物馆,沙土地面,有围栏 +一块大石头 - 埋在沙土里半露在外面,上面有纹理 ; +巨大的恐龙骨架化石 - 立在场地中央,骨骼巨大清晰可见 ; +蔚蓝的天空 - 有几朵白云,阳光明媚,不是阴天灰色 ; +一块巨大的岩石 - 竖立在挖掘坑旁边,比人还高 ; +沙土地面 - 到处是石头和化石碎片,没有鲜花和绿色植物。 + +2. + +风格: 卡通,简单线条 +场景:科学实验室/探险研究基地,墙上贴满图表 +实验桌上 - 放着扳手、螺丝刀等工具 ; +穿白色实验服的科学家 - 戴着护目镜,站在实验台前做实验 ; +透明玻璃罐里 - 浸泡着一个大脑模型,放在架子上 ; +墙上贴着潜水海报 - 海报里有人在海里潜水,实验室里的男孩指着海报看,男孩本人不在海里 ; +实验室内 - 地上放着探险设备(背包、绳索、指南针等),没有电视,实验室门上有"Adventure Lab"标志。""", +} + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +results = {} +for record_id, desc in IMAGE_DESCRIPTIONS.items(): + print(f"Updating {record_id}...") + resp = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"图片描述": desc}}, + ) + code = resp.json().get("code") + print(f" code={code}") + results[record_id] = code + time.sleep(0.5) + +# Verify +print("\n=== Verification ===") +for record_id in IMAGE_DESCRIPTIONS: + resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}", + headers={"Authorization": f"Bearer {token}"}, + ) + img_desc = resp.json()["data"]["record"]["fields"].get("图片描述", "") + print(f" {record_id}: 图片描述={len(img_desc)} chars {'✅' if len(img_desc) > 100 else '❌ EMPTY'}") + +print("\nDone.") diff --git a/scripts/update_l2_difficulty_skills.py b/scripts/update_l2_difficulty_skills.py new file mode 100644 index 0000000..75caa7b --- /dev/null +++ b/scripts/update_l2_difficulty_skills.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +"""批量更新 L2 和共用题型 SKILL.md 中的难度对应关系表""" + +import os +import re + +BASE = "/root/.openclaw/workspace-xiaoyan/business_production/单元挑战/skills/unit_challenge/questions" + +# New L2 8-stage difficulty table +L2_NEW_TABLE = """## 难度对应关系(与教研标准对齐) +| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 | +|------|---------|---------|------| +""" + +# Store the A/B/C/D rows with the new mapping - 通用声明式,实际难度描述(最后一列)保留原样 +L2_NEW_HEADER = "| 难度等级 | 对应L2阶段(Unit范围) | 对标说明 | 难度描述 |" + +# Maps for old->new grade columns +L2_GRADE_MAP = { + "A级": "| A级 | U1-U12(阶段1-2) | Flyers入门→基础 |", + "B级": "| B级 | U13-U24(阶段3-4) | Flyers达标→KET入门 |", + "C级": "| C级 | U25-U36(阶段5-6) | KET基础→强化 |", + "D级": "| D级 | U37-U48(阶段7-8) | KET高位→达标 |", +} + +def update_l2_exclusive(filepath): + """Update L2专属题型: replace entire difficulty table with 8-stage version""" + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + original = content + + # ---- Part 1: Replace the difficulty table ---- + # Find the section from "## 难度对应关系" to next heading/separator + pattern = r'(## 难度对应关系.*?\n)\| 难度等级 \|.*?\n\|[-|\s]*\n((?:\| [ABCD]级 \|.*?\n)+)' + + def replace_table(match): + section_title = match.group(1) + old_rows = match.group(2) + + new_rows = [] + for old_row in old_rows.strip().split('\n'): + # Extract the last column (难度描述) from old row + parts = old_row.split('|') + if len(parts) >= 4: + desc = parts[-2].strip() if len(parts) > 4 else parts[-1].strip() + # Determine which grade + for grade, new_prefix in L2_GRADE_MAP.items(): + if grade in old_row: + new_rows.append(f"{new_prefix} {desc} |") + break + else: + new_rows.append(old_row) + + return section_title + L2_NEW_HEADER + '\n' + '\n'.join(new_rows) + '\n' + + content = re.sub(pattern, replace_table, content, flags=re.DOTALL) + + # ---- Part 2: Update config field description for 难度等级 ---- + # Pattern: | 难度等级 | ✅ | `A级` / `...` / `...` | ... | + # Replace with new references + def replace_config(match): + full_line = match.group(0) + # Replace old stage/cambridge refs with new ones + new_line = re.sub( + r'`[ABCD]级` / `(?:L[12])(?:第)?[\d-]+阶段` / `(?:Starters|Movers|Flyers)[\s\d-]*`', + lambda m: m.group(0).replace( + m.group(0), + re.sub( + r'`([ABCD]级)` / `.*?` / `.*?`', + lambda g: { + 'A级': '`A级` / `U1-U12(阶段1-2)` / `Flyers入门→基础`', + 'B级': '`B级` / `U13-U24(阶段3-4)` / `Flyers达标→KET入门`', + 'C级': '`C级` / `U25-U36(阶段5-6)` / `KET基础→强化`', + 'D级': '`D级` / `U37-U48(阶段7-8)` / `KET高位→达标`', + }[g.group(1)], + m.group(0) + ) + ), + full_line + ) + # Also update the note column + new_line = new_line.replace('难度等级或对应阶段', '难度等级(L2用Unit范围)') + return new_line + + content = re.sub( + r'\| 难度等级 \| ✅ \|.*?\|.*?\|', + replace_config, + content + ) + + if content != original: + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return True + return False + + +def update_common(filepath): + """Update L1&L2共用题型: add L2 dimension after L1 table""" + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + original = content + + # Check if already has L2 dimension + if 'L2 维度' in content or 'L2阶段(Unit范围)' in content: + return False + + # Find end of L1 difficulty table (## 难度对应关系 ... through last D级 row) + pattern = r'(## 难度对应关系.*?\n(?:\| .*?\n)*)' + + def add_l2_dimension(match): + table_block = match.group(1) + l2_note = ( + "\n> **L2 难度维度(对标 Flyers/KET,每2阶段为1个难度等级):**\n" + "> - A级 = U1-U12(阶段1-2):Flyers入门→Flyers基础\n" + "> - B级 = U13-U24(阶段3-4):Flyers达标→KET入门\n" + "> - C级 = U25-U36(阶段5-6):KET基础→KET强化\n" + "> - D级 = U37-U48(阶段7-8):KET高位→KET达标\n" + ) + return table_block + l2_note + + content = re.sub(pattern, add_l2_dimension, content, flags=re.DOTALL) + + # Also update config row if it references Starters/Movers + content = re.sub( + r'\| 难度等级 \| ✅ \|.*?\| 难度等级或对应阶段 \|', + lambda m: m.group(0).replace('难度等级或对应阶段', '难度等级(L1用Starters/Movers,L2用Unit范围)'), + content + ) + + if content != original: + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return True + return False + + +def main(): + L2_FILES = [ + "listening/L2/listening_short_conversation/SKILL.md", + "listening/L2/listening_long_conversation/SKILL.md", + "listening/L2/listening_form_fill/SKILL.md", + "listening/L2/listening-choicePic/SKILL.md", + "listening/L2/listening_info_match/SKILL.md", + "reading/reading_info_match/SKILL.md", + "reading/reading_paragraph_match/SKILL.md", + "reading/reading_long_passage/SKILL.md", + "reading/reading_cloze/SKILL.md", + "reading/reading_open_fill/SKILL.md", + "writing/writing_email_reply/SKILL.md", + "writing/writing_picture_writing/SKILL.md", + "speaking/L2/speaking_topic_discussion/SKILL.md", + ] + + COMMON_FILES = [ + "reading/common/reading_pic_qa/SKILL.md", + "reading/common/reading_pic_judge/SKILL.md", + "speaking/common/speaking_daily_qa/SKILL.md", + "writing/common/writing_pic_qa/SKILL.md", + ] + + updated = 0 + + print("=== L2 专属题型 ===") + for rel_path in L2_FILES: + full = os.path.join(BASE, rel_path) + if not os.path.exists(full): + print(f" [MISS] {rel_path}") + continue + if update_l2_exclusive(full): + print(f" [OK] {rel_path}") + updated += 1 + else: + print(f" [NO CHANGE] {rel_path}") + + print("\n=== L1&L2 共用题型 ===") + for rel_path in COMMON_FILES: + full = os.path.join(BASE, rel_path) + if not os.path.exists(full): + print(f" [MISS] {rel_path}") + continue + if update_common(full): + print(f" [OK] {rel_path}") + updated += 1 + else: + print(f" [NO CHANGE] {rel_path}") + + print(f"\n=== Done: {updated} files updated ===") + +if __name__ == '__main__': + main() diff --git a/scripts/update_pic_judge_descriptions.py b/scripts/update_pic_judge_descriptions.py new file mode 100644 index 0000000..e92c900 --- /dev/null +++ b/scripts/update_pic_judge_descriptions.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 +"""Update 图片描述 for reading_pic_judge records.""" +import json, requests, time + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblJc60aO0T163MJ" + +IMAGE_DESC = { + # ---- 121301 ---- + "recvj5nIAxF3XG": """1. + +风格: 卡通,简单线条 +场景:住宅街道,阳光下三栋彩色小房子并排而立 +三栋卡通房子 - 黄色屋顶、红色屋顶、蓝色屋顶,并排站着,前面有小路和花草。 + +2. + +风格: 卡通,简单线条 +场景:厨房一角 +棕色木质橱柜 - 靠着墙,柜子上放着几个盘子,柜门半开能看到里面的杯子。 + +3. + +风格: 卡通,简单线条 +场景:客厅一角 +一张大沙发 - 蓝色软沙发,沙发上有两个彩色靠垫,沙发前有小茶几。 + +4. + +风格: 卡通,简单线条 +场景:卧室 +一间卧室 - 里面有粉色小床、床头柜,墙上有时钟和画,地板上铺着地毯。 + +5. + +风格: 卡通,简单线条 +场景:卧室 +一张小床 - 粉色小床上有枕头和被子,床头柜上放着一盏台灯,房间墙上贴着星星贴纸。 + +6. + +风格: 卡通,简单线条 +场景:动物园门口 +动物园入口 - 大拱门上写着"ZOO",门外能看到长颈鹿和狮子在围栏里。 + +7. + +风格: 卡通,简单线条 +场景:动物园水池 +河马 - 一只胖胖的河马泡在浅蓝色的水池里,只露出头和两只小耳朵,旁边有水草。 + +8. + +风格: 卡通,简单线条 +场景:卧室衣柜前 +牛仔裤 - 一条蓝色牛仔裤叠放在椅子上。 + +9. + +风格: 卡通,简单线条 +场景:卧室衣柜前 +牛仔裤 - 一条蓝色牛仔裤挂在衣架上。 + +10. + +风格: 卡通,简单线条 +场景:森林里 +大棕熊 - 一只棕色大熊站在绿色森林中,旁边有大树。""", + + # ---- 121401 ---- + "recvj5nJG33bBE": """1. + +风格: 卡通,简单线条 +场景:小学教室 +学生们 - 三个穿校服的小学生坐在课桌前,女孩扎着马尾,男孩短发,每人桌上放着课本。 + +2. + +风格: 卡通,简单线条 +场景:学校外观 +学校建筑 - 一栋红色砖墙的学校大楼,楼顶有国旗飘动,大门敞开,门口有"School"标牌。 + +3. + +风格: 卡通,简单线条 +场景:美术课桌面 +蜡笔 - 桌上散落着红黄蓝绿紫五支彩色蜡笔。 + +4. + +风格: 卡通,简单线条 +场景:美术课桌面 +蜡笔 - 红黄蓝绿四支蜡笔整齐排放在白纸上。 + +5. + +风格: 卡通,简单线条 +场景:教室 +女老师 - 穿着裙子的女老师站在讲台前,手里拿着粉笔,身后的黑板上写着字母。 + +6. + +风格: 卡通,简单线条 +场景:儿童卧室地面 +垫子 - 一张彩色图案的方形游戏垫铺在地板上,垫子旁边散放着积木玩具。 + +7. + +风格: 卡通,简单线条 +场景:儿童卧室 +课桌 - 一张木色小课桌,桌上有台灯、铅笔筒和一本打开的书。 + +8. + +风格: 卡通,简单线条 +场景:衣帽间 +手提包 - 一个红色女士手提包挂在挂钩上,包上有金色扣子。 + +9. + +风格: 卡通,简单线条 +场景:卧室门口 +书包 - 一个蓝色卡通书包靠在门边。 + +10. + +风格: 卡通,简单线条 +场景:衣帽间 +手提包 - 一个红色手提包放在白色架子上。""", + + # ---- 121501 ---- + "recvj5nKxhm8X6": """1. + +风格: 卡通,简单线条 +场景:白色背景前 +小男孩全身 - 一个穿红色T恤和蓝色短裤的男孩,从头到脚完整站立,双臂自然下垂。 + +2. + +风格: 卡通,简单线条 +场景:白色背景前 +嘴的特写 - 一张微笑的嘴,上下两排白色牙齿,嘴唇粉色。 + +3. + +风格: 卡通,简单线条 +场景:公园草地上 +两个小朋友 - 男孩和女孩在草地上一起玩球,两人都笑得很开心。 + +4. + +风格: 卡通,简单线条 +场景:白色背景前 +耳朵的特写 - 一只人的耳朵,耳廓清晰可见。 + +5. + +风格: 卡通,简单线条 +场景:公园 +两个小朋友 - 一个男孩和一个女孩手牵手站在树下,两人微笑着看向前方。 + +6. + +风格: 卡通,简单线条 +场景:白色背景前 +相机 - 一台黑色单反相机,镜头朝前,闪光灯在顶部。 + +7. + +风格: 卡通,简单线条 +场景:公园 +小男孩 - 一个戴棒球帽的男孩,穿着条纹T恤和短裤,站在草地上微笑。 + +8. + +风格: 卡通,简单线条 +场景:背景前 +滑板 - 一个蓝色滑板平放在地上,侧面有火焰图案。 + +9. + +风格: 卡通,简单线条 +场景:户外滑板场 +女孩滑滑板 - 一个扎马尾的女孩踩在滑板上滑行,滑板在脚下。 + +10. + +风格: 卡通,简单线条 +场景:背景前 +滑板 - 一个蓝色带火焰图案的滑板平放在地面。""", + + # ---- 121601 ---- + "recvj5nLUpubaU": """1. + +风格: 卡通,简单线条 +场景:海滩 +夏天 - 金色沙滩上有一把遮阳伞,蓝色大海远处有小帆船,天空挂着大太阳。 + +2. + +风格: 卡通,简单线条 +场景:户外 +冬天雪景 - 白色雪覆盖地面和房屋屋顶,树木光秃秃的没有树叶,空中飘着雪花。 + +3. + +风格: 卡通,简单线条 +场景:户外 +冬天雪景 - 一个雪人戴着帽子和围巾站在雪地里,房屋和树上都覆盖着白雪。 + +4. + +风格: 卡通,简单线条 +场景:冬天户外 +穿厚棉衣的男孩 - 一个男孩穿着厚厚的外套,戴着围巾和手套,嘴里呼出白气,背景是雪地。 + +5. + +风格: 卡通,简单线条 +场景:户外 +秋天 - 橙色和黄色的落叶铺满地面,树木只剩下稀疏的叶子,天空有一群飞鸟。 + +6. + +风格: 卡通,简单线条 +场景:马路上 +公交车 - 一辆红色双层公交车停在路边,有窗户和车门。 + +7. + +风格: 卡通,简单线条 +场景:卧室 +长裤 - 一条蓝色长裤叠放在床边的凳子上。 + +8. + +风格: 卡通,简单线条 +场景:卧室 +长裤 - 一条深蓝色长裤挂在衣架上。 + +9. + +风格: 卡通,简单线条 +场景:户外 +下雪 - 白色雪花从天空中纷纷落下,地面和房屋屋顶积了厚厚一层雪。 + +10. + +风格: 卡通,简单线条 +场景:户外 +雪地 - 白色雪覆盖了整个地面,空中飘着雪花,远处有积雪覆盖的屋顶。""", +} + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +for rid, desc in IMAGE_DESC.items(): + resp = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"图片描述": desc}}, + ) + code = resp.json().get("code") + print(f" {rid}: code={code}, desc_len={len(desc)}") + time.sleep(0.5) + +# Verify +print("\nVerification:") +for rid in IMAGE_DESC: + resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{rid}", + headers={"Authorization": f"Bearer {token}"}, + ) + desc_len = len(resp.json()["data"]["record"]["fields"].get("图片描述", "")) + print(f" {rid}: 图片描述={desc_len}chars {'✅' if desc_len > 100 else '❌'}") +print("\nDone.") diff --git a/scripts/update_speaking_qa_ability.py b/scripts/update_speaking_qa_ability.py new file mode 100644 index 0000000..7e84e42 --- /dev/null +++ b/scripts/update_speaking_qa_ability.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Update ability labels for speaking_qa records based on question content.""" +import json, requests, time + +CRED = "/root/.openclaw/credentials/xiaoyan/config.json" +TOKEN_APP = "CMHSbUUjka3TrUsaxxEc297ongf" +TBL = "tblRGv7k4WH58Jgq" + +L_INFO = "基础信息表达|个人信息问答" +L_PREF = "表达喜好与理由" +L_PAST = "过去经历描述|Past Activities" +L_INTR = "互动应答|问答交流" +L_EXCH = "信息交换|双向问答" + +def classify(question): + q = question.lower() + + # 互动应答 + if any(kw in q for kw in ["what do you say", "what will you say", "what would you say", + "what shouldn't we do", "what must we not do", "what should we do", + "what do we do next", "what do the shop workers say"]): + return L_INTR + + # 过去经历描述 + if any(kw in q for kw in ["what did you", "when did you last", "who did you help", + "last weekend", "what have you learned", "what have you decided", + "what toy did you buy", "where did you meet", + "what did your father say", "what did you first find", + "what gift have you got", "what did you see", + "what you're glad you could do"]): + return L_PAST + + # 信息交换 + if any(kw in q for kw in ["what do we need", "what things are there", + "what will your friends bring", "what do people use", + "what do we use", "what shall we", "whose bag", + "whose schoolbag", "whose pen is this", "who is this gift for"]): + return L_EXCH + + # 个人信息: name, age, from, family, appearance + if any(kw in q for kw in ["what's your name", "how old", "where are you from", + "who do you live", "is your home in", "what rooms", + "what does your dad look like", "what does your mother look like", + "what is your mom's hair", "what are your mom's", + "what does your sister wear", "what does a dinosaur look like", + "what do the clouds look like", "what is the moon like", + "what's your best friend like", "what does your cat look like", + "how is the bear", "who is your best", "who is taller", + "what are you good at", "what is your best friend good at", + "what is your main study goal", "what is your goal", + "what habit is very important", "what information", + "what do you have to do every day", + "tell me what you're glad", "please introduce your pet", + "what pet do you love", "any animals. which one", + "what toys do you have", "how many books does your dad have", + "what is in your schoolbag", "what's in your schoolbag", + "what can you find", "what is there in your schoolbag", + "what is in your classroom", "what are in the classroom"]): + return L_INFO + + # 信息交换 fallback + if any(kw in q for kw in ["where is your book", "is this pen yours", "where are your books", + "which page", "where should you put", "can you swim", + "is there a book", "can you help me", "what do you say to make", + "what do you say when others", "what do you say when your friend", + "what do you say when you want", "what do you say to draw", + "what do you say to introduce"]): + return L_INTR + + # 表达喜好 + if any(kw in q for kw in ["favorite", "favourite", "what do you like", "what kind of", + "what do you think", "what do you want", "do you like", + "what do you enjoy", "what books do you", "do you keep", + "are your classmates", "are you afraid", + "what do your family plan", "what do your parents plan", + "what do you plan", "what are you going to", + "what will you", "what would you", "what can you do", + "how do you like", "what colour do you like", "what colour do you want", + "what gift do you want", "what do you want to eat", "why do you like", + "which car is faster", "what sport can't you do", + "what can you see", "who will you sit with", + "how is the ice cream", "what animal might it be", + "what is the rabbit eating", "what is the bird", + "what do you need", "what will we have at the party", + "what must we protect", "what do you believe", + "what makes your dad unhappy", "what do your friends", + "whose pen do you think", "what hair has your dad", + "what sports can you do", "what do you need for", + "if you can't find", "where will we go", "what do people use", + "the radio has no sound", "what do you need to do"]): + return L_PREF + + return L_INFO + +# Main +with open(CRED) as f: + cred = json.load(f) +aid = cred["apps"][0]["appId"] +asecret = cred["apps"][0]["appSecret"] + +def get_token(): + r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": aid, "app_secret": asecret}) + return r.json()["tenant_access_token"] + +token = get_token() + +all_items = [] +page_token = None +while True: + url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records?page_size=50" + if page_token: + url += f"&page_token={page_token}" + resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}) + data = resp.json() + all_items.extend(data["data"].get("items", [])) + if not data["data"].get("has_more"): + break + page_token = data["data"].get("page_token") + +updates = 0 +stats = {} +for item in all_items: + rid = item["record_id"] + r = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records/{rid}", + headers={"Authorization": f"Bearer {token}"}) + fields = r.json()["data"]["record"]["fields"] + fid = fields.get("题目集合 ID", "") + jd = fields.get("jsonData", "") + + if not jd or fid in ("L1", "L2", None): + continue + + try: + j = json.loads(jd) + except: + continue + + changed = False + for grp_name in ["first", "second"]: + if grp_name not in j: + continue + for q in j[grp_name].get("questionSet", []): + existing = q.get("ability", []) + valid_labels = {L_INFO, L_PREF, L_PAST, L_INTR, L_EXCH} + if existing and all(a in valid_labels for a in existing): + continue + + content = q.get("content", q.get("question", "")) + label = classify(content) + q["ability"] = [label] + changed = True + stats[label] = stats.get(label, 0) + 1 + + if changed: + new_jd = json.dumps(j, ensure_ascii=False) + r = requests.put( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{TOKEN_APP}/tables/{TBL}/records/{rid}", + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + json={"fields": {"jsonData": new_jd}}) + code = r.json().get("code") + if code == 0: + updates += 1 + print(f"✅ {fid}: updated") + else: + print(f"❌ {fid}: code={code}") + time.sleep(0.3) + +print(f"\nUpdated {updates} records") +print("\nLabel distribution:") +for label, count in sorted(stats.items(), key=lambda x: -x[1]): + print(f" {label}: {count}") diff --git a/scripts/verify_record.py b/scripts/verify_record.py new file mode 100644 index 0000000..c071f25 --- /dev/null +++ b/scripts/verify_record.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Verify a single bitable record.""" +import json, requests, sys + +CRED_FILE = "/root/.openclaw/credentials/xiaoyan/config.json" +APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf" +TABLE_ID = "tblweY65jGBiwSdt" +RECORD_ID = sys.argv[1] if len(sys.argv) > 1 else "recvjhdXiBCoE4" + +with open(CRED_FILE) as f: + cred = json.load(f) +app_id = cred["apps"][0]["appId"] +app_secret = cred["apps"][0]["appSecret"] + +token_resp = requests.post( + "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", + json={"app_id": app_id, "app_secret": app_secret}, +) +token = token_resp.json()["tenant_access_token"] + +resp = requests.get( + f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{RECORD_ID}", + headers={"Authorization": f"Bearer {token}"}, +) +data = resp.json() +jd = data["data"]["record"]["fields"]["jsonData"] +print(f"jsonData length: {len(jd)} chars") +j = json.loads(jd) +fqs = j["first"]["questionSet"] +sqs = j["second"]["questionSet"] +print(f"first questionSet: {len(fqs)} items") +print(f"second questionSet: {len(sqs)} items") +if fqs: + for i, q in enumerate(fqs): + print(f" first[{i}]: {q['answerText']} | {q['question'][:60]}") +if sqs: + for i, q in enumerate(sqs): + print(f" second[{i}]: {q['answerText']} | {q['question'][:60]}")