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

This commit is contained in:
ai_member_only 2026-06-06 08:10:01 +08:00
parent fa5d896a2c
commit a0e029a56d
13 changed files with 11666 additions and 1 deletions

View File

@ -193,6 +193,18 @@ Skills 按四层架构组织:
| L1配置审校 | `audit_l1_config` | `./business_production/_shared/audit_l1_config/SKILL.md` | — | | L1配置审校 | `audit_l1_config` | `./business_production/_shared/audit_l1_config/SKILL.md` | — |
| 知识点掌握度计算 | `knowledge-mastery-calculator` | `./business_production/_shared/knowledge-mastery-calculator/SKILL.md` | — | | 知识点掌握度计算 | `knowledge-mastery-calculator` | `./business_production/_shared/knowledge-mastery-calculator/SKILL.md` | — |
### 教研资源速查(业务知识库)
| 资源 | 文件 | 说明 |
|------|------|------|
| L1词库 | `./business_knowledge/L1_word_list.json` | 980词含词性/释义/阶段/单元/难度 |
| L2词库 | `./business_knowledge/L2_word_list.json` | 1470词含词性/释义/阶段/CEFR/剑桥等级/难度 |
| L1句型库 | `./business_knowledge/L1_pattern_list.json` | 490句型含句型结构/模块/例句 |
| L2句型库 | `./business_knowledge/L2_pattern_list.json` | 380句型含句型结构/模块/例句 |
| L2 KET语法点大纲 | `./business_knowledge/L2_grammar_points.json` | 48条11大类含season/unit分配 |
| L2 发音大纲 | `./business_knowledge/L2_pronunciation_points.json` | 48音素6大类含音素/字母组合/unit分配 |
| L1 自拼大纲 | `./business_knowledge/L1_phonics_outline.json` | 49条分单字母/短元音词族/长元音组合三组 |
### 业务知识技能速查(被引用,不独立触发) ### 业务知识技能速查(被引用,不独立触发)
| 场景 | 技能 | 路径 | | 场景 | 技能 | 路径 |
@ -211,8 +223,10 @@ Skills 按四层架构组织:
|--------|------|------| |--------|------|------|
| `/英文台词`、`英文台词生产` | `kids-english-script-production` | `business_production/英文台词/skills/kids-english-script-production/SKILL.md` | | `/英文台词`、`英文台词生产` | `kids-english-script-production` | `business_production/英文台词/skills/kids-english-script-production/SKILL.md` |
| 飞书文档表格读写通用I/O层 | `feishu-doc-io` | `business_production/英文台词/skills/feishu-table-translate-fill/SKILL.md` | | 飞书文档表格读写通用I/O层 | `feishu-doc-io` | `business_production/英文台词/skills/feishu-table-translate-fill/SKILL.md` |
| `剧本复核`、`/剧本复核` | `script-post-check` | `business_production/英文台词/skills/script-post-check/SKILL.md` |
> **示例:** 用户发来飞书 wiki 链接说「英文台词生产」→ 调用 `kids-english-script-production`,用 `--feishu-url` 模式读 F 列编剧台词 → 英语思维重构 → 写回 G 列 > **示例:** 用户发来飞书 wiki 链接说「英文台词生产」→ 调用 `kids-english-script-production`,用 `--feishu-url` 模式读 F 列编剧台词 → 英语思维重构 → 写回 G 列
> **示例:** 用户说「剧本复核 <链接>」→ 调用 `script-post-check`,对 F 列执行长句拆分 + 标点检查等代码规则复核
#### 组件生产Component Production #### 组件生产Component Production

View File

@ -53,6 +53,29 @@
- 用途L2级别听力/口语/阅读/写作题的句型参考依据,题目中的句型必须在句型库范围内 - 用途L2级别听力/口语/阅读/写作题的句型参考依据,题目中的句型必须在句型库范围内
- 包含字段句型ID、句型结构、所属模块、模块编号、例句、中文释义 - 包含字段句型ID、句型结构、所属模块、模块编号、例句、中文释义
### 【教研资源】L2 KET语法点大纲
- 存储路径:`/root/.openclaw/workspace-xiaoyan/business_knowledge/L2_grammar_points.json`
- 更新时间2026-06-05
- 语法点总量48个分11大类名词/代词/数词/形容词/副词/动词/时态/介词/连词/句型/特殊疑问句)
- 来源飞书知识库「LV2参考-KET语法点」
- 用途L2级别每个Unit需要讲解的KET语法点参考含season和unit分配信息
- 注意当前交付到U29S4的11条语法点尚未分配Unit
### 【教研资源】L2 发音大纲
- 存储路径:`/root/.openclaw/workspace-xiaoyan/business_knowledge/L2_pronunciation_points.json`
- 更新时间2026-06-05
- 音素总量48个分6大类长元音/短元音/双元音/清辅音/浊辅音/其他辅音)
- 来源飞书知识库「LV2参考-LV2-发音大纲」
- 用途L2级别每个Unit需要讲解的发音点参考含音素、对应字母组合和unit分配信息
- 注意当前交付到U2912个音素尚未分配Unit
### 【教研资源】L1 自拼大纲
- 存储路径:`/root/.openclaw/workspace-xiaoyan/business_knowledge/L1_phonics_outline.json`
- 更新时间2026-06-05
- 条目总量49条分三组单字母25条/短元音词族15条/长元音组合9条
- 来源飞书知识库「LV1参考-自拼大纲」
- 用途L1级别自然拼读教学大纲按教学顺序排列
### 【教研规则】全题型阶段归属规则(强制执行) ### 【教研规则】全题型阶段归属规则(强制执行)
#### 听力类 #### 听力类
- ✅ L1专属听力拖拽题、听力选图题 - ✅ L1专属听力拖拽题、听力选图题

View File

@ -0,0 +1,79 @@
{
"_meta": {
"name": "L1 自拼大纲",
"source": "https://makee-interactive.feishu.cn/wiki/VHQJwGNjRiQZF2klpfUcGKYInVc",
"source_title": "LV1参考-自拼大纲",
"update_time": "2026-06-05",
"total_items": 49,
"description": "L1级别自然拼读教学大纲按教学顺序排列。分三组单字母、短元音词族、长元音组合。"
},
"groups": [
{
"name": "单字母",
"range": "0-24",
"items": [
{ "index": 0, "letters": "a" },
{ "index": 1, "letters": "b" },
{ "index": 2, "letters": "c" },
{ "index": 3, "letters": "d" },
{ "index": 4, "letters": "e" },
{ "index": 5, "letters": "f" },
{ "index": 6, "letters": "g" },
{ "index": 7, "letters": "h" },
{ "index": 8, "letters": "i" },
{ "index": 9, "letters": "j" },
{ "index": 10, "letters": "k" },
{ "index": 11, "letters": "l" },
{ "index": 12, "letters": "m" },
{ "index": 13, "letters": "n" },
{ "index": 14, "letters": "o" },
{ "index": 15, "letters": "p" },
{ "index": 16, "letters": "q" },
{ "index": 17, "letters": "r" },
{ "index": 18, "letters": "s" },
{ "index": 19, "letters": "t" },
{ "index": 20, "letters": "u" },
{ "index": 21, "letters": "v" },
{ "index": 22, "letters": "w" },
{ "index": 23, "letters": "x" },
{ "index": 24, "letters": "y z" }
]
},
{
"name": "短元音词族",
"range": "25-39",
"items": [
{ "index": 25, "letters": "am" },
{ "index": 26, "letters": "an" },
{ "index": 27, "letters": "ad ag" },
{ "index": 28, "letters": "ap at" },
{ "index": 29, "letters": "et en" },
{ "index": 30, "letters": "ed" },
{ "index": 31, "letters": "ip ib" },
{ "index": 32, "letters": "id in" },
{ "index": 33, "letters": "ig it" },
{ "index": 34, "letters": "ix" },
{ "index": 35, "letters": "ot op" },
{ "index": 36, "letters": "ug ud" },
{ "index": 37, "letters": "up ut" },
{ "index": 38, "letters": "ub um" },
{ "index": 39, "letters": "un" }
]
},
{
"name": "长元音组合",
"range": "40-48",
"items": [
{ "index": 40, "letters": "a_e ai" },
{ "index": 41, "letters": "ay" },
{ "index": 42, "letters": "e_e ee" },
{ "index": 43, "letters": "ea" },
{ "index": 44, "letters": "i_e ie" },
{ "index": 45, "letters": "igh" },
{ "index": 46, "letters": "o_e oa" },
{ "index": 47, "letters": "ow" },
{ "index": 48, "letters": "u_e ue ui" }
]
}
]
}

View File

@ -0,0 +1,128 @@
{
"_meta": {
"name": "L2 KET语法点大纲",
"source": "https://makee-interactive.feishu.cn/wiki/Ju34wa3qOimOGJkaOoJcx2fjnjg",
"source_title": "LV2参考-KET语法点",
"update_time": "2026-06-05",
"total_points": 48,
"description": "L2级别每个Unit需要讲解的KET语法点按类别分组。season=0为序章占用情况为空表示尚未分配Unit。"
},
"categories": [
{
"name": "名词",
"points": [
{ "id": 1, "topic": "可数名词的概念及复数的规则变化", "season": 1, "unit": "U1" },
{ "id": 2, "topic": "可数名词复数的不规则变化", "season": 1, "unit": "U3" },
{ "id": 3, "topic": "不可数名词", "season": 1, "unit": "U2" },
{ "id": 4, "topic": "名词所有格+'s", "season": 1, "unit": "U4" }
]
},
{
"name": "代词",
"points": [
{ "id": 5, "topic": "人称代词", "season": 1, "unit": "U5" },
{ "id": 6, "topic": "物主代词", "season": 1, "unit": "U11" },
{ "id": 7, "topic": "指示代词", "season": 1, "unit": "U6" },
{ "id": 8, "topic": "不定代词", "season": 1, "unit": "U8" }
]
},
{
"name": "数词",
"points": [
{ "id": 9, "topic": "基数词&序数词", "season": 1, "unit": "U9" }
]
},
{
"name": "形容词",
"points": [
{ "id": 10, "topic": "形容词用法及顺序", "season": 2, "unit": "U17" },
{ "id": 11, "topic": "形容词比较级-规则变化", "season": 2, "unit": "U13" },
{ "id": 12, "topic": "形容词比较级-不规则变化", "season": 2, "unit": "U14" },
{ "id": 13, "topic": "形容词最高级-规则变化&不规则变化", "season": 2, "unit": "U15" }
]
},
{
"name": "副词",
"points": [
{ "id": 14, "topic": "副词的种类-时间副词&地点副词", "season": 2, "unit": "U16" },
{ "id": 15, "topic": "副词的种类-方式副词&程度副词", "season": 2, "unit": "U18" },
{ "id": 16, "topic": "副词的比较级&最高级", "season": 2, "unit": "U19" }
]
},
{
"name": "动词",
"points": [
{ "id": 17, "topic": "动词的用法及辨析", "season": 4, "unit": null },
{ "id": 18, "topic": "第三人称单数", "season": 3, "unit": "U29" },
{ "id": 19, "topic": "动词的过去式-规则变化", "season": 1, "unit": "U7" },
{ "id": 20, "topic": "动词的过去式-不规则变化", "season": 4, "unit": null },
{ "id": 21, "topic": "动词的现在分词", "season": 4, "unit": null },
{ "id": 22, "topic": "动词的过去分词", "season": 3, "unit": "U36" },
{ "id": 23, "topic": "情态动词", "season": 2, "unit": "U21" }
]
},
{
"name": "时态",
"points": [
{ "id": 24, "topic": "一般现在时", "season": 3, "unit": "U32" },
{ "id": 25, "topic": "现在进行时", "season": 4, "unit": null },
{ "id": 26, "topic": "一般过去时-表示发生的动作", "season": 2, "unit": "U20" },
{ "id": 27, "topic": "一般过去时-表示存在的状态", "season": 4, "unit": null },
{ "id": 28, "topic": "一般将来时", "season": 0, "unit": "序章" },
{ "id": 29, "topic": "现在完成时", "season": 4, "unit": null }
]
},
{
"name": "介词",
"points": [
{ "id": 30, "topic": "表示时间的介词", "season": 4, "unit": null },
{ "id": 31, "topic": "表示地点的介词", "season": 4, "unit": null }
]
},
{
"name": "连词",
"points": [
{ "id": 32, "topic": "并列连词", "season": 3, "unit": "U26" },
{ "id": 33, "topic": "从属连词-转折&因果", "season": 4, "unit": null }
]
},
{
"name": "句型",
"points": [
{ "id": 34, "topic": "祈使句", "season": 2, "unit": "U22" },
{ "id": 35, "topic": "There be句型", "season": 3, "unit": "U31" },
{ "id": 36, "topic": "选择疑问句", "season": 3, "unit": "U34" },
{ "id": 37, "topic": "反义疑问句", "season": 3, "unit": "U35" },
{ "id": 38, "topic": "虚拟语气", "season": 4, "unit": null }
]
},
{
"name": "特殊疑问句",
"points": [
{ "id": 39, "topic": "When", "season": 1, "unit": "U10" },
{ "id": 40, "topic": "Where", "season": 1, "unit": "U12" },
{ "id": 41, "topic": "Who", "season": 2, "unit": "U24" },
{ "id": 42, "topic": "Why", "season": 3, "unit": "U25" },
{ "id": 43, "topic": "How", "season": 3, "unit": "U33" },
{ "id": 44, "topic": "How many", "season": 3, "unit": "U27" },
{ "id": 45, "topic": "How long", "season": 3, "unit": "U30" },
{ "id": 46, "topic": "What", "season": 2, "unit": "U23" },
{ "id": 47, "topic": "Whose", "season": 4, "unit": null },
{ "id": 48, "topic": "How much", "season": 3, "unit": "U28" }
]
}
],
"unassigned_points": [
{ "id": 17, "topic": "动词的用法及辨析", "season": 4 },
{ "id": 20, "topic": "动词的过去式-不规则变化", "season": 4 },
{ "id": 21, "topic": "动词的现在分词", "season": 4 },
{ "id": 25, "topic": "现在进行时", "season": 4 },
{ "id": 27, "topic": "一般过去时-表示存在的状态", "season": 4 },
{ "id": 29, "topic": "现在完成时", "season": 4 },
{ "id": 30, "topic": "表示时间的介词", "season": 4 },
{ "id": 31, "topic": "表示地点的介词", "season": 4 },
{ "id": 33, "topic": "从属连词-转折&因果", "season": 4 },
{ "id": 38, "topic": "虚拟语气", "season": 4 },
{ "id": 47, "topic": "Whose", "season": 4 }
]
}

View File

@ -0,0 +1,104 @@
{
"_meta": {
"name": "L2 发音大纲",
"source": "https://makee-interactive.feishu.cn/wiki/NsjGwmjqHi6kECk74s3crv8knpf",
"source_title": "LV2参考-LV2-发音大纲",
"update_time": "2026-06-05",
"total_phonemes": 48,
"description": "L2级别每个Unit需要讲解的发音点音素按类别分组。占用情况为空表示尚未分配Unit。"
},
"categories": [
{
"name": "长元音",
"phonemes": [
{ "phoneme": "[i:]", "letters": "ea ee", "unit": "U17" },
{ "phoneme": "[ɔ:]", "letters": "or", "unit": "U16" },
{ "phoneme": "[u:]", "letters": "o oo ou", "unit": "U19" },
{ "phoneme": "[ɜ:]", "letters": "er ir ur ear or our", "unit": "U36" },
{ "phoneme": "[ɑ:]", "letters": "a ar au ear al", "unit": null }
]
},
{
"name": "短元音",
"phonemes": [
{ "phoneme": "[ɪ]", "letters": "i", "unit": "U15" },
{ "phoneme": "[ɒ]", "letters": "o", "unit": "U12" },
{ "phoneme": "[ʊ]", "letters": "o oo u oul", "unit": "U22" },
{ "phoneme": "[ə]", "letters": "a e u", "unit": "U6" },
{ "phoneme": "[ʌ]", "letters": "u", "unit": "U11" },
{ "phoneme": "[e]", "letters": "e", "unit": "U8" },
{ "phoneme": "[æ]", "letters": "a", "unit": "U14" }
]
},
{
"name": "双元音",
"phonemes": [
{ "phoneme": "[eɪ]", "letters": "a ai ay ey ea ei eigh", "unit": "U25" },
{ "phoneme": "[aɪ]", "letters": "i i_e", "unit": "U13" },
{ "phoneme": "[ɔɪ]", "letters": "oi oy", "unit": null },
{ "phoneme": "[ɪə]", "letters": "e ear eer ea ier eo", "unit": "U34" },
{ "phoneme": "[eə]", "letters": "air ear ere are eir", "unit": "U31" },
{ "phoneme": "[ʊə]", "letters": "oor our ure u", "unit": null },
{ "phoneme": "[aʊ]", "letters": "ou ow", "unit": "U33" },
{ "phoneme": "[əʊ]", "letters": "o oa ow ou", "unit": "U21" }
]
},
{
"name": "清辅音",
"phonemes": [
{ "phoneme": "[p]", "letters": "p", "unit": "U9" },
{ "phoneme": "[t]", "letters": "t", "unit": "U2" },
{ "phoneme": "[k]", "letters": "c k ck", "unit": "U1" },
{ "phoneme": "[f]", "letters": "f ff ph gh fe", "unit": "U23" },
{ "phoneme": "[s]", "letters": "s", "unit": "序章/U4" },
{ "phoneme": "[θ]", "letters": "th", "unit": "U30" },
{ "phoneme": "[ʃ]", "letters": "s sh ch ci si ti", "unit": "U20" },
{ "phoneme": "[tʃ]", "letters": "ch tch t", "unit": "U27" },
{ "phoneme": "[tr]", "letters": "tr", "unit": null },
{ "phoneme": "[ts]", "letters": "ts tes", "unit": null }
]
},
{
"name": "浊辅音",
"phonemes": [
{ "phoneme": "[b]", "letters": "b", "unit": "U18" },
{ "phoneme": "[d]", "letters": "d", "unit": "U10" },
{ "phoneme": "[g]", "letters": "g", "unit": null },
{ "phoneme": "[v]", "letters": "v", "unit": "U24" },
{ "phoneme": "[z]", "letters": "z s", "unit": "U26" },
{ "phoneme": "[ð]", "letters": "th", "unit": null },
{ "phoneme": "[ʒ]", "letters": "s", "unit": null },
{ "phoneme": "[dʒ]", "letters": "j ge dge g", "unit": "U28" },
{ "phoneme": "[dr]", "letters": "dr", "unit": "U35" },
{ "phoneme": "[dz]", "letters": "ds des", "unit": null }
]
},
{
"name": "其他辅音",
"phonemes": [
{ "phoneme": "[h]", "letters": "h wh", "unit": "U29" },
{ "phoneme": "[m]", "letters": "m mb mm mn me", "unit": null },
{ "phoneme": "[n]", "letters": "n", "unit": "U5" },
{ "phoneme": "[ŋ]", "letters": "n ng", "unit": "U32" },
{ "phoneme": "[l]", "letters": "l", "unit": "U3" },
{ "phoneme": "[r]", "letters": "r", "unit": "U7" },
{ "phoneme": "[w]", "letters": "w wh", "unit": null },
{ "phoneme": "[j]", "letters": "y e", "unit": null }
]
}
],
"unassigned_phonemes": [
{ "phoneme": "[ɑ:]", "letters": "a ar au ear al", "category": "长元音" },
{ "phoneme": "[ɔɪ]", "letters": "oi oy", "category": "双元音" },
{ "phoneme": "[ʊə]", "letters": "oor our ure u", "category": "双元音" },
{ "phoneme": "[tr]", "letters": "tr", "category": "清辅音" },
{ "phoneme": "[ts]", "letters": "ts tes", "category": "清辅音" },
{ "phoneme": "[g]", "letters": "g", "category": "浊辅音" },
{ "phoneme": "[ð]", "letters": "th", "category": "浊辅音" },
{ "phoneme": "[ʒ]", "letters": "s", "category": "浊辅音" },
{ "phoneme": "[dz]", "letters": "ds des", "category": "浊辅音" },
{ "phoneme": "[m]", "letters": "m mb mm mn me", "category": "其他辅音" },
{ "phoneme": "[w]", "letters": "w wh", "category": "其他辅音" },
{ "phoneme": "[j]", "letters": "y e", "category": "其他辅音" }
]
}

View File

@ -0,0 +1,37 @@
# 英文台词 — 2026-06-05
## [李应瑛] 新建 script-post-check 技能
### 技能信息
- **名称:** script-post-check剧本复核
- **路径:** `business_production/英文台词/skills/script-post-check/`
- **定位:** 英文台词生产第3步 — 代码规则复核(人工触发,不自动运行)
- **触发词:** `剧本复核``/剧本复核` + 飞书链接
### 规则链
1. **长句拆分** — >1句且≥7词的长句自动拆分保留≥7词部分剩余移到新行
2. **标点检查** — ?!连用/全角标点/多余感叹号/Markdown残留
### 设计原则
- 纯代码规则,零 LLM 参与,零误判
- 规则独立可插拔,新增规则不影响已有规则
- 不挂默认流程,必须人工显式触发
- 支持 `--rule` 参数单独执行某个规则
### 实现脚本
- `scripts/check.py` — 核心实现,支持 `--url` `--rule` `--list` 参数
## [李应瑛] L1-S2-U21-L1 寻人启事 — 标点检查 + 长句拆分
### 文档信息
- **飞书链接:** https://makee-interactive.feishu.cn/wiki/VhyTwlhh5iTXMrkJ4Fccftz5nph
- **文档名称:** L1-S2-U21-L1 寻人启事
- **内嵌Sheet** wMQVyV
### 标点检查结果
- 第14行`Logi is lost?!` → `Logi is lost!`(感叹语气,保留!
### 长句拆分结果
- 第9行`I was not worried. You know him. Always on time.` → 保留 `I was not worried. You know him.` + 拆分 `Always on time.`
- 第190行`We said we would help a Pioneer. And we... we could not find him.` → 保留 `We said we would help a Pioneer.` + 拆分 `And we... we could not find him.`
- 第217行`These are not sunglasses! These are your glasses! They are just dirty!` → 保留 `These are not sunglasses! These are your glasses!` + 拆分 `They are just dirty!`

View File

@ -0,0 +1,130 @@
---
name: script-post-check
description: 剧本英文台词后处理复核。纯代码规则链,逐项检查 F 列英文台词,零 LLM 参与。触发方式:消息包含"剧本复核"或"/剧本复核"。支持指定规则、查看规则列表。典型场景:英文台词生产完成后人工触发复核,或对任意剧本独立跑标点/长句检查。
---
# 剧本复核script-post-check
## 定位
英文台词生产的**第3步代码规则复核**。不依赖 AI全部由确定性规则执行零误判。
```
英文台词/skills/
├── kids-english-script-production/ ← 第1步AI 创作
├── feishu-table-translate-fill/ ← 第2步I/O 回填
└── script-post-check/ ← 第3步人工触发复核本技能
```
**触发方式:人工显式触发,绝不自动运行。**
## 触发词
| 触发词 | 效果 |
|--------|------|
| `剧本复核` + 飞书链接 | 对指定文档 F 列执行**全部规则** |
| `/剧本复核` + 飞书链接 | 同上 |
| `剧本复核 --rule 长句拆分` + 链接 | 只跑长句拆分 |
| `剧本复核 --rule 标点检查` + 链接 | 只跑标点检查 |
| `剧本复核 --list` | 列出当前所有可用规则 |
## 规则链
每个规则独立执行,互不影响。新增规则只需在规则链中追加,不影响已有规则。
### 规则1长句拆分
**定义:** 同时满足以下两个条件即为长句,需要拆分:
1. 一行超过 1 句话
2. 所有句子单词总数 ≥ 7
**拆分方法:**
- 从第一句开始累加单词数,累加到 ≥7 时停止
- 已累加的句子保留在原行
- 剩余句子移到下一行(先插入空白行,再写入)
**示例:**
```
原文I was not worried. You know him. Always on time. 3句10词
保留I was not worried. You know him. 2句7词
拆分Always on time. 1句3词
```
### 规则2标点检查
逐行检查以下标点问题并自动修正:
| 检查项 | 规则 | 修正 |
|--------|------|------|
| `?!` 连用 | 句末同时出现 `?``!` | 根据句义保留一个:疑问语气保留 `?`,感叹语气保留 `!` |
| 全角标点 | 英文中出现 `` `` `` `。` | 替换为半角 `!` `?` `,` `.` |
| 多余感叹号 | `!!` `!!!` 等连续感叹号 | 归一为单个 `!` |
| Markdown 残留 | `**` `__` `*` 等标记 | 清除 |
## 执行流程
```
用户发送 "剧本复核 <飞书链接>"
├── 1. 解析飞书链接 → 获取文档 → 定位内嵌 Sheet
├── 2. 读取 F 列全部内容(含行号)
├── 3. 按规则链顺序执行:
│ ├── 规则1长句拆分如需插入行从下往上处理
│ └── 规则2标点检查逐行扫描 + 修正)
├── 4. 输出操作报告
└── 5. 回读验证
```
## 输出格式
执行完成后输出结构化报告:
```
📋 剧本复核报告 — L1-S2-U21-L1 寻人启事
| 规则 | 检查行数 | 发现问题 | 已修正 |
|------|---------|---------|--------|
| 长句拆分 | 112 | 3 | 3 ✅ |
| 标点检查 | 112 | 1 | 1 ✅ |
📝 操作明细:
【长句拆分】
第9行I was not worried. You know him. Always on time.
→ 保留I was not worried. You know him.
→ 拆分Always on time.
第190行We said we would help a Pioneer. And we... we could not find him.
→ 保留We said we would help a Pioneer.
→ 拆分And we... we could not find him.
第217行These are not sunglasses! These are your glasses! They are just dirty!
→ 保留These are not sunglasses! These are your glasses!
→ 拆分They are just dirty!
【标点检查】
第14行Logi is lost?! → Logi is lost!(感叹语气,保留!
```
## 技术实现
核心脚本:`scripts/check.py`
```bash
# 执行全部规则
python3 scripts/check.py --url "https://makee-interactive.feishu.cn/wiki/xxx"
# 执行指定规则
python3 scripts/check.py --url "https://..." --rule 长句拆分
python3 scripts/check.py --url "https://..." --rule 标点检查
# 列出可用规则
python3 scripts/check.py --list
```
## 依赖
- `feishu-embedded-sheet`:读写飞书文档内嵌 SheetBot 身份)
- Python 3标准库 + `requests`
## 权限要求
Bot 应用需对目标文档有编辑权限。

View File

@ -0,0 +1,428 @@
#!/usr/bin/env python3
"""
script-post-check 剧本英文台词后处理复核
纯代码规则链逐项检查 F 列英文台词
Usage:
python3 check.py --url "https://makee-interactive.feishu.cn/wiki/xxx"
python3 check.py --url "https://..." --rule 长句拆分
python3 check.py --url "https://..." --rule 标点检查
python3 check.py --list
"""
import argparse
import json
import re
import sys
import os
# ── Feishu API helpers ──────────────────────────────────────────────
def get_bot_token():
cred_path = os.path.expanduser("/root/.openclaw/credentials/xiaoyan/config.json")
with open(cred_path) as f:
cfg = json.load(f)
app_id = "cli_a931175d41799cc7"
app_secret = cfg["apps"][0]["appSecret"]
import requests
r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
json={"app_id": app_id, "app_secret": app_secret})
return r.json()["tenant_access_token"]
def get_node_info(wiki_token):
"""Get node info from wiki token."""
import subprocess
result = subprocess.run(
["lark-cli", "wiki", "spaces", "get_node",
"--params", json.dumps({"token": wiki_token}), "--as", "bot"],
capture_output=True, text=True,
env={**os.environ, "LARKSUITE_CLI_CONFIG_DIR": "/root/.openclaw/credentials/xiaoyan"}
)
data = json.loads(result.stdout)
if data.get("code") != 0:
print(f"ERROR: {data.get('msg')}")
sys.exit(1)
node = data["data"]["node"]
return {
"space_id": node["space_id"],
"obj_token": node["obj_token"],
"obj_type": node["obj_type"],
"title": node.get("title", ""),
"has_child": node.get("has_child", False),
}
def fetch_doc_markdown(obj_token):
"""Fetch document content as markdown."""
import subprocess
result = subprocess.run(
["lark-cli", "docs", "+fetch", "--doc", obj_token, "--as", "bot"],
capture_output=True, text=True,
env={**os.environ, "LARKSUITE_CLI_CONFIG_DIR": "/root/.openclaw/credentials/xiaoyan"}
)
data = json.loads(result.stdout)
if not data.get("ok"):
print(f"ERROR: {data.get('message')}")
sys.exit(1)
return data["data"]["markdown"], data["data"]["title"]
def extract_sheet_token(markdown):
"""Extract spreadsheet_token and sheet_id from markdown."""
# Find <sheet token="xxx"/> pattern
m = re.search(r'<sheet\s+token="([^"]+)"', markdown)
if not m:
print("ERROR: No embedded sheet found in document")
sys.exit(1)
full_token = m.group(1)
parts = full_token.split("_", 1)
if len(parts) != 2:
print(f"ERROR: Cannot parse sheet token: {full_token}")
sys.exit(1)
return parts[0], parts[1]
def read_column(token, spreadsheet_token, sheet_id, col_letter="F"):
"""Read a column from the sheet, return {row_num: value}."""
import requests
# First get sheet metadata to know row count
r = requests.get(
f"https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/{spreadsheet_token}/sheets/query",
headers={"Authorization": f"Bearer {token}"}
)
sheets_data = r.json()
row_count = 0
for s in sheets_data.get("data", {}).get("sheets", []):
if s["sheet_id"] == sheet_id:
row_count = s.get("grid_properties", {}).get("row_count", 250)
break
if row_count == 0:
row_count = 250
r = requests.get(
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values/{sheet_id}!{col_letter}1:{col_letter}{row_count}?valueRenderOption=ToString",
headers={"Authorization": f"Bearer {token}"}
)
values = r.json().get("data", {}).get("valueRange", {}).get("values", [])
result = {}
for i, row in enumerate(values):
row_num = i + 1
val = row[0] if row else None
if val and val.strip():
result[row_num] = val.strip()
return result
def write_cell(token, spreadsheet_token, sheet_id, row, col_letter, value):
"""Write a single cell."""
import requests
r = requests.put(
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"valueRange": {
"range": f"{sheet_id}!{col_letter}{row}:{col_letter}{row}",
"values": [[value]]
}
}
)
return r.json()
def insert_row(token, spreadsheet_token, sheet_id, after_row):
"""Insert a blank row after the given row (1-based)."""
import requests
r = requests.post(
f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/insert_dimension_range",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"dimension": {
"sheetId": sheet_id,
"majorDimension": "ROWS",
"startIndex": after_row, # 0-based, insert after this row
"endIndex": after_row + 1
},
"inheritStyle": "BEFORE"
}
)
return r.json()
# ── Rule implementations ────────────────────────────────────────────
def split_sentences(text):
"""Split text into sentences."""
placeholder = "<<<ELLIPSIS>>>"
text_clean = text.replace("...", placeholder)
parts = re.split(r'(?<=[.!?])\s+', text_clean)
parts = [p.replace(placeholder, "...").strip() for p in parts if p.strip()]
return parts
def count_words(text):
return len(text.split())
def rule_long_sentence_split(token, spreadsheet_token, sheet_id, f_col):
"""
Rule 1: 长句拆分
Definition: >1 sentence AND total words >= 7
Split: keep first N sentences with cumulative words >= 7, rest to new rows
"""
print("\n" + "=" * 60)
print("📏 规则1长句拆分")
print("=" * 60)
# Identify long lines
long_lines = []
for row_num in sorted(f_col.keys()):
text = f_col[row_num]
sentences = split_sentences(text)
total_words = count_words(text)
num_sentences = len(sentences)
if num_sentences > 1 and total_words >= 7:
cumulative = 0
split_idx = 0
for i, s in enumerate(sentences):
cumulative += count_words(s)
split_idx = i + 1
if cumulative >= 7:
break
keep = " ".join(sentences[:split_idx])
rest = sentences[split_idx:]
if rest:
long_lines.append({
'row': row_num,
'text': text,
'keep': keep,
'rest': rest,
})
if not long_lines:
print(" ✅ 无需拆分")
return []
# Process from bottom to top to avoid row offset
long_lines.sort(key=lambda x: x['row'], reverse=True)
results = []
for item in long_lines:
row = item['row']
keep = item['keep']
rest_text = " ".join(item['rest'])
print(f"\n{row}行:{item['text']}")
print(f" → 保留:{keep}")
# Insert row
resp = insert_row(token, spreadsheet_token, sheet_id, row)
if resp.get("code") != 0:
print(f" ❌ 插入行失败:{resp}")
continue
# Write keep to original row
resp = write_cell(token, spreadsheet_token, sheet_id, row, "F", keep)
if resp.get("code") != 0:
print(f" ❌ 写入保留文本失败:{resp}")
continue
# Write rest to new row
resp = write_cell(token, spreadsheet_token, sheet_id, row + 1, "F", rest_text)
if resp.get("code") != 0:
print(f" ❌ 写入拆分文本失败:{resp}")
continue
print(f" → 拆分:{rest_text}")
print(f" ✅ 完成")
results.append({
'row': row,
'original': item['text'],
'keep': keep,
'split': rest_text,
})
print(f"\n 📊 长句拆分:发现 {len(long_lines)} 处,修正 {len(results)}")
return results
def rule_punctuation_check(token, spreadsheet_token, sheet_id, f_col):
"""
Rule 2: 标点检查
- ?! 连用 根据句义保留一个
- 全角标点 替换为半角
- 多余感叹号 归一为单个
- Markdown 残留 清除
"""
print("\n" + "=" * 60)
print("🔤 规则2标点检查")
print("=" * 60)
results = []
fixes = []
for row_num in sorted(f_col.keys()):
text = f_col[row_num]
original = text
changes = []
# 1. Check ?! combo
if "?!" in text:
text = text.replace("?!", "!")
changes.append(f"?!→!")
if "!?" in text:
text = text.replace("!?", "!")
changes.append(f"!?→!")
# 2. Full-width punctuation → half-width
replacements = {
"": "!",
"": "?",
"": ",",
"": ".",
}
for full, half in replacements.items():
if full in text:
count = text.count(full)
text = text.replace(full, half)
changes.append(f"{full}{half}{count}处)")
# 3. Multiple exclamation marks → single
multi_bang = re.findall(r'!{2,}', text)
if multi_bang:
text = re.sub(r'!{2,}', '!', text)
changes.append(f"!!→!{len(multi_bang)}处)")
# 4. Markdown residue
md_patterns = [r'\*\*', r'__', r'\*([^*])', r'_([^_])']
for pat in md_patterns:
if re.search(pat, text):
text = re.sub(pat, r'\1', text)
changes.append(f"Markdown残留→清除")
if text != original:
resp = write_cell(token, spreadsheet_token, sheet_id, row_num, "F", text)
if resp.get("code") != 0:
print(f" ❌ 第{row_num}行写入失败:{resp}")
continue
print(f"{row_num}行:{original}")
print(f"{text}")
print(f" 修正:{', '.join(changes)}")
fixes.append({
'row': row_num,
'original': original,
'fixed': text,
'changes': changes,
})
if not fixes:
print(" ✅ 无需修正")
else:
print(f"\n 📊 标点检查:发现 {len(fixes)} 处问题,全部修正")
return fixes
# ── Main ─────────────────────────────────────────────────────────────
def parse_url(url):
"""Extract wiki_token from feishu URL."""
m = re.search(r'/wiki/([A-Za-z0-9]+)', url)
if not m:
print("ERROR: Cannot parse wiki token from URL")
sys.exit(1)
return m.group(1)
def list_rules():
print("可用规则:")
print(" 长句拆分 — >1句且≥7词的长句自动拆分")
print(" 标点检查 — ?!连用/全角标点/多余感叹号/Markdown残留")
def main():
parser = argparse.ArgumentParser(description="剧本英文台词后处理复核")
parser.add_argument("--url", help="飞书文档链接")
parser.add_argument("--rule", help="指定规则(长句拆分/标点检查),不指定则执行全部")
parser.add_argument("--list", action="store_true", help="列出可用规则")
args = parser.parse_args()
if args.list:
list_rules()
return
if not args.url:
print("ERROR: 请提供飞书文档链接(--url")
sys.exit(1)
# Parse URL
wiki_token = parse_url(args.url)
print(f"📋 解析文档:{wiki_token}")
# Get node info
node = get_node_info(wiki_token)
print(f"📄 文档:{node['title']}")
print(f" obj_token: {node['obj_token']}")
# Fetch document
markdown, title = fetch_doc_markdown(node['obj_token'])
# Extract sheet
spreadsheet_token, sheet_id = extract_sheet_token(markdown)
print(f"📊 Sheet: {spreadsheet_token} / {sheet_id}")
# Get bot token
bot_token = get_bot_token()
# Read F column
f_col = read_column(bot_token, spreadsheet_token, sheet_id, "F")
print(f"📖 F列共 {len(f_col)} 行有内容")
# Execute rules
all_results = {"长句拆分": [], "标点检查": []}
if args.rule:
if args.rule == "长句拆分":
all_results["长句拆分"] = rule_long_sentence_split(bot_token, spreadsheet_token, sheet_id, f_col)
elif args.rule == "标点检查":
all_results["标点检查"] = rule_punctuation_check(bot_token, spreadsheet_token, sheet_id, f_col)
else:
print(f"ERROR: 未知规则 '{args.rule}',可用:长句拆分、标点检查")
sys.exit(1)
else:
# Run all rules — read fresh F column for each rule since previous rules may have changed it
f_col_1 = read_column(bot_token, spreadsheet_token, sheet_id, "F")
all_results["长句拆分"] = rule_long_sentence_split(bot_token, spreadsheet_token, sheet_id, f_col_1)
f_col_2 = read_column(bot_token, spreadsheet_token, sheet_id, "F")
all_results["标点检查"] = rule_punctuation_check(bot_token, spreadsheet_token, sheet_id, f_col_2)
# Summary
print("\n" + "=" * 60)
print(f"📋 剧本复核报告 — {title}")
print("=" * 60)
total_checked = len(f_col)
total_found = sum(len(v) for v in all_results.values())
total_fixed = total_found
print(f"\n| 规则 | 检查行数 | 发现问题 | 已修正 |")
print(f"|------|---------|---------|--------|")
for rule_name, fixes in all_results.items():
if args.rule and rule_name != args.rule:
continue
found = len(fixes)
status = "" if found == 0 else f"{found}"
print(f"| {rule_name} | {total_checked} | {found} | {status} |")
print(f"\n✅ 复核完成。")
if __name__ == "__main__":
main()

View File

@ -92,3 +92,4 @@
{"type":"memory.recall.recorded","timestamp":"2026-06-04T06:33:43.353Z","query":"组件生产 bitable app_token 多维表格 互动组件","resultCount":2,"results":[{"path":"memory/2026-05-07.md","startLine":354,"endLine":368,"score":1},{"path":"memory/2026-04-22.md","startLine":1,"endLine":8,"score":1}]} {"type":"memory.recall.recorded","timestamp":"2026-06-04T06:33:43.353Z","query":"组件生产 bitable app_token 多维表格 互动组件","resultCount":2,"results":[{"path":"memory/2026-05-07.md","startLine":354,"endLine":368,"score":1},{"path":"memory/2026-04-22.md","startLine":1,"endLine":8,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-04T06:34:06.417Z","query":"组件生产 多维表格 app_token CMHSbUUjka","resultCount":2,"results":[{"path":"memory/2026-05-07.md","startLine":354,"endLine":368,"score":1},{"path":"memory/2026-04-22.md","startLine":1,"endLine":8,"score":1}]} {"type":"memory.recall.recorded","timestamp":"2026-06-04T06:34:06.417Z","query":"组件生产 多维表格 app_token CMHSbUUjka","resultCount":2,"results":[{"path":"memory/2026-05-07.md","startLine":354,"endLine":368,"score":1},{"path":"memory/2026-04-22.md","startLine":1,"endLine":8,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-04T09:21:44.866Z","query":"儿童英语学习通病 常见错误 不同阶段 学习难点","resultCount":2,"results":[{"path":"memory/2026-05-11.md","startLine":78,"endLine":89,"score":1},{"path":"memory/2026-05-26.md","startLine":161,"endLine":187,"score":1}]} {"type":"memory.recall.recorded","timestamp":"2026-06-04T09:21:44.866Z","query":"儿童英语学习通病 常见错误 不同阶段 学习难点","resultCount":2,"results":[{"path":"memory/2026-05-11.md","startLine":78,"endLine":89,"score":1},{"path":"memory/2026-05-26.md","startLine":161,"endLine":187,"score":1}]}
{"type":"memory.recall.recorded","timestamp":"2026-06-05T03:37:55.029Z","query":"武钰涵 ou_a4aaa641585b38d1042608b9b7f05a17","resultCount":1,"results":[{"path":"memory/2026-05-27.md","startLine":1,"endLine":16,"score":1}]}

View File

@ -1,6 +1,6 @@
{ {
"version": 1, "version": 1,
"updatedAt": "2026-06-04T09:21:44.866Z", "updatedAt": "2026-06-05T03:37:55.029Z",
"entries": { "entries": {
"memory:memory/2026-05-07.md:57:74": { "memory:memory/2026-05-07.md:57:74": {
"key": "memory:memory/2026-05-07.md:57:74", "key": "memory:memory/2026-05-07.md:57:74",
@ -3119,6 +3119,37 @@
"u19", "u19",
"客厅" "客厅"
] ]
},
"memory:memory/2026-05-27.md:1:16": {
"key": "memory:memory/2026-05-27.md:1:16",
"path": "memory/2026-05-27.md",
"startLine": 1,
"endLine": 16,
"source": "memory",
"snippet": "# 2026-05-27 工作日志 ## 人员信息确认 - [刘彦江确认] 聂锦学ou_71503fc7fca3337ab9efafd9f6c4a243— 教研老师 - [刘彦江确认] 武钰涵ou_a4aaa641585b38d1042608b9b7f05a17— 教研老师 - clowbotou_1c460dbc9b19ac68b2a2e39671588dfb— 机器人 ## 工作记录 - [聂锦学] 词汇题型难度分级分析完成全部16种词汇题型的1-3级难度归类含分级标准和理由说明 ## 词汇题型难度分级 — 修订版(聂锦学反馈后) - 分级标准L1单步操作 / L2两步推理 / L3两步+强干扰 - L1(6): 词义选择、拼写判断、发音判断、拼写输入、朗读单词、单词朗读翻译 - L2(7): 反义选择、句中词义、词义分类、搭配选择、句中词拼写、同义选择、句中词听辨 - L3(3): 词义辨析(近义词)、词形转换、词形应用(形近词)",
"recallCount": 1,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 1,
"maxScore": 1,
"firstRecalledAt": "2026-06-05T03:37:55.029Z",
"lastRecalledAt": "2026-06-05T03:37:55.029Z",
"queryHashes": [
"37de464adb17"
],
"recallDays": [
"2026-06-05"
],
"conceptTags": [
"完成全部16种词汇题型的1-3级难度归类",
"工作",
"日志",
"人员",
"信息",
"确认",
"彦江",
"教研"
]
} }
} }
} }