auto backup: 2026-05-12 08:10:01

This commit is contained in:
ai_member_only 2026-05-12 08:10:01 +08:00
parent f7661fc026
commit e251630966
40 changed files with 4294 additions and 132 deletions

View File

@ -125,18 +125,6 @@ System: [...] Feishu[xiaobian] group oc_xxx | 发送者 (ou_xxx) [msg:om_xxx]
2. **无 `GroupSystemPrompt` 或无特殊任务时:** 不执行任何操作
3. ⚠️ **最终必须回复 `NO_REPLY`** 无论是否执行了静默任务,未被 @ 的群聊消息**一律以 `NO_REPLY` 结束**,禁止输出任何对话内容
**简言之:群聊中未被 @ → 可以做事(静默任务),但绝不说话。**
#### 何时发言(仅在被 @ 的前提下)
即使被 @以下情况仍应保持沉默NO_REPLY
- 同事之间的闲聊,@ 你只是无意的
- 已经有人回答了问题
- 你的回复只是"是的"或"收到",没有实质价值
- 对话在没有你的情况下进展顺利
参与,而非主导。质量 > 数量。
## 工作区目录规范(强制执行)

View File

@ -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_qa2026-05-11
- 000001, 010101, 010201, 010301, 010401, 010501, 0106017套 L2 A级
- 含图片描述
- 脚本:`scripts/batch_reading_pic_qa.py`、`scripts/batch_create_delayed.py`、`scripts/update_image_descriptions.py`

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为家庭厨房` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为学校报名` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为生日派对` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为周末购物` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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段对话 |
| 场景要求 | 🟢 | `场景为家庭日常` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -11,6 +11,12 @@ description: K12英语阅读看图判断题标准化设计、生产、审校工
- 答案统一为"Yes"或"No"
## 难度对应关系(与教研标准对齐)
| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 |
> **L2 难度维度(对标 Flyers/KET每2阶段为1个难度等级**
> - A级 = U1-U12阶段1-2Flyers入门→Flyers基础
> - B级 = U13-U24阶段3-4Flyers达标→KET入门
> - C级 = U25-U36阶段5-6KET基础→KET强化
> - D级 = U37-U48阶段7-8KET高位→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/MoversL2用Unit范围 |
| 题量 | 🟢 | `5` | 生成题目数量默认5道 |
| 场景要求 | 🟢 | `场景为日常物品` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范JSON

View File

@ -11,6 +11,12 @@ description: K12英语阅读看图回答题标准化设计、生产、审校工
- 答案统一为"Yes"或"No"
## 难度对应关系(与教研标准对齐)
| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 |
> **L2 难度维度(对标 Flyers/KET每2阶段为1个难度等级**
> - A级 = U1-U12阶段1-2Flyers入门→Flyers基础
> - B级 = U13-U24阶段3-4Flyers达标→KET入门
> - C级 = U25-U36阶段5-6KET基础→KET强化
> - D级 = U37-U48阶段7-8KET高位→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/MoversL2用Unit范围 |
| 题量 | 🟢 | `5` | 生成题目数量小题数默认5道 |
| 场景要求 | 🟢 | `场景为阳光海滩` | 指定场景内容,不指定则自动匹配知识点 |
## 输出格式规范JSON

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为生日派对` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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小题 |
| 场景要求 | 🟢 | `场景为公共场所标识` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为周末活动` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为活动通知` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为校园生活` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `话题为食物` | 指定话题范围,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -7,6 +7,12 @@ description: K12英语口语日常回答题标准化设计、生产、审校工
本技能对应官方题型:**口语 Part1 日常回答【阶段归属L1&L2共用通过难度参数区分】**,考察核心能力为**口语表达(日常用语运用+快速反应)**学生回答3-4个日常问题每个问题回答1-3句话考察日常词汇、句式的运用能力和表达流畅度。
## 难度对应关系(与教研标准对齐)
| 难度等级 | 对应L1阶段 | 对应剑桥考试等级 | 难度描述 |
> **L2 难度维度(对标 Flyers/KET每2阶段为1个难度等级**
> - A级 = U1-U12阶段1-2Flyers入门→Flyers基础
> - B级 = U13-U24阶段3-4Flyers达标→KET入门
> - C级 = U25-U36阶段5-6KET基础→KET强化
> - D级 = U37-U48阶段7-8KET高位→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/MoversL2用Unit范围 |
| 题量 | 🟢 | `5` | 生成题目数量默认1套4个问题 |
| 场景要求 | 🟢 | `场景为个人介绍` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -73,20 +73,27 @@ description: K12英语写作看图回答题标准化设计、生产、审校工
### 3.3 语言难度 = 欧标 A1-A2
- 句型控制在 Starters/Movers 级别A1-A2
- 句型控制在 StartersL1/ Flyers-KETL2级别
- L1 对标 A1Starters/MoversL2 对标 A1-A2Flyers/KET
- 题干用词优先使用 L1 词库词汇(`business_knowledge/L1_word_list.json`
- 基础功能词代词、be动词、介词当前 L1/L2 词库暂不收录,写题时绕过词汇校验硬性约束
### 3.4 阶段变化原则
当前规格适用 **L2 第一阶段Unit0-6**,后续阶段规则将随难度递增而变化:
当前规格适用 **L2 U1-U6阶段1A级Flyers入门**,后续阶段规则将随难度递增而变化:
| 维度 | L2 Unit0-6当前 | 后续阶段 |
|------|-------------------|---------|
| 维度 | L2 U1-U6阶段1A级 | 后续阶段 |
|------|----------------------|---------|
| 挖空单位 | 1 个单词 | 短语 / 完整句子 |
| 题组小题数 | 2 小题 / 题组 | 4 小题 / 题组 |
| 能力标签 | 简短作答 / 一词答案 | (待补充) |
> **L2 8阶段难度体系每2阶段为1个难度等级**
> - A级 = U1-U12阶段1-2Flyers入门→Flyers基础
> - B级 = U13-U24阶段3-4Flyers达标→KET入门
> - C级 = U25-U36阶段5-6KET基础→KET强化
> - D级 = U37-U48阶段7-8KET高位→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阶段1A级固定 `["简短作答", "一词答案"]` |
| questionList[].explanation | ✅ | string | 解析:图片内容 + 考点 + 字母数 |
---
@ -352,13 +359,19 @@ description: K12英语写作看图回答题标准化设计、生产、审校工
## 九、阶段与难度对应
当前首个阶段为 **L2 Unit0-6**,后续阶段规则将随难度递增:
当前首个阶段为 **L2 U1-U6阶段1A级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-2Flyers入门→Flyers基础
> - B级 = U13-U24阶段3-4Flyers达标→KET入门
> - C级 = U25-U36阶段5-6KET基础→KET强化
> - D级 = U37-U48阶段7-8KET高位→KET达标
> **历史 L1 规则(供参考):**
>
> | 难度等级 | 对应阶段 | 剑桥对标 | 答案要求 |
@ -372,7 +385,7 @@ description: K12英语写作看图回答题标准化设计、生产、审校工
## 十、能力标签
- **L2 Unit0-6 阶段** `["简短作答", "一词答案"]`
- **L2 U1-U6阶段1A级** `["简短作答", "一词答案"]`
- 后续阶段的能力标签将在交付时逐个补充
- 所有同阶段产出的题目,能力标签必须统一

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为生日派对邀请` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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道 |
| 场景要求 | 🟢 | `场景为公园玩耍` | 指定题目场景,不指定则自动匹配知识点 |
## 输出格式规范

View File

@ -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}]}

View File

@ -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=0000012题组各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"
]
}
}
}

109
memory/2026-05-11-0956.md Normal file
View File

@ -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: 看图判断题,产出填入飞书多维表格
ID121301
L1 A级难度
能力tag图文判断 | 句图一致性
题组1
home
house
sofa
cupboard
bedroom
题组2
zoo
hippo
bear
jeans
sock
ID121401
L1 A级难度
能力tag图文判断 | 句图一致性
题组1
student
school
teacher
crayon
board
题组2
mat
letter
desk
schoolbag
handbag
ID121501
L1 A级难度
能力tag图文判断 | 句图一致性
题组1
body
head
mouth
ear
friend
题组2
camera
boy
girl
skateboarding
skateboard
ID121601
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个** 不在词库。已按您直接提供的词汇产出,未禁用超纲词汇。需不需要后续调整?

10
memory/2026-05-11-1129.md Normal file
View File

@ -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: 你好,若松。有什么需要处理的?

14
memory/2026-05-11-1131.md Normal file
View File

@ -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: 好的,已重置。有什么需要处理的随时说。

88
memory/2026-05-11.md Normal file
View File

@ -0,0 +1,88 @@
### 刘彦江 — reading_pic_judge 看图判断题批量生产4套17:28 ~ 17:35
- **需求:** 4套 reading_pic_judgeL1 A级能力标签 图文匹配
- **题型特点:** 每题独立配图questionImagefirst/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/01080118:00 ~ 18:10
- **需求:** 2套 reading_pic_qaL2 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-日常回答tblRGv7k4WH58Jgq26条有效记录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/01120118:55 ~ 19:15
- **需求:** 4套 reading_matchInfoL2 A级能力标签 标识与通知理解|标识 & 通知
- 每套 first=5q + second=5q每题含 questionImage/question/options/answer/ability/explanation
- **表:** 阅读-P1-信息匹配题tblCgfYDnnqwLfgH
- **自动化问题:** 该表存在异步自动化,创建后会将 jsonData 改写为模板壳(减少题量+覆盖 ability
- **解决方案:** 两阶段延迟写入先创建空记录→等35s让自动化完成→PUT jsonData010901/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-02180119:30 ~ 19:45
- **需求:** 6套 reading_matchInfoL2 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 |

View File

@ -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张场景大图textImageN道 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阶段1A级 | 后续阶段 |
|------|----------------------|---------|
| 挖空单位 | 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阶段1A级ability 固定为 `["简短作答", "一词答案"]`
**完整样题questionSetID = 000001**
题组1first— 校门口场景:
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
题组2second— 生日派对场景:
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 个题型规范文件)及近期生产实践经验教训整理,可作为审校工作的一站式参照标准。*

View File

@ -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'}")

View File

@ -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}")

View File

@ -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}")

View File

@ -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 todayLily 今天很勇敢。"),
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 minutes30分钟修好轮子。"),
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.")

View File

@ -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'}")

View File

@ -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()

View File

@ -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.")

View File

@ -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]}")

View File

@ -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.")

View File

@ -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-2Flyers入门→Flyers基础\n"
"> - B级 = U13-U24阶段3-4Flyers达标→KET入门\n"
"> - C级 = U25-U36阶段5-6KET基础→KET强化\n"
"> - D级 = U37-U48阶段7-8KET高位→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/MoversL2用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()

View File

@ -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.")

View File

@ -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}")

38
scripts/verify_record.py Normal file
View File

@ -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]}")