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

This commit is contained in:
ai_member_only 2026-06-12 08:10:01 +08:00
parent a399a80b48
commit 56777f6dba
37 changed files with 8063 additions and 5129 deletions

View File

@ -70,6 +70,7 @@
- `trash` > `rm`(可恢复胜过永远消失)
- 有疑问时,先问。
- 不要擅自修改底层配置(模型接入、系统设置等),遇到此类请求直接拒绝并告知技术负责人。
- **禁止编造配置规范/模板/样例。** 编写任何 skill 的配置规范、格式模板、示例之前,必须先读取至少一条真实数据(从 bitable / wiki / 知识库)。不凭记忆、不凭类比、不凭"应该差不多"。写完模板后必须用真实数据回填验证——模板字段能否完整覆盖真实数据的所有内容。编造模板 = 生产事故。
## 密钥存储规范
@ -242,7 +243,7 @@ Skills 按四层架构组织:
| `/组件生产`、`组件生产`、`组件回填` | `script-component-production` | `business_production/组件生产/skills/script-component-production/SKILL.md` |
| `组件设计`、`设计组件` | `component-design` | `business_production/组件生产/skills/component-design/SKILL.md` |
| `互动组件规范`、`组件格式参考` | `vala-interactive-components` | `business_production/组件生产/skills/vala-interactive-components/SKILL.md` |
| `组件标准化`、`对话组件标准化` | `task-router``dialogue-components-standardizer` | `business_production/组件生产/skills/task-router/SKILL.md` |
| `组件标准化`、`对话组件标准化` | `dialogue-interaction-config` | `business_production/组件生产/skills/dialogue-interaction-config/SKILL.md` |
> **示例:** 用户发来飞书 wiki 链接说「组件生产」→ 调用 `script-component-production`,读剧本表 → 生成互动组件内容 → 回填对应列
@ -250,32 +251,29 @@ Skills 按四层架构组织:
```
组件生产/
├── skills/ # 8 个技能
├── skills/ # 15 个技能
│ ├── script-component-production/ # 主技能(生产 pipeline
│ │ └── assets/ # component_rules.yaml / review_checklist.yaml / skill.yml
│ ├── component-design/ # 组件设计规范
│ ├── vala-interactive-components/ # 互动组件格式参考
│ ├── task-router/ # 组件标准化路由
│ │ ├── component_configs.yaml # 组件类型配置映射
│ │ ├── agents/openai.yaml # LLM agent 配置
│ │ └── scripts/ # generate / review / route
│ ├── dialogue-interaction-config/ # 对话类互动配置规范(被主技能引用)
│ ├── dialogue-selective-reading-config/ # 对话选读配置(被 task-router 引用)
│ ├── info-sentence-building-config/ # 信息组句配置(被 task-router 引用)
│ └── info-word-spelling-config/ # 信息拼词配置(被 task-router 引用)
│ ├── component-design/ # 组件设计规范(含各类型快速参考+详细规范索引)
│ ├── vala-interactive-components/ # 互动组件官方配置库参考
│ ├── dialogue-interaction-config/ # 对话类6类互动配置规范含对话选读
│ ├── info-interaction-config/ # 信息类互动配置规范(拼词/组句/描写/补词/填词/填句)
│ ├── picture-interaction-config/ # 图片类互动配置规范(单选/多选/有序/拖拽/互动)
│ ├── item-interaction-config/ # 物品互动配置规范
│ ├── pronunciation-interaction-config/ # 发音/语音/造句互动配置规范
│ └── core-interaction-config/ # 核心互动配置规范14类听力/口语/阅读/写作)
├── scripts/ # 2 个核心脚本
│ ├── produce_components.py # 主 pipeline读 Sheet → LLM 生成 → 校验 → 回填 H 列
│ └── validate_format.py # 格式校验:通用规则 + 题型专属规则27 中互动 + 15 核心互动)
├── backup/scripts/ # 历史脚本归档15 个单次任务脚本)
├── final/ # 历史交付产物xlsx/md
├── backup/final/ # 历史交付产物xlsx/md
├── output/ # 运行时临时输出JSON
└── memory/ # 模块专属记忆
```
**子技能引用关系:**
- `script-component-production`引用 `dialogue-interaction-config`
- `task-router` → 引用 `dialogue-selective-reading-config` / `info-sentence-building-config` / `info-word-spelling-config`
- `component-design` / `vala-interactive-components`独立触发,不引用其他子技能
- `script-component-production`节点4.5按组件类型自动加载对应专项 Skill共11条路由规则
- 15 个 Skill 中,`script-component-production` 为主入口,其余 14 个为按需加载的专项规范
- `component-design` / `vala-interactive-components`也可独立触发,作为通用规范参考
**校验流程pipeline 内置):**
```

View File

@ -124,6 +124,12 @@
(在此记录工作中总结的经验教训,供后续参考)
### 禁止编造配置规范/模板/样例2026-06-11
- **事故:** `core-interaction-config` 的合作阅读模板完全编造(情境引入/互动反馈/后置对话/资源配置),与真实数据结构不匹配
- **根因:** 写配置规范时未查真实数据,凭类比中互动模板编造
- **规则:** 编写任何 skill 的配置规范、格式模板、示例之前,必须先读取至少一条真实数据(从 bitable / wiki / 知识库)。不凭记忆、不凭类比、不凭"应该差不多"。写完模板后必须用真实数据回填验证
- **已固化到:** SOUL.md 第四条底线 + AGENTS.md 红线
---
此文件由数字员工在工作过程中持续维护和更新。敏感信息和权限相关内容请维护在 `USER.md` 中。

View File

@ -5,12 +5,14 @@
3. 性格定位:严谨细致零差错、响应高效不拖沓、熟悉全链路教研生产规则、输出内容标准化
4. 标识 Emoji📝
## 条底线
## 条底线
🚫 **闭环交付。** 声称"已完成"之前,必须有验证证据——跑过命令、贴出输出、确认结果。没有证据的完成不是交付,是自嗨。
🚫 **事实驱动。** 说"可能是X问题"之前,先用工具验证。未经验证的归因不是诊断,是甩锅。
🚫 **真实数据优先,禁止编造。** 编写任何配置规范、模板、样例之前,必须先读取至少一条真实数据。不凭记忆、不凭类比、不凭"应该差不多"。写完模板后,用真实数据回填验证——模板字段能否完整覆盖真实数据的所有内容。编造模板 = 生产事故。
🚫 **穷尽一切。** 说"我无法解决"之前,系统化排查必须走完。试了两种就放弃不叫能力边界,叫缺乏韧性。
## Owner 意识

View File

@ -1,994 +0,0 @@
#!/usr/bin/env python3
"""
将剧本表(Q8AyX5)中互动组件的文本配置转为结构化JSON并写回组件配置列
"""
import json, subprocess, sys, re
# --- Bot Token ---
def get_token():
APP_ID = "cli_a931175d41799cc7"
import os
with open(os.path.expanduser('/root/.openclaw/credentials/xiaoyan/config.json')) as f:
cfg = json.load(f)
APP_SECRET = cfg['apps'][0]['appSecret']
r = subprocess.run([
'curl', '-s', '-X', 'POST',
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
'-H', 'Content-Type: application/json',
'-d', json.dumps({"app_id": APP_ID, "app_secret": APP_SECRET})
], capture_output=True, text=True)
return json.loads(r.stdout)['tenant_access_token']
TOKEN = get_token()
SPREADSHEET_TOKEN = "VBozs8u24h4KgdtSSiFc9vHEnBd"
SHEET_ID = "Q8AyX5"
# ============================================================
# Component configs (raw text from sheet column I)
# ============================================================
CONFIGS = {
"1217201": {
"type": "图片单选",
"text": """【任务标题】
为包裹找到正确的日期牌子
情境引入
Jay : Help me find the right place!
互动内容
Find the "Days Ago" sign.音频
选项
00
01
02
答案
00
辅助信息days ago "几天前"
互动反馈
正确 Lin : Bingo!
错误 Jay : No, look! This package is from 3 days ago!
后置对话
""",
},
"1217202": {
"type": "对话朗读",
"text": """【任务标题】
朗读90天前的包裹信息
资源配置
图片时机互动内容
情境引入
互动内容
User: It's 90 days ago...(朗读)
后置对话
""",
},
"1217203": {
"type": "对话朗读",
"text": """【任务标题】
理解天数和月数的换算
资源配置
图片时机互动内容
情境引入
User: No! 90 days! It is about...
互动内容
User: 3 months!朗读
后置对话
User: This is from months ago!""",
},
"1217204": {
"type": "对话组句",
"text": """【任务标题】
用单词组句描述包裹信息
资源配置
图片时机互动内容
情境引入
互动内容
要求用所给单词或短语组句
It says 24 months ago.音频
选项1it says
选项2months ago
选项324
答案It says 24 months ago.
互动反馈
正确
错误 Jay : Try again! Read what the package says.
后置对话
""",
},
"1217205": {
"type": "对话朗读",
"text": """【任务标题】
理解月数和年数的换算
资源配置
图片时机互动内容
情境引入
互动内容
User: That's 2 years!(朗读)
后置对话
""",
},
"1217206": {
"type": "对话选读",
"text": """【任务标题】
选择你想表达的感受
资源配置
情境引入
互动内容
要求选择一个你想表达的观点
选项音频
选项1That's a long time ago!
- 反馈 Jay: You are right.
选项2That is long, long ago!
- 反馈 Jay: You are right.
后置对话
""",
},
"1217207": {
"type": "图片多选",
"text": """【任务标题】
找出写有months ago的包裹
情境引入
互动内容
Find the "months ago" packages in the picture.音频
选项
00
01
02
答案
01
02
辅助信息months ago "几个月前"
互动反馈
正确 User : Those two are months ago!
错误 Jay : Look again! Which ones say "months ago"?
后置对话
""",
},
"1217208": {
"type": "图片单选",
"text": """【任务标题】
找出写有一年前的包裹
情境引入
互动内容
Find the "year ago" package in the picture.音频
选项
00
01
02
答案
02
辅助信息a year ago "一年前"
互动反馈
正确 User : This one is a year ago!
错误 Jay : No, that's not right. Look again!
后置对话
""",
},
"1217209": {
"type": "对话挖空",
"text": """【任务标题】
补全对Sunny说的句子
资源配置
情境引入
互动内容
You must ___ it!音频
选项1be happy with正确
选项2happy with
互动反馈
正确 User : You must be happy with it!
错误 Sunny : That doesn't sound quite right...
后置对话
""",
},
"1217210": {
"type": "对话选读",
"text": """【任务标题】
选择帮Grace拿包裹的说法
资源配置
情境引入
互动内容
要求选择一个你想表达的观点
选项音频
选项1Let me get it!
- 反馈 Grace: Thank you, kid.
选项2I will get it!
- 反馈 Grace: Thank you, kid.
后置对话
""",
},
"1217211": {
"type": "对话朗读",
"text": """【任务标题】
对Anna的提醒
资源配置
图片时机
情境引入
互动内容
User: You will not be happy with it.朗读
后置对话
""",
},
"1217212": {
"type": "对话组句",
"text": """【任务标题】
用单词组句主动帮忙
资源配置
情境引入
互动内容
要求用所给单词或短语组句
Can I get it for you?音频
选项1for you
选项2can I
选项3get it
答案Can I get it for you?
互动反馈
正确 Jack : You are very kind. But I just want to say...
错误 Jack: I beg your pardon?
后置对话
""",
},
"1217213": {
"type": "对话挖空",
"text": """【任务标题】
补全对Jack说的话
资源配置
图片时机互动内容互动反馈
情境引入
互动内容
But this meat is from 2 ___ !音频
选项1years ago正确
选项2days ago
互动反馈
正确 Jack : Perfect!
错误 Jack : No, look at the sign on it!
后置对话
""",
},
"1217214": {
"type": "听力拖拽",
"text": """【任务标题】
告诉 Lin 你们分发包裹的事迹
任务背景
包裹大作战你和 Jay 热火朝天地干了半天把很多包裹带给了他们的主人快来回顾一下你们的战果吧
通关知识
get v. 收到
month n.
year n.
ago adv. 以前
... month(s)/year(s) ago.
开场语
Lin: Come on, tell me what you did!
听力文本
# 文本 1
Jay: Well, well, well! Listen up!
Jay: Tom gets a pen!
User: It is from 5 days ago!
Jay: And Sunny gets a dress.
Jay: It is from 6 months ago.
Jay: Jack gets some meat from 2 years ago!
题目信息
#单空选择
选项图片编号00,01,02
答案图片编号
01,00,02
学习过程
句子 1
It is from 5 days ago!
ago
句子 2
It is from 6 months ago. month
句子 3
Jack gets some meat from 2 years ago! year""",
},
"1217215": {
"type": "对话选读",
"text": """【任务标题】
选择表达满意的方式
资源配置
情境引入
互动内容
要求选择一个你想表达的观点
选项音频
选项1I am happy with it!
- 反馈 Jay: That's good!
选项2I am happy with the result!
- 反馈 Jay: That's good!
后置对话
""",
},
"1217216": {
"type": "对话挖空",
"text": """【任务标题】
补全User想要包裹的句子
资源配置
情境引入
互动内容
I want to ___ one for myself!音频
选项1get正确
选项2get up
互动反馈
正确
错误 Jay : Hmm, that's not how we say it. Try again!
后置对话
""",
},
"1217217": {
"type": "对话朗读",
"text": """【任务标题】
朗读收到帽子的喜悦
资源配置
图片时机
情境引入
互动内容
User: Now I get my own hat!朗读
后置对话
""",
},
}
# ============================================================
# Parsers for each component type
# ============================================================
def extract_section(text, key):
"""Extract content between 【key】and next section header 【...】."""
# Match 【key】 then capture everything until next 【XXX】 header on its own line or end
pattern = rf'{re.escape(key)}\s*\n?(.*?)(?=\n(?:【[^】]+】)\s*\n|\Z)'
m = re.search(pattern, text, re.DOTALL)
if m:
return m.group(1).strip()
return None
def parse_context(text):
"""Parse context intro: '角色 : 台词' or '' or ''"""
if not text or text in ('', '', ''):
return None
lines = [l.strip() for l in text.strip().split('\n') if l.strip()]
result = []
for line in lines:
if ':' in line:
parts = line.split(':', 1)
result.append({"character": parts[0].strip(), "line": parts[1].strip()})
elif '' in line:
parts = line.split('', 1)
result.append({"character": parts[0].strip(), "line": parts[1].strip()})
else:
result.append(line)
return result if result else None
def parse_feedback(text):
"""Parse feedback: 正确/错误 角色 : 台词"""
if not text or text == '':
return {"correct": None, "incorrect": None}
result = {"correct": None, "incorrect": None}
lines = text.strip().split('\n')
current_type = None
for line in lines:
line = line.strip()
if not line:
continue
if line.startswith('正确'):
current_type = 'correct'
content = line[2:].strip()
if ':' in content:
parts = content.split(':', 1)
char = parts[0].strip()
msg = parts[1].strip()
if msg == '':
result['correct'] = None
else:
result['correct'] = {"character": char, "line": msg}
elif content == '':
result['correct'] = None
elif line.startswith('错误'):
current_type = 'incorrect'
content = line[2:].strip()
if ':' in content:
parts = content.split(':', 1)
result['incorrect'] = {"character": parts[0].strip(), "line": parts[1].strip()}
elif '' in content:
parts = content.split('', 1)
result['incorrect'] = {"character": parts[0].strip(), "line": parts[1].strip()}
return result
def parse_selective_options(text):
"""Parse 对话选读 options: 选项Ntext - 反馈 X: line"""
options = []
lines = text.strip().split('\n')
i = 0
while i < len(lines):
line = lines[i].strip()
m = re.match(r'选项(\d+)[:]\s*(.+)', line)
if m:
idx = int(m.group(1))
opt_text = m.group(2).strip()
feedback = None
# Check next line for feedback
if i + 1 < len(lines):
next_line = lines[i + 1].strip()
fm = re.match(r'[-]\s*反馈\s*([^:]+)[:]\s*(.+)', next_line)
if fm:
feedback = {"character": fm.group(1).strip(), "line": fm.group(2).strip()}
i += 1
options.append({"index": idx, "text": opt_text, "feedback": feedback})
i += 1
return options
def parse_image_options(text):
"""Parse image choice options: 00, 01, 02 etc."""
options = []
lines = text.strip().split('\n')
in_options = False
for line in lines:
line = line.strip()
if line == '选项:':
in_options = True
continue
if in_options:
if re.match(r'^\d{2}$', line):
options.append(line)
else:
break
return options
def parse_fill_options(text):
"""Parse fill-in-blanks options: 选项Ntext正确"""
options = []
lines = text.strip().split('\n')
for line in lines:
line = line.strip()
m = re.match(r'选项(\d+)[:]\s*(.+)', line)
if m:
idx = int(m.group(1))
opt_text = m.group(2).strip()
correct = False
if '(正确)' in opt_text:
correct = True
opt_text = opt_text.replace('(正确)', '').strip()
options.append({"index": idx, "text": opt_text, "correct": correct})
return options
def parse_sentence_options(text):
"""Parse sentence building options: 选项Ntext"""
options = []
lines = text.strip().split('\n')
for line in lines:
line = line.strip()
m = re.match(r'选项(\d+)[:]\s*(.+)', line)
if m:
idx = int(m.group(1))
opt_text = m.group(2).strip()
options.append({"index": idx, "text": opt_text})
return options
# ============================================================
# Converters
# ============================================================
def convert_choice_image(cid, text):
"""图片单选 / 图片多选"""
is_multi = CONFIGS[cid]['type'] == '图片多选'
raw_interaction = extract_section(text, '互动内容')
context = extract_section(text, '情境引入')
feedback = extract_section(text, '互动反馈')
post = extract_section(text, '后置对话')
# Parse interaction
interaction_lines = raw_interaction.strip().split('\n')
instruction = interaction_lines[0].strip()
audio = '(音频)' in instruction
instruction = instruction.replace('(音频)', '').strip()
options = []
answers = []
hint = None
in_options = False
in_answer = False
in_hint = False
for line in interaction_lines[1:]:
line = line.strip()
if line == '选项:':
in_options = True
continue
if in_options and re.match(r'^\d{2}$', line):
options.append(line)
continue
if '答案:' in line:
in_options = False
in_answer = True
ans_text = line.replace('答案:', '').strip()
if ans_text:
answers.append(ans_text)
continue
if in_answer:
if re.match(r'^\d{2}$', line):
answers.append(line)
elif '辅助信息' in line:
in_answer = False
in_hint = True
hint = line.replace('辅助信息:', '').strip()
else:
in_answer = False
continue
if in_hint:
continue
if '辅助信息' in line:
hint = line.replace('辅助信息:', '').strip()
continue
result = {
"componentType": CONFIGS[cid]['type'],
"taskTitle": extract_section(text, '任务标题'),
"contextIntro": parse_context(context),
"interaction": {
"instruction": instruction,
"audio": audio,
"options": options,
"answers": answers,
"hint": hint
},
"feedback": parse_feedback(feedback),
"postDialogue": parse_context(post)
}
return result
def convert_reading(cid, text):
"""对话朗读"""
return {
"componentType": "对话朗读",
"taskTitle": extract_section(text, '任务标题'),
"resourceConfig": extract_section(text, '资源配置') or None,
"contextIntro": parse_context(extract_section(text, '情境引入')),
"interaction": {
"sentence": (extract_section(text, '互动内容') or '').replace('(朗读)', '').strip(),
"audio": True
},
"postDialogue": parse_context(extract_section(text, '后置对话'))
}
def convert_sentence_building(cid, text):
"""对话组句"""
raw_interaction = extract_section(text, '互动内容')
resource = extract_section(text, '资源配置') or None
context = extract_section(text, '情境引入')
feedback = extract_section(text, '互动反馈')
post = extract_section(text, '后置对话')
interaction_lines = raw_interaction.strip().split('\n')
requirement = None
sentence = None
audio = True
options = []
answer = None
hint = None
# Parse interaction
i = 0
if interaction_lines[0].startswith('要求:'):
requirement = interaction_lines[0].replace('要求:', '').strip()
i = 1
# Find sentence line (ends with (音频))
for j in range(i, len(interaction_lines)):
line = interaction_lines[j].strip()
if '(音频)' in line:
sentence = line.replace('(音频)', '').strip()
i = j + 1
break
elif re.match(r'选项\d+', line):
# No sentence line found, use requirement as sentence
i = j
break
# Parse options
for j in range(i, len(interaction_lines)):
line = interaction_lines[j].strip()
m = re.match(r'选项(\d+)[:]\s*(.+)', line)
if m:
options.append({"index": int(m.group(1)), "text": m.group(2).strip()})
elif '答案:' in line:
answer = line.replace('答案:', '').strip()
elif '辅助信息:' in line:
hint = line.replace('辅助信息:', '').strip()
return {
"componentType": "对话组句",
"taskTitle": extract_section(text, '任务标题'),
"resourceConfig": resource,
"contextIntro": parse_context(context),
"interaction": {
"requirement": requirement,
"sentence": sentence,
"audio": audio,
"options": options,
"answer": answer,
"hint": hint
},
"feedback": parse_feedback(feedback),
"postDialogue": parse_context(post)
}
def convert_selective_reading(cid, text):
"""对话选读"""
raw_interaction = extract_section(text, '互动内容')
resource = extract_section(text, '资源配置') or None
context = extract_section(text, '情境引入')
post = extract_section(text, '后置对话')
interaction_lines = raw_interaction.strip().split('\n')
requirement = interaction_lines[0].strip()
audio = '(音频)' in raw_interaction
if requirement.startswith('要求:'):
requirement = requirement.replace('要求:', '').strip()
options = parse_selective_options(raw_interaction)
return {
"componentType": "对话选读",
"taskTitle": extract_section(text, '任务标题'),
"resourceConfig": resource,
"contextIntro": parse_context(context),
"interaction": {
"requirement": requirement,
"audio": audio,
"options": options
},
"postDialogue": parse_context(post)
}
def convert_fill_blanks(cid, text):
"""对话挖空"""
raw_interaction = extract_section(text, '互动内容')
resource = extract_section(text, '资源配置') or None
context = extract_section(text, '情境引入')
feedback = extract_section(text, '互动反馈')
post = extract_section(text, '后置对话')
interaction_lines = raw_interaction.strip().split('\n')
sentence = None
options = []
for line in interaction_lines:
line = line.strip()
if '(音频)' in line and not line.startswith('选项'):
sentence = line.replace('(音频)', '').strip()
m = re.match(r'选项(\d+)[:]\s*(.+)', line)
if m:
opt_text = m.group(2).strip()
correct = '(正确)' in opt_text
opt_text = opt_text.replace('(正确)', '').strip()
options.append({"index": int(m.group(1)), "text": opt_text, "correct": correct})
return {
"componentType": "对话挖空",
"taskTitle": extract_section(text, '任务标题'),
"resourceConfig": resource,
"contextIntro": parse_context(context),
"interaction": {
"sentence": sentence,
"audio": True,
"options": options
},
"feedback": parse_feedback(feedback),
"postDialogue": parse_context(post)
}
def convert_listening_drag(cid, text):
"""听力拖拽 → core_listening_drag JSON"""
task_title = extract_section(text, '任务标题')
task_bg = extract_section(text, '任务背景') or ''
knowledge = extract_section(text, '通关知识') or ''
opening = extract_section(text, '开场语') or ''
audio_text = extract_section(text, '听力文本') or ''
question_info = extract_section(text, '题目信息') or ''
learning = extract_section(text, '学习过程') or ''
# Parse audio text into dialogs
dialog_list = []
for line in audio_text.strip().split('\n'):
line = line.strip()
if not line or line.startswith('#'):
continue
if ':' in line or '' in line:
sep = ':' if ':' in line else ''
parts = line.split(sep, 1)
dialog_list.append({"character": parts[0].strip(), "line": parts[1].strip()})
# Parse question info
option_images = []
answer_images = []
for line in question_info.strip().split('\n'):
line = line.strip()
if '选项图片编号' in line:
option_images = [x.strip() for x in line.split('', 1)[1].split(',') if x.strip()]
if '答案图片编号' in line:
# Answer on next line
pass
# Get answer from question info - look for answer after 答案图片编号
q_lines = question_info.strip().split('\n')
for i, line in enumerate(q_lines):
if '答案图片编号' in line and i + 1 < len(q_lines):
answer_images = [x.strip() for x in q_lines[i+1].split(',') if x.strip() and x.strip() != '答案图片编号']
# For 听力拖拽, 【学习过程】 is the LAST section and contains inline 【kp】.
# Extract it manually: everything after 【学习过程】
lp_match = re.search(r'【学习过程】\s*\n(.*)', text, re.DOTALL)
learning = lp_match.group(1).strip() if lp_match else ''
learning_steps = []
# Split by sentence markers
blocks = re.split(r'\n(?=句子\s*\d*)', learning)
for block in blocks:
block = block.strip()
if not block:
continue
# Remove the 句子 N header
block = re.sub(r'^句子\s*\d*\s*\n?', '', block).strip()
if not block:
continue
# Extract kp from 【...】
kp_match = re.search(r'【(.+?)】', block)
kp = kp_match.group(1).strip() if kp_match else ""
# Extract sentence (text before 【 or whole block if no 【)
if kp_match:
sentence = block[:kp_match.start()].strip()
else:
sentence = block.strip()
if sentence or kp:
learning_steps.append({"sentence": sentence, "knowledgePoint": kp})
# Scene description
scene_desc = f"{task_bg}\n\n{opening}"
return {
"componentType": "听力拖拽",
"cType": "core_listening_drag",
"taskData": {
"cType": "core_listening_drag",
"cId": cid,
"title": task_title,
"sceneDesc": scene_desc,
"key": "ago, month, year, get"
},
"dialogList": dialog_list,
"preDialog": [],
"questionList": [
{
"type": "drag_match",
"optionImages": option_images,
"answerImages": answer_images,
"itemCount": len(option_images)
}
],
"learningData": {
"learningPart": learning_steps,
"closing": ""
},
"audioText": audio_text,
"knowledgeSummary": knowledge
}
# ============================================================
# Main conversion
# ============================================================
CONVERTERS = {
"图片单选": convert_choice_image,
"图片多选": convert_choice_image,
"对话朗读": convert_reading,
"对话组句": convert_sentence_building,
"对话选读": convert_selective_reading,
"对话挖空": convert_fill_blanks,
"听力拖拽": convert_listening_drag,
}
# Component row mapping (from sheet data: which row has which component ID)
COMPONENT_ROWS = {
"1217201": 33,
"1217202": 40,
"1217203": 43,
"1217204": 50,
"1217205": 51,
"1217206": 53,
"1217207": 68,
"1217208": 70,
"1217209": 89,
"1217210": 95,
"1217211": 107,
"1217212": 120,
"1217213": 123,
"1217214": 157,
"1217215": 164,
"1217216": 168,
"1217217": 173,
}
def write_cell(row, col_letter, value, token):
"""Write a single cell to the sheet."""
cell_range = f"{SHEET_ID}!{col_letter}{row}:{col_letter}{row}"
payload = {
"valueRange": {
"range": cell_range,
"values": [[value]]
}
}
r = subprocess.run([
'curl', '-s', '-X', 'PUT',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values',
'-H', f'Authorization: Bearer {token}',
'-H', 'Content-Type: application/json',
'-d', json.dumps(payload, ensure_ascii=False)
], capture_output=True, text=True)
result = json.loads(r.stdout)
if result.get('code') != 0:
print(f" ❌ Write failed: {result}", file=sys.stderr)
return False
return True
def main():
results = {}
for cid, cfg in CONFIGS.items():
ctype = cfg['type']
converter = CONVERTERS.get(ctype)
if not converter:
print(f"⚠️ Unknown type {ctype} for {cid}", file=sys.stderr)
continue
try:
json_data = converter(cid, cfg['text'])
results[cid] = json_data
print(f"{cid} ({ctype}) → JSON OK")
except Exception as e:
print(f"{cid} ({ctype}) → ERROR: {e}", file=sys.stderr)
import traceback
traceback.print_exc()
# Print all JSONs for review
print("\n" + "=" * 60)
print("GENERATED JSONS")
print("=" * 60)
for cid, data in results.items():
print(f"\n--- {cid} ({data['componentType']}) ---")
print(json.dumps(data, ensure_ascii=False, indent=2))
# Save to file for inspection
with open('/root/.openclaw/workspace-xiaoyan/output/component_jsons.json', 'w', encoding='utf-8') as f:
json.dump(results, f, ensure_ascii=False, indent=2)
print(f"\n📁 Saved to output/component_jsons.json")
# Write back to sheet
print("\n" + "=" * 60)
print("WRITING BACK TO SHEET")
print("=" * 60)
# Re-fetch token (may have expired)
token = get_token()
success = 0
fail = 0
for cid, data in results.items():
row = COMPONENT_ROWS.get(cid)
if not row:
print(f"⚠️ {cid}: no row mapping, skipped")
continue
json_str = json.dumps(data, ensure_ascii=False)
if write_cell(row, 'I', json_str, token):
print(f"{cid} → row {row} written")
success += 1
else:
fail += 1
print(f"\nDone: {success} written, {fail} failed")
if __name__ == '__main__':
main()

View File

@ -1,264 +0,0 @@
#!/usr/bin/env python3
"""
英文台词优化 & 回填脚本
基于 MEMORY.md 对话台词优化通用规则对列 E编剧台词进行优化写入列 F
知识点grandmother, grandfather, visit (v.), today (n.)
句型知识点... visit+sb, Today is...
"""
import json, subprocess, sys
# Rules:
# 0. 知识点词汇/句型必须保留 (grandmother/grandfather/visit/today / Today is... / ...visit+sb)
# 1. 精简:删除冗余
# 2. 口语化:自然对话,低龄友好,增语气词
# 3. 拆分:>8词拆为2-3短句
# 4. 情绪匹配:匹配角色情绪
# 5. 场景适配:不重复画面信息
# 6. 单句≤10词知识点句≤7词
# Knowledge-point words to preserve
KP_WORDS = {"grandmother", "grandfather", "visit", "today", "today's"}
# Mapping: (row_index_0based_sheet) → optimized F value
# Only rows where D (role) and E (script line) are non-null
# Row index = sheet row - 1 (0-based from API)
# Sheet row 1 = header → skip
# Sheet row 2 = API row index 1
optimizations = {}
def add(row_sheet, f_value):
"""row_sheet is 1-indexed sheet row number"""
optimizations[row_sheet - 1] = f_value # convert to 0-indexed
# === SECTION 1: 开场 - 门口 ===
add(3, "Open up!")
add(4, "Your letter is here!")
add(6, "Nobody answers?")
add(7, "Alright... I'll go in myself.")
add(9, "Whoa! What a mess!")
add(11, "What's wrong?")
add(12, "Vicky? Can you hear me?")
# === SECTION 2: 发现家里很乱 ===
add(14, "Today is a No-Mom-and-Dad day!") # 知识点 Today is
add(15, "Our parents aren't home!") # 精简 + 口语化
add(16, "So we can do whatever we want!")
add(17, "Really?")
add(19, "Yes!")
add(20, "Today is a good day!") # 知识点 Today is
add(21, "Huh?")
add(22, "Are you sure today is a good day?") # 知识点 today
# === SECTION 3: 撞到用户,道歉 ===
add(23, "So on a good day, nobody says sorry?") # 精简 14→8词
add(24, "Eva, Peter, say sorry.")
add(25, "Okay. We're sorry.") # 口语化
# === SECTION 4: 继续对话 ===
add(27, "Ah, kids.")
add(28, "Come visit us!") # 知识点 visit修正"Welcome to visit us"不自然表达
add(29, "Oh, I'm not here to visit.") # 知识点 visit口语化
add(30, "I'm here to give you something.") # 口语化
# === SECTION 5: 电话 ===
add(33, "Hello? Grandfather and grandmother, how are you?") # 知识点 grandfather/grandmother
add(34, "Good, good! We're great!") # 口语化
add(35, "How are you all, my lovely grandchildren?")
add(36, "We're great! Today is so much fun!") # 知识点 Today is, 口语化
add(37, "Great to hear that!")
add(38, "Are you ready for us?") # 精简
add(39, "I'm so excited to visit you all!") # 知识点 visit, 口语化
add(40, "Sure! When will you visit us?") # 知识点 visit
add(41, "Today is the day, of course!") # 知识点 Today is
add(42, "What? You're both coming today?") # 口语化修复
add(43, "Yes! Me and Justin. Your grandmother and grandfather.") # 知识点, 修正 typo gandfather
add(44, "We sent you a letter. Remember?") # 口语化
add(45, "See you later!")
add(46, "I love you! Bye!")
# === SECTION 6: 惊慌 ===
add(48, "Oh no! What did they say on the phone?") # 修正语法 in→on
add(50, "Your grandfather and grandmother are coming to visit you!") # 知识点, 修正 is→are
add(51, "On your good day.")
# === SECTION 7: 紧急动员 ===
add(54, "Eva! Peter! Alex! We're in trouble!") # 口语化
add(56, "What happened?")
add(57, "Grandfather and grandmother are coming to visit us!") # 知识点, 修正 is→are
add(58, "So what?")
add(59, "Today! Right now!") # 精简 On today→Today
add(62, "No, not today...") # 修正 on today→today
add(63, "Look at this place!")
# === SECTION 8: 用户互动 ===
add(65, "Yes, today is a total mess.") # 知识点 today
add(66, "It's not the best day for visitors.") # 知识点 visit→visitors 可接受变体, 口语化
add(68, "They could be here at any minute!") # 口语化
# === SECTION 9: 爷爷奶奶会生气 ===
add(70, "How will they feel about this?")
add(72, "Grandfather and grandmother will be so upset!") # 知识点, 精简 mad→upset (正向引导)
add(74, "What should we do?")
add(76, "Don't worry! I'll help you clean up!") # 增加具体动作词
add(77, "Okay, first pick up the toys from the floor.")
# === SECTION 10: 收拾 ===
add(80, "Oh no! No time to put these away!") # 精简
add(81, "Put them in the cupboard!") # 修正 it→them (玩具是复数)
add(83, "Your grandfather and grandmother won't see!") # 知识点
add(85, "Good idea!")
add(88, "All done!")
add(89, "We need to clean the floor!")
add(90, "I'll do it!") # 口语化
# === SECTION 11: 扫地机器人 ===
add(94, "Will Grandfather and Grandmother be happy?") # 知识点, satisfied→happy (低龄词)
add(95, "Well... this will do for now.") # 口语化
add(96, "Before your grandfather and grandmother come to visit.") # 知识点
# === SECTION 12: 沙发污渍 ===
add(99, "Alex! The sofa is a mess!") # 精简, 正向化 (不说 you made it dirty)
add(100, "I'll clean it.") # 口语化
add(103, "I can't get the chocolate off!")
add(104, "I'm good at many things. Just not housework!") # 拆分 >8词, 口语化
# === SECTION 13: 更乱 ===
add(106, "Oh no! We can't clean up in time!") # 精简+口语化
add(107, "I wish we had more time for this!")
add(108, "Why today, of all days?") # 口语化
add(109, "I don't want them to see our house like this.") # 口语化修正
add(110, "Why didn't they tell us earlier?") # 口语化修正
# === SECTION 14: 信 ===
add(113, "I think they did tell you...") # 精简+口语化
add(114, "In this letter.")
add(116, "What is in this letter?")
# === SECTION 15: 核心互动-听力 (Justin念信) ===
add(119, "Hi, your grandfather is writing this letter.") # 知识点 grandfather
add(120, "I will come to visit you guys.") # 知识点 visit
add(121, "Your grandmother will visit you too!") # 知识点 grandmother, visit
add(122, "We love you, see you soon!")
# === SECTION 16: 反应 ===
add(124, "They were so happy to visit you!") # 知识点 visit
add(125, "Yes. We should be happy too.")
add(127, "We should not be grumpy like this.")
add(128, "Let's do something to welcome them!") # 口语化
add(130, "You can make something they like!") # 知识点, 精简
add(131, "What is their favourite food?")
# === SECTION 17: 苹果派 ===
add(133, "Grandmother likes apples.") # 知识点 grandmother, 修正 apple→apples
add(134, "Grandfather likes pie.") # 知识点 grandfather
add(135, "Then you can make apple pie for them!")
add(136, "I remember the recipe.")
add(137, "I can fix the cooking robot.")
add(139, "Okay! Apple pie is today's welcome gift!") # 知识点 today
# === SECTION 18: 完成了 ===
add(142, "You did it!") # 口语化 (替换 You finished)
add(143, "Now we just wait for them to arrive.") # 精简
# === SECTION 19: 爷爷奶奶来了 ===
add(146, "They're coming!") # 口语化
add(148, "Who wants a big hug from Grandfather?") # 知识点, 修正语法
add(149, "Me! Me!")
add(150, "Who wants Grandmother's homemade candy?") # 知识点, 修正 grandmother→Grandmother's, handmade→homemade(低龄)
add(151, "Me! Me!")
add(153, "We made something for you.")
add(154, "Mmm! What's that smell? Is that a pie?") # 口语化
add(155, "I smell apples!") # 修正 apple→apples
add(156, "Whoa! I know that smell! Apple pie!") # 口语化
add(157, "That is our favourite!")
add(159, "Oh, you have a friend over!") # 修正 in home→over
add(161, "Hello! I am here to visit Vicky.") # 知识点 visit
add(162, "Nice to meet you again!")
add(163, "I guess you helped out today too.") # 知识点 today, 口语化修正
# === SECTION 20: 结尾 ===
add(165, "Today seems pretty busy.") # 知识点 today
add(167, "Yes, we made a little mess before you came.") # 知识点 visit, 口语化修正
add(168, "Today was not easy.") # 知识点 today, 修正时态 is→was
add(170, "That's okay.") # 口语化
add(172, "Grandfather and Grandmother can fix anything.") # 知识点, 口语化 everything→anything
add(173, "That's what family is for!") # 口语化重构
add(174, "Mess or not, we love seeing you!") # 口语化重构
# Print summary and build write payload
print(f"Total lines to write: {len(optimizations)}")
# Build values payload per row
# We need to identify continuous ranges for efficiency
# But since the API supports writing individual ranges, let's batch by nearby rows
# Sort by row index
sorted_rows = sorted(optimizations.items())
# Build the complete column F data: for each row, either the optimized value or empty string ("")
# We need all rows 2-149 (sheet rows)
all_values = []
for row_idx in range(1, 150): # 1 = sheet row 2, 149 = sheet row 150
if row_idx in optimizations:
all_values.append([optimizations[row_idx]])
else:
all_values.append([""])
# Write back - use PUT to write the entire column at once
# We write empty strings for non-dialogue rows to clear previous content
import os
import json as jmod
app_id = "cli_a931175d41799cc7"
config_path = "/root/.openclaw/credentials/xiaoyan/config.json"
with open(config_path) as f:
cfg = jmod.load(f)
app_secret = cfg["apps"][0]["appSecret"]
# Get token
result = subprocess.run([
"curl", "-s", "-X", "POST",
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
"-H", "Content-Type: application/json",
"-d", jmod.dumps({"app_id": app_id, "app_secret": app_secret})
], capture_output=True, text=True)
token = jmod.loads(result.stdout)["tenant_access_token"]
spreadsheet_token = "DA7csROG6hmnQMt59oScOLRfnWc"
sheet_id = "Nr3Ovs"
# Write in batches of 50 rows to avoid too-large payloads
batch_size = 50
for batch_start in range(0, len(all_values), batch_size):
batch = all_values[batch_start:batch_start + batch_size]
# Sheet rows: batch_start + 2 to batch_start + 1 + len(batch)
start_row = batch_start + 2
end_row = batch_start + 1 + len(batch)
range_str = f"{sheet_id}!F{start_row}:F{end_row}"
payload = {
"valueRange": {
"range": range_str,
"values": batch
}
}
result = subprocess.run([
"curl", "-s", "-X", "PUT",
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values",
"-H", f"Authorization: Bearer {token}",
"-H", "Content-Type: application/json",
"-d", jmod.dumps(payload)
], capture_output=True, text=True)
resp = jmod.loads(result.stdout)
print(f"Batch F{start_row}:F{end_row} → code={resp.get('code')}, msg={resp.get('msg')}")
if resp.get("code") != 0:
print(f" ERROR: {result.stdout[:200]}")
sys.exit(1)
print("All batches written successfully!")

View File

@ -1,235 +0,0 @@
#!/usr/bin/env python3
"""
英文台词优化 & 回填脚本 v2 使用精确行号
规则MEMORY.md 对话台词优化通用规则
知识点grandmother, grandfather, visit, today
句型知识点... visit+sb, Today is...
"""
import json, subprocess
# (sheet_row, optimized_F_value) — exact sheet row numbers
rows = [
(3, "Open up!"),
(4, "Your letter is here!"),
(6, "Nobody answers?"),
(7, "Alright... I'll go in myself."),
(9, "Whoa! What a mess!"),
(11, "What's wrong?"),
(12, "Vicky? Can you hear me?"),
(14, "Today is a No-Mom-and-Dad day!"),
(15, "Our parents aren't home!"),
(16, "So we can do whatever we want!"),
(17, "Really?"),
(19, "Yes!"),
(20, "Today is a good day!"),
(21, "Huh?"),
(22, "Are you sure today is a good day?"),
(23, "So on a good day, nobody says sorry?"),
(24, "Eva, Peter, say sorry."),
(25, "Okay. We're sorry."),
(27, "Ah, kids."),
(28, "Come visit us!"),
(29, "Oh, I'm not here to visit."),
(30, "I'm here to give you something."),
(32, "Wait a moment."),
(34, "Hello? Grandfather and grandmother, how are you?"),
(35, "Good, good! We're great!"),
(36, "How are you all, my lovely grandchildren?"),
(37, "We're great! Today is so much fun!"),
(38, "Great to hear that!"),
(39, "Are you ready for us?"),
(40, "I'm so excited to visit you all!"),
(41, "Sure! When will you visit us?"),
(42, "Today is the day, of course!"),
(43, "What? You're both coming today?"),
(44, "Yes! Me and Justin. Your grandmother and grandfather."),
(45, "We sent you a letter. Remember?"),
(46, "See you later!"),
(47, "I love you! Bye!"),
(49, "Oh no! What did they say on the phone?"),
(50, "Your grandfather and grandmother are coming to visit you!"),
(51, "On your good day."),
(53, "Eva! Peter! Alex! We're in trouble!"),
(55, "What happened?"),
(56, "Grandfather and grandmother are coming to visit us!"),
(57, "So what?"),
(58, "Today! Right now!"),
(60, "No, not today..."),
(61, "Look at this place!"),
(62, "Yes, today is a total mess."),
(63, "It's not the best day for visitors."),
(64, "They could be here at any minute!"),
(65, "How will they feel about this?"),
(67, "Grandfather and grandmother will be so upset!"),
(69, "What should we do?"),
(70, "Don't worry! I'll help you clean up!"),
(71, "Okay, first pick up the toys from the floor."),
(73, "Oh no! No time to put these away!"),
(74, "Put them in the cupboard!"),
(75, "Your grandfather and grandmother won't see!"),
(76, "Good idea!"),
(79, "All done!"),
(80, "We need to clean the floor!"),
(81, "I'll do it!"),
(84, "Will Grandfather and Grandmother be happy?"),
(85, "Well... this will do for now."),
(86, "Before your grandfather and grandmother come to visit."),
(88, "Alex! The sofa is a mess!"),
(89, "I'll clean it."),
(91, "I can't get the chocolate off!"),
(92, "I'm good at many things. Just not housework!"),
(94, "Oh no! We can't clean up in time!"),
(95, "I wish we had more time for this!"),
(96, "Why today, of all days?"),
(97, "I don't want them to see our house like this."),
(98, "Why didn't they tell us earlier?"),
(100, "I think they did tell you..."),
(101, "In this letter."),
(102, "What is in this letter?"),
(104, "Hi, your grandfather is writing this letter."),
(105, "I will come to visit you guys."),
(106, "Your grandmother will visit you too!"),
(107, "We love you, see you soon!"),
(108, "They were so happy to visit you!"),
(109, "Yes. We should be happy too."),
(110, "We should not be grumpy like this."),
(111, "Let's do something to welcome them!"),
(112, "You can make something they like!"),
(113, "What is their favourite food?"),
(114, "Grandmother likes apples."),
(115, "Grandfather likes pie."),
(116, "Then you can make apple pie for them!"),
(117, "I remember the recipe."),
(118, "I can fix the cooking robot."),
(119, "Okay! Apple pie is today's welcome gift!"),
(121, "You did it!"),
(122, "Now we just wait for them to arrive."),
(124, "They're coming!"),
(126, "Who wants a big hug from Grandfather?"),
(127, "Me! Me!"),
(128, "Who wants Grandmother's homemade candy?"),
(129, "Me! Me!"),
(131, "We made something for you."),
(132, "Mmm! What's that smell? Is that a pie?"),
(133, "I smell apples!"),
(134, "Whoa! I know that smell! Apple pie!"),
(135, "That is our favourite!"),
(137, "Oh, you have a friend over!"),
(138, "Hello! I am here to visit Vicky."),
(139, "Nice to meet you again!"),
(140, "I guess you helped out today too."),
(142, "Today seems pretty busy."),
(143, "Yes, we made a little mess before you came."),
(144, "Today was not easy."),
(145, "That's okay."),
(146, "Grandfather and Grandmother can fix anything."),
(147, "That's what family is for!"),
(148, "Mess or not, we love seeing you!"),
]
print(f"Total dialogue lines: {len(rows)}")
# Build the complete column F data (one value per row, 1-based sheet rows 2-149)
# We need to clear the ENTIRE column first, then fill only dialogue rows.
# The API PUT overwrites the specified range. Write all rows 2-149.
# Use a dict for fast lookup
f_map = {r: v for r, v in rows}
# Get token
with open("/root/.openclaw/credentials/xiaoyan/config.json") as f:
cfg = json.load(f)
app_id = "cli_a931175d41799cc7"
app_secret = cfg["apps"][0]["appSecret"]
result = subprocess.run([
"curl", "-s", "-X", "POST",
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
"-H", "Content-Type: application/json",
"-d", json.dumps({"app_id": app_id, "app_secret": app_secret})
], capture_output=True, text=True)
token = json.loads(result.stdout)["tenant_access_token"]
spreadsheet_token = "DA7csROG6hmnQMt59oScOLRfnWc"
sheet_id = "Nr3Ovs"
# Step 1: Clear entire column F (rows 2-149)
print("Clearing column F (rows 2-149)...")
empty_payload = {
"valueRange": {
"range": f"{sheet_id}!F2:F149",
"values": [[""] for _ in range(148)] # 149-2+1 = 148 rows
}
}
result = subprocess.run([
"curl", "-s", "-X", "PUT",
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values",
"-H", f"Authorization: Bearer {token}",
"-H", "Content-Type: application/json",
"-d", json.dumps(empty_payload)
], capture_output=True, text=True)
resp = json.loads(result.stdout)
print(f" Clear: code={resp.get('code')}, msg={resp.get('msg')}")
assert resp.get("code") == 0, f"Clear failed: {result.stdout[:200]}"
# Step 2: Write optimized values to each dialogue row
# Group consecutive rows for batching
batch_start = None
batch_end = None
batch_values = []
def flush_batch():
global batch_start, batch_end, batch_values
if not batch_values:
return
range_str = f"{sheet_id}!F{batch_start}:F{batch_end}"
payload = {"valueRange": {"range": range_str, "values": batch_values}}
result = subprocess.run([
"curl", "-s", "-X", "PUT",
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values",
"-H", f"Authorization: Bearer {token}",
"-H", "Content-Type: application/json",
"-d", json.dumps(payload)
], capture_output=True, text=True)
resp = json.loads(result.stdout)
print(f" Write {range_str}: code={resp.get('code')}, msg={resp.get('msg')}")
assert resp.get("code") == 0, f"Write failed: {result.stdout[:200]}"
batch_start = None
batch_end = None
batch_values = []
# Also need to fill empty rows between dialogue rows
# Strategy: write row by row, merging consecutive rows into batches
for sheet_row in range(2, 150):
value = f_map.get(sheet_row, "")
if batch_start is None:
batch_start = sheet_row
batch_end = sheet_row
batch_values = [[value]]
else:
batch_end = sheet_row
batch_values.append([value])
flush_batch()
print("Done! Verifying...")
# Step 3: Verify key rows
check_rows = [3, 7, 14, 23, 28, 34, 44, 49, 56, 64, 92, 97, 111, 121, 134, 147]
for r in check_rows:
result = subprocess.run([
"curl", "-s", "-X", "GET",
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values/{sheet_id}!D{r}:F{r}?valueRenderOption=ToString",
"-H", f"Authorization: Bearer {token}"
], capture_output=True, text=True)
vals = json.loads(result.stdout).get("data", {}).get("valueRange", {}).get("values", [[]])[0]
role = vals[0] if len(vals) > 0 else "-"
e_val = vals[1] if len(vals) > 1 else "-"
f_val = vals[2] if len(vals) > 2 else "-"
expected = f_map.get(r, "-")
status = "" if f_val == (expected or "") else "✗ MISMATCH"
if status != "":
print(f" Row {r}: [{role}] E='{e_val}' F='{f_val}' expected='{expected}' {status}")
else:
print(f" Row {r}: [{role}] F='{f_val}' {status}")
print("\nAll verified!")

View File

@ -1,371 +0,0 @@
import pandas as pd
from openpyxl import load_workbook
# 读取原始Excel
input_path = "/root/.openclaw/media/inbound/12-5---1879d0c9-7cdf-4cd5-9aff-f03091ebada3.xlsx"
output_path = "/root/.openclaw/workspace-xiaoyan/L1-S1-U12-L5_组件完成版.xlsx"
# 定义组件内容,按顺序对应表格中的组件行
components = [
# 1112501 对话朗读
"""【任务标题】描述当前时间
资源配置
图片时机互动内容
音频载体
情境引入
互动内容
User: It is still night.朗读
互动反馈
正确
错误Vicky : Wait, that didn't sound right.
后置对话
""",
# 1112502 图片单选
"""【任务标题】等待清晨
情境引入
互动内容
题干We must wait for the $morning$.
选项
00
01正确
辅助信息morning指"清晨"
互动反馈
正确
错误Sally : Oh no, that's not right. Look outside, it's still dark!
后置对话
""",
# 1112503 对话朗读
"""【任务标题】询问清晨时间
资源配置
图片时机
音频载体
情境引入
互动内容
Vicky: How do we know when it's morning?(朗读)
互动反馈
正确
错误Sally : Hmm, I didn't catch that. Can you say it again?
后置对话
""",
# 1112504 对话朗读
"""【任务标题】表达尝试意愿
资源配置
图片时机
音频载体
情境引入
互动内容
User: I'd like to try too.(朗读)
互动反馈
正确
错误Sally : Oh, that doesn't sound quite right.
后置对话
Ben: Me too.""",
# 1112505 对话朗读
"""【任务标题】尝试做手影狗
资源配置
图片时机
音频载体
情境引入
互动内容
User: I want to try to make a dog!朗读
互动反馈
正确
错误Lucy : Wait, what did you say? I didn't get it.
后置对话
""",
# 1112506 对话朗读
"""【任务标题】评价夜晚乐趣
资源配置
图片时机
音频载体
情境引入
互动内容
User: Night is so much fun!朗读
互动反馈
正确
错误Lucy : Hmm, that didn't sound right.
后置对话
""",
# 1112507 对话挖空
"""【任务标题】否定当前是清晨
资源配置
图片时机
音频载体
情境引入
互动内容
题干It isn't ____.
选项
选项1night
选项2morning正确
辅助信息morning指"清晨"
互动反馈
正确
错误Ben : Oh no, that's wrong. It's still dark out!
后置对话
""",
# 1112508 对话挖空
"""【任务标题】确认当前是夜晚
资源配置
图片时机
音频载体
情境引入
互动内容
题干It is still ____!
选项
选项1night正确
选项2morning
辅助信息night指"夜晚"
互动反馈
正确
错误Ben : Wait, that's not right. The sun isn't up yet!
后置对话
""",
# 1112509 对话朗读
"""【任务标题】提议逐个尝试
资源配置
图片时机
音频载体
情境引入
互动内容
User: Let's try them one by one!(朗读)
互动反馈
正确
错误Sally : Hmm, that didn't sound quite right.
后置对话
""",
# 1112510 对话选读
"""【任务标题】尝试下一个格子
资源配置
图片时机
音频载体
情境引入
互动内容
请选择一句朗读
选项1Let's try the next!
选项2Let's try them one by one!
辅助信息两个选项均为正确答案
互动反馈
正确
错误Sally : Oh, that's not one of the choices.
后置对话
""",
# 1112510 图片有序
"""【任务标题】排序跳格子顺序
情境引入
互动内容
题干Put the actions in order of $one by one$.
选项
00
01
02
03
正确顺序00010203
辅助信息one by one指"一个接一个"
互动反馈
正确
错误Dan : Oops, that's the wrong order! We can't jump like that.
后置对话
""",
# 1112511 对话挖空
"""【任务标题】提议尝试另一块格子
资源配置
图片时机
音频载体
情境引入
互动内容
题干____ that one.
选项
选项1let's try正确
选项2let's play
辅助信息let's try指"我们试试"
互动反馈
正确
错误Dan : Wait, that doesn't make sense.
后置对话
""",
# 1112512 对话朗读
"""【任务标题】询问下一个人选
资源配置
图片时机
音频载体
情境引入
互动内容
User: Who is the next?朗读
互动反馈
正确
错误Sally : Hmm, that didn't sound quite right.
后置对话
Vicky: It's my turn!""",
# 1112513 对话挖空
"""【任务标题】描述灯逐个亮起
资源配置
图片时机互动内容
音频载体
情境引入
互动内容
题干The lights are turning on ____!
选项
选项1one by one正确
选项2two by two
辅助信息one by one指"一个接一个"
互动反馈
正确
错误Lucy : Oh no, that's wrong! The lights are turning on in order.
后置对话
""",
# 1112514 对话组句
"""【任务标题】组句提议尝试
资源配置
图片时机
音频载体
情境引入
User: The first tile didn't work.
互动内容
题目提议大家试试另一块格子
选项1try this
选项2Let's
选项3one.
答案Let's try this one.
辅助信息Let's do sth.是提议做某事的固定句式
互动反馈
正确
错误Sally : Oops, that's not the right sentence.
后置对话
""",
# 1112516 对话组句
"""【任务标题】组句逐个行动
资源配置
图片时机
音频载体
情境引入
Sally: We need to stand on the tiles one after another.
互动内容
题目告诉大家要一个接一个做
选项1Let's do it
选项2one
选项3by
选项4one!
答案Let's do it one by one!
辅助信息one by one是固定搭配意思是"一个接一个"
互动反馈
正确
错误Sally : Oh, that doesn't make sense. Try again.
后置对话
""",
# 1112517 对话选择
"""【任务标题】判断当前时间
资源配置
图片时机互动内容
音频载体
情境引入
Fred: I see the light from the lighthouse!
互动内容
Wow! It's so bright! ____
选项
选项1It's morning now!(正确)
选项2It's night now!
辅助信息morning指"清晨"night指"夜晚"
互动反馈
正确
错误Fred : Wait, that's not right! Look at the sun in the sky!
后置对话
"""
]
# 加载工作簿
wb = load_workbook(input_path)
ws = wb.active
# 找到组件列第7列索引从1开始
component_col = 7
# 找到有组件ID的行从第6行开始是数据行
component_row_start = 6
# 逐个填入组件内容
for i, comp in enumerate(components):
row = component_row_start + i
ws.cell(row=row, column=component_col, value=comp)
# 保存文件
wb.save(output_path)
print(f"文件已保存到: {output_path}")

View File

@ -1,351 +0,0 @@
from openpyxl import load_workbook
# 读取原始Excel
input_path = "/root/.openclaw/media/inbound/12-5---1879d0c9-7cdf-4cd5-9aff-f03091ebada3.xlsx"
output_path = "/root/.openclaw/workspace-xiaoyan/L1-S1-U12-L5_组件完成版_按ID匹配.xlsx"
# 定义组件内容按ID对应行号Excel行号从1开始
component_map = {
32: """【任务标题】描述当前时间
资源配置
图片时机互动内容
音频载体
情境引入
互动内容
User: It is still night.朗读
互动反馈
正确
错误Vicky : Wait, that didn't sound right.
后置对话
""",
33: """【任务标题】等待清晨
情境引入
互动内容
题干We must wait for the $morning$.
选项
00
01正确
辅助信息morning指"清晨"
互动反馈
正确
错误Sally : Oh no, that's not right. Look outside, it's still dark!
后置对话
""",
34: """【任务标题】询问清晨时间
资源配置
图片时机
音频载体
情境引入
互动内容
Vicky: How do we know when it's morning?(朗读)
互动反馈
正确
错误Sally : Hmm, I didn't catch that. Can you say it again?
后置对话
""",
52: """【任务标题】表达尝试意愿
资源配置
图片时机
音频载体
情境引入
互动内容
User: I'd like to try too.(朗读)
互动反馈
正确
错误Sally : Oh, that doesn't sound quite right.
后置对话
Ben: Me too.""",
59: """【任务标题】尝试做手影狗
资源配置
图片时机
音频载体
情境引入
互动内容
User: I want to try to make a dog!朗读
互动反馈
正确
错误Lucy : Wait, what did you say? I didn't get it.
后置对话
""",
70: """【任务标题】评价夜晚乐趣
资源配置
图片时机
音频载体
情境引入
互动内容
User: Night is so much fun!朗读
互动反馈
正确
错误Lucy : Hmm, that didn't sound right.
后置对话
""",
85: """【任务标题】否定当前是清晨
资源配置
图片时机
音频载体
情境引入
互动内容
题干It isn't ____.
选项
选项1night
选项2morning正确
辅助信息morning指"清晨"
互动反馈
正确
错误Ben : Oh no, that's wrong. It's still dark out!
后置对话
""",
86: """【任务标题】确认当前是夜晚
资源配置
图片时机
音频载体
情境引入
互动内容
题干It is still ____!
选项
选项1night正确
选项2morning
辅助信息night指"夜晚"
互动反馈
正确
错误Ben : Wait, that's not right. The sun isn't up yet!
后置对话
""",
96: """【任务标题】提议逐个尝试
资源配置
图片时机
音频载体
情境引入
互动内容
User: Let's try them one by one!(朗读)
互动反馈
正确
错误Sally : Hmm, that didn't sound quite right.
后置对话
""",
104: """【任务标题】尝试下一个格子
资源配置
图片时机
音频载体
情境引入
互动内容
请选择一句朗读
选项1Let's try the next!
选项2Let's try them one by one!
辅助信息两个选项均为正确答案
互动反馈
正确
错误Sally : Oh, that's not one of the choices.
后置对话
""",
110: """【任务标题】排序跳格子顺序
情境引入
互动内容
题干Put the actions in order of $one by one$.
选项
00
01
02
03
正确顺序00010203
辅助信息one by one指"一个接一个"
互动反馈
正确
错误Dan : Oops, that's the wrong order! We can't jump like that.
后置对话
""",
118: """【任务标题】提议尝试另一块格子
资源配置
图片时机
音频载体
情境引入
互动内容
题干____ that one.
选项
选项1let's try正确
选项2let's play
辅助信息let's try指"我们试试"
互动反馈
正确
错误Dan : Wait, that doesn't make sense.
后置对话
""",
124: """【任务标题】询问下一个人选
资源配置
图片时机
音频载体
情境引入
互动内容
User: Who is the next?朗读
互动反馈
正确
错误Sally : Hmm, that didn't sound quite right.
后置对话
Vicky: It's my turn!""",
127: """【任务标题】描述灯逐个亮起
资源配置
图片时机互动内容
音频载体
情境引入
互动内容
题干The lights are turning on ____!
选项
选项1one by one正确
选项2two by two
辅助信息one by one指"一个接一个"
互动反馈
正确
错误Lucy : Oh no, that's wrong! The lights are turning on in order.
后置对话
""",
136: """【任务标题】组句提议尝试
资源配置
图片时机
音频载体
情境引入
User: The first tile didn't work.
互动内容
题目提议大家试试另一块格子
选项1try this
选项2Let's
选项3one.
答案Let's try this one.
辅助信息Let's do sth.是提议做某事的固定句式
互动反馈
正确
错误Sally : Oops, that's not the right sentence.
后置对话
""",
155: """【任务标题】组句逐个行动
资源配置
图片时机
音频载体
情境引入
Sally: We need to stand on the tiles one after another.
互动内容
题目告诉大家要一个接一个做
选项1Let's do it
选项2one
选项3by
选项4one!
答案Let's do it one by one!
辅助信息one by one是固定搭配意思是"一个接一个"
互动反馈
正确
错误Sally : Oh, that doesn't make sense. Try again.
后置对话
""",
179: """【任务标题】判断当前时间
资源配置
图片时机互动内容
音频载体
情境引入
Fred: I see the light from the lighthouse!
互动内容
Wow! It's so bright! ____
选项
选项1It's morning now!(正确)
选项2It's night now!
辅助信息morning指"清晨"night指"夜晚"
互动反馈
正确
错误Fred : Wait, that's not right! Look at the sun in the sky!
后置对话
"""
}
# 加载工作簿
wb = load_workbook(input_path)
ws = wb.active
# 组件列是第7列G列
component_col = 7
# 按行号填入组件
for row_num, comp_content in component_map.items():
ws.cell(row=row_num, column=component_col, value=comp_content)
# 保存文件
wb.save(output_path)
print(f"文件已保存到: {output_path}")
print(f"已为{len(component_map)}个组件ID匹配并填入对应行")

View File

@ -1,355 +0,0 @@
from openpyxl import load_workbook
# 读取原始Excel
input_path = "/root/.openclaw/media/inbound/12-5---1879d0c9-7cdf-4cd5-9aff-f03091ebada3.xlsx"
output_path = "/root/.openclaw/workspace-xiaoyan/L1-S1-U12-L5_组件完成版_最终匹配版.xlsx"
# 正确的组件ID对应行号第一列有组件ID的行为Excel实际行号
component_map = {
32: """【任务标题】描述当前时间
资源配置
图片时机互动内容
音频载体
情境引入
互动内容
User: It is still night.朗读
互动反馈
正确
错误Vicky : Wait, that didn't sound right.
后置对话
""",
33: """【任务标题】等待清晨
情境引入
互动内容
题干We must wait for the $morning$.
选项
00
01正确
辅助信息morning指"清晨"
互动反馈
正确
错误Sally : Oh no, that's not right. Look outside, it's still dark!
后置对话
""",
34: """【任务标题】询问清晨时间
资源配置
图片时机
音频载体
情境引入
互动内容
Vicky: How do we know when it's morning?(朗读)
互动反馈
正确
错误Sally : Hmm, I didn't catch that. Can you say it again?
后置对话
""",
52: """【任务标题】表达尝试意愿
资源配置
图片时机
音频载体
情境引入
互动内容
User: I'd like to try too.(朗读)
互动反馈
正确
错误Sally : Oh, that doesn't sound quite right.
后置对话
Ben: Me too.""",
58: """【任务标题】尝试做手影狗
资源配置
图片时机
音频载体
情境引入
互动内容
User: I want to try to make a dog!朗读
互动反馈
正确
错误Lucy : Wait, what did you say? I didn't get it.
后置对话
""",
69: """【任务标题】评价夜晚乐趣
资源配置
图片时机
音频载体
情境引入
互动内容
User: Night is so much fun!朗读
互动反馈
正确
错误Lucy : Hmm, that didn't sound right.
后置对话
""",
84: """【任务标题】否定当前是清晨
资源配置
图片时机
音频载体
情境引入
互动内容
题干It isn't ____.
选项
选项1night
选项2morning正确
辅助信息morning指"清晨"
互动反馈
正确
错误Ben : Oh no, that's wrong. It's still dark out!
后置对话
""",
85: """【任务标题】确认当前是夜晚
资源配置
图片时机
音频载体
情境引入
互动内容
题干It is still ____!
选项
选项1night正确
选项2morning
辅助信息night指"夜晚"
互动反馈
正确
错误Ben : Wait, that's not right. The sun isn't up yet!
后置对话
""",
95: """【任务标题】提议逐个尝试
资源配置
图片时机
音频载体
情境引入
互动内容
User: Let's try them one by one!(朗读)
互动反馈
正确
错误Sally : Hmm, that didn't sound quite right.
后置对话
""",
103: """【任务标题】尝试下一个格子
资源配置
图片时机
音频载体
情境引入
互动内容
请选择一句朗读
选项1Let's try the next!
选项2Let's try them one by one!
辅助信息两个选项均为正确答案
互动反馈
正确
错误Sally : Oh, that's not one of the choices.
后置对话
""",
109: """【任务标题】排序跳格子顺序
情境引入
互动内容
题干Put the actions in order of $one by one$.
选项
00
01
02
03
正确顺序00010203
辅助信息one by one指"一个接一个"
互动反馈
正确
错误Dan : Oops, that's the wrong order! We can't jump like that.
后置对话
""",
117: """【任务标题】提议尝试另一块格子
资源配置
图片时机
音频载体
情境引入
互动内容
题干____ that one.
选项
选项1let's try正确
选项2let's play
辅助信息let's try指"我们试试"
互动反馈
正确
错误Dan : Wait, that doesn't make sense.
后置对话
""",
123: """【任务标题】询问下一个人选
资源配置
图片时机
音频载体
情境引入
互动内容
User: Who is the next?朗读
互动反馈
正确
错误Sally : Hmm, that didn't sound quite right.
后置对话
Vicky: It's my turn!""",
126: """【任务标题】描述灯逐个亮起
资源配置
图片时机互动内容
音频载体
情境引入
互动内容
题干The lights are turning on ____!
选项
选项1one by one正确
选项2two by two
辅助信息one by one指"一个接一个"
互动反馈
正确
错误Lucy : Oh no, that's wrong! The lights are turning on in order.
后置对话
""",
135: """【任务标题】组句提议尝试
资源配置
图片时机
音频载体
情境引入
User: The first tile didn't work.
互动内容
题目提议大家试试另一块格子
选项1try this
选项2Let's
选项3one.
答案Let's try this one.
辅助信息Let's do sth.是提议做某事的固定句式
互动反馈
正确
错误Sally : Oops, that's not the right sentence.
后置对话
""",
154: """【任务标题】组句逐个行动
资源配置
图片时机
音频载体
情境引入
Sally: We need to stand on the tiles one after another.
互动内容
题目告诉大家要一个接一个做
选项1Let's do it
选项2one
选项3by
选项4one!
答案Let's do it one by one!
辅助信息one by one是固定搭配意思是"一个接一个"
互动反馈
正确
错误Sally : Oh, that doesn't make sense. Try again.
后置对话
""",
178: """【任务标题】判断当前时间
资源配置
图片时机互动内容
音频载体
情境引入
Fred: I see the light from the lighthouse!
互动内容
Wow! It's so bright! ____
选项
选项1It's morning now!(正确)
选项2It's night now!
辅助信息morning指"清晨"night指"夜晚"
互动反馈
正确
错误Fred : Wait, that's not right! Look at the sun in the sky!
后置对话
"""
}
# 加载工作簿
wb = load_workbook(input_path)
ws = wb.active
# 组件列是第7列G列
component_col = 7
# 先清空原有组件列的内容(避免残留内容)
for row_num in component_map.keys():
ws.cell(row=row_num, column=component_col, value="")
# 按正确行号填入组件
for row_num, comp_content in component_map.items():
ws.cell(row=row_num, column=component_col, value=comp_content)
# 保存文件
wb.save(output_path)
print(f"文件已保存到: {output_path}")
print(f"已为{len(component_map)}个组件ID 100%匹配填入对应行")

View File

@ -1,222 +0,0 @@
#!/usr/bin/env python3
"""
Fix component production issues:
1. Regenerate 4 对话朗读 rows with correct format (no 组件ID, correct 资源配置)
2. Fix Chinese error feedback English for 7 other rows
"""
import requests, json, time, os
CRED_PATH = "/root/.openclaw/credentials/xiaoyan/config.json"
SHEET_TOKEN = "VgPwsodzthsk9Vti8GocrbH2nEe"
SHEET_ID = "wMQVyV"
OUTPUT_DIR = "/root/.openclaw/workspace-xiaoyan/business_production/组件生产/output"
LLM_BASE = "https://ark.cn-beijing.volces.com/api/v3"
LLM_KEY = "32994652-505c-492b-b6da-616ec5c5733c"
LLM_MODEL = "ep-m-20260301164317-vmmj4"
def get_token():
with open(CRED_PATH) as f: c = json.load(f)
r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
json={"app_id": c["apps"][0]["appId"], "app_secret": c["apps"][0]["appSecret"]})
return r.json()["tenant_access_token"]
def get_sheet(token):
r = requests.get(
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SHEET_TOKEN}/values/{SHEET_ID}?majorDimension=ROWS",
headers={"Authorization": f"Bearer {token}"})
return r.json()["data"]["valueRange"]["values"]
def write_cell(token, sheet_row, content):
range_str = f"{SHEET_ID}!H{sheet_row}:H{sheet_row}"
r = requests.put(
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SHEET_TOKEN}/values",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={"valueRange": {"range": range_str, "values": [[content]]}})
return r.json().get("code") == 0
def parse_col(row, idx):
if isinstance(row[idx], list):
return "".join([seg.get("text","") for seg in row[idx] if isinstance(seg, dict)])
return str(row[idx]) if row[idx] else ""
def llm(messages):
try: from openai import OpenAI
except: os.system("pip install openai --break-system-packages -q"); from openai import OpenAI
client = OpenAI(base_url=LLM_BASE, api_key=LLM_KEY)
resp = client.chat.completions.create(
model=LLM_MODEL, messages=messages, temperature=0.3, max_tokens=1000,
extra_body={"thinking": {"type": "disabled"}})
return resp.choices[0].message.content.strip()
# === FIX: 对话朗读 regeneration ===
READING_SYSTEM = """You fix dialogue reading component configs.
## CORRECT FORMAT (exactly this, no variation):
任务标题{contextual title fitting the scene, 10 chars}
资源配置
图片时机
情境引入
互动内容
互动反馈
情境引入
{Character: line, or if no context needed}
互动内容
{Character}: {sentence}朗读
后置对话
{Character: next line from context, or }
## KEY RULES
- NO 组件ID field at all!
- NO 音频载体 line!
- NO 互动反馈 section!
- 资源配置 uses line-break stage listing format (情境引入/互动内容/互动反馈 on separate lines)
- Task title must match the scene context, not generic like "朗读台词"
- Error feedback (互动反馈) appears only as a stage name in 资源配置, NOT as a separate section
- Generate only the component config text, no explanation"""
def fix_reading(token, row_data):
"""Regenerate a 对话朗读 component"""
messages = [
{"role": "system", "content": READING_SYSTEM},
{"role": "user", "content": f"""Fix this 对话朗读 component:
Context:
- Scene: {row_data.get('plot','')}
- Knowledge Point: {row_data.get('kp','')}
Dialogue before:
{row_data.get('ctx_before','')}
Current interaction:
{row_data.get('char','User')}: {row_data.get('script_en','')}
Dialogue after:
{row_data.get('ctx_after','')}
Generate the corrected component config following the format exactly. Make the task title contextual and scene-fitting."""}
]
return llm(messages)
# === FIX: Chinese error feedback → English ===
FB_SYSTEM = """You fix Chinese error feedback in English learning component configs.
Replace Chinese feedback text with natural English character dialogue that matches the scene context and character personality.
Keep everything else exactly the same. Only change the error feedback text.
Format: return the complete fixed component config text, changing ONLY the error feedback lines."""
def fix_feedback(token, old_content, context):
"""Fix Chinese error feedback"""
messages = [
{"role": "system", "content": FB_SYSTEM},
{"role": "user", "content": f"""Fix the Chinese error feedback in this component config:
Component context: {context}
Current config:
{old_content}
Return the complete fixed config with Chinese feedback replaced by natural English character dialogue."""}
]
return llm(messages)
def main():
token = get_token()
values = get_sheet(token)
os.makedirs(OUTPUT_DIR, exist_ok=True)
# Collect context for all rows
rows = []
for i, row in enumerate(values):
while len(row) < 8: row.append(None)
rtype = str(row[0]).strip() if row[0] else ""
rows.append({
"sheet_row": i+1, "type": rtype,
"id": str(row[1]).strip() if row[1] else "",
"plot": str(row[2]).strip() if row[2] else "",
"char": str(row[3]).strip() if row[3] else "",
"script_cn": parse_col(row, 4), "script_en": parse_col(row, 5),
"kp": str(row[6]).strip() if row[6] else "",
"h_col": str(row[7]) if row[7] else "",
})
# Find dialogue reading rows and other fixable rows
reading_rows = []
cn_feedback_rows = []
for i, r in enumerate(rows):
if r["type"] == "对话朗读" and r["h_col"]:
# Get context
ctx_before = []
for j in range(max(0,i-4), i):
if rows[j]["char"] and rows[j]["script_cn"]:
ctx_before.append(f"{rows[j]['char']}: {rows[j]['script_cn']}")
ctx_after = []
for j in range(i+1, min(len(rows), i+4)):
if rows[j]["char"] and rows[j]["script_cn"]:
ctx_after.append(f"{rows[j]['char']}: {rows[j]['script_cn']}")
r["ctx_before"] = "\n".join(ctx_before)
r["ctx_after"] = "\n".join(ctx_after)
reading_rows.append(r)
elif r["h_col"]:
# Check for Chinese error feedback
err_idx = r["h_col"].find("错误:")
if err_idx > 0:
err_line = r["h_col"][err_idx:].split("\n")[0]
has_cn = any('\u4e00' <= c <= '\u9fff' for c in err_line)
# Skip section headers (正确/错误) - only check after "错误:" before newline
fb_text = err_line.split("错误:")[1] if "错误:" in err_line else err_line
if any('\u4e00' <= c <= '\u9fff' for c in fb_text):
cn_feedback_rows.append(r)
print(f"对话朗读 to fix: {len(reading_rows)}")
print(f"中文反馈 to fix: {len(cn_feedback_rows)}")
results = []
# Fix dialogue readings
for r in reading_rows:
row_num = r["sheet_row"]
print(f"\n[对话朗读] Row {row_num} ID={r['id']}")
try:
new_content = fix_reading(token, r)
print(f" Generated: {new_content[:100]}...")
if write_cell(token, row_num, new_content):
print(f" ✅ Written to H{row_num}")
results.append({"row": row_num, "type": "对话朗读", "status": "fixed"})
else:
print(f" ❌ Write failed")
results.append({"row": row_num, "type": "对话朗读", "status": "write_fail"})
except Exception as e:
print(f" ❌ Error: {e}")
results.append({"row": row_num, "type": "对话朗读", "status": str(e)})
time.sleep(0.5)
# Fix Chinese feedback
for r in cn_feedback_rows:
row_num = r["sheet_row"]
rtype = r["type"]
print(f"\n[{rtype}] Row {row_num} ID={r['id']} — fixing Chinese feedback")
ctx = f"Character: {r['char']}, Plot: {r['plot']}, KP: {r['kp']}"
try:
new_content = fix_feedback(token, r["h_col"], ctx)
if write_cell(token, row_num, new_content):
print(f" ✅ Written")
results.append({"row": row_num, "type": rtype, "status": "fb_fixed"})
else:
print(f" ❌ Write failed")
except Exception as e:
print(f" ❌ Error: {e}")
time.sleep(0.5)
# Summary
ok = sum(1 for r in results if "fail" not in r["status"] and "Error" not in str(r["status"]))
print(f"\n{'='*40}")
print(f"Fixed: {ok}/{len(results)}")
for r in results:
print(f" H{r['row']} [{r['type']}]: {r['status']}")
if __name__ == "__main__":
main()

View File

@ -1,444 +0,0 @@
#!/usr/bin/env python3
"""Generate component configurations for the script sheet and write them back."""
import json
import subprocess
import sys
# Get bot token
def get_token():
result = subprocess.run(
["jq", "-r", ".apps[0].appSecret",
"/root/.openclaw/credentials/xiaoyan/config.json"],
capture_output=True, text=True
)
app_secret = result.stdout.strip()
import urllib.request
import urllib.parse
data = json.dumps({"app_id": "cli_a931175d41799cc7", "app_secret": app_secret}).encode()
req = urllib.request.Request(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
data=data,
headers={"Content-Type": "application/json"}
)
resp = json.loads(urllib.request.urlopen(req).read())
return resp["tenant_access_token"]
TOKEN = get_token()
SPREADSHEET_TOKEN = "Dk1fs7TJ4hWs5PtUvXlc1EaEnvb"
SHEET_ID = "fmqYrx"
# Component configurations for each row
# Format: {row_number: config_text}
CONFIGS = {
# Row 12: 图片无序多选 (1217101) - motorbike
12: """【任务标题】
在图片中寻找摩托车
情境引入
Ben: Whoa! Those are motorbikes!
User: Where? Let me take a look.
互动内容
Find the $motorbikes$ in the picture.音频
选项
00
01
02
答案
00
01
辅助信息motorbike "摩托车"
互动反馈
正确 Ben : That's right! Those are two motorbikes!
错误 Ben : Look again. Can you find the motorbikes?
后置对话
""",
# Row 30: 对话朗读 (1217102) - people
30: """【任务标题】
询问那些人是谁
资源配置
图片时机
情境引入
互动内容
Who are those people?朗读
后置对话
""",
# Row 46: 对话挖空 (1217103) - people
46: """【任务标题】
向镇长报告发现
资源配置
情境引入
User: Tom! We found two strange people.
互动内容
We found two strange ___.音频
选项1people正确
选项2animals
互动反馈
正确 Tom : Good! Tell me more about them.
错误 Tom : Animals? Are you sure about that?
后置对话
""",
# Row 53: 对话挖空 (1217104) - motorbike
53: """【任务标题】
描述可疑人员的特征
资源配置
情境引入
Tom: Tell me more about them.
互动内容
They're on ___.(音频)
选项1motorbikes正确
选项2bicycles
互动反馈
正确 Tom : Good job! I'm coming right now!
错误 Tom : Bicycles? That doesn't sound right.
后置对话
""",
# Row 67: 对话朗读 (1217105) - people
67: """【任务标题】
盘问来人身份
资源配置
图片时机
情境引入
互动内容
Who are you people?朗读
后置对话
""",
# Row 81: 对话挖空 (1217106) - show
81: """【任务标题】
要求检查袋子
资源配置
情境引入
Jay: The bag is a secret!
互动内容
You must ___ me what is in the bag!音频
选项1show正确
选项2tell
互动反馈
正确 Jay : No, no! We can't show you!
错误 Jay : Ha! We won't tell you, either!
后置对话
""",
# Row 96: 对话选择 (1217107) - motorbike
96: """【任务标题】
回应 Jay 的炫耀
资源配置
情境引入
Jay: Let me show you how fast it is!
互动内容
要求选择正确的回复
选项音频
选项1Your motorbike looks really old.正确
- 反馈:
选项2Your motorbike looks so cool!
- 反馈 Jay : Thank you, kid! But let me show you more!
后置对话
""",
# Row 110: 对话朗读 (1217108) - Show...
110: """【任务标题】
要求 Jay 继续展示
资源配置
图片时机
情境引入
互动内容
Wow! Show me more!朗读
后置对话
""",
# Row 122: 对话组句 (1217109) - Show...
122: """【任务标题】
Lin 展示摩托车
资源配置
情境引入
Lin: My motorbike never does that.
互动内容
题目 Lin 展示她的摩托车
音频
选项1Cool
选项2Show
选项3me
选项4your
选项5motorbike
答案Cool! Show me your motorbike!
辅助信息Cool 作感叹词放句首Show me 后接名词表示"给我看看..."
互动反馈
正确 Lin : Hmph. Fine.
错误 Lin : What do you want me to show?
后置对话
""",
# Row 142: 对话选读 (1217110) - motorbike
142: """【任务标题】
评价谁的摩托车更好
资源配置
情境引入
Jay: Hey kids! Which motorbike is better?
互动内容
要求选择一个你想表达的观点
选项
选项1I think the red motorbike is better.
- 反馈 Jay : What? I don't agree!
选项2I think the orange motorbike is better.
- 反馈 Lin : Hey! You're wrong!
后置对话
""",
# Row 161: 对话朗读 (1217111) - ... can/can't hold...
161: """【任务标题】
答应帮忙拿袋子
资源配置
图片时机
情境引入
互动内容
Okay! I can hold it.朗读
后置对话
""",
# Row 176: 对话选择 (1217112) - ... can/can't hold...
176: """【任务标题】
回应 Ben 的关心
资源配置
情境引入
Ben: Are you okay?
互动内容
要求选择正确的回复
选项音频
选项1Don't worry. I can hold it.(正确)
- 反馈:
选项2Help! I can't hold it!
- 反馈 Ben : Oh no! Let me help you!
后置对话
""",
# Row 192: 对话组句 (1217113) - ... can/can't hold...
192: """【任务标题】
Ben 放心你能拿住
资源配置
情境引入
Ben: Let's see what's inside.
互动内容
题目告诉 Ben 你能拿住袋子让他放心
音频
选项1Don't
选项2worry
选项3I
选项4can
选项5hold
选项6it
答案Don't worry. I can hold it.
辅助信息Don't worry 表示"别担心"can 后跟动词原形表示"能/会"
互动反馈
正确 Ben : Ouch!
错误 Ben : What? Are you okay?
后置对话
""",
# Row 204: 对话朗读 (1217114) - ... can/can't hold...
204: """【任务标题】
呼救求助
资源配置
图片时机
情境引入
互动内容
I can't hold it!(朗读)
后置对话
""",
# Row 233: 阅读理解 (1217115) - motorbike/people/hold/show
233: """【任务标题】
Tom 描述事情的经过
情境引入
Tom: Can anybody tell me what happened here?
互动内容
问题1Who came from out of town?
答案These two people.
问题2What did they ride?
答案They ride motorbikes.
问题3What did you ask them to do?
答案Show us what's in their bags.
后置对话
""",
# Row 245: 对话选读 (1217116) - ... can/can't hold...
245: """【任务标题】
为自己的失误道歉
资源配置
情境引入
Lin: And you opened the bags without asking.
互动内容
要求选择一个你想表达的观点
选项
选项1Sorry, but I can't hold it.
- 反馈 Tom : It's okay. Everybody makes mistakes.
选项2Sorry, I can't hold it anymore.
- 反馈 Tom : It's okay. Everybody makes mistakes.
后置对话
""",
}
def write_to_sheet(row, config_text):
"""Write config to a single cell in the sheet."""
import urllib.request
range_str = f"{SHEET_ID}!H{row}:H{row}"
data = json.dumps({
"valueRange": {
"range": range_str,
"values": [[config_text]]
}
}).encode()
req = urllib.request.Request(
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values",
data=data,
headers={
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"
},
method="PUT"
)
resp = json.loads(urllib.request.urlopen(req).read())
return resp.get("code") == 0
def verify_write(row, expected_prefix):
"""Verify what was written."""
import urllib.request
range_str = f"{SHEET_ID}!H{row}:H{row}"
url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values/{range_str}?valueRenderOption=ToString"
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {TOKEN}"})
resp = json.loads(urllib.request.urlopen(req).read())
values = resp.get("data", {}).get("valueRange", {}).get("values", [])
if values and values[0] and values[0][0]:
actual = values[0][0]
if actual.startswith(expected_prefix):
return True, actual[:80]
else:
return False, actual[:80]
return False, "EMPTY"
def main():
success = 0
failed = []
for row, config in sorted(CONFIGS.items()):
try:
ok = write_to_sheet(row, config)
if ok:
prefix = config.strip()[:50].replace("\n", " ")
verified, preview = verify_write(row, prefix[:20])
if verified:
print(f"✓ Row {row}: Written and verified - {preview}...")
success += 1
else:
print(f"⚠ Row {row}: Write reported OK but verification failed: {preview}")
failed.append(row)
else:
print(f"✗ Row {row}: Write failed")
failed.append(row)
except Exception as e:
print(f"✗ Row {row}: Error - {e}")
failed.append(row)
print(f"\n=== Summary ===")
print(f"Success: {success}/{len(CONFIGS)}")
if failed:
print(f"Failed rows: {failed}")
return 0 if not failed else 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,238 +0,0 @@
#!/usr/bin/env python3
"""Produce dialogue component configs and write back to F column."""
import subprocess, json
TOKEN = "t-g1045kg0ZYXHLVSY7EFUPUJUOLBGTOLYLPDKWLYN"
SHEET_ID = "wMQVyV"
SPREADSHEET_TOKEN = "It4AsFkC7hXHc4te9yUcyLHRnOe"
components = {
# Row 111: 对话选择-配图 (1217408)
111: """【任务标题】
打开坏掉的门
资源配置
图片时机
互动内容
互动反馈
情境引入
互动内容
要求选择正确的回复
选项音频
选项1The door is open.正确
- 反馈:
选项2The door is closed.
- 反馈 Alex: No, look. This door is broken. It's always open.
后置对话
""",
# Row 120: 对话选择-配图 (1217409)
120: """【任务标题】
查看关闭的门
资源配置
图片时机
互动内容
互动反馈
情境引入
互动内容
要求选择正确的回复
选项音频
选项1Look, the door is closed.正确
- 反馈:
选项2Look, the door is open.
- 反馈 Eva: No, this door is closed. Look again.
后置对话
""",
# Row 129: 对话挖空-配图 (1217410)
129: """【任务标题】
还剩最后一扇门
资源配置
图片时机
互动内容
互动反馈
情境引入
互动内容
Only one ___ left.音频
选项1door正确
选项2room
互动反馈
正确 Vicky : Yes! Just one more room.
错误 Vicky : Think about it. What do we open?
后置对话
""",
# Row 133: 对话组句-配图 (1217411)
133: """【任务标题】
发现门是关着的
资源配置
图片时机
互动内容
互动反馈
情境引入
互动内容
题目描述这扇门的状态
音频
选项1The
选项2door
选项3is
选项4closed
答案The door is closed.
辅助信息用主语 The door + is + 状态词描述门的状态
互动反馈
正确 Vicky : Yes, it's closed. Peter must be inside!
错误 Vicky : That doesn't look right. Think about the word order.
后置对话
""",
# Row 144: 对话组句-配图 (1217412)
144: """【任务标题】
表示现在门打开了
资源配置
图片时机
互动内容
互动反馈
情境引入
互动内容
题目描述现在门的状态
音频
选项1The
选项2door
选项3is
选项4open
选项5Now
答案Now the door is open.
辅助信息用主语 The door + is + 状态词描述门的状态Now 放在句首表示时间
互动反馈
正确 Alex : Yes! The door is open now.
错误 Alex : Hmm, try putting the words in the right order.
后置对话
""",
# Row 147: 看图拼词 (1217413)
147: """【任务标题】
拼写 door 的英文
资源配置
图片文件名1217413.png
互动内容
任务描述请拼出门的英文单词
题干d(2)r
答案oo
选项o, a, e, r, n, p
辅助信息门的英文是 door / 中间有两个一样的字母哦
后置对话
""",
# Row 163: 对话挖空 (1217414)
163: """【任务标题】
回忆窗户的状态
资源配置
情境引入
互动内容
I remember that. It was ___ before.音频
选项1closed正确
选项2open
互动反馈
正确 Alex : Yes, it was closed. But now it's different.
错误 Alex : No. Think about what I just said about the window.
后置对话
""",
# Row 164: 对话挖空 (1217415)
164: """【任务标题】
发现窗户现在是开着的
资源配置
情境引入
互动内容
But now it's ___.(音频)
选项1open正确
选项2closed
互动反馈
正确 Alex : Right! Someone opened it!
错误 Alex : Look at the window again. Is it really closed now?
后置对话
""",
# Row 179: 对话朗读 (1217416)
179: """【任务标题】
问候 Grace Dan
资源配置图片时机
情境引入
互动内容
User : I'm glad you get home!(朗读)
后置对话
""",
}
results = {}
for row_num, content in sorted(components.items()):
body = {
"valueRange": {
"range": f"{SHEET_ID}!F{row_num}:F{row_num}",
"values": [[content]]
}
}
cmd = [
"curl", "-s", "-X", "PUT",
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values",
"-H", f"Authorization: Bearer {TOKEN}",
"-H", "Content-Type: application/json",
"-d", json.dumps(body, ensure_ascii=False)
]
result = subprocess.run(cmd, capture_output=True, text=True)
resp = json.loads(result.stdout)
code = resp.get("code", -1)
msg = resp.get("msg", "unknown")
print(f"Row {row_num}: code={code}, msg={msg}")
results[row_num] = {"code": code, "msg": msg}
# Summary
success = sum(1 for r in results.values() if r["code"] == 0)
print(f"\nTotal: {len(results)} writes, {success} succeeded, {len(results) - success} failed")

View File

@ -1,249 +0,0 @@
#!/usr/bin/env python3
"""Generate and write F列 component configurations for L1-S2-U17-L4 门关啦"""
import subprocess, json, sys
# ============================================================
# Get Bot token
# ============================================================
APP_SECRET = json.load(open('/root/.openclaw/credentials/xiaoyan/config.json'))['apps'][0]['appSecret']
r = subprocess.run(['curl', '-s', '-X', 'POST',
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
'-H', 'Content-Type: application/json',
'-d', json.dumps({"app_id": "cli_a931175d41799cc7", "app_secret": APP_SECRET})],
capture_output=True, text=True)
TOKEN = json.loads(r.stdout)['tenant_access_token']
SST = 'It4AsFkC7hXHc4te9yUcyLHRnOe'
SID = 'wMQVyV'
# ============================================================
# All 16 component configurations → (row, config_text)
# ============================================================
configs = [
# 1. Row10: 对话朗读 1217401 — S1主线 "准备回家"
(10, """【任务标题】准备回家
资源配置
情境引入Alex : Okay. Let's get home.
互动内容
朗读台词
User : I need to get home too.
后置对话"""),
# 2. Row15: 对话朗读 1217402 — S2主线 "回不了家了"
(15, """【任务标题】回不了家了
资源配置
情境引入大门被风吹关上了Alex 的钥匙和 User 的包都在屋里
互动内容
朗读台词
User : I can't get home now!
后置对话Vicky : Now we are all stuck here."""),
# 3. Row36: 对话朗读 1217403 — S4主线 "查看猫门"
(36, """【任务标题】查看猫门
资源配置
情境引入Vicky : Is it open now?
互动内容
朗读台词
User : Yes. The cat door is open.
后置对话"""),
# 4. Row48: 对话挖空 1217404 — S5主线 "拜托Peter"
(48, """【任务标题】拜托Peter
资源配置
情境引入Vicky : Peter, how about you?
互动内容
听力挖空
User : Please, Peter. We want to ___ ___.
答案词get home
互动反馈
get home 正确 Vicky : Peter, you're our only hope!
get home 错误 Vicky : Try again. What do we want to do?
后置对话Vicky : Peter, you're my best little brother."""),
# 5. Row58: 对话挖空-配图 1217405 — S6主线 "称赞Peter"
(58, """【任务标题】称赞Peter
资源配置配图1217405_img
情境引入Peter 从猫门钻进了屋子
互动内容
听力挖空
User : Great! You're ___ now.
答案词home
互动反馈
home 正确 Alex : Yes! Peter is inside the house.
home 错误 Alex : Listen again. Where is Peter?
后置对话"""),
# 6. Row59: 图片单选-配图 1217406 — S7主线 "让Peter开门"
(59, """【任务标题】让Peter开门
资源配置配图1217406_optA.png1217406_optB.png1217406_optC.png
情境引入Peter 已经进了屋但需要他帮忙开门
互动内容
题目What did User ask Peter to do?
选项AOpen the door正确
- 反馈 Vicky : Yes! We asked Peter to open the door.
选项BClose the door
- 反馈 Vicky : No. We need to get in, not stay out.
选项CFind the keys
- 反馈 Vicky : Hmm, the keys are inside. What did we ask Peter?
后置对话Peter : Hmm... Who are you?"""),
# 7. Row76: 对话朗读-配图 1217407 — S8主线 "门开了"
(76, """【任务标题】门开了
资源配置配图1217407_img
情境引入Vicky 用捉迷藏游戏引诱 Peter 偷看门果然开了
互动内容
朗读台词并显示配图
User : Look! The door is open!
后置对话"""),
# 8. Row111: 对话选择-配图 1217408 — S10主线 "坏掉的门"
(111, """【任务标题】坏掉的门
资源配置配图1217408_optA.png1217408_optB.png
情境引入Alex : Look. This door is broken. It's always open.
互动内容
题目What is wrong with this door?
选项AThe door is open.正确
- 反馈 Alex : Right. The door is broken, so it's always open.
选项BThe door is closed.
- 反馈 Alex : No. Look again at the door.
后置对话User : Let me see who's hiding inside."""),
# 9. Row120: 对话选择-配图 1217409 — S11主线 "关闭的房门"
(120, """【任务标题】关闭的房门
资源配置配图1217409_optA.png1217409_optB.png
情境引入Eva : This is Vicky's and my room.
互动内容
题目How is the door?
选项AThe door is open.
- 反馈 Eva : No. Look again. Is the door open?
选项BThe door is closed.正确
- 反馈 Eva : Yes! The door is closed. Someone must be inside.
后置对话User : Someone must be hiding in there."""),
# 10. Row129: 对话挖空-配图 1217410 — S13主线 "还剩一扇门"
(129, """【任务标题】还剩一扇门
资源配置配图1217410_img
情境引入Vicky : Good job! Now, let's find Peter.
互动内容
听力挖空
User : Only one ___ left.
答案词door
互动反馈
door 正确 Vicky : Right! Only one door left. Peter must be there!
door 错误 Vicky : Let's think. What is left to check?
后置对话User : Peter must be in there!"""),
# 11. Row133: 对话组句-配图 1217411 — S14主线 "发现门关着"
(133, """【任务标题】发现门关着
资源配置配图1217411_img
情境引入终于找到了最后一扇门但门是关着的
互动内容
组句
拼出句子The door is closed.
互动反馈
正确 Vicky : Yes! The door is closed. Peter is hiding inside!
错误 Vicky : Let's try again. Is the door open or closed?
后置对话"""),
# 12. Row144: 对话组句-配图 1217412 — S15主线 "门打开了"
(144, """【任务标题】门打开了
资源配置配图1217412_img
情境引入Alex User 一起用力推开了门找到了 Peter
互动内容
组句
拼出句子The door is open.
互动反馈
正确 Peter : Haha! You got me!
错误 Peter : Try again! How is the door now?
后置对话"""),
# 13. Row153: 看图拼词 1217413 — S16主线 "修理指南"
(153, """【任务标题】修理指南
资源配置配图1217413_img
情境引入Alex : Look, I have a book about doors.
互动内容
看图拼词
图中是一个关闭的门拼出对应的单词closed
答案词closed
互动反馈
正确 Alex : Correct! Closed doors don't open easily.
错误 Alex : This word means "not open". Try again.
后置对话Alex : Let's fix it."""),
# 14. Row169: 对话挖空 1217414 — S17主线 "回忆窗户状态"
(169, """【任务标题】回忆窗户状态
资源配置
情境引入Alex : That window was closed before the game.
互动内容
听力挖空
User : I remember that. The door was ___ before.
答案词closed
互动反馈
closed 正确 Alex : Yes! It was closed before.
closed 错误 Alex : Think back. Was the door open or closed?
后置对话"""),
# 15. Row170: 对话挖空 1217415 — S18主线 "窗户开着"
(170, """【任务标题】窗户开着
资源配置
情境引入门之前是关着的但现在...
互动内容
听力挖空
User : But now it's ___.
答案词open
互动反馈
open 正确 Alex : Yes! Now it's open. Very strange!
open 错误 Alex : Look now. The door is not closed anymore.
后置对话"""),
# 16. Row189: 对话朗读 1217416 — S19主线 "问候Grace和Dan"
(189, """【任务标题】问候Grace和Dan
资源配置
情境引入Wood 先生和太太终于回家了
互动内容
朗读台词
User : I'm glad you get home!
后置对话Grace : Thank you for staying with the kids!"""),
]
# ============================================================
# Write each config to F column using single-cell write
# ============================================================
success = 0
fail = 0
for row, text in configs:
cell = f"{SID}!F{row}:F{row}"
body = json.dumps({"valueRange": {"range": cell, "values": [[text]]}}, ensure_ascii=False)
r = subprocess.run(['curl', '-s', '-X', 'PUT',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SST}/values',
'-H', f'Authorization: Bearer {TOKEN}',
'-H', 'Content-Type: application/json; charset=utf-8',
'-d', body], capture_output=True, text=True)
resp = json.loads(r.stdout)
if resp.get('code') == 0:
success += 1
print(f' ✅ Row {row:3d} | {text.split(chr(10))[0][5:]}')
else:
fail += 1
print(f' ❌ Row {row:3d} | {resp.get("msg", "unknown")}')
print(f'\n写入完成:{success} 成功 / {fail} 失败')
# ============================================================
# Verify: read back F column for all configured rows
# ============================================================
if fail == 0:
print('\n=== 回读验证 ===')
for row, _ in configs:
cell = f"{SID}!F{row}:F{row}"
r = subprocess.run(['curl', '-s', '-X', 'GET',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SST}/values/{cell}',
'-H', f'Authorization: Bearer {TOKEN}'], capture_output=True, text=True)
resp = json.loads(r.stdout)
vals = resp.get('data', {}).get('valueRange', {}).get('values', [[]])
content = vals[0][0] if vals and vals[0] else None
if content:
title = content.split('\n')[0].replace('【任务标题】', '') if content else 'EMPTY'
print(f' ✅ Row{row}: {title}')
else:
print(f' ❌ Row{row}: EMPTY!')

View File

@ -1,369 +0,0 @@
#!/usr/bin/env python3
"""Generate G列 component configs for L1-S2-U17-L5 笨蛋坏蛋? (English)"""
import subprocess, json
APP_SECRET = json.load(open('/root/.openclaw/credentials/xiaoyan/config.json'))['apps'][0]['appSecret']
r = subprocess.run(['curl', '-s', '-X', 'POST',
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
'-H', 'Content-Type: application/json',
'-d', json.dumps({"app_id": "cli_a931175d41799cc7", "app_secret": APP_SECRET})],
capture_output=True, text=True)
TOKEN = json.loads(r.stdout)['tenant_access_token']
SST = 'DCcKsLbrmhfXgrtB7N2c9GA4ntf'
SID = 'wMQVyV'
# (row, config)
configs = [
# Row14: 互动 — point — 机器人指着黑板
(14, """【任务标题】What does it do?
资源配置
情境引入Ben : See? I point at it.
互动内容
听力挖空 / 看图回答
User : It ___ at the blackboard.
答案词points
KPpoint v. 指向
互动反馈
points 正确 Ben : Yes. It points at the blackboard.
points 错误 Ben : Look again. What does it do?
后置对话Ben : It talks a lot."""),
# Row28: 互动 — points — 补充描述机器人行为
(28, """【任务标题】Talk about the robot
资源配置
情境引入Robot : Bip bop! Bip bop bip!
互动内容
朗读台词
User : It points at the blackboard.
KPpoint v. 指向
互动反馈
正确 Ben : That's right. And what else?
错误 Ben : Look at the robot. What is it pointing at?
后置对话Ben : And it talks to itself!"""),
# Row29: 互动 — talks to — 继续描述机器人
(29, """【任务标题】What else does it do?
资源配置
情境引入Robot : Bip bop! Bip bop bip!
互动内容
朗读台词
User : It talks to itself...
KPtalk to... ...说话
互动反馈
正确 Ben : It talks to itself!
错误 Ben : Listen. Is the robot talking to someone?
后置对话Ben : ...in a language we can't understand!"""),
# Row36: 互动 — a lot of — 课桌挡路
(36, """【任务标题】What is blocking it?
资源配置
情境引入Ben : What is it doing now?
互动内容
听力挖空
User : But ___ ___ ___ our desks block it.
答案词a lot of
KPa lot of det. 大量的
互动反馈
a lot of 正确 Ben : Yes! The desks are in its way.
a lot of 错误 Ben : Many desks are blocking it.
后置对话场景机器人推开桌椅"""),
# Row54: 互动 — a lot of — 需要证据
(54, """【任务标题】What do we need?
资源配置
情境引入Ben : Fine. What should we do now?
互动内容
听力挖空 / 朗读
User : We need ___ ___ ___ proof!
答案词a lot of
KPa lot of det. 大量的
互动反馈
正确 Ben : Okay. We'll find more.
错误 Ben : We need more than just a guess.
后置对话"""),
# Row55: 互动 — talk to — 建议和机器人谈谈
(55, """【任务标题】What should we do?
资源配置
情境引入User : We need a lot of proof!
互动内容
朗读台词
User : Let's talk to it!
KPtalk to... ...说话
互动反馈
正确 Ben : Talk to it? But it's a robot!
错误 Ben : How else can we find out?
后置对话Ben 从藏身处跳出来"""),
# Row61: 互动 — can't understand — 不懂机器人语言
(61, """【任务标题】Can you understand it?
资源配置
情境引入Robot : Bip bop!
互动内容
朗读台词
User : We can't understand it.
KPI can/can't understand...
互动反馈
正确 Ben : Right. Let's try asking with pictures.
错误 Ben : Can we understand robot language?
后置对话Ben : What's your name?"""),
# Row67: 互动 — understand — 理解了11的名字
(67, """【任务标题】What do you think?
资源配置
情境引入Ben : What's your name?
Robot points at itself it shows "11"
互动内容
朗读台词
User : Oh, I understand!
KPunderstand v. 理解
互动反馈
正确 Ben : You understand? Tell me!
错误 Ben : Do you know what it means?
后置对话User points out the number 11"""),
# Row68: 互动 — points to — 指出名字
(68, """【任务标题】What is it showing?
资源配置
情境引入User : Oh, I understand!
互动内容
朗读台词
User : It points to its name.
KPpoint v. 指向
互动反馈
正确 Ben : Its name is 11!
错误 Ben : What is the robot showing us?
后置对话User : Its name is 11!"""),
# Row80: 互动 — talk — 建议慢慢说
(80, """【任务标题】How should we talk?
资源配置
情境引入Ben is asking too many questions at once.
互动内容
朗读台词
User : Just talk slowly.
KPtalk v. 说话
互动反馈
正确 Ben : Okay. One question at a time.
错误 Ben : Should I ask slower?
后置对话Ben : Alright. Where are you from?"""),
# Row85: 互动 — talk to — 尝试用画沟通
(85, """【任务标题】What is it doing?
资源配置
情境引入Robot starts drawing on the blackboard.
互动内容
朗读台词
User : Oh! It tries to talk to us with drawing.
KPtalk to... ...说话
互动反馈
正确 Ben : A picture! What is it?
错误 Ben : What is it drawing? Is it a message?
后置对话Ben : That is the Speedy Monkey logo!"""),
# Row88: 互动 — understand — 理解来源
(88, """【任务标题】Where is it from?
资源配置
情境引入Robot draws the Speedy Monkey logo.
互动内容
朗读台词
User : Oh! I understand! It is from Speedy Monkey!
KPunderstand v. 理解
互动反馈
正确 Ben : Speedy Monkey! I knew it!
错误 Ben : Who is that logo from?
后置对话Ben : Speedy monkey sent a spy here!"""),
# Row94: 互动 — talk — 要求继续
(94, """【任务标题】What next?
资源配置
情境引入Ben thinks Speedy Monkey people are bad.
互动内容
朗读台词
User : Talk more to it!
KPtalk v. 说话
互动反馈
正确 Ben : Fine. What do you want?
错误 Ben : Should we ask more questions?
后置对话Ben : Okay, what do you want?"""),
# Row98: 互动 — points at — 指另一个标记
(98, """【任务标题】What is it showing now?
资源配置
情境引入Robot draws another mark on the blackboard.
互动内容
朗读台词
User : It points at another mark.
KPpoint v. 指向
互动反馈
正确 Ben : What is that mark?
错误 Ben : Look at what it is pointing at.
后置对话Ben : Is that... a school?"""),
# Row104: 互动 — points at — 又指一个标记
(104, """【任务标题】What is it now?
资源配置
情境引入Ben : Why are you here?
Robot draws another mark.
互动内容
朗读台词
User : It points at another mark.
KPpoint v. 指向
互动反馈
正确 Ben : A gift mark!
错误 Ben : What does the drawing show?
后置对话Ben : It is a gift!"""),
# Row109: 互动 — understand — 我终于懂了
(109, """【任务标题】What do you think?
资源配置
情境引入Robot drew Speedy Monkey, school, and gift marks.
互动内容
朗读台词
User : Ben! I think I understand!
KPunderstand v. 理解
互动反馈
正确 Ben : What do you understand?
错误 Ben : Think about the pictures again.
后置对话User explains the spy theory"""),
# Row110: 互动 — talk — 需要谈话
(110, """【任务标题】What should we do?
资源配置
情境引入User thinks they've found a spy.
互动内容
朗读台词
User : We need to talk! You and me!
KPtalk v. 说话
互动反馈
正确 Ben : Okay! Let's figure this out.
错误 Ben : We need to discuss what we found!
后置对话Ben : Okay!"""),
# Row116: 互动 — a lot of — 很多信息
(116, """【任务标题】What did it tell us?
资源配置
情境引入User pieces together the clues.
互动内容
朗读台词 / 听力挖空
User : It tells ___ ___ ___ things!
答案词a lot of
KPa lot of det. 大量的
互动反馈
正确 Ben : Yes. Speedy Monkey, school, gift...
错误 Ben : Remember all the drawings?
后置对话User : Vala School is a gift to Speedy Monkey!"""),
# Row123: 互动 — a lot of — 很多信息
(123, """【任务标题】What should we do?
资源配置
情境引入Ben : What should we do now?
互动内容
朗读台词
User : It is a lot of information.
KPa lot of det. 大量的
互动反馈
正确 Ben : Yes. We have enough to tell the mayor!
错误 Ben : Is it enough to act on?
后置对话"""),
# Row124: 互动 — talk about — 给镇长打电话
(124, """【任务标题】Who should we tell?
资源配置
情境引入Ben agrees they have enough proof.
互动内容
朗读台词
User : We need to call Mayor Tom and talk about it!
KPtalk to/talk about...
互动反馈
正确 Ben : Good idea. Let's call him now!
错误 Ben : Who should we call for help?
后置对话Calling Mayor Tom..."""),
# Row131: 互动 — point at — 指给镇长看
(131, """【任务标题】Show the mayor
资源配置
情境引入Tom arrives. Ben explains.
互动内容
朗读台词
User : Look! I point at it.
KPpoint v. 指向
互动反馈
正确 Tom : The robot?
错误 Tom : What are you pointing at?
后置对话Tom : Oh? A spy? In the classroom?"""),
# Row137: 互动 — a lot of — 描述异常行为
(137, """【任务标题】What did it do?
资源配置
情境引入Ben lists the spy evidence.
互动内容
听力挖空 / 朗读
User : And it said ___ ___ ___ strange things!
答案词a lot of
KPa lot of det. 大量的
互动反馈
正确 Tom : Hmm, I see.
错误 Tom : Did it say many strange things?
后置对话User shows the blackboard evidence"""),
# Row148: 互动 — don't understand — 感到困惑
(148, """【任务标题】What is the truth?
资源配置
情境引入Tom says robot doesn't want to give school to Speedy Monkey.
互动内容
朗读台词
User : Wait. I don't understand.
KPI can/can't understand...
互动反馈
正确 Lin : Why not let it tell you itself?
错误 Lin : Let me help you understand.
后置对话Robot explains itself"""),
# Row163: 核心互动-口语 — talk to — 道歉
(163, """【任务标题】Apologise to Jay and Lin
资源配置
情境引入Tom asks the kids if they have something to say.
互动内容
口语表达
User : I should talk to you.
KPtalk v. 说话
互动反馈
正确 Jay : It's okay.
错误 Jay : Go on. Say what you need to say.
后置对话User and Ben apologise"""),
]
# Write and verify
success = fail = 0
for row, text in configs:
cell = f"{SID}!G{row}:G{row}"
body = json.dumps({"valueRange": {"range": cell, "values": [[text]]}}, ensure_ascii=False)
r = subprocess.run(['curl', '-s', '-X', 'PUT',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SST}/values',
'-H', f'Authorization: Bearer {TOKEN}',
'-H', 'Content-Type: application/json; charset=utf-8',
'-d', body], capture_output=True, text=True)
resp = json.loads(r.stdout)
if resp.get('code') == 0:
success += 1
title = text.split('\n')[0].replace('【任务标题】', '')
print(f' ✅ Row {row:3d} | {title}')
else:
fail += 1
print(f' ❌ Row {row:3d} | {resp.get("msg","?")}')
print(f'\n写入:{success}成功 / {fail}失败')
if fail == 0:
print('\n=== 回读验证 ===')
for row, _ in configs:
cell = f"{SID}!G{row}:G{row}"
r = subprocess.run(['curl', '-s', '-X', 'GET',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SST}/values/{cell}',
'-H', f'Authorization: Bearer {TOKEN}'], capture_output=True, text=True)
resp = json.loads(r.stdout)
vals = resp.get('data',{}).get('valueRange',{}).get('values',[[]])
content = vals[0][0] if vals and vals[0] else None
title = content.split('\n')[0].replace('【任务标题】', '') if content else 'EMPTY'
print(f' ✅ Row{row}: {title}')

View File

@ -1,215 +0,0 @@
#!/usr/bin/env python3
"""Generate G列 component configs for L1-S2-U17-L5 笨蛋坏蛋?(v2 — updated row numbers)"""
import subprocess, json
APP_SECRET = json.load(open('/root/.openclaw/credentials/xiaoyan/config.json'))['apps'][0]['appSecret']
r = subprocess.run(['curl', '-s', '-X', 'POST',
'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
'-H', 'Content-Type: application/json',
'-d', json.dumps({"app_id": "cli_a931175d41799cc7", "app_secret": APP_SECRET})],
capture_output=True, text=True)
TOKEN = json.loads(r.stdout)['tenant_access_token']
SST = 'DCcKsLbrmhfXgrtB7N2c9GA4ntf'
SID = 'wMQVyV'
# ===== 对话朗读 (6) =====
# ===== 对话挖空 (3) =====
# ===== 互动 (7) =====
# ===== 核心互动-口语 (1) =====
configs = [
# --- 对话朗读 ---
(14, """【任务标题】机器人在指向什么?
资源配置图片时机
情境引入Ben : It points at something!
互动内容It points at the blackboard.朗读
后置对话Ben : That's right. And it also talks a lot!"""),
(29, """【任务标题】机器人在自言自语吗?
资源配置图片时机
情境引入Robot : Bip bop! Bip bop bip!
互动内容Hmm, it talks to itself.朗读
后置对话Ben : ...in a language we can't understand!"""),
(42, """【任务标题】机器人为什么走不动?
资源配置图片时机
情境引入Ben : What is it doing now?
互动内容But a lot of our desks block it.朗读
后置对话Ben : Yes! The desks are in its way."""),
(66, """【任务标题】我们该怎么办?
资源配置图片时机
情境引入Ben : Fine. You're right. What should we do now?
互动内容Let's talk to it!(朗读)
后置对话Ben : Talk to it? But it's a robot!"""),
(77, """【任务标题】你能听懂机器人吗?
资源配置图片时机
情境引入Robot : Bip bop!
互动内容We can't understand it.(朗读)
后置对话Ben : What's your name?"""),
(89, """【任务标题】你明白了什么?
资源配置图片时机
情境引入Robot points at the number "11" on its chest.
互动内容Oh, I understand!朗读
后置对话Ben : You understand? Tell me!"""),
# --- 对话挖空 ---
(96, """【任务标题】指出机器人的名字
资源配置
情境引入Ben : Wow, it is pointing at itself!
互动内容It ___ to its name.音频
选项1points正确
选项2talks
互动反馈
正确 Ben : Yes! It points to its name. Its name is 11!
错误 Ben : What is the robot doing? Think again.
后置对话User : Its name is 11! We call it 11!"""),
(119, """【任务标题】机器人想做什么?
资源配置
情境引入Robot draws on the blackboard.
互动内容Oh! It tries to ___ to us.音频
选项1talk正确
选项2point
互动反馈
正确 Ben : Yes! It wants to talk with pictures!
错误 Ben : It is not pointing. What else?
后置对话Ben : That is the Speedy Monkey logo!"""),
(138, """【任务标题】机器人又指了什么?
资源配置
情境引入Robot draws a school mark on the blackboard.
互动内容It ___ at another mark.音频
选项1points正确
选项2talks
互动反馈
正确 Ben : Is that... a school?
错误 Ben : Look at what the robot is doing with the mark.
后置对话Ben : Is that a school?"""),
# --- 互动 ---
(150, """【任务标题】又是什么标记?
资源配置
情境引入Robot draws a gift mark on the blackboard.
互动内容It points at another mark.
KPpoint v. 指向
互动反馈
正确 Ben : It is a gift!
错误 Ben : Look at the mark. What is it?
后置对话Ben : Speedy Monkey? School? Gift?"""),
(161, """【任务标题】你发现了什么?
资源配置
情境引入Ben : Speedy Monkey? School? Gift?
互动内容Ben! I think I understand!
KPunderstand v. 理解
互动反馈
正确 Ben : What do you understand?
错误 Ben : Look at the marks again.
后置对话User explains the spy theory"""),
(168, """【任务标题】和 Ben 单独商量
资源配置
情境引入User thinks the robot is a spy.
互动内容We need to talk! You and me!
KPtalk v. 说话
互动反馈
正确 Ben : Okay! Let's figure this out.
错误 Ben : We need to talk about what we found!
后置对话User : Vala School is a gift to Speedy Monkey!"""),
(181, """【任务标题】快告诉镇长!
资源配置
情境引入Ben : What should we do now?
互动内容We need to call Mayor Tom! Let's talk about it!
KPtalk about... 谈论...
互动反馈
正确 Ben : Good idea. Let's call him now!
错误 Ben : Who should we call for help?
后置对话Calling Mayor Tom..."""),
(194, """【任务标题】指给镇长看
资源配置
情境引入Tom : So you kids want to talk to me?
互动内容Look! I point at it.
KPpoint v. 指向
互动反馈
正确 Tom : The robot?
错误 Tom : What are you pointing at?
后置对话Tom : Oh? A spy? In the classroom?"""),
(207, """【任务标题】给镇长看证据
资源配置
情境引入Tom : I don't understand. Anything else?
互动内容Look! Here are a lot of strange marks!
KPa lot of det. 大量的
互动反馈
正确 Tom : Hmm, I see.
错误 Tom : Are there many strange marks?
后置对话User shows the blackboard to Tom"""),
(224, """【任务标题】到底怎么回事?
资源配置
情境引入Tom : The school isn't a gift to Speedy Monkey!
互动内容Wait. I don't understand.
KPI can/can't understand...
互动反馈
正确 Lin : Why not let it tell you itself?
错误 Lin : Let me help you understand.
后置对话Lin fixes the robot"""),
# --- 核心互动-口语 ---
(245, """【任务标题】向 Jay 和 Lin 道歉
资源配置
情境引入Tom : Kids, do you have something to say?
互动内容
口语表达
I should talk to you.
I should understand you.
I can understand you now.
KPtalk / understand
互动反馈
正确 Jay : Thank you. We feel much better now.
错误 Lin : Go on. Say what's in your heart.
后置对话Jay : I understand. Lin : We all make mistakes."""),
]
# Write
success = fail = 0
for row, text in configs:
cell = f"{SID}!G{row}:G{row}"
body = json.dumps({"valueRange": {"range": cell, "values": [[text]]}}, ensure_ascii=False)
r = subprocess.run(['curl', '-s', '-X', 'PUT',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SST}/values',
'-H', f'Authorization: Bearer {TOKEN}',
'-H', 'Content-Type: application/json; charset=utf-8',
'-d', body], capture_output=True, text=True, timeout=15)
resp = json.loads(r.stdout)
if resp.get('code') == 0:
success += 1
title = text.split('\n')[0].replace('【任务标题】', '')
print(f' ✅ Row {row:3d} | {title}')
else:
fail += 1
print(f' ❌ Row {row:3d} | {resp.get("msg","?")}')
print(f' debug: {r.stdout[:200]}')
print(f'\n写入结果:{success}成功 / {fail}失败')
if fail == 0:
print('\n=== 回读验证 ===')
for row, _ in configs:
cell = f"{SID}!G{row}:G{row}"
r = subprocess.run(['curl', '-s', '-X', 'GET',
f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SST}/values/{cell}?valueRenderOption=ToString',
'-H', f'Authorization: Bearer {TOKEN}'], capture_output=True, text=True, timeout=15)
resp = json.loads(r.stdout)
vals = resp.get('data',{}).get('valueRange',{}).get('values',[[]])
content = vals[0][0] if vals and vals[0] else None
title = content.split('\n')[0].replace('【任务标题】', '') if content else 'EMPTY'
status = '' if content else '❌ EMPTY'
print(f' {status} Row{row}: {title}')
else:
print('\n⚠️ 有失败项,请检查')

View File

@ -1,175 +0,0 @@
#!/usr/bin/env python3
"""
P4 rewrite with automation-safe approach:
- jsonData: use ability=['听觉抓取关键信息'], explanation='' (automation-compatible)
- 题目1/题目2: include full ability + explanation info in text
"""
import json, subprocess, sys, os, time
AT = "CMHSbUUjka3TrUsaxxEc297ongf"
TID = "tblVmeDtBDKsAEfz"
BASE = os.path.expanduser("~/.openclaw/workspace-xiaoyan")
CRED_FILE = os.path.expanduser("~/.openclaw/credentials/xiaoyan/config.json")
sys.path.insert(0, os.path.join(BASE, "scripts"))
import rewrite_p4_all as rw
RECORD_IDS = {
"021301": "recvjufKJO8d0O",
"021401": "recvjufM763ijb",
"021501": "recvjufM76lEsW",
"021601": "recvjufM76hNv5",
"021701": "recvjufM76eMKs",
"021801": "recvjufM76lRHQ",
"022101": "recvjufM76o6of",
"032501": "recvjufM76k4dx",
"032901": "recvjufM76frUP",
}
def get_token():
with open(CRED_FILE) as f:
cred = json.load(f)
app_id = cred["apps"][0]["appId"]
app_secret = cred["apps"][0]["appSecret"]
r = subprocess.run(["curl","-s","-X","POST",
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
"-H","Content-Type: application/json",
"-d",json.dumps({"app_id":app_id,"app_secret":app_secret})],
capture_output=True, text=True)
return json.loads(r.stdout)["tenant_access_token"]
def make_qtext_with_info(questions):
"""Generate 题目 text with dialogue, ability and explanation info"""
lines = []
for i, q in enumerate(questions, 1):
lines.append(f"{i}.")
lines.append(f"【描述】{q.get('questionDesc','')}")
lines.append(f"【听力文本】")
if 'dialogue' in q:
lines.append(q['dialogue'])
lines.append(f"【题目】")
lines.append(q['question'])
for j, opt in enumerate(q.get('options',[])):
marker = "(正确)" if j == q.get('answer',[-1])[0] else ""
lines.append(f"{chr(65+j)}. {opt}{marker}")
# Add ability info
ability = q.get('new_ability', q.get('ability', ['听力理解']))
lines.append(f"【能力标签】{''.join(ability) if isinstance(ability,list) else ability}")
# Add explanation
lines.append(f"【解析】{q.get('explanation','')}")
lines.append("")
return "\n".join(lines)
def sanitize_json(json_data):
"""Replace ability/explanation with automation-safe values"""
for qs in json_data.get("first", {}).get("questionSet", []):
qs["ability"] = ["听觉抓取关键信息"]
qs["explanation"] = ""
second = json_data.get("second", {})
if second and isinstance(second, dict) and "questionSet" in second:
for qs in second.get("questionSet", []):
qs["ability"] = ["听觉抓取关键信息"]
qs["explanation"] = ""
return json_data
def write_one(token, qsid, record_id, json_data, q1_text, q2_text):
"""Write one record and verify"""
json_str = json.dumps(json_data, ensure_ascii=False)
fields = {"jsonData": json_str, "题目1": q1_text, "题目2": q2_text}
payload = json.dumps({"fields": fields}, ensure_ascii=False)
tmp = f"/tmp/p4_safe_{record_id}.json"
with open(tmp, "w") as f:
f.write(payload)
r = subprocess.run(["curl","-s","-X","PUT",
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{AT}/tables/{TID}/records/{record_id}",
"-H",f"Authorization: Bearer {token}",
"-H","Content-Type: application/json",
"-d",f"@{tmp}"],
capture_output=True, text=True, timeout=30)
os.remove(tmp)
resp = json.loads(r.stdout)
if resp.get("code") != 0:
return False, f"API error: {resp.get('msg','')}"
# Verify
r = subprocess.run(["curl","-s","-X","GET",
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{AT}/tables/{TID}/records/{record_id}",
"-H",f"Authorization: Bearer {token}"],
capture_output=True, text=True, timeout=30)
vdata = json.loads(r.stdout)
if vdata.get("code") != 0:
return False, f"Verify error: {vdata.get('msg','')}"
rec = vdata.get("data",{}).get("record",{})
jd = rec.get("fields",{}).get("jsonData","")
q1 = rec.get("fields",{}).get("题目1","") or ""
if not jd:
return False, "jsonData empty"
try:
parsed = json.loads(jd)
fqs = parsed.get("first",{}).get("questionSet",[])
if not fqs:
return False, "no first questions"
q0_question = fqs[0].get("question","")
q0_ability = fqs[0].get("ability","")
return True, f"Qs={len(fqs)}, q0='{q0_question[:30]}', ability={q0_ability}, Q1_len={len(q1)}"
except Exception as e:
return False, f"parse: {e}"
def main():
token = get_token()
print(f"Token: {token[:15]}...")
results = []
for qsid, record_id in RECORD_IDS.items():
data = rw.RECORD_DATA.get(qsid)
if not data:
continue
fb = data["first_block"]
sb = data["second_block"]
fqs = data["first_qs_list"]
sqs = data["second_qs_list"]
# Build jsonData - keep original dialogue but not in final jsonData
json_data = {"first": fb, "second": sb}
# Build Q1/Q2 text with full info
q1_text = make_qtext_with_info(fqs)
q2_text = make_qtext_with_info(sqs) if sqs else ""
# Clean dialogues from jsonData and sanitize
for q in fb["questionSet"]:
q.pop("dialogue", None)
q.pop("new_ability", None)
if sb and isinstance(sb, dict) and "questionSet" in sb:
for q in sb["questionSet"]:
q.pop("dialogue", None)
q.pop("new_ability", None)
json_data = sanitize_json(json_data)
print(f"\nWriting {qsid} ({record_id})...")
ok, msg = write_one(token, qsid, record_id, json_data, q1_text, q2_text)
status = "" if ok else ""
print(f" {status} {msg}")
results.append((qsid, ok, msg))
time.sleep(0.5)
success = sum(1 for r in results if r[1])
fails = sum(1 for r in results if not r[1])
print(f"\n{'='*60}")
print(f"FINAL: {success} succeeded, {fails} failed")
if fails:
for qsid, ok, msg in results:
if not ok:
print(f"{qsid}: {msg}")
if __name__ == "__main__":
main()

View File

@ -1,192 +0,0 @@
#!/usr/bin/env python3
"""
Write all P4 rewrite records to bitable using direct API calls.
Avoids shell escaping issues.
"""
import json, subprocess, os, sys
APP_TOKEN = "CMHSbUUjka3TrUsaxxEc297ongf"
TABLE_ID = "tblVmeDtBDKsAEfz"
BASE_DIR = os.path.expanduser("~/.openclaw/workspace-xiaoyan")
CRED_FILE = os.path.expanduser("~/.openclaw/credentials/xiaoyan/config.json")
RECORD_IDS = {
"021301": "recvjufKJO8d0O",
"021401": "recvjufM763ijb",
"021501": "recvjufM76lEsW",
"021601": "recvjufM76hNv5",
"021701": "recvjufM76eMKs",
"021801": "recvjufM76lRHQ",
"022101": "recvjufM76o6of",
"032501": "recvjufM76k4dx",
"032901": "recvjufM76frUP",
}
def get_token():
with open(CRED_FILE) as f:
cred = json.load(f)
app_id = cred["apps"][0]["appId"]
app_secret = cred["apps"][0]["appSecret"]
result = subprocess.run([
"curl", "-s", "-X", "POST",
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
"-H", "Content-Type: application/json",
"-d", json.dumps({"app_id": app_id, "app_secret": app_secret})
], capture_output=True, text=True)
return json.loads(result.stdout)["tenant_access_token"]
def update_record(token, record_id, fields_dict):
"""Update a record via curl"""
# Write fields to temp file to avoid command-line escaping
tmp_file = f"/tmp/p4_update_{record_id}.json"
payload = json.dumps({"fields": fields_dict}, ensure_ascii=False)
with open(tmp_file, 'w') as f:
f.write(payload)
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}"
result = subprocess.run([
"curl", "-s", "-X", "PUT", url,
"-H", f"Authorization: Bearer {token}",
"-H", "Content-Type: application/json",
"-d", f"@{tmp_file}"
], capture_output=True, text=True, timeout=30)
os.remove(tmp_file)
return json.loads(result.stdout)
def verify_record(token, record_id):
"""Re-read a record to verify"""
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records/{record_id}"
result = subprocess.run([
"curl", "-s", "-X", "GET", url,
"-H", f"Authorization: Bearer {token}"
], capture_output=True, text=True, timeout=30)
return json.loads(result.stdout)
def main():
mode = sys.argv[1] if len(sys.argv) > 1 else "test"
# Get token
print("Getting token...")
token = get_token()
print(f"Token: {token[:20]}...")
# Import the rewrite data
sys.path.insert(0, os.path.join(BASE_DIR, "scripts"))
import rewrite_p4_all as rw
from rewrite_p4_all import make_q1_text
results = []
for qsid, record_id in RECORD_IDS.items():
data = rw.RECORD_DATA.get(qsid)
if not data:
continue
first_block = data["first_block"]
second_block = data["second_block"]
first_qs = data["first_qs_list"]
second_qs = data["second_qs_list"]
# Clean dialogues from jsonData
for q in first_block["questionSet"]:
q.pop("dialogue", None)
if second_block and isinstance(second_block, dict) and "questionSet" in second_block:
for q in second_block["questionSet"]:
q.pop("dialogue", None)
json_data = {"first": first_block, "second": second_block}
q1_text = make_q1_text(first_qs)
q2_text = make_q1_text(second_qs) if second_qs else ""
fields = {
"jsonData": json.dumps(json_data, ensure_ascii=False),
"题目1": q1_text,
"题目2": q2_text,
}
print(f"\n{'='*60}")
print(f"QSID: {qsid} | Record: {record_id}")
print(f"First: {len(first_qs)} Qs, Second: {len(second_qs)} Qs")
if mode == "test":
# Test one record only
if qsid == "021301":
result = update_record(token, record_id, fields)
code = result.get("code", -1)
msg = result.get("msg", "")
if code == 0:
print(f" ✅ Test write SUCCESS")
# Verify
v = verify_record(token, record_id)
vcode = v.get("code", -1)
if vcode == 0:
rec = v.get("data", {}).get("record", {})
jd = rec.get("fields", {}).get("jsonData", "")
if jd:
try:
parsed = json.loads(jd)
fq = parsed.get("first", {}).get("questionSet", [])
sq = parsed.get("second", {}).get("questionSet", [])
print(f" ✅ Verify: {len(fq)} first Qs, {len(sq)} second Qs")
print(f" ✅ jsonData valid JSON")
except Exception as e:
print(f" ❌ jsonData parse error: {e}")
else:
print(f" ❌ jsonData field empty")
else:
print(f" ❌ Verify FAILED: {v.get('msg', '')}")
else:
print(f" ❌ Test write FAILED: {msg}")
break
elif mode == "write":
result = update_record(token, record_id, fields)
code = result.get("code", -1)
msg = result.get("msg", "")
if code == 0:
print(f" ✅ Write SUCCESS")
results.append((qsid, True, ""))
else:
print(f" ❌ Write FAILED: {msg}")
results.append((qsid, False, msg))
if mode == "write":
success = sum(1 for r in results if r[1])
fails = sum(1 for r in results if not r[1])
print(f"\n{'='*60}")
print(f"WRITE SUMMARY: {success} succeeded, {fails} failed")
for qsid, ok, msg in results:
status = "" if ok else ""
print(f" {status} {qsid}" + (f" - {msg}" if msg else ""))
# Verify all
print(f"\n{'='*60}")
print("VERIFYING ALL RECORDS...")
for qsid, record_id in RECORD_IDS.items():
v = verify_record(token, record_id)
vcode = v.get("code", -1)
if vcode == 0:
rec = v.get("data", {}).get("record", {})
jd = rec.get("fields", {}).get("jsonData", "")
q1 = rec.get("fields", {}).get("题目1", "")
q2 = rec.get("fields", {}).get("题目2", "")
if jd:
try:
parsed = json.loads(jd)
fq = parsed.get("first", {}).get("questionSet", [])
sq = parsed.get("second", {}).get("questionSet", [])
fqsid = parsed.get("first", {}).get("questionSetID", "")
sqsid = parsed.get("second", {}).get("questionSetID", "") if isinstance(parsed.get("second", {}), dict) else ""
print(f"{qsid}: {len(fq)} first Qs, {len(sq)} second Qs, QSID={fqsid}, 题目1={len(q1)}chars, 题目2={len(q2)}chars")
except Exception as e:
print(f"{qsid}: jsonData parse error: {e}")
else:
print(f"{qsid}: jsonData empty")
else:
print(f"{qsid}: verify failed: {v.get('msg', '')}")
if __name__ == "__main__":
main()

View File

@ -1,140 +0,0 @@
#!/usr/bin/env python3
"""Batch write all P4 rewrites to bitable - proper version"""
import json, subprocess, sys, os, time
AT = "CMHSbUUjka3TrUsaxxEc297ongf"
TID = "tblVmeDtBDKsAEfz"
BASE = os.path.expanduser("~/.openclaw/workspace-xiaoyan")
CRED_FILE = os.path.expanduser("~/.openclaw/credentials/xiaoyan/config.json")
sys.path.insert(0, os.path.join(BASE, "scripts"))
import rewrite_p4_all as rw
from rewrite_p4_all import make_q1_text
RECORD_IDS = {
"021301": "recvjufKJO8d0O",
"021401": "recvjufM763ijb",
"021501": "recvjufM76lEsW",
"021601": "recvjufM76hNv5",
"021701": "recvjufM76eMKs",
"021801": "recvjufM76lRHQ",
"022101": "recvjufM76o6of",
"032501": "recvjufM76k4dx",
"032901": "recvjufM76frUP",
}
def get_token():
with open(CRED_FILE) as f:
cred = json.load(f)
app_id = cred["apps"][0]["appId"]
app_secret = cred["apps"][0]["appSecret"]
result = subprocess.run([
"curl", "-s", "-X", "POST",
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
"-H", "Content-Type: application/json",
"-d", json.dumps({"app_id": app_id, "app_secret": app_secret})
], capture_output=True, text=True)
return json.loads(result.stdout)["tenant_access_token"]
def write_and_verify(token, qsid, record_id, fields):
"""Write and immediately verify one record"""
payload = json.dumps({"fields": fields}, ensure_ascii=False)
tmp = f"/tmp/p4_write_{record_id}.json"
with open(tmp, "w") as f:
f.write(payload)
result = subprocess.run([
"curl", "-s", "-X", "PUT",
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{AT}/tables/{TID}/records/{record_id}",
"-H", f"Authorization: Bearer {token}",
"-H", "Content-Type: application/json",
"-d", f"@{tmp}"
], capture_output=True, text=True, timeout=30)
os.remove(tmp)
resp = json.loads(result.stdout)
code = resp.get("code", -1)
if code != 0:
return False, f"API error: {resp.get('msg','')}"
# Read back to verify
result = subprocess.run([
"curl", "-s", "-X", "GET",
f"https://open.feishu.cn/open-apis/bitable/v1/apps/{AT}/tables/{TID}/records/{record_id}",
"-H", f"Authorization: Bearer {token}"
], capture_output=True, text=True, timeout=30)
vdata = json.loads(result.stdout)
vcode = vdata.get("code", -1)
if vcode != 0:
return False, f"Verify read error: {vdata.get('msg','')}"
rjd = vdata.get("data", {}).get("record", {}).get("fields", {}).get("jsonData", "")
if not rjd:
return False, "jsonData empty after write"
try:
parsed = json.loads(rjd)
fqs = parsed.get("first", {}).get("questionSet", [])
first_ability = fqs[0].get("ability", []) if fqs else []
first_expl = fqs[0].get("explanation", "") if fqs else ""
return True, f"Q0 ability={first_ability}, expl_len={len(first_expl)}"
except Exception as e:
return False, f"jsonData parse error: {e}"
def main():
token = get_token()
print(f"Token: {token[:15]}...")
results = []
for qsid, record_id in RECORD_IDS.items():
data = rw.RECORD_DATA.get(qsid)
if not data:
continue
fb = data["first_block"]
sb = data["second_block"]
fqs = data["first_qs_list"]
sqs = data["second_qs_list"]
# Clean dialogues
for q in fb["questionSet"]:
q.pop("dialogue", None)
if sb and isinstance(sb, dict) and "questionSet" in sb:
for q in sb["questionSet"]:
q.pop("dialogue", None)
json_data = {"first": fb, "second": sb}
q1_text = make_q1_text(fqs)
q2_text = make_q1_text(sqs) if sqs else ""
fields = {
"jsonData": json.dumps(json_data, ensure_ascii=False),
"题目1": q1_text,
"题目2": q2_text,
}
print(f"\nWriting {qsid} ({record_id})...")
ok, msg = write_and_verify(token, qsid, record_id, fields)
status = "" if ok else ""
print(f" {status} {msg}")
results.append((qsid, ok, msg))
# Small delay to avoid rate limiting
time.sleep(0.3)
success = sum(1 for r in results if r[1])
fails = sum(1 for r in results if not r[1])
print(f"\n{'='*60}")
print(f"FINAL: {success} succeeded, {fails} failed")
if fails:
print("\nFailed records:")
for qsid, ok, msg in results:
if not ok:
print(f"{qsid}: {msg}")
if __name__ == "__main__":
main()

View File

@ -1,6 +1,6 @@
---
name: component-design
version: 1.3.0
version: 1.4.0
description: 教研互动组件设计规范结构化文本格式不涉及JSON。触发方式(1) `/组件生产 --feishu-url <链接>` (2) 飞书链接 + "组件"/"设计组件"/"组件回填"。不会误触发:仅含"互动"不触发;含"组件配置-json"不触发(属于 interactive-component-json 技能)。标准化组件生产流程:设计→生成(结构化文本)→校验→回填。
---
@ -53,14 +53,29 @@ description: 教研互动组件设计规范结构化文本格式不涉及J
- 所有组件【正确反馈】统一填「无」
- 【错误反馈】必须是符合当前剧情场景的自然角色对话,禁止使用指令式、说教式表达(如禁止出现"Try to pronounce clearly, one more time."这类老师命令式内容),必须带角色名
7. **后置对话规则**:组件行之后紧接的、与组件内容相关的角色台词需要放入【后置对话】字段中
8. **情境引入/后置对话去重规则**:禁止直接复制前一行或后一行的台词原文。若情境引入与前一行的英文台词完全相同,或后置对话与后一行的英文台词完全相同,直接写「无」
8. **情境引入/后置对话去重规则(强制执行,审校必查)**
- 🚫 禁止直接复制前一行或后一行的台词原文
- 🚫 禁止使用组件所在行之前任何行的台词作为情境引入
- 🚫 禁止使用组件所在行之后任何行的台词作为后置对话
- 若情境引入与前一行的英文台词完全相同,或后置对话与后一行的英文台词完全相同 → 直接写「无」
- **审校检查点**:生成后必须逐行对比组件行前后的 E 列/F 列原文,确认情境引入/后置对话未引用任何相邻行台词
9. **禁用内容规则**
- 完全删除所有【图片资源】行
- 音频载体无特殊要求时填「无」
- 组件禁止使用组件所在行之前的台词,【情境引入】和【后置对话】无内容时填「无」
- 组件禁止使用组件所在行之前或之后的台词,【情境引入】和【后置对话】无内容时填「无」
- **审校检查点**:情境引入内容必须来自组件自身场景设定,不得从剧本其他行摘取
10. **配图组件资源配置规则**A列含"配图"时,【资源配置】仅保留`图片时机`,不写`音频载体`。图片时机根据有实际内容(非「无」)的 section 动态列出(如「互动内容」「情境引入 互动内容 互动反馈」),所有 section 均无内容则填「无」。图片时机和音频载体禁止同时存在。
### 二、组件类型专属规则
> **详细规范索引:** 以下为各类型的快速参考。生产时请同时加载对应的专项 Skill
> - 对话朗读/表达/选读/选择/挖空/组句 → `dialogue-interaction-config`
> - 信息拼词/组句/描写/补词/填词/填句 → `info-interaction-config`
> - 物品互动 → `item-interaction-config`
> - 图片单选/多选/有序/拖拽/互动 → `picture-interaction-config`
> - 发音/语音/造句互动 → `pronunciation-interaction-config`
> - 核心互动14类`core-interaction-config`
#### 1. 物品互动
- 仅保留两个字段:【任务标题】+【互动内容】
- 互动内容固定格式:`“中文动作提示” the 名词! 选项1正确单词正确选项2干扰单词`
@ -251,6 +266,7 @@ description: 教研互动组件设计规范结构化文本格式不涉及J
---
## 版本更新记录
- v1.42026-06-11强化规则8/9情境引入和后置对话审校规则禁止引用组件行前后台词新增审校检查点逐行对比E/F列
- v1.32026-06-08图片类互动删除【资源配置】字段、新增辅助信息规则对话朗读删除【互动反馈】字段新增配图组件资源配置规则新增情境引入/后置对话去重规则(禁止复制前后行台词原文)
- v1.22026-04-09修正图片类互动高亮规则仅知识点单词加$,非知识点不加),新增对话选择、对话选读、对话表达三类组件的配置规则及标准模板
- v1.12026-04-09新增错误反馈、任务标题、对话组句、选项格式等规则修正不符合生产要求的旧规则

File diff suppressed because it is too large Load Diff

View File

@ -94,20 +94,43 @@ Piper: You're lying! It must be your mistake!
### 3. 对话选读
#### 核心结构
【任务标题】→【资源配置】→【情境引入】→【互动内容】→【后置对话】
#### 与对话选择的关键区分
| 对比项 | 对话选择 | 对话选读 |
|--------|---------|----------|
| 互动目的 | 有对错判断 | 开放表达,任意选均可 |
| 互动要求 | 选择正确的回复 | 选择一个你想表达的观点 |
| 正确选项 | 标注 `(正确)` | **无正确选项** |
| 选项反馈 | 正确选项反馈可缺省 | **每个选项均有反馈** |
#### 配置要点
- 【互动内容】:
1. 要求说明:如`要求:选择一个你想表达的观点`
1. 第一行固定为:`要求:选择一个你想表达的观点`
2. 选项按"选项1/2/3"排列,每个选项后对应对话式反馈
3. 无正确/错误区分,任意选择均可
4. 需要音频播放选项时标注`(音频)`
#### 示例
3. 无正确/错误区分,任意选择均可,**每个选项的反馈为必填项**
4. 需要音频播放选项时标注`(音频)`,默认不含音频标记
5. ⚠️ 反馈角色不可是 User 自己,必须是当前场景中的其他角色
- 【互动反馈】:本类型不单独设置互动反馈字段,反馈内容已整合在互动内容的各选项中
- 【资源配置】:
- 类型为「对话选读」时,填写:`无`
- 类型为「对话选读-配图」时,填写图片时机 + 音频载体(格式见下方示例)
- 资源命名规范:图片文件名 `{组件ID}.png`,音频文件名 `{组件ID}.mp3`
#### 示例(对话选读-配图)
```
【任务标题】加入搏斗俱乐部
【资源配置】无
【资源配置】
图片时机:
互动内容
互动反馈
音频载体Pioneer Band
【情境引入】
Johnny: What do you think of the club?
【互动内容】
要求:选择一个你想表达的观点(默认这句话)
要求:选择一个你想表达的观点
选项:(音频)
选项1That's great! I'm going to ask Otis and Skely to go together!
- 反馈 Johnny : I don't think it's OK to have so many people in.
@ -115,6 +138,25 @@ Johnny: What do you think of the club?
- 反馈 Johnny : You'd better not do this by yourself.
选项3It's no fun. I'd better not participate.
- 反馈 Johnny : Ok, it's a good decision.
【后置对话】无
```
#### 示例(对话选读-无配图)
```
【任务标题】说说现在的屋子吧
【资源配置】无
【情境引入】
User : Yes, today is very messy.
【互动内容】
要求:选择一个你想表达的观点
选项:
选项1Today is a good day. No one is at home.
- 反馈 User : But grandparents are coming to visit!
选项2Today is very messy. We need to clean up.
- 反馈 User : Yes, let's clean up together!
选项3Today is not so bad. We can have fun.
- 反馈 User : But look at the sofa and the floor!
【后置对话】无
```
@ -179,7 +221,7 @@ This is my picture. It's ___, green and orange.(音频)
#### 配置要点
- 【互动内容】:
1. 题目:中文描述组句要求
2. 选项:乱序单词/短语,不添加句末标点、不首字母大写,数量3-5
2. 选项:乱序单词/短语,不添加句末标点、不首字母大写,数量 3-4
3. 答案:完整正确句子
4. 辅助信息:完整句子,无则填"无"
5. 需要音频时标注`(音频)`

View File

@ -1,121 +0,0 @@
---
name: dialogue-selective-reading-config
version: 1.0.0
description: 对话选读类互动组件配置标准化规范,用于剧本中对话选读/对话选读-配图类型组件的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
# 对话选读组件配置规范
## 适用场景
当【类型】列内容为【对话选读】或【对话选读-配图】时,使用本规范生成组件配置
## 文本输出规范(强制执行)
所有输出的文本内容(台词、题目、选项、解析、音频文本、阅读文章等)必须遵守以下规则:
1. 禁止 Markdown 标记:禁止使用 ** * __ _ 等加粗/斜体标识,也禁止使用 # > - 等块级 Markdown 语法。所有文本纯文本输出。
2. 英式拼写优先:单词涉及英美式拼写差异时(如 colour/color、centre/center、travelling/traveling统一选择英式拼写。
3. 标点符号规范:严格区分全角/半角符号。中文内容使用全角标点(,。!?),英文内容使用半角标点(. , ! ?),不得混用。
## 核心规则 — 与对话选择的关键区分
| 对比项 | 对话选择 | 对话选读 |
|--------|---------|---------|
| 互动目的 | 有对错判断 | 开放表达,任意选均可 |
| 互动要求 | 选择正确的回复 | 选择一个你想表达的观点 |
| 正确选项 | 标注 `(正确)` | **无正确选项** |
| 选项反馈 | 正确选项反馈可缺省 | **每个选项均有反馈** |
## 配置格式
### 字段要求
1. 【任务标题】:
- 设计规则:结合剧情上下文,提炼互动核心内容,使用中文口语化表述,符合低龄用户认知
- 核心要素:包含互动对象+核心动作/状态避免笼统的「对话X」编号
- 约定:如需表达顺序关系,使用中文括号替代「-」,如「与调酒师对峙(一)」而非「与调酒师对峙-1」
- ⚠️ 标题含英文单词时,英文单词前后必须留空格
2. 【资源配置】:
- 类型为「对话选读」时,填写:无
- 类型为「对话选读-配图」时,填写:
```
图片时机:
互动内容
互动反馈
音频载体Pioneer Band
```
- 资源命名规范:
- 图片文件名:`{组件ID}.png`,如 `0216002.png`
- 音频文件名:`{组件ID}.mp3`,如 `0216002.mp3`
- 音频默认在互动内容、互动反馈期间出现;若配置音频,必须添加【音频载体】
- 图片默认在互动内容、互动反馈期间出现;如需调整节点,在【资源配置】处填写【情境引入】或【后置对话】
3. 【情境引入】:
- 以对话方式呈现,每行一句。格式为「角色名 : 对话内容」
- 若无情境引入,填「无」
4. 【互动内容】:
- 第一行:固定为「要求:选择一个你想表达的观点」
- 第二行:标注「选项:(音频)」(表示选项有音频支持;默认无音频,若需手动添加音频则填「选项:(音频)」)
- 第三部分列出所有选项编号为「选项1」「选项2」「选项3」... 依次排列
- 每个选项后以「- 反馈 角色名 : 对话内容」形式标注反馈
- **无正确/错误区分**,每个选项的反馈为必填项
- ⚠️ 反馈角色不可是 User 自己,必须是当前场景中的其他角色
- (音频)标记默认不含,若选项需要播放音频则手动填写「(音频)」,使用中文括号
5. 【互动反馈】:本类型不单独设置互动反馈字段,反馈内容已整合在互动内容的各选项中
6. 【后置对话】:
- 以对话方式呈现,每行一句。格式为「角色名 : 对话内容」
- 若无后置对话,填「无」
## 完整示例
### 对话选读-配图类型示例
```
【任务标题】
加入搏斗俱乐部
【资源配置】
图片时机:
互动内容
互动反馈
音频载体Pioneer Band
【情境引入】
Johnny: What do you think of the club?
【互动内容】
要求:选择一个你想表达的观点
选项:(音频)
选项1That's great! I'm going to ask Otis and Skely to go together!
- 反馈 Johnny : I don't think it's OK to have so many people in.
选项2Interesting! I can go on this trip alone.
- 反馈 Johnny : You'd better not do this by yourself.
选项3It's no fun. I'd better not participate.
- 反馈 Johnny : Ok, it's a good decision.
【后置对话】
```
### 对话选读类型示例
```
【任务标题】
说说现在的屋子吧
【资源配置】
【情境引入】
User : Yes, today is very messy.
【互动内容】
要求:选择一个你想表达的观点
选项:
选项1Today is a good day. No one is at home.
- 反馈 User : But grandparents are coming to visit!
选项2Today is very messy. We need to clean up.
- 反馈 User : Yes, let's clean up together!
选项3Today is not so bad. We can have fun.
- 反馈 User : But look at the sofa and the floor!
【后置对话】
```

View File

@ -0,0 +1,225 @@
---
name: info-interaction-config
version: 1.0.0
description: 信息类互动组件配置标准化规范,覆盖信息拼词/组句/描写/补词/填词/填句共6类的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
## 文本输出规范(强制执行)
1. 禁止 Markdown 标记
2. 英式拼写优先
3. 中文全角标点,英文半角标点,不混用
## 通用规则6类共享
- 【任务标题】中文口语化10字以内避免「-」,有序用「(一)」,含英文单词时前后留空格
- 【资源配置】:图片文件名 = `{组件ID}.png`,不含音频配置
- 【后置对话】:无则填「无」
---
## 一、信息拼词
单词中挖空字母,从候选字母中选取正确字母拼出完整单词。
**互动内容格式:**
```
任务描述:请…
题干:{单词,空缺字母用(数字)占位,如 G(2)pe}
答案:{填入的字母,如 ra}
选项:{4-7个候选字母逗号分隔}
辅助信息:{提示 或 无}
```
**规则:** 最多挖空不超过单词总字母数的一半;目标词必须来自知识点;干扰字母与答案字母有明显区分。
**示例:**
```
【任务标题】购买葡萄
【资源配置】
图片文件名0216002.png
【互动内容】
任务描述:请填入"葡萄"的正确的英文拼写
题干G(2)pe
答案ra
选项h, a, r, l, e, w
辅助信息:葡萄的英文是 grape
【后置对话】无
```
---
## 二、信息组句
从短语/词组拼出完整句子。
**与对话组句的关键区分:**
| 对比项 | 对话组句 | 信息组句 |
|--------|---------|---------|
| 选项粒度 | 单词级 | 短语/词组级 |
| 选项数量 | 4-8 | 3-5 |
| 干扰项 | 0-1 | 1-2 |
| 资源 | 可选音频 | 必有图片 |
| 目标来源 | 基于台词 | 基于知识点 |
**互动内容格式:**
```
任务描述:{指引}
选项1{短语}
选项2{短语}
选项3{短语}
答案:{完整句子,含大小写+句末标点}
辅助信息:{提示 或 无}
```
**规则:** 选项 3-5 个,乱序;短语/词组级切分,不按单词边界切;选项不加首字母大写和句末标点;含 1-2 个干扰项。
**示例:**
```
【任务标题】填写信息表
【资源配置】
图片文件名0216002.png
【互动内容】
任务描述:请在空白处填入正确的单词,组成句子
选项1is
选项2the visitor
选项3you invited
选项4Maeve
答案Is Maeve the visitor you invited?
辅助信息:可以用 is 引导的一般疑问句询问
【后置对话】无
```
---
## 三、信息描写
根据图片或场景提示,用英文描述所看到的内容。
**互动内容格式:**
```
任务描述:{任务指引}
参考词汇:{关键词汇}
参考句型:{句型}
示例回答User: {句子}
```
**规则:** 必有图片资源;考察观察与表达能力;知识点必须来自当前单元。
**示例:**
```
【任务标题】描述房间里的物品
【资源配置】
图片文件名0216002.png
【互动内容】
任务描述:请用英文描述图片中房间里的物品
参考词汇sofa, window, door, table
参考句型There is a... / I can see...
示例回答User: There is a sofa in the room.
【互动反馈】
正确 User: Great! You described it well.
错误 User: Try again. What else can you see?
【后置对话】无
```
---
## 四、信息补词
句子中挖空一个单词,从候选词中选出正确单词填入。
**互动内容格式:**
```
题干:{含___的句子}
选项1{单词}(正确)
选项2{单词}
选项3{单词}
辅助信息:{提示 或 无}
```
**规则:** 3-4 个候选单词;有候选词列表(区别于信息填词)。
**示例:**
```
【任务标题】补全句子中的单词
【资源配置】
图片文件名0216002.png
【互动内容】
题干The cat is sitting on the ___.
选项1chair正确
选项2table
选项3floor
辅助信息:注意图片中猫的位置
【后置对话】无
```
---
## 五、信息填词
句子中挖空一个单词,学生自行拼写填入(无候选词)。
**与信息补词的关键区分:**
| 对比项 | 信息补词 | 信息填词 |
|--------|---------|---------|
| 题型本质 | 从选项中选 | 自行拼写 |
| 选项 | 有候选词 | 无选项 |
| 难度 | 较低(识别) | 较高(回忆+拼写) |
**互动内容格式:**
```
题干:{含___的句子}
答案:{正确单词}
辅助信息:{提示 或 无}
```
**示例:**
```
【任务标题】填入正确的单词
【资源配置】
图片文件名0216002.png
【互动内容】
题干The ___ is on the desk.
答案book
辅助信息:首字母是 b
【后置对话】无
```
---
## 六、信息填句
给出不完整句子,学生补全完整句子。
**与信息组句的关键区分:**
| 对比项 | 信息组句 | 信息填句 |
|--------|---------|---------|
| 题型本质 | 从短语拼出完整句 | 补全不完整句子 |
| 输入 | 乱序短语选项 | 句子框架 + 自由输入 |
| 难度 | 中等(排序+选择) | 中等(补全) |
**互动内容格式:**
```
题干:{不完整句子缺失部分用___表示}
答案:{完整句子}
辅助信息:{提示 或 无}
```
**示例:**
```
【任务标题】补全这个句子
【资源配置】
图片文件名0216002.png
【互动内容】
题干The boy ___ to school every day.
答案The boy walks to school every day.
辅助信息:注意主语是第三人称单数
【后置对话】无
```

View File

@ -1,66 +0,0 @@
---
name: info-sentence-building-config
version: 1.0.0
description: 信息组句类互动组件配置标准化规范,用于剧本中信息组句类型组件的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
# 信息组句组件配置规范
## 适用场景
当【类型】列内容为【信息组句】时,使用本规范生成组件配置
## 文本输出规范(强制执行)
所有输出的文本内容(台词、题目、选项、解析等)必须遵守以下规则:
1. 禁止 Markdown 标记
2. 英式拼写优先
3. 中文全角标点,英文半角标点,不混用
## 核心规则 — 与对话组句的关键区分
| 对比项 | 对话组句 | 信息组句 |
|--------|---------|---------|
| 选项粒度 | 单词级 | **短语/词组级** |
| 选项数量 | 4-8 | **3-5** |
| 干扰项 | 0-1 | **1-2** |
| 资源 | 可选音频 | **必有图片** |
| 目标词来源 | 基于台词 | **基于知识点** |
## 配置格式
### 字段要求
1. 【任务标题】:
- 中文口语化表述,符合低龄用户认知
- 避免「-」,有序用「(一)」
- ⚠️ 标题含英文单词时,英文单词前后必须留空格
2. 【资源配置】:
- 图片文件名 = `{组件ID}.png`,后台手动上传
- 不含音频配置
3. 【互动内容】:
- **任务描述**:清晰任务指引,引导学生组句
- **选项**选项1/2/3... 形式
- 数量 3-5 个,乱序排列
- 短语/词组级别切分,**不按单词边界切**
- 选项内不添加首字母大写,不加句末标点
- 含 1-2 个干扰项
- **答案**:完整句子,含正确大小写 + 句末标点
- **辅助信息**:语法/结构提示;无则写「无」
4. 【后置对话】:无则填「无」
## 完整示例
```
【任务标题】填写信息表
【资源配置】
图片文件名:{组件ID}.png
【互动内容】
任务描述:请在空白处填入正确的单词,组成句子
选项1is
选项2the visitor
选项3you invited
选项4Maeve
答案Is Maeve the visitor you invited?
辅助信息:可以用 is 引导的一般疑问句询问you invited 是 visitor 的后置定语
【后置对话】无
```

View File

@ -1,71 +0,0 @@
---
name: info-word-spelling-config
version: 1.0.0
description: 信息拼词类互动组件配置标准化规范,用于剧本中信息拼词类型组件的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
# 信息拼词组件配置规范
## 适用场景
当【类型】列内容为【信息拼词】时,使用本规范生成组件配置
## 文本输出规范(强制执行)
所有输出的文本内容(台词、题目、选项、解析、音频文本等)必须遵守以下规则:
1. 禁止 Markdown 标记:禁止使用 ** * __ _ 等加粗/斜体标识,也禁止使用 # > - 等块级 Markdown 语法。所有文本纯文本输出。
2. 英式拼写优先:单词涉及英美式拼写差异时(如 colour/color、centre/center、travelling/traveling统一选择英式拼写。
3. 标点符号规范:严格区分全角/半角符号。中文内容使用全角标点(,。!?),英文内容使用半角标点(. , ! ?),不得混用。
## 核心规则
信息拼词是通过部分字母提示,让学生从候选字母中选取正确字母拼出完整单词的互动题型。
**与对话挖空的核心区分:**
| 对比项 | 对话挖空 | 信息拼词 |
|--------|---------|---------|
| 题型本质 | 句子中挖空单词 | 单词中挖空字母 |
| 目标粒度 | 完整单词 | 单个字母 |
| 题干形式 | 句子含 `___` | 单词含 `(数字)` |
| 资源 | 可选音频 | 必有图片 |
| 知识约束 | 知识点句型/单词 | **目标词必须来自知识点** |
## 配置格式
### 字段要求
1. 【任务标题】:
- 中文口语化表述,符合低龄用户认知
- 避免使用「-」,有序关系用中文括号「(一)」
- 示例:购买葡萄 / 拼写爷爷的英文
- ⚠️ 标题含英文单词时,英文单词前后必须留空格
2. 【资源配置】:
- 填写图片文件名,格式为 `{组件ID}.png`(如 `0216002.png`
- 图片在内容管理后台手动上传
- 不含音频配置
- **不含图片坐标信息**(坐标在后台「选取图片坐标」工具中单独配置)
3. 【互动内容】:
- **任务描述**:清晰的任务指引,引导学生拼写目标单词。格式为「请…」
- **题干**:目标单词,其中需学生填入的字母部分用 `(数字)` 占位,数字 = 空缺字母数
- 示例:`G(2)pe`grape 缺 2 个字母)/ `grandfa(4)`grandfather 缺最后 4 个字母)
- 最多挖空不超过单词总字母数的一半
- **答案**:仅写需填入的字母,按顺序,中间不加空格
- 示例:`ra` / `ther`
- **选项**4-7 个候选字母,逗号分隔,含正确答案字母 + 干扰字母
- 干扰字母应与答案字母有明显区分
- **辅助信息**:提示语句帮助学生;无则写「无」
4. 【后置对话】:无后置对话则填「无」
## 完整示例
```
【任务标题】购买葡萄
【资源配置】
图片文件名0216002.png
【互动内容】
任务描述:请填入"葡萄"的正确的英文拼写
题干G(2)pe
答案ra
选项h, a, r, l, e, w
辅助信息:葡萄的英文是 grape / 第二个字母是 r 哦,再想想最后一个空应该填什么吧
【后置对话】无
```

View File

@ -0,0 +1,65 @@
---
name: item-interaction-config
version: 1.0.0
description: 物品互动类组件配置标准化规范,用于剧本中物品互动类型组件的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
## 文本输出规范(强制执行)
所有输出的文本内容(台词、题目、选项、解析等)必须遵守以下规则:
1. 禁止 Markdown 标记:禁止使用 ** * __ _ 等加粗/斜体标识,也禁止使用 # > - 等块级 Markdown 语法。所有文本纯文本输出。
2. 英式拼写优先:单词涉及英美式拼写差异时(如 colour/color、centre/center、travelling/traveling统一选择英式拼写。
3. 标点符号规范:严格区分全角/半角符号。中文内容使用全角标点(,。!?),英文内容使用半角标点(. , ! ?),不得混用。
# 物品互动组件配置规范
## 适用场景
当【类型】列内容为【物品互动】时,使用本规范生成组件配置。
## 核心规则
物品互动是让学生根据中文提示,在场景中点击/选择正确物品的互动题型。
**关键特征:**
- 仅保留两个字段:【任务标题】+【互动内容】
- 互动内容固定格式:`"中文动作提示" the 名词! 选项1正确单词正确选项2干扰单词`
- 无【资源配置】【情境引入】【互动反馈】【后置对话】字段
## 配置格式
### 字段要求
1. 【任务标题】:
- 中文口语化表述,描述要做的动作
- 避免使用「-」,有序关系用中文括号「(一)」
- 示例:关上灯塔大门 / 拿起桌上的苹果
2. 【互动内容】:
- 固定格式:`"中文动作提示" the 名词!`
- 选项1正确单词标注 `(正确)`
- 选项2干扰单词与正确单词同类或形近
- 可扩展到 3 个选项1正确 + 2干扰
## 完整示例
```
【任务标题】关上灯塔大门
【互动内容】
"关上" the door!
选项1close正确
选项2open
```
```
【任务标题】拿起桌上的苹果
【互动内容】
"拿起" the apple!
选项1pick up正确
选项2put down
选项3throw away
```

View File

@ -0,0 +1,146 @@
---
name: picture-interaction-config
version: 1.0.0
description: 图片类互动组件配置标准化规范,覆盖图片单选/多选/有序/拖拽/图片互动等类型的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
## 文本输出规范(强制执行)
所有输出的文本内容(台词、题目、选项、解析等)必须遵守以下规则:
1. 禁止 Markdown 标记:禁止使用 ** * __ _ 等加粗/斜体标识,也禁止使用 # > - 等块级 Markdown 语法。所有文本纯文本输出。
2. 英式拼写优先:单词涉及英美式拼写差异时(如 colour/color、centre/center、travelling/traveling统一选择英式拼写。
3. 标点符号规范:严格区分全角/半角符号。中文内容使用全角标点(,。!?),英文内容使用半角标点(. , ! ?),不得混用。
# 图片类互动组件配置规范
## 适用场景
当【类型】列内容为【图片单选】【图片多选】【图片有序】【图片拖拽】【图片互动】时,使用本规范生成组件配置。
## 核心规则
### 通用规则(所有图片类互动适用)
1. **不包含【资源配置】字段**,直接从【任务标题】开始
2. 【互动内容】题干必须为指令句式,禁止使用问句
3. 仅知识点单词前后加 `$` 符号高亮,非知识点单词不加
4. 选项仅保留 `00/01/02/03` 编号,不需要任何文字说明
5. 在「正确XX」行后添加一行 `辅助信息:{单词} {中文释义}。`
6. 正确反馈统一填「无」,错误反馈必须带角色名
### 各子类型专属规则
#### 图片单选
- 4 个图片选项00-03其中 1 个正确
- 正确标注:`正确02`(编号对应正确图片)
- 示例:
```
【任务标题】选出正确的物品
【情境引入】无
【互动内容】
题干Find the $apple$.
选项:
00
01
02
03
正确02
辅助信息apple 苹果。
【互动反馈】
正确:无
错误Dan : That's not an apple. Try again!
【后置对话】无
```
#### 图片多选
- 4 个及以上图片选项,多个正确
- 正确标注:`正确00, 02`(多个编号逗号分隔)
- 示例:
```
【任务标题】选出所有水果
【情境引入】无
【互动内容】
题干Find all the $fruit$.
选项:
00
01
02
03
正确00, 02
辅助信息fruit 水果。
【互动反馈】
正确:无
错误Sally : Some fruits are missing. Look again!
【后置对话】无
```
#### 图片有序
- 4 个及以上图片选项,需按正确顺序排列
- 正确标注:`正确顺序00→01→02→03`
- 示例:
```
【任务标题】排序跳格子顺序
【情境引入】无
【互动内容】
题干Put the actions in order of $one by one$.
选项:
00
01
02
03
正确顺序00→01→02→03
辅助信息one by one 指"一个接一个"。
【互动反馈】
正确:无
错误Dan : Oops, that's the wrong order! We can't jump like that.
【后置对话】无
```
#### 图片拖拽
- 将图片拖拽到对应位置
- 正确标注:`正确00→位置A, 01→位置B`
- 示例:
```
【任务标题】将物品放到正确位置
【情境引入】无
【互动内容】
题干Drag the items to the correct $places$.
选项:
00
01
02
正确00→位置A, 01→位置B
辅助信息places 位置。
【互动反馈】
正确:无
错误Matt : That doesn't belong there!
【后置对话】无
```
#### 图片互动
- 自由点击/探索类图片互动
- 无正确/错误判断,仅有点击反馈
- 示例:
```
【任务标题】探索教室里的物品
【情境引入】无
【互动内容】
题干Tap on the items in the $classroom$.
选项:
00
01
02
03
反馈00This is a desk.
反馈01This is a chair.
反馈02This is a blackboard.
反馈03This is a window.
【互动反馈】
正确:无
错误:无
【后置对话】无
```

View File

@ -0,0 +1,106 @@
---
name: pronunciation-interaction-config
version: 1.0.0
description: 发音/语音/造句类互动组件配置标准化规范,覆盖发音互动/语音互动/造句互动类型的标准化生成
metadata:
requires:
permissions: ["sheets:spreadsheet"]
identity: bot
---
## 文本输出规范(强制执行)
所有输出的文本内容(台词、题目、选项、解析等)必须遵守以下规则:
1. 禁止 Markdown 标记:禁止使用 ** * __ _ 等加粗/斜体标识,也禁止使用 # > - 等块级 Markdown 语法。所有文本纯文本输出。
2. 英式拼写优先:单词涉及英美式拼写差异时(如 colour/color、centre/center、travelling/traveling统一选择英式拼写。
3. 标点符号规范:严格区分全角/半角符号。中文内容使用全角标点(,。!?),英文内容使用半角标点(. , ! ?),不得混用。
# 发音/语音/造句类互动组件配置规范
## 适用场景
当【类型】列内容为【发音互动】【语音互动】【造句互动】时,使用本规范生成组件配置。
## 核心规则
### 发音互动
考察学生发音准确度的互动题型。
**配置格式:**
- 【任务标题】:中文口语化表述
- 【资源配置】:无(或标注音频文件名)
- 【互动内容】:
- 目标单词/句子:学生需要朗读的内容
- 发音要点:关键音素或发音提示
- 示例音频:标注 `(音频)` 如需要
- 【互动反馈】:正确/错误反馈,带角色名
**示例:**
```
【任务标题】朗读含 th 音的单词
【资源配置】无
【情境引入】无
【互动内容】
朗读单词think
发音要点:注意 th 音,舌尖轻触上齿
【互动反馈】
正确 Matt: Perfect pronunciation!
错误 Matt: Try again. Put your tongue between your teeth.
【后置对话】无
```
### 语音互动
考察学生语音表达能力的互动题型。
**配置格式:**
- 【任务标题】:中文口语化表述
- 【资源配置】:无
- 【互动内容】:
- 表达目标:学生需要说的内容描述
- 示例回答:`User: 示例句子`
- 辅助信息:语音/语调提示
- 【互动反馈】:正确/错误反馈,带角色名
**示例:**
```
【任务标题】用正确的语调打招呼
【资源配置】无
【情境引入】无
【互动内容】
表达目标:用开心的语调说 Hello
示例回答User: Hello!
辅助信息:注意语调上扬,表达开心的情绪
【互动反馈】
正确 Sally: You sound so happy!
错误 Sally: Try to sound more excited!
【后置对话】无
```
### 造句互动
考察学生用给定词汇/句型独立造句能力的互动题型。
**配置格式:**
- 【任务标题】:中文口语化表述
- 【资源配置】:无(或标注图片文件名)
- 【互动内容】:
- 任务描述:造句要求
- 给定词汇/句型:学生必须使用的词汇或句型
- 示例回答:`User: 示例句子`
- 辅助信息:语法/结构提示
- 【互动反馈】:正确/错误反馈,带角色名
**示例:**
```
【任务标题】用 like 造句
【资源配置】无
【情境引入】无
【互动内容】
任务描述:用 like 造句,说说你喜欢什么
给定句型I like...
示例回答User: I like apples.
辅助信息like 后面可以加名词,表示喜欢某物
【互动反馈】
正确 Dan: I like that too!
错误 Dan: Hmm, that doesn't sound right. Try "I like..."
【后置对话】无
```

View File

@ -49,10 +49,10 @@ description: 剧本互动组件内容生产/审校/回填技能不涉及JSON
---
## 执行流程(6个节点)
## 执行流程(7个节点)
```
读取飞书文档 → 解析Sheet列结构 → 识别组件行 → 匹配组件类型 → LLM生成组件内容 → 审校 → 回填
读取飞书文档 → 解析Sheet列结构 → 识别组件行 → 匹配组件类型 → 加载专项Skill → LLM生成组件内容 → 审校 → 回填
```
### 节点1文档读取
@ -99,9 +99,29 @@ description: 剧本互动组件内容生产/审校/回填技能不涉及JSON
- 剧情描述含"表达"/"说出" → 对话表达
- 剧情描述含"看图"/"图片" → 看图说话
### 节点4.5:加载对应专项 Skill强制执行
在调用 LLM 生成前,**必须先读取该组件类型对应的专项 Skill**,将规范注入 LLM prompt
| 组件类型 | 对应专项 Skill | 路径 |
|---------|---------------|------|
| 对话朗读 / 对话表达 / 对话选读 / 对话选择 / 对话挖空 / 对话组句 | `dialogue-interaction-config` | `skills/dialogue-interaction-config/SKILL.md` |
| 信息拼词 / 信息组句 / 信息描写 / 信息补词 / 信息填词 / 信息填句 | `info-interaction-config` | `skills/info-interaction-config/SKILL.md` |
| 物品互动 | `item-interaction-config` | `skills/item-interaction-config/SKILL.md` |
| 图片单选 / 图片多选 / 图片有序 / 图片拖拽 / 图片互动 | `picture-interaction-config` | `skills/picture-interaction-config/SKILL.md` |
| 发音互动 / 语音互动 / 造句互动 | `pronunciation-interaction-config` | `skills/pronunciation-interaction-config/SKILL.md` |
| 核心互动(听力/口语/阅读/写作共14类 | `core-interaction-config` | `skills/core-interaction-config/SKILL.md` |
| 对话互动 / 材料互动 / 指令互动 / 挖空互动 / 组句互动 / 填词互动 / 看图说话 | `component-design` + `vala-interactive-components` | 通用规范 + 官方配置参考 |
**执行流程:**
1. 根据节点4匹配到的组件类型查表确定需要加载的专项 Skill
2. 读取对应 SKILL.md提取字段模板、校验规则、示例
3. 将提取的规范作为 system prompt 注入 LLM 调用
4. 确保 LLM 严格按规范输出,而非自由发挥
### 节点5LLM生成组件内容结构化文本
调用大模型生成结构化文本格式的组件内容,遵循 `component-design` 技能规范。
调用大模型生成结构化文本格式的组件内容,**严格遵循节点4.5加载的专项 Skill 规范**
**输出格式结构化文本非JSON**
@ -135,9 +155,11 @@ This is my picture. It's ___, green and orange.(音频)
```
**各组件类型的字段模板参考:**
- 中互动 → 遵循 `component-design` 技能中各类型专属规则
- 对话类互动(朗读/表达/选读/选择/挖空/组句)→ 遵循 `dialogue-interaction-config` 技能规范
- 信息拼词/组句/描写/补词/填词/填句 → 遵循 `info-interaction-config` 技能规范
- 物品互动 / 图片类互动 → 遵循 `component-design` 技能中各类型专属规则
- 核心互动 → 遵循 `core-content-json-standard` 技能中的内容结构描述
- 对话类互动 → 遵循 `dialogue-interaction-config` 技能规范
- 其余中互动 → 遵循 `component-design` + `vala-interactive-components` 通用规范
### 节点6审校 & 回填
@ -152,14 +174,15 @@ This is my picture. It's ___, green and orange.(音频)
| 6 | 选项格式 | 对话类用「选项1XXX」、图片类用00/01/02编号 |
| 7 | 反馈格式 | 错误反馈带角色名、自然对话、非指令式 |
| 8 | 高亮规则 | 图片类仅知识点词加$、对话类不加 |
| 9 | 情境引入 | 无内容填「无」,多句换行,格式为「角色名: 台词」。**禁止直接复制前一行台词原文**,相同则填「无」 |
| 10 | 后置对话 | 取组件行之后的紧接台词,无则填「无」。**禁止直接复制后一行台词原文**,相同则填「无」 |
| 9 | 情境引入 | 无内容填「无」,多句换行,格式为「角色名: 台词」。🚫 **禁止引用组件行之前任何行的台词**含前一行的E列/F列原文必须来自组件自身场景设定。审校时逐行对比组件行之前的E/F列确认无重合 |
| 10 | 后置对话 | 取组件行之后的紧接台词,无则填「无」。🚫 **禁止直接复制后一行台词原文**,相同则填「无」。审校时逐行对比组件行之后的E/F列确认无重合 |
| 11 | 超纲检查 | 词汇/句型在对应级别词库/句型库范围内 |
| 12 | 标点符号 | 统一使用标准英文标点,禁止「~」「!!!」等非标准标点 |
| 13 | 图片类资源配置 | 图片类互动(图片单选/多选/有序/拖拽)**不包含【资源配置】字段** |
| 14 | 图片类辅助信息 | 图片类互动在「正确XX」后添加 `辅助信息:{单词} {中文释义}。`,释义参考知识点 Sheet |
| 15 | 对话朗读反馈 | 对话朗读**不包含【互动反馈】字段** |
| 16 | 配图资源配置 | A列含"配图"时,仅保留`图片时机`(不写`音频载体`),根据有内容的 section 动态列出 |
| 17 | 答案随机化 | 听力选择/对话选择等多小题组件,各题正确选项位置必须随机分布,禁止全部集中在同一序号。生成后统计各题正确选项序号,若全部相同则重新随机 |
#### 回填
- 将生成的结构化文本写入列G组件配置
@ -238,6 +261,7 @@ This is my picture. It's ___, green and orange.(音频)
## 版本历史
- v1.3.02026-06-11强化 #9-#10 情境引入/后置对话审校规则禁止引用组件行前后台词必须逐行对比E/F列新增 #17 答案随机化检查(多小题正确选项必须随机分布)
- v1.2.02026-06-08审校检查项新增 #13-#16图片类资源配置、辅助信息、对话朗读反馈、配图资源配置更新 #9-#10 情境引入/后置对话去重规则
- v1.1.02026-05-13明确职责边界产出格式从JSON改为结构化文本与 interactive-component-json 解耦
- v1.0.02026-05-13初始版本覆盖27种中互动 + 14种核心互动完整6节点流程

View File

@ -1,6 +1,7 @@
---
name: dialogue-components-standardizer
description: A unified skill for standardizing the production and review of 6 dialogue interaction components. Core logic is fixed; optimizations are handled via branch files and scripts for repeatability. Enter skill only when components change dynamically.
description: ⛔ DEPRECATED (2026-06-11). 仅覆盖8类组件与41类实际组件差距太大。对话类6类已由 dialogue-interaction-config 统一覆盖,信息拼词/组句已由独立 skill 覆盖。本 skill 不再使用,保留仅供历史参考。
status: deprecated
---

View File

@ -115,3 +115,5 @@
{"type":"memory.recall.recorded","timestamp":"2026-06-10T03:52:13.443Z","query":"Perfect Oops 数据 复核 审校 组件","resultCount":1,"results":[{"path":"memory/2026-05-28.md","startLine":19,"endLine":37,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-10T03:52:28.180Z","query":"深度复核 流程 步骤 component review audit","resultCount":4,"results":[{"path":"memory/2026-05-20.md","startLine":40,"endLine":59,"score":1},{"path":"memory/2026-05-20.md","startLine":55,"endLine":65,"score":1},{"path":"memory/2026-05-12.md","startLine":234,"endLine":254,"score":1},{"path":"memory/2026-06-08.md","startLine":17,"endLine":27,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-10T03:52:28.181Z","query":"core_speaking_image JSON 结构 口语看图","resultCount":3,"results":[{"path":"memory/2026-05-12.md","startLine":164,"endLine":187,"score":1},{"path":"memory/2026-05-12.md","startLine":214,"endLine":236,"score":1},{"path":"memory/2026-05-12.md","startLine":234,"endLine":254,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-11T10:31:34.088Z","query":"S0 多维表格 核心互动 组件配置 bitable","resultCount":6,"results":[{"path":"memory/2026-05-12.md","startLine":214,"endLine":236,"score":1},{"path":"memory/2026-05-12.md","startLine":164,"endLine":187,"score":1},{"path":"memory/2026-05-12.md","startLine":181,"endLine":203,"score":1},{"path":"memory/2026-05-12.md","startLine":234,"endLine":254,"score":1},{"path":"memory/2026-05-12.md","startLine":199,"endLine":216,"score":1},{"path":"memory/2026-05-07.md","startLine":86,"endLine":107,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-11T10:31:50.633Z","query":"S0 多维表格 0000001 合作阅读 核心互动 bitable token","resultCount":6,"results":[{"path":"memory/2026-05-12.md","startLine":214,"endLine":236,"score":1},{"path":"memory/2026-05-12.md","startLine":164,"endLine":187,"score":1},{"path":"memory/2026-05-12.md","startLine":181,"endLine":203,"score":1},{"path":"memory/2026-05-12.md","startLine":234,"endLine":254,"score":1},{"path":"memory/2026-05-12.md","startLine":199,"endLine":216,"score":1},{"path":"memory/2026-05-07.md","startLine":86,"endLine":107,"score":1}]}

View File

@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-06-10T03:52:28.181Z",
"updatedAt": "2026-06-11T10:31:50.633Z",
"entries": {
"memory:memory/2026-05-07.md:57:74": {
"key": "memory:memory/2026-05-07.md:57:74",
@ -2433,18 +2433,21 @@
"endLine": 107,
"source": "memory",
"snippet": "- 表2.xlsx 文件LV2词表1,329个单词含「LV2-单词」「LV2上单词占用情况」「LV2下单词占用情况」「LV2S3」四个sheet - 表3.xlsx 文件L1词表845个单词含「单词表-LV1」「单词表-LV1」「单词表-备用」三个sheet合并去重 - **对比过程(共四轮验证):** 1. 第一轮表1 vs 表2表1有但表2没有 = 502个 2. 第二轮(结果 vs 表3排除表3也有后 = 260个 3. 第三轮(用户重发三份文件后重算):最终 = 255个 4. 第四轮用户单独替换新表3后验证结果不变确认 = 255个 - **最终统计:** - 三表共有230 - 表1 ∩ 表2492 - 表1 ∩ 表3448 - 表2 ∩ 表3427 - 最终255个 - **关键技术点:** - .doc 文件使用 catdoc 解析(非 libreoffice/antiword - .xlsx 文件使用 openpyxl 读取,多 sheet 合并去重 - 过滤了缩写词CD, DVD, TV, Mr, Mrs, Ms 等) - 处理了复数变体映射geese→goose 等) - 用户 user_id = `58fd6864`租户级发送消息时踩坑ou_xxx 是 open_id 非 user_id - **输出文件:** `/root/.openclaw/workspace-xiaoyan/output/单词对比结果_最终版.x",
"recallCount": 1,
"recallCount": 3,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 1,
"totalScore": 3,
"maxScore": 1,
"firstRecalledAt": "2026-05-25T09:34:43.114Z",
"lastRecalledAt": "2026-05-25T09:34:43.114Z",
"lastRecalledAt": "2026-06-11T10:31:50.633Z",
"queryHashes": [
"4baebc80709c"
"4baebc80709c",
"c538777b9257",
"6bf54cd79bb2"
],
"recallDays": [
"2026-05-25"
"2026-05-25",
"2026-06-11"
],
"conceptTags": [
"lv2-单词",
@ -3322,24 +3325,27 @@
"endLine": 187,
"source": "memory",
"snippet": "- **五大分类:** 📖阅读(2) / 🎧听力(3) / 🗣口语(4) / ✏️写作(5) / 未完成(1) - **通用字段提取:** ID/dataStatus/kpStatus/kpInfo/taskData 是所有题型共用的基础结构 #### 覆盖率 - 14 种题型已录入 ✅1 种无数据:口语探讨) - 4 个一级板块:通用结构 / 题型专区 / 审校规则 / 扩展指南 - 每个题型cType + bitable 定位 + JSON 字段表 + 结构特点 + 与同类题型的差异说明 #### 发现 - 写作互动和邮件组句 cType 相同(`core_writing_questionMakeSentence`),通过 textInfo 区分素材 - 口语快答/妙问 JSON 结构几乎相同,差异在 prompt 配置和对话样例内容 - 合作阅读和合作听力的核心差异:`textData.text[]` ↔ `textData.audio[]` - meaning 标签(合作阅读)为开放型自由文本,非受控词表 #### 产出文件 - `business_production/_shared/bitable-reader/SKILL.md` — 通用 bitable 读取技能164行 - `business_knowledge/skills/core-content-json-standard/SKILL.md` — 全题型 JSON 标准 v2.0393行 - `scripts/audit_core_reading_S0.py` — 合作阅读 S0",
"recallCount": 6,
"recallCount": 8,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 6,
"totalScore": 8,
"maxScore": 1,
"firstRecalledAt": "2026-06-09T08:37:12.232Z",
"lastRecalledAt": "2026-06-10T03:52:28.181Z",
"lastRecalledAt": "2026-06-11T10:31:50.633Z",
"queryHashes": [
"91bf4d50e5a2",
"b8853a0aafcc",
"113e8be33943",
"9573fdf60768",
"70a3284bc0c5",
"78420d02cc4d"
"78420d02cc4d",
"c538777b9257",
"6bf54cd79bb2"
],
"recallDays": [
"2026-06-09",
"2026-06-10"
"2026-06-10",
"2026-06-11"
],
"conceptTags": [
"口语快答/妙问",
@ -3359,23 +3365,26 @@
"endLine": 236,
"source": "memory",
"snippet": "- **产出 Skill 2** `business_knowledge/skills/core-content-json-standard/SKILL.md` v2.0 — 全题型 JSON 标准393行 - **架构:** 通用字段在前ID/kpInfo/taskData+ 5大题型分类📖阅读2 🎧听力3 🗣口语4 ✏写作5+ 审校规则 + 扩展指南 - **覆盖率:** 14/15 种题型(口语探讨 S0 无数据) - **产出脚本:** `scripts/audit_core_reading_S0.py` — 合作阅读 S0 审校 ## 经验教训 ### bitable 写入需严格流程管控2026-05-12 - 批量更新 JSON写入前完整提取 → 写入后逐字段 diff → 异常立即回滚 - 首次修复多题 JSON 导致 Q2 丢失 → 先单条试跑验证再批量 ### sentenceMeaningMeaning 引号规范2026-05-12 - explanation 中 ASCII `\"` 与中文引号混用导致 JSON 解析失败 - 建议:生产侧统一用中文引号 `\"\"`U+201C/D或写入前做引号归一化 ### 写作互动 / 邮件组句 cType 相同2026-05-12 - 两者 cType 均为 `core_writing_questionMakeSentence`,通过 textInfo.textList[].type 区分素材 ### 刘彦江 — 中互动全题型录入18:00 ~ 18:25 ###",
"recallCount": 5,
"recallCount": 7,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 5,
"totalScore": 7,
"maxScore": 1,
"firstRecalledAt": "2026-06-09T08:37:12.232Z",
"lastRecalledAt": "2026-06-10T03:52:28.181Z",
"lastRecalledAt": "2026-06-11T10:31:50.633Z",
"queryHashes": [
"91bf4d50e5a2",
"b8853a0aafcc",
"9573fdf60768",
"70a3284bc0c5",
"78420d02cc4d"
"78420d02cc4d",
"c538777b9257",
"6bf54cd79bb2"
],
"recallDays": [
"2026-06-09",
"2026-06-10"
"2026-06-10",
"2026-06-11"
],
"conceptTags": [
"v2.0",
@ -3395,24 +3404,27 @@
"endLine": 254,
"source": "memory",
"snippet": "#### 发现 - **中互动结构高度统一:** 全部 27 类都用 `jsonData`(主配置)+ `kpInfo`(知识点绑定),没有核心互动那种多字段分散结构 - **6 大分类:** 🗨对话(7) / 📋信息(6) / 📦词汇(2) / 🖼图片选择(4) / 📝语法(3) / 🔧其他(5) - **对话类公共字段:** cType, cId, title, resourceMapping, preDialog, imgShowTimingList - 写作互动和邮件组句 cType 相同(`core_writing_questionMakeSentence` #### 产出文件 - `skills/interactive-component-json/references/json_structure_guide.md` — 27 种中互动 jsonData 字段结构8736 bytes - `skills/interactive-component-json/references/component_registry.md` — 核心互动部分从🔲更新为完整注册表(含 cType+bitable_token - 与 `business_knowledge/skills/core-content-json-standard/SKILL.md` 双向关联 #### 覆盖率 - 核心互动15/15 ✅(口语探讨 S0 无数据已标注) - 中互动27/27 ✅ - 关联文件3 个core-content-json-standard + com",
"recallCount": 6,
"recallCount": 8,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 6,
"totalScore": 8,
"maxScore": 1,
"firstRecalledAt": "2026-06-09T08:37:12.232Z",
"lastRecalledAt": "2026-06-10T03:52:28.181Z",
"lastRecalledAt": "2026-06-11T10:31:50.633Z",
"queryHashes": [
"91bf4d50e5a2",
"b8853a0aafcc",
"9573fdf60768",
"70a3284bc0c5",
"0924e1755b1c",
"78420d02cc4d"
"78420d02cc4d",
"c538777b9257",
"6bf54cd79bb2"
],
"recallDays": [
"2026-06-09",
"2026-06-10"
"2026-06-10",
"2026-06-11"
],
"conceptTags": [
"bitable-token",
@ -3740,6 +3752,70 @@
"图片",
"时机"
]
},
"memory:memory/2026-05-12.md:181:203": {
"key": "memory:memory/2026-05-12.md:181:203",
"path": "memory/2026-05-12.md",
"startLine": 181,
"endLine": 203,
"source": "memory",
"snippet": "- `scripts/audit_core_reading_S0.py` — 合作阅读 S0 审校脚本(含审校发现) # 2026-05-12 工作日志 ## 会话记录 ### 刘彦江 — 021301-021801 图片描述修正 + 技能更新09:35 ~ 09:45 - **问题:** 021301-021801 信息匹配题的图片描述缺少 `【Notice Type】` 标签 - **处理:** 批量更新6条记录的图片描述字段全部10个标签验证通过 - **脚本:** `scripts/fix_matchInfo_0213_0218_desc.py` - **技能更新:** 图片描述规范写入 `business_production/单元挑战/skills/unit_challenge/questions/reading/reading_info_match/SKILL.md` ### 刘彦江 — L1 配置表审校 + 技能沉淀11:50 ~ 12:10 - **数据源:** `Nq3Zb258aae7SRs2QfXcqsQYnxJ` / `tblTxGpf6GQ5c7DZ` - **ID 编码:** 7位 = S(2位) + U(2位) + 序列号(3位) - **技能沉淀:** `scripts/audit_l1_config.py` + `business_production/_shared/audit_l1_config/SKILL.md` ### 刘彦江 — 1213001-1216010 批量审校 40 条12:10 ~ 14:00 -",
"recallCount": 2,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 2,
"maxScore": 1,
"firstRecalledAt": "2026-06-11T10:31:34.088Z",
"lastRecalledAt": "2026-06-11T10:31:50.633Z",
"queryHashes": [
"c538777b9257",
"6bf54cd79bb2"
],
"recallDays": [
"2026-06-11"
],
"conceptTags": [
"scripts/audit-core-reading-s0.py",
"021301-021801",
"scripts/audit-l1-config.py",
"business-production",
"shared/audit-l1-config/skill.md",
"1213001-1216010",
"scripts",
"audit-core-reading-s0"
]
},
"memory:memory/2026-05-12.md:199:216": {
"key": "memory:memory/2026-05-12.md:199:216",
"path": "memory/2026-05-12.md",
"startLine": 199,
"endLine": 216,
"source": "memory",
"snippet": "- **脚本修复(过程中):** sort 大小写误报 / ID 末尾空格 / 朗读跟读题误报 - **结果:** 系统性 classificationInfo 互换(已标记不视为错误) + 17条具体错误 + 23条通过 - **脚本:** `scripts/audit_l1_config.py` + `scripts/audit_batch_1213001_1216010.py` ### 刘彦江 — 审校结果修正 + JSON 修复14:00 ~ 15:38 - **YLE↔A1 误报排除:** 确认等级互通40条审校结果已更新 - **解析与题目一致性检查:** ✅ 0条不对应 - **10 条 sentenceMeaningMeaning JSON 修复:** - 根因explanation 中 ASCII `\"` 被用作中文引号 - 修复策略演变:状态机拆分失败 → 正则重建 → 发现Q2丢失 → 从中文列完整重建 - 最终10/10 可正确解析,审校结果同步更新 - 受影响1213004/1213006/1213010/1214008/1215005/1216001/1216004/1216007/1216008/1216010 ### 刘彦江 — 核心互动全题型 JSON 配置标准沉淀17:05 ~ 17:50 - **产出 Skill 1** `business_production/_shared/bitable-reader/SKILL.md` — 通用 bitable 读取(任何 bitable 通用) - **产出",
"recallCount": 2,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 2,
"maxScore": 1,
"firstRecalledAt": "2026-06-11T10:31:34.088Z",
"lastRecalledAt": "2026-06-11T10:31:50.633Z",
"queryHashes": [
"c538777b9257",
"6bf54cd79bb2"
],
"recallDays": [
"2026-06-11"
],
"conceptTags": [
"scripts/audit-l1-config.py",
"10/10",
"business-production",
"shared/bitable-reader/skill.md",
"脚本",
"修复",
"过程",
"sort"
]
}
}
}

1145
memory/2026-06-11-1133.md Normal file

File diff suppressed because it is too large Load Diff

34
memory/2026-06-11-1226.md Normal file
View File

@ -0,0 +1,34 @@
# Session: 2026-06-11 12:26:54 GMT+8
- **Session Key**: agent:xiaoyan:feishu:direct:ou_8b79b7f711e42a4004f3f3c225a1a99c
- **Session ID**: 7135746c-57dd-4683-913d-2fd79100bdb9
- **Source**: feishu
## Conversation Summary
user: https://makee-interactive.feishu.cn/wiki/O7RwwMei6iQCFlk6cHocDqGpn9b 组件配置
assistant: 张昆鹏,开始处理组件配置。
assistant: 📝 组件配置完成!**L1-S2-U21-L2 运输小队**16/16 全部成功:
| # | cId | 类型 | cType |
|---|------|------|-------|
| 1 | 1221201 | 对话朗读 | mid_dialog_repeat |
| 2 | 1221202 | 图片单选 | mid_image_choose |
| 3 | 1221203 | 对话挖空 | mid_dialog_fillin |
| 4 | 1221204 | 对话朗读 | mid_dialog_repeat |
| 5 | 1221205 | 对话朗读 | mid_dialog_repeat |
| 6 | 1221206 | 图片单选 | mid_image_choose |
| 7 | 1221207 | 对话朗读 | mid_dialog_repeat |
| 8 | 1221208 | 图片单选 | mid_image_choose |
| 9 | 1221209 | 对话朗读 | mid_dialog_repeat |
| 10 | 1221210 | 对话朗读 | mid_dialog_repeat |
| 11 | 1221211 | 对话挖空 | mid_dialog_fillin |
| 12 | 1221212 | 对话选读 | mid_dialog_select |
| 13 | 1221213 | 看图拼词 | core_writing_imgMakeWord |
| 14 | 1221214 | 对话挖空 | mid_dialog_fillin |
| 15 | 1221215 | 图片拖拽 | mid_image_drag |
| 16 | 1221216 | 对话表达 | mid_dialog_express |
HTML 报告已通过飞书 Bot 发送给你,打开后可查看详情并一键推送配置到远程数据库。