ai_member_xiaoyan/output/l1_pedagogy_rules_v4.js

487 lines
59 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* PedagogyRules v4.0.0 — L1/L2 教研规则诊断引擎
*
* ========== v4 更新 ==========
* 新增 5 个校验维度(来自 16 个教研 Skill
* 6. 题型合规检测componentCompliance— dialogue/info config skills
* 7. 配置字段完整性configIntegrity— audit_l1_config 7项自动检查
* 8. 知识点掌握度预测masteryPrediction— knowledge-mastery-calculator
* 9. 格式规范质量formatQuality— 文本输出规范校验
* 10. 语法问题检测grammarIssues— 三单/拼写/混用检测
*
* 数据源:
* - business_knowledge/L1_word_list.json + L2_word_list.json
* - business_knowledge/L1_pattern_list.json + L2_pattern_list.json
* - MEMORY.md 教研规范
* - 16 个 skill 规则文件
*
* 接入:<script src="l1_pedagogy_rules_v4.js"></script>
* API PedagogyRules.evaluate(summary, item, level, options)
*/
(function(g){"use strict";
var VERSION = '4.0.0';
// ═══════════ DATA: WORD LISTS ═══════════
var W={L1:{"red":{"pos":"adj","meaning":"红色的","unit":"S0-U0-L1","diff":1},"hat":{"pos":"n","meaning":"帽子","unit":"S0-U0-L1","diff":1},"blue":{"pos":"adj","meaning":"蓝的","unit":"S0-U0-L1","diff":1},"bag":{"pos":"n","meaning":"袋,包","unit":"S0-U0-L1","diff":1},"orange":{"pos":"n","meaning":"橙子","unit":"S1-U10-L3","diff":1},"green":{"pos":"adj","meaning":"绿色的","unit":"S0-U0-L2","diff":1},"dress":{"pos":"n","meaning":"连衣裙","unit":"S0-U0-L2","diff":1},"there":{"pos":"adv","meaning":"那里","unit":"S0-U0-L3","diff":1},"put":{"pos":"v","meaning":"放","unit":"S0-U0-L3","diff":1},"purple":{"pos":"adj","meaning":"紫色的","unit":"S0-U0-L5","diff":1},"give":{"pos":"v","meaning":"给","unit":"S0-U0-L3","diff":1},"water":{"pos":"n","meaning":"水","unit":"S0-U0-L4","diff":1},"here":{"pos":"adv","meaning":"在这里","unit":"S0-U0-L4","diff":1},"dirty":{"pos":"adj","meaning":"肮脏的","unit":"S0-U0-L4","diff":1},"clean":{"pos":"v","meaning":"清洁","unit":"S0-U0-L4","diff":1},"pink":{"pos":"adj","meaning":"粉红色的","unit":"S0-U0-L2","diff":1},"get":{"pos":"v","meaning":"收到","unit":"","diff":1},"wall":{"pos":"n","meaning":"墙","unit":"S1-U1-L1","diff":1},"table":{"pos":"n","meaning":"桌子","unit":"S1-U1-L1","diff":1},"floor":{"pos":"n","meaning":"地板","unit":"S1-U1-L1","diff":1},"hand":{"pos":"n","meaning":"手","unit":"S1-U1-L2","diff":1},"foot":{"pos":"n","meaning":"脚","unit":"S1-U1-L2","diff":1},"black":{"pos":"adj","meaning":"黑的","unit":"S1-U1-L2","diff":1},"tomato":{"pos":"n","meaning":"西红柿","unit":"S1-U1-L3","diff":1},"fish":{"pos":"n","meaning":"鱼","unit":"S1-U1-L3","diff":1},"chicken":{"pos":"n","meaning":"鸡肉","unit":"S1-U1-L3","diff":1},"nose":{"pos":"n","meaning":"鼻子","unit":"S1-U1-L4","diff":1},"eye":{"pos":"n","meaning":"眼睛","unit":"S1-U1-L4","diff":1},"brown":{"pos":"adj","meaning":"棕色的","unit":"S1-U1-L4","diff":1},"yellow":{"pos":"adj","meaning":"黄色的","unit":"S1-U1-L5","diff":1},"white":{"pos":"adj","meaning":"白色的","unit":"S1-U1-L5","diff":1},"go":{"pos":"v","meaning":"去,走","unit":"S1-U2-L1","diff":1},"dog":{"pos":"n","meaning":"狗","unit":"S1-U2-L1","diff":1},"come":{"pos":"v","meaning":"来","unit":"S1-U2-L1","diff":1},"cat":{"pos":"n","meaning":"猫","unit":"S1-U2-L1","diff":1},"pie":{"pos":"n","meaning":"馅饼","unit":"S1-U2-L2","diff":1},"can":{"pos":"v","meaning":"能,会","unit":"S1-U2-L4","diff":1},"open":{"pos":"adj","meaning":"开着的","unit":"","diff":1},"happy":{"pos":"adj","meaning":"快乐的","unit":"S1-U2-L5","diff":1},"see":{"pos":"v","meaning":"看见","unit":"S1-U3-L1","diff":1},"1":{"pos":"num","meaning":"1","unit":"S1-U3-L1","diff":1},"paper":{"pos":"adj","meaning":"纸质的","unit":"","diff":1},"book":{"pos":"n","meaning":"书","unit":"S1-U3-L2","diff":1},"3":{"pos":"num","meaning":"3","unit":"S1-U3-L2","diff":1},"2":{"pos":"num","meaning":"2","unit":"S1-U3-L2","diff":1},"pen":{"pos":"n","meaning":"钢笔","unit":"S1-U3-L3","diff":1},"box":{"pos":"n","meaning":"盒子","unit":"S1-U3-L3","diff":1},"4":{"pos":"num","meaning":"4","unit":"S1-U3-L3","diff":1},"6":{"pos":"num","meaning":"6","unit":"S1-U3-L4","diff":1},"5":{"pos":"num","meaning":"5","unit":"S1-U3-L4","diff":1},"sing":{"pos":"v","meaning":"唱","unit":"S1-U4-L1","diff":1},"eat":{"pos":"v","meaning":"吃","unit":"S1-U4-L2","diff":1},"7":{"pos":"num","meaning":"7","unit":"S1-U4-L2","diff":1},"rug":{"pos":"n","meaning":"小地毯","unit":"S1-U4-L3","diff":1},"door":{"pos":"n","meaning":" 门","unit":"","diff":1},"bed":{"pos":"n","meaning":"床","unit":"S1-U4-L3","diff":1},"9":{"pos":"num","meaning":"9","unit":"S1-U4-L4","diff":1},"8":{"pos":"num","meaning":"8","unit":"S1-U4-L4","diff":1},"10":{"pos":"num","meaning":"10","unit":"S1-U4-L5","diff":1},"zoo":{"pos":"n","meaning":"动物园","unit":"","diff":1},"zebra":{"pos":"n","meaning":"斑马","unit":"S1-U7-L2","diff":1},"young":{"pos":"adj","meaning":"年轻的","unit":"","diff":1},"year":{"pos":"n","meaning":"年","unit":"","diff":1},"write":{"pos":"v","meaning":"写","unit":"","diff":1},"woman":{"pos":"n","meaning":"女人","unit":"S1-U5-L1","diff":1},"window":{"pos":"n","meaning":"窗户","unit":"S1-U12-L4","diff":1},"warm":{"pos":"adj","meaning":"温暖的","unit":"","diff":1},"want":{"pos":"v","meaning":"想要","unit":"","diff":1},"walk":{"pos":"v","meaning":"走","unit":"S1-U7-L2","diff":1},"very":{"pos":"adv","meaning":"非常","unit":"","diff":1},"under":{"pos":"prep","meaning":"在...下面","unit":"S2-U14-L5","diff":1},"try":{"pos":"v","meaning":"尝试","unit":"S1-U12-L5","diff":1},"tree":{"pos":"n","meaning":"树","unit":"S1-U7-L1","diff":1},"train":{"pos":"n","meaning":"火车","unit":"S1-U8-L1","diff":1},"too":{"pos":"adv","meaning":"也","unit":"S1-U12-L2","diff":1},"tomorrow":{"pos":"n","meaning":"明天","unit":"","diff":1},"today":{"pos":"n","meaning":"今天","unit":"","diff":1},"tiger":{"pos":"n","meaning":"老虎","unit":"S1-U5-L3","diff":1},"those":{"pos":"pron","meaning":"那些","unit":"S1-U12-L1","diff":1},"this":{"pos":"pron","meaning":"这个","unit":"S1-U12-L1","diff":1},"these":{"pos":"pron","meaning":"这些","unit":"S1-U12-L1","diff":1},"that":{"pos":"pron","meaning":"那个","unit":"S1-U12-L1","diff":1},"tell":{"pos":"v","meaning":"告诉","unit":"","diff":1},"teacher":{"pos":"n","meaning":"老师","unit":"S2-U14-L2","diff":1},"talk":{"pos":"v","meaning":"说话","unit":"","diff":1},"swim":{"pos":"v","meaning":"游泳","unit":"S1-U11-L5","diff":1},"sun":{"pos":"n","meaning":"太阳","unit":"S1-U8-L2","diff":1},"study":{"pos":"v","meaning":"学习","unit":"S2-U14-L1","diff":1},"student":{"pos":"n","meaning":"学生","unit":"S2-U14-L1","diff":1},"speak":{"pos":"v","meaning":"说","unit":"","diff":1},"some":{"pos":"det","meaning":"一些","unit":"","diff":1},"sofa":{"pos":"n","meaning":"沙发","unit":"","diff":1},"sock":{"pos":"n","meaning":"袜子","unit":"","diff":1},"small":{"pos":"adj","meaning":"小的","unit":"S1-U9-L1","diff":1},"sleep":{"pos":"v","meaning":"睡觉","unit":"S1-U7-L4","diff":1},"skirt":{"pos":"n","meaning":"裙子","unit":"S1-U9-L4","diff":1},"sit":{"pos":"v","meaning":"坐下","unit":"S1-U8-L1","diff":1},"sister":{"pos":"n","meaning":"姐姐、妹妹","unit":"S1-U8-L1","diff":1},"short":{"pos":"adj","meaning":"短的","unit":"","diff":1},"shoe":{"pos":"n","meaning":"鞋","unit":"","diff":1},"shirt":{"pos":"n","meaning":"衬衫","unit":"S1-U9-L1","diff":1},"ship":{"pos":"n","meaning":"船","unit":"","diff":1},"sheep":{"pos":"n","meaning":"绵羊","unit":"S1-U5-L3","diff":1},"school":{"pos":"n","meaning":"学校","unit":"S2-U14-L1","diff":1},"say":{"pos":"v","meaning":"说","unit":"","diff":1},"sad":{"pos":"adj","meaning":"悲伤的","unit":"","diff":1},"run":{"pos":"v","meaning":"跑","unit":"","diff":1},"ruler":{"pos":"n","meaning":"尺子","unit":"","diff":1},"room":{"pos":"n","meaning":"房间","unit":"","diff":1},"rice":{"pos":"n","meaning":"米饭","unit":"S1-U6-L4","diff":1},"read":{"pos":"v","meaning":"阅读","unit":"","diff":1},"radio":{"pos":"n","meaning":"收音机","unit":"S1-U9-L2","diff":1},"potato":{"pos":"n","meaning":"土豆","unit":"S1-U7-L3","diff":1},"play":{"pos":"v","meaning":"玩","unit":"S2-U14-L1","diff":1},"plane":{"pos":"n","meaning":"飞机","unit":"S1-U8-L3","diff":1},"pig":{"pos":"n","meaning":"猪","unit":"S1-U7-L4","diff":1},"phone":{"pos":"v","meaning":"给…...打电话","unit":"","diff":1},"people":{"pos":"n","meaning":"人","unit":"","diff":1},"pencil":{"pos":"n","meaning":"铅笔","unit":"","diff":1},"pea":{"pos":"n","meaning":"豌豆","unit":"","diff":1},"pe":{"pos":"n","meaning":"体育","unit":"S1-U10-L4","diff":1},"paint":{"pos":"v","meaning":"(用颜料)绘画","unit":"","diff":1},"one":{"pos":"pron","meaning":"一个人(或物)","unit":"","diff":1},"old":{"pos":"adj","meaning":"年老的","unit":"","diff":1},"now":{"pos":"adv","meaning":"现在","unit":"","diff":1},"night":{"pos":"n","meaning":"夜晚","unit":"S1-U12-L5","diff":1},"nice":{"pos":"adj","meaning":"好的","unit":"S1-U6-L3","diff":1},"new":{"pos":"adj","meaning":"新的","unit":"S1-U9-L2","diff":1},"mum":{"pos":"n","meaning":"妈妈","unit":"S1-U8-L4","diff":1},"mouth":{"pos":"n","meaning":"嘴巴","unit":"","diff":1},"mother":{"pos":"n","meaning":"母亲","unit":"","diff":1},"morning":{"pos":"n","meaning":"早上","unit":"S1-U12-L5","diff":1},"month":{"pos":"n","meaning":"月","unit":"","diff":1},"monkey":{"pos":"n","meaning":"猴子","unit":"S1-U7-L1","diff":1},"minute":{"pos":"n","meaning":"分钟","unit":"","diff":1},"mine":{"pos":"pron","meaning":"我的","unit":"","diff":1},"milk":{"pos":"n","meaning":"牛奶","unit":"S1-U5-L2","diff":1},"men":{"pos":"n","meaning":"男人man 的复数)","unit":"","diff":1},"meat":{"pos":"n","meaning":"肉类","unit":"S1-U10-L3","diff":1},"me":{"pos":"pron","meaning":"我","unit":"S1-U12-L2","diff":1},"mat":{"pos":"n","meaning":"地垫","unit":"S2-U14-L4","diff":1},"many":{"pos":"det","meaning":"许多","unit":"","diff":1},"man":{"pos":"n","meaning":"男人","unit":"S1-U5-L1","diff":1},"make":{"pos":"v","meaning":"制造","unit":"S1-U6-L3","diff":1},"love":{"pos":"v","meaning":"爱","unit":"S1-U12-L2","diff":1},"lots of":{"pos":"det","meaning":"大量的","unit":"","diff":1},"long":{"pos":"adj","meaning":"长的","unit":"S1-U7-L5 ","diff":1}},L2:{"dentist":{"pos":"n","meaning":"牙医","cefr":"A1","cambridge":"Movers","diff":2},"department":{"pos":"n","meaning":"部门","cefr":"A2","cambridge":"KET","diff":2},"department store":{"pos":"n","meaning":"百货商店","cefr":"A2","cambridge":"KET","diff":2},"describe":{"pos":"v","meaning":"描述","cefr":"A2","cambridge":"KET","diff":2},"desert":{"pos":"v","meaning":"遗弃","cefr":"A2","cambridge":"KET","diff":2},"design":{"pos":"v","meaning":"设计","cefr":"A2","cambridge":"KET","diff":2},"dessert":{"pos":"n","meaning":"甜点","cefr":"A2","cambridge":"KET","diff":2},"detailed":{"pos":"adj","meaning":"详细的","cefr":"A2","cambridge":"KET","diff":2},"diary":{"pos":"n","meaning":"日记","cefr":"A2","cambridge":"KET/Flyers","diff":2},"dictionary":{"pos":"n","meaning":"词典","cefr":"A2","cambridge":"KET/Flyers","diff":2},"die":{"pos":"v","meaning":"消失;灭亡","cefr":"A2","cambridge":"KET","diff":2},"difference":{"pos":"n","meaning":"差别","cefr":"A1","cambridge":"Movers","diff":2},"different":{"pos":"adj","meaning":"不同的","cefr":"A1","cambridge":"Movers","diff":2},"difficult":{"pos":"adj","meaning":"困难的","cefr":"A1","cambridge":"Movers","diff":2},"digital":{"pos":"adj","meaning":"数字的","cefr":"A2","cambridge":"KET","diff":2},"digital camera":{"pos":"n","meaning":"数码相机","cefr":"A2","cambridge":"KET","diff":2},"dinosaur":{"pos":"n","meaning":"恐龙","cefr":"A2","cambridge":"KET/Flyers","diff":2},"diploma":{"pos":"n","meaning":"文凭","cefr":"A2","cambridge":"KET","diff":2},"directions":{"pos":"n","meaning":"方向","cefr":"A2","cambridge":"KET","diff":2},"dirty":{"pos":"adj","meaning":"脏的","cefr":"Pre-A1","cambridge":"Starters","diff":2},"disco":{"pos":"n","meaning":"迪斯科","cefr":"A2","cambridge":"KET","diff":2},"discount":{"pos":"n","meaning":"折扣","cefr":"A2","cambridge":"KET","diff":2},"discover":{"pos":"v","meaning":"发现","cefr":"A2","cambridge":"KET","diff":2},"discuss":{"pos":"vt","meaning":"讨论","cefr":"A2","cambridge":"KET","diff":2},"dish":{"pos":"n","meaning":"菜肴","cefr":"A2","cambridge":"KET","diff":2},"display":{"pos":"v","meaning":"显示","cefr":"B1","cambridge":"PET","diff":2},"dive":{"pos":"v","meaning":"潜水","cefr":"A2","cambridge":"KET","diff":2},"diving":{"pos":"n","meaning":"潜水","cefr":"A2","cambridge":"KET","diff":2},"doctor":{"pos":"n","meaning":"医生","cefr":"A1","cambridge":"Movers","diff":2},"document":{"pos":"n","meaning":"文件","cefr":"A2","cambridge":"KET","diff":2},"dollar":{"pos":"n","meaning":"美元","cefr":"A2","cambridge":"KET","diff":2},"dot":{"pos":"n","meaning":"点","cefr":"A2","cambridge":"KET","diff":2},"double":{"pos":"v","meaning":"使加倍","cefr":"A2","cambridge":"KET","diff":2},"double room":{"pos":"phrase","meaning":"双人间","cefr":"A2","cambridge":"KET","diff":2},"doubt":{"pos":"v","meaning":"怀疑","cefr":"B1","cambridge":"PET","diff":2},"download":{"pos":"v","meaning":"下载","cefr":"A2","cambridge":"KET","diff":2},"downstairs":{"pos":"adj","meaning":"楼下的","cefr":"A2","cambridge":"KET","diff":2},"dozen":{"pos":"det","meaning":"十二","cefr":"B1","cambridge":"PET","diff":2},"draw":{"pos":"n","meaning":"抽签","cefr":"A2","cambridge":"KET","diff":2},"drawer":{"pos":"n","meaning":"抽屉","cefr":"A2","cambridge":"KET","diff":2},"dream":{"pos":"v","meaning":"梦想","cefr":"A2","cambridge":"KET","diff":2},"dress":{"pos":"v","meaning":"穿衣服","cefr":"A2","cambridge":"KET","diff":2},"dressed":{"pos":"adj","meaning":"穿着衣服的","cefr":"A2","cambridge":"KET","diff":2},"drive":{"pos":"v","meaning":"驾车送(人)","cefr":"A2","cambridge":"KET","diff":2},"driving licence":{"pos":"n","meaning":"驾驶执照","cefr":"A2","cambridge":"KET","diff":2},"drop":{"pos":"n","meaning":"滴","cefr":"A2","cambridge":"KET","diff":2},"drugstore":{"pos":"n","meaning":"药店","cefr":"A2","cambridge":"KET","diff":2},"drum":{"pos":"n","meaning":"鼓","cefr":"A2","cambridge":"KET/Flyers","diff":2},"dry":{"pos":"v","meaning":"变干","cefr":"A1","cambridge":"Movers","diff":2},"during":{"pos":"prep","meaning":"在……期间","cefr":"A2","cambridge":"KET/Flyers","diff":2},"duty-free":{"pos":"adj","meaning":"免税的","cefr":"B1","cambridge":"PET","diff":2},"dvd player":{"pos":"n","meaning":"DVD播放机","cefr":"A2","cambridge":"KET","diff":2}}},P={L1:[{"structure":"be + adj","module":"形容词结构","mid":"M1","examples":["I am happy today.","I am sad today."],"meaning":["今天我很开心。","今天我很难过。"]},{"structure":"feel + adj","module":"系表结构","mid":"M1","examples":["I feel tired.","I feel happy."],"meaning":["我感到累。","我感到开心。"]},{"structure":"look + adj","module":"系表结构","mid":"M1","examples":["You look happy.","He looks tired."],"meaning":["你看起来很高兴。","他看起来累了。"]},{"structure":"adj and adj","module":"并列形容词","mid":"M1","examples":["The room is big and clean.","The dog is small and cute."],"meaning":["房间又大又干净。","这只狗又小又可爱。"]},{"structure":"S + V","module":"动词与时态","mid":"M2","examples":["I read books.","We go to school."],"meaning":["我读书。","我们去上学。"]},{"structure":"S + be + V-ing","module":"动词与时态","mid":"M2","examples":["I am reading now.","She is cooking."],"meaning":["我正在读书。","她在做饭。"]},{"structure":"do/does + V","module":"动词与时态","mid":"M2","examples":["Do you like apples?","Does he like milk?"],"meaning":["你喜欢苹果吗?","他喜欢牛奶吗?"]},{"structure":"can + V","module":"情态动词","mid":"M2","examples":["I can swim.","Can you help me?"],"meaning":["我会游泳。","你能帮我吗?"]}],L2:[{"structure":"S + be + adj + to + V","module":"复合结构","mid":"M1","examples":["I am happy to see you."],"meaning":["我很开心见到你。"]}]};
// ═══════════ BENCHMARKS ═══════════
var BM={L1:{idealRounds:6,targetWordsPerRound:3,idealWordCount:7,targetExposure:{min:2,ideal:3},dialogRounds:{min:4,max:12,ideal:8},sentenceLength:{min:3,max:8,ideal:5}},L2:{idealRounds:8,targetWordsPerRound:4,idealWordCount:9,targetExposure:{min:2,ideal:4},dialogRounds:{min:6,max:16,ideal:10},sentenceLength:{min:4,max:12,ideal:7}}};
var DIFF_WEIGHT={A0:0,A1:1,A2:2,B1:4,B2:6,C1:8,C2:10,Pre_A1:0.5,Starters:0,Movers:1,Flyers:2,KET:3,PET:5};
// ═══════════ BRITISH SPELLING MAP (from MEMORY.md + skills) ═══════════
var BRITISH_SPELLING={color:"colour",favorite:"favourite",center:"centre",theater:"theatre",meter:"metre",liter:"litre",traveling:"travelling",traveled:"travelled",canceled:"cancelled",canceling:"cancelling",dialog:"dialogue",catalog:"catalogue",analog:"analogue",monolog:"monologue",program:"programme",ton:"tonne",gray:"grey",practice:"practise",license:"licence",defense:"defence",offense:"offence","pretense":"pretence",realize:"realise",recognize:"recognise",organize:"organise",apologize:"apologise",analyze:"analyse",paralyze:"paralyse","plow":"plough","draft":"draught","jewelry":"jewellery","maneuver":"manoeuvre","skeptical":"sceptical","behavior":"behaviour","neighbor":"neighbour","harbor":"harbour","labor":"labour","favor":"favour","honor":"honour","colorful":"colourful","flavor":"flavour","humor":"humour","rumor":"rumour","savor":"savour","vapor":"vapour","rigor":"rigour","vigor":"vigour","armor":"armour","clamor":"clamour","demeanor":"demeanour","splendor":"splendour","rancor":"rancour","odor":"odour","parlor":"parlour","endeavor":"endeavour","ardor":"ardour","candor":"candour","succor":"succour","valour":"valor","enrolment":"enrollment","fulfil":"fulfill","distil":"distill","instil":"instill","skilful":"skillful","wilful":"willful","enrol":"enroll","instalment":"installment","fibre":"fiber","litre":"liter","lustre":"luster","meagre":"meager","sceptre":"scepter","sombre":"somber","theatre":"theater","calibre":"caliber","centre":"center","goitre":"goiter","manoeuvre":"maneuver","metre":"meter","mitre":"miter","ochre":"ocher","reconnoitre":"reconnoiter","sabre":"saber","saltpetre":"saltpeter","sepulchre":"sepulcher","sombre":"somber","spectre":"specter","cheque":"check","chequer":"checker","kosher":"kosher"};
// ═══════════ COMPONENT TYPE RULES (from dialogue-*/info-* config skills) ═══════════
var COMPONENT_RULES={"dialogue_reading":{label:"对话朗读",family:"dialogue",hasCorrectAnswer:false,fields:{readingSentence:{required:true,max:1,desc:"朗读句子,末尾必须加(朗读)"},contextIntro:{required:false,desc:"情境引入"}},imageRequired:{base:false,withImage:true},optionsRequired:false,maxSentenceLength:15,check:function(c){var issues=[];var s=c.readingSentence||c.sentence||c.interactiveContent||"";if(!s)issues.push({field:"readingSentence",msg:"缺少朗读句子"});if(s&&!/[(]朗读[)]$/.test(s))issues.push({field:"readingSentence",msg:["朗读句子末尾缺少(朗读)标记:",s].join(" ")});return issues;}},"dialogue_choose":{label:"对话选择",family:"dialogue",hasCorrectAnswer:true,fields:{options:{required:true,min:2,desc:"2+选项,一个标(正确)"},contextIntro:{required:false}},imageRequired:{base:false,withImage:true},optionsRequired:true,maxCorrectCount:1,check:function(c){var issues=[],opts=c.options||c.choices||[];if(opts.length<2)issues.push({field:"options","msg":"选项不足2个当前"+opts.length+"个)"});var corrects=opts.filter(function(o){return o&&(o.isCorrect||o.correct||/[(]正确[)]/.test(o.text||o.label||""));});if(corrects.length===0)issues.push({field:"options","msg":"缺少正确选项(需标(正确))"});if(corrects.length>1)issues.push({field:"options","msg":"多个选项标为正确(需唯一正确)"});opts.forEach(function(o,i){var fb=o.feedback||o.fb;if(!fb)issues.push({field:"options","msg":["选项"+(i+1)+"缺少反馈台词"].join(" ")});});return issues;}},"dialogue_selective_reading":{label:"对话选读",family:"dialogue",hasCorrectAnswer:false,fields:{options:{required:true,min:2,max:4,desc:"2-4选项无正确每个有反馈"},requirement:{fixed:"选择一个你想表达的观点"}},imageRequired:{base:false,withImage:true},optionsRequired:true,noCorrectAnswer:true,feedbackPerOption:true,check:function(c){var issues=[],opts=c.options||c.choices||[],req=c.requirement||c.interactiveContent||"";if(req&&!req.includes("选择一个你想表达的观点"))issues.push({field:"requirement","msg":['互动要求固定为"选择一个你想表达的观点",当前: '+req].join(" ")});if(opts.length<2)issues.push({field:"options","msg":"选项不足2个"});opts.forEach(function(o,i){if(o&&(o.isCorrect||o.correct))issues.push({field:"options","msg":["选项"+(i+1)+"不应标注正确(选读无正确选项)"].join(" ")});var fb=o&&(o.feedback||o.fb);if(!fb)issues.push({field:"options","msg":["选项"+(i+1)+"缺少反馈(选读每个选项必有反馈)"].join(" ")});});return issues;}},"dialogue_fill_in_the_blanks":{label:"对话挖空",family:"dialogue",hasCorrectAnswer:true,fields:{stem:{required:true,desc:"含___的句子"},options:{required:true,min:2,desc:"选项列表,正确标(正确)"},feedback:{required:true,desc:"正确+错误反馈"}},imageRequired:{base:false,withImage:true},optionsRequired:true,blankCount:1,check:function(c){var issues=[],stem=c.stem||c.sentence||c.interactiveContent||"";if(!stem)issues.push({field:"stem","msg":"缺少题干句子"});if(stem&&!/___|____/.test(stem))issues.push({field:"stem","msg":"题干缺少挖空标记 ___"});var opts=c.options||c.choices||[];if(opts.length<2)issues.push({field:"options","msg":"选项不足2个"});var corrects=opts.filter(function(o){return o&&(o.isCorrect||o.correct||/[(]正确[)]/.test(o.text||o.label||""));});if(corrects.length!==1)issues.push({field:"options","msg":["应有1个正确选项当前"+corrects.length+"个"].join(" ")});return issues;}},"dialogue_sentence_building":{label:"对话组句",family:"dialogue",hasCorrectAnswer:true,fields:{options:{required:true,min:4,max:8,desc:"单词/短语选项"},answer:{required:true,desc:"完整句子"},hint:{required:false,desc:"辅助信息"}},imageRequired:{base:false,withImage:true},optionsRequired:true,maxDistractors:1,check:function(c){var issues=[],opts=c.options||c.choices||[],ans=c.answer||c.correctSentence||"";if(opts.length<4)issues.push({field:"options","msg":"组句选项至少4个当前"+opts.length+"个)"});if(opts.length>8)issues.push({field:"options","msg":"组句选项最多8个当前"+opts.length+"个)"});if(!ans)issues.push({field:"answer","msg":"缺少正确句子答案"});return issues;}},"info_word_spelling":{label:"信息拼词",family:"info",hasCorrectAnswer:true,fields:{stem:{required:true,desc:"单词含(数字)如G(2)pe"},answer:{required:true,desc:"需填入的字母"},options:{required:true,min:4,max:7,desc:"候选字母4-7个"},taskDesc:{required:true,desc:"任务描述"}},imageRequired:{base:true,withImage:true},targetFromKnowledgePoint:true,maxBlankHalfWord:true,check:function(c){var issues=[],stem=c.stem||c.word||"",ans=c.answer||c.missingLetters||"";if(!stem)issues.push({field:"stem","msg":"缺少拼词题干"});if(stem&&!/[(]\d+[)]/.test(stem))issues.push({field:"stem","msg":"题干缺少(数字)占位符如G(2)pe"});if(!ans)issues.push({field:"answer","msg":"缺少需填入的字母"});if(stem&&ans){var digits=(stem.match(/[(](\d+)[)]/)||[])[1];if(digits&&parseInt(digits)!==ans.length)issues.push({field:"answer","msg":"答案字母数("+ans.length+")与占位数字("+digits+")不一致"});}var optsRaw=c.options||c.letters||"";var opts=typeof optsRaw==="string"?optsRaw.split(/[,]/).filter(Boolean):optsRaw;if(opts.length<4)issues.push({field:"options","msg":"候选字母不足4个"});if(opts.length>7)issues.push({field:"options","msg":"候选字母超过7个"});if(ans&&ans.length){var ansLetters=ans.split(""),letterSet=opts.map(function(l){return l.trim()});ansLetters.forEach(function(l){if(letterSet.indexOf(l)===-1)issues.push({field:"options","msg":["答案字母 '"+l+"' 不在候选项"].join(" ")});});}return issues;}},"info_sentence_building":{label:"信息组句",family:"info",hasCorrectAnswer:true,fields:{options:{required:true,min:3,max:5,desc:"短语/词组选项3-5个乱序"},answer:{required:true,desc:"完整句子,含大小写+标点"},hint:{required:false,desc:"辅助信息"}},imageRequired:{base:true,withImage:true},distractors:{min:1,max:2},phraseLevel:true,noCapsNoPunct:true,check:function(c){var issues=[],opts=c.options||c.choices||[],ans=c.answer||c.correctSentence||"";if(opts.length<3)issues.push({field:"options","msg":"信息组句选项至少3个当前"+opts.length+"个)"});if(opts.length>5)issues.push({field:"options","msg":"信息组句选项最多5个当前"+opts.length+"个)"});if(!ans)issues.push({field:"answer","msg":"缺少正确句子答案"});if(ans&&!/[A-Z]/.test(ans.charAt(0)))issues.push({field:"answer","msg":"答案句首缺少大写"});if(ans&&!/[.!?]$/.test(ans))issues.push({field:"answer","msg":"答案句末缺少标点"});opts.forEach(function(o,i){var t=typeof o==="string"?o:(o.text||o.label||"");if(t&&/[A-Z]/.test(t.charAt(0)))issues.push({field:"options","msg":"选项"+(i+1)+"有首字母大写(信息组句选项不得大写)"});if(t&&/[.!?]$/.test(t))issues.push({field:"options","msg":"选项"+(i+1)+"有句末标点(信息组句选项不得带标点)"});});return issues;}},"speaking_image":{label:"看图说话",family:"speaking",hasCorrectAnswer:true,fields:{taskTitle:{required:true},taskBackground:{required:true},taskDescription:{required:true},knowledge:{required:true},dialogueRole:{required:true},interactionQuestions:{required:true},passRules:{required:true}},imageRequired:{base:true,withImage:true},check:function(c){var issues=[];["taskTitle","taskBackground","taskDescription","knowledge","dialogueRole","interactionQuestions","passRules"].forEach(function(f){if(!c[f])issues.push({field:f,msg:"缺少必填字段: "+f});});return issues;}},"core_navigation":{label:"核心互动导览",family:"core",hasCorrectAnswer:true,fields:{taskTitle:{required:true,maxLen:10},taskBackground:{required:true,maxLen:30},taskDescription:{required:true,maxLen:20},knowledge:{required:true},dialogueRole:{required:true},roleBackground:{required:true},interactionQuestions:{required:true},roundSetting:{required:false},passRules:{required:true},exampleDialog:{required:true}},imageRequired:{base:false,withImage:true},check:function(c){var issues=[];["taskTitle","taskBackground","taskDescription","knowledge","dialogueRole","roleBackground","interactionQuestions","passRules","exampleDialog"].forEach(function(f){if(!c[f])issues.push({field:f,msg:"缺少必填字段: "+f});});if(c.taskTitle&&c.taskTitle.length>10)issues.push({field:"taskTitle","msg":["任务标题超过10字当前"+c.taskTitle.length+"字)"].join(" ")});if(c.knowledge){var kn=c.knowledge;if(typeof kn==="string"&&!/[a-zA-Z]/.test(kn))issues.push({field:"knowledge","msg":"知识缺少英文句型"});}return issues;}},"core_reading":{label:"合作阅读",family:"core",hasCorrectAnswer:true,fields:{taskData:{required:true},sequenceData:{required:true},textData:{required:true},learningData:{required:true}},check:function(c){var issues=[];if(!c.textData||!c.textData.text||!c.textData.text.length)issues.push({field:"textData","msg":"阅读材料(textData.text)为空"});if(c.textData&&c.textData.question){c.textData.question.forEach(function(q,i){if(!q.meaning||!q.meaning.trim())issues.push({field:"textData.question","msg":"第"+(i+1)+"道阅读题缺少 meaning 标签"});if(q.optionList){q.optionList.forEach(function(opt,j){if(!opt.feedback)issues.push({field:"textData.question","msg":"第"+(i+1)+"题选项"+(j+1)+"缺少 feedback"});});}});}return issues;}},"core_listening":{label:"合作听力",family:"core",hasCorrectAnswer:true,fields:{taskData:{required:true},sequenceData:{required:true},textData:{required:true},learningData:{required:true}},check:function(c){var issues=[];if(c.textData&&(!c.textData.audio||!c.textData.audio.length))issues.push({field:"textData.audio","msg":"听力材料(textData.audio)为空"});if(c.taskData&&!c.taskData.sceneDesc)issues.push({field:"taskData.sceneDesc","msg":"缺少场景描述"});return issues;}},"core_writing":{label:"写作互动",family:"core",hasCorrectAnswer:true,fields:{taskInfo:{required:true},textInfo:{required:true},evalInfo:{required:true},studyInfo:{required:true}},check:function(c){var issues=[];if(c.textInfo&&(!c.textInfo.textList||!c.textInfo.textList.length))issues.push({field:"textInfo.textList","msg":"写作素材(textInfo.textList)为空"});if(!c.evalInfo)issues.push({field:"evalInfo","msg":"缺少写作评分配置(evalInfo)"});return issues;}}};
// ═══════════ MASTERY WEIGHTS (from knowledge-mastery-calculator) ═══════════
var MASTERY_WEIGHTS={componentTypes:{pronunciation:0.2,repeat:0.2,reading_aloud:0.2,input:0.3,choice:0.3,matching:0.3,drag:0.3,listening:0.3,output:0.5,fill_blank:0.5,sentence_building:0.5,speaking:0.5,writing:0.5,image_speaking:0.5},repetitionMultiplier:[1.0,1.0,1.5,2.0,2.0,2.5],masteryThresholds:{mastered:80,basic:60,partial:40}};
// ═══════════ NEGATIVE WORDS (from MEMORY.md 价值观导向规范) ═══════════
var NEGATIVE_WORDS=["stupid","ugly","fat","dumb","idiot","loser","hate","terrible","awful","horrible","worst","failure","useless","hopeless"];
// ═══════════ THIRD-PERSON SINGULAR SUBJECTS ═══════════
var THIRD_SINGULAR=["he","she","it"];
// ═══════════ Utils ═══════════
function ew(t){return t?t.toLowerCase().replace(/[^a-z\s'-]/g,' ').split(/\s+/).filter(function(w){return w.length>1}):[]}
function wc(t){return t?t.split(/\s+/).length:0}
function clamp(v,lo,hi){return Math.max(lo,Math.min(hi,v))}
function avg(arr){return arr.length?arr.reduce(function(a,b){return a+b},0)/arr.length:0}
function escReg(s){return s.replace(/[-\/\\^$*+?.()|[\]{}]/g,'\\$&')}
// ═══════════ QUALITY SCORERS (v3 + v4 extensions) ═══════════
// 1. Vocab Alignment (0-100, higher=more over-level issues)
function scoreVocabAlignment(keyPreview,lvl){
var targets=keyPreview||[],wl=W[lvl||'L1']||W.L1,bm=BM[lvl||'L1']||BM.L1;
if(!targets.length)return{score:0,label:'无目标词',details:[],totalCount:0,overLevelCount:0,interpretation:'无目标词数据'};
var scores=[],details=[],overCount=0;
targets.forEach(function(w){
var lo=(w||'').toLowerCase().trim();if(!lo)return;
var v=wl[lo];if(v){scores.push(0);details.push({word:w,status:'in_level',diff:v.diff||1,msg:'"'+w+'" 在词库内 ✓'});}
else{
var l2w=W.L2[lo];if(l2w){scores.push(50);details.push({word:w,status:'above_level',diff:l2w.diff||2,msg:'"'+w+'" 是L2词 ⚠️'});overCount++;}
else{scores.push(100);details.push({word:w,status:'out_of_scope',diff:99,msg:'"'+w+'" 超纲 ✗'});overCount++;}
}
});
var sc=avg(scores),above=details.filter(function(d){return d.status==='above_level'||d.status==='out_of_scope'}).length;
var interp=sc===0?'全部在词库内 ✓':sc<20?'个别超纲':sc<50?'部分超纲':'严重超纲';
return{score:Math.round(sc),label:'词汇匹配度',details:details,totalCount:targets.length,overLevelCount:above,interpretation:interp};
}
// 2. Sentence Complexity (0-100, higher=too complex)
function scoreSentenceComplexity(dialogs,lvl){
var lines=(dialogs||[]).filter(function(l){return typeof l==='string'&&l.trim()}),bm=BM[lvl||'L1']||BM.L1;
if(!lines.length)return{score:0,label:'无台词',details:[],interpretation:'无法评分:缺少对话台词'};
var scores=[],details=[],maxL=bm.sentenceLength.max,idealL=bm.sentenceLength.ideal;
lines.forEach(function(line){
var n=wc(line);scores.push(n>maxL?100:Math.round(Math.abs(n-idealL)/idealL*60));
if(n>maxL)details.push({line:line,words:n,status:'too_long',msg:n+'词 → 超过上限'+maxL+'词'});
});
var over=details.length,total=lines.length,sc=Math.round(avg(scores));
var interp=over===0?'句长全部合规 ✓':over/total<0.2?'少数偏长 ('+over+'/'+total+')':'句长问题较多 ('+over+'/'+total+')';
return{score:sc,label:'句子复杂度',overCount:over,totalCount:total,overRate:Math.round(over/total*100),maxLineLength:maxL,interpretation:interp};
}
// 3. Knowledge Density
function scoreKnowledgeDensity(keyPreview,dialogRounds,lvl){
var bm=BM[lvl||'L1']||BM.L1,tc=(keyPreview||[]).length,dr=Math.max(dialogRounds||1,1);
var ratio=tc/dr,idealRatio=bm.targetWordsPerRound/bm.idealRounds*4;
var sc=ratio>1?Math.round(Math.min(ratio/idealRatio*100,100)):Math.round(ratio/idealRatio*60);
var interp=sc<30?'知识密度偏低 ('+tc+'词/'+dr+'轮),学习效率可提升':sc<55?'知识密度适中 ('+tc+'词/'+dr+'轮),节奏合理 ✓':sc<75?'知识密度偏高 ('+tc+'词/'+dr+'轮),学生可能吸收不足':'知识密度过高 ('+tc+'词/'+dr+'轮),建议拆分或减少目标';
return{score:sc,label:'知识密度',targetCount:tc,dialogRounds:dr,ratio:Math.round(ratio*100)/100,idealRatio:Math.round(idealRatio*100)/100,interpretation:interp};
}
// 4. Knowledge Exposure
function scoreKnowledgeExposure(keyPreview,allDialogs,lvl){
var targets=keyPreview||[],dialogs=allDialogs||[],bm=BM[lvl||'L1']||BM.L1;
if(!targets.length||!dialogs.length)return{score:0,label:'无数据',details:[],interpretation:'无法评分:缺少目标词或台词数据'};
var dText=dialogs.join(' ').toLowerCase(),scores=[],details=[];
targets.forEach(function(w){
var lo=(w||'').toLowerCase().trim();if(!lo)return;
var re=new RegExp('\\b'+escReg(lo)+'\\b','g');
var cnt=(dText.match(re)||[]).length;
var sc=cnt===0?0:cnt===1?30:cnt===2?70:cnt>=bm.targetExposure.ideal?100:Math.round(70+(cnt-2)/(bm.targetExposure.ideal-2)*30);
scores.push(sc);
details.push({word:w,count:cnt,score:sc,status:cnt===0?'zero':cnt===1?'low':cnt===2?'moderate':'good',msg:'"'+w+'" 出现'+cnt+'次'+(cnt===0?' ⚠️ 零暴露':cnt<bm.targetExposure.ideal?'':' ✓')});
});
var ascore=Math.round(avg(scores));
var zeroCnt=details.filter(function(d){return d.status==='zero'}).length;
var interp=ascore>=90?'全部目标词曝光充分 ✓':ascore>=60?'多数目标词曝光良好,'+zeroCnt+'个零暴露':ascore>=30?zeroCnt+'个目标词零暴露,'+details.filter(function(d){return d.status==='low'}).length+'个曝光不足':'知识点严重曝光不足,'+zeroCnt+'个目标词完全未出现';
return{score:ascore,label:'知识点曝光度',details:details,zeroExposureCount:zeroCnt,lowExposureCount:details.filter(function(d){return d.status==='low'}).length,perfectExposureCount:details.filter(function(d){return d.status==='good'}).length,idealExposure:bm.targetExposure.ideal,interpretation:interp};
}
// 5. Content Coverage
function scoreContentCoverage(summary,lvl){
var bm=BM[lvl||'L1']||BM.L1,s=summary||{},score=0;
if(s.keyPreview&&s.keyPreview.length)score+=25;
if(s.dialogRounds&&s.dialogRounds>=bm.dialogRounds.min)score+=25;
if(s.dialogLines&&s.dialogLines.length>=4)score+=25;
if(s.realContent&&s.realContent.dialogs&&s.realContent.dialogs.length>=4)score+=25;
var interp=score>=75?'内容结构完整 ✓':score>=50?'基本完整,可补充':'内容结构薄弱,缺少关键要素';
return{score:score,label:'内容覆盖度',details:{hasKeyPreview:!!(s.keyPreview&&s.keyPreview.length),hasDialogRounds:!!(s.dialogRounds&&s.dialogRounds>=bm.dialogRounds.min),hasDialogLines:!!(s.dialogLines&&s.dialogLines.length>=4)},interpretation:interp};
}
// ═══════════ v4 NEW DIMENSION SCORERS ═══════════
// 6. Component Type Compliance (from dialogue-*/info-* config skills)
// Checks whether a component's config matches the rules for its type
function scoreComponentCompliance(component,level){
var l=level||'L1',c=component||{};
if(!c||!c.type&&!c.cType)return{score:null,label:'组件合规',skip:true,interpretation:'缺少组件类型信息,跳过检测'};
var ct=(c.type||c.cType||'').toLowerCase().replace(/[\s-]/g,'_'),rules=COMPONENT_RULES[ct];
if(!rules)return{score:null,label:'组件合规',skip:true,type:ct,interpretation:'组件类型 "'+ct+'" 未在规则库中注册,跳过检测'};
var issues=rules.check(c),maxIssues=10;
var sc=issues.length===0?0:Math.min(Math.round(issues.length/maxIssues*100),100);
var interp=issues.length===0?'组件配置完全合规 ✓':issues.length<=2?'少量配置问题 ('+issues.length+'个)':'配置问题较多 ('+issues.length+'个),需整改';
return{score:sc,label:'组件合规性',componentType:ct,typeLabel:rules.label,family:rules.family,issues:issues,issueCount:issues.length,interpretation:interp};
}
// 7. Config Field Integrity (from audit_l1_config 7 auto checks)
// Validates JSON structure, field consistency, classification correctness
function scoreConfigIntegrity(component,level){
var l=level||'L1',c=component||{},issues=[];
// 7.1 basicInfo/config/usageInfo completeness
var requiredTop=["type","cType","id","cId","title","taskTitle"];
var found=requiredTop.filter(function(k){return c[k]!==undefined&&c[k]!==null&&c[k]!=='';});
if(found.length<2)issues.push({check:"字段完整性","msg":"核心字段缺失type/cType/id/title等已找到: "+found.join(', ')});
// 7.2 Classification swap detection (cambridgeLevel ↔ cefrLevel)
if(c.cambridgeLevel&&c.cefrLevel){
var camLevels=["YLE","Starters","Movers","Flyers","KET","PET","FCE"];
var cefrLevels=["Pre-A1","A1","A2","B1","B2","C1","C2"];
if(cefrLevels.indexOf(c.cambridgeLevel)>=0&&camLevels.indexOf(c.cefrLevel)>=0)
issues.push({check:"分类互换","msg":"cambridgeLevel="+c.cambridgeLevel+"(应为剑桥等级) ↔ cefrLevel="+c.cefrLevel+"(应为CEFR等级),疑似值互换"});
}
// 7.3 JSON structure integrity (options answer range check)
if(c.options&&c.answer!==undefined){
var ans=parseInt(c.answer);
if(!isNaN(ans)&&c.options.length&&(ans<0||ans>=c.options.length))
issues.push({check:"答案越界","msg":"answer="+ans+" 超出 options 范围 [0,"+(c.options.length-1)+"]"});
}
// 7.4 Word bank ↔ answer consistency (sentenceStructureSort-like)
if(c.wordBank&&c.answerSentence){
var built=c.wordBank.join(' ').toLowerCase().replace(/[^a-z\s]/g,'');
var expected=c.answerSentence.toLowerCase().replace(/[^a-z\s]/g,'');
if(built!==expected)
issues.push({check:"单词库一致性","msg":"单词库拼出句子与答案不一致"});}
// 7.5 Coverage: all mandatory fields non-empty
var requiredFields=["id","cType","title","taskDesc","kpInfo"];
requiredFields.forEach(function(f){
if(c[f]===undefined||c[f]===null||(typeof c[f]==='string'&&!c[f].trim()))
issues.push({check:"覆盖性",msg:"必填字段 '"+f+"' 为空"});});
// 7.6 Chinese ↔ JSON field consistency
if(c.title_zh&&!c.title)issues.push({check:"中英一致性","msg":"有中文标题但无 title 字段"});
if(c.desc_zh&&!c.taskDesc)issues.push({check:"中英一致性","msg":"有中文描述但无 taskDesc 字段"});
// 7.7 component type registration check
if(c.cType&&!COMPONENT_RULES[c.cType.toLowerCase().replace(/[\s-]/g,'_')])
issues.push({check:"类型注册","msg":"cType '"+c.cType+"' 未在规则库中注册"});
var sc=issues.length===0?0:Math.min(Math.round(issues.length/7*100),100);
var interp=issues.length===0?'字段完整性全部通过 ✓':issues.length<=2?'少量字段问题 ('+issues.length+'个)':'字段完整性问题较多 ('+issues.length+'个)';
return{score:sc,label:'字段完整性',issues:issues,issueCount:issues.length,interpretation:interp};
}
// 8. Knowledge Mastery Prediction (from knowledge-mastery-calculator)
// Predicts expected mastery rate given component exposure patterns
function scoreMasteryPrediction(componentExposures,level){
var exps=componentExposures||[],l=level||'L1';
if(!exps.length)return{score:null,label:'掌握度预测',skip:true,interpretation:'缺少组件曝光数据,跳过预测'};
var totalScore=0,fullScore=0,details=[];
exps.forEach(function(exp,i){
var ct=(exp.componentType||exp.type||'input').toLowerCase().replace(/[\s-]/g,'_');
var w=MASTERY_WEIGHTS.componentTypes[ct]||MASTERY_WEIGHTS.componentTypes.input;
var occIdx=Math.min(i,MASTERY_WEIGHTS.repetitionMultiplier.length-1);
var m=MASTERY_WEIGHTS.repetitionMultiplier[occIdx];
var passed=exp.passed||exp.perfect||exp.success||1;
var s=passed*(w*m);
totalScore+=s;fullScore+=w*m;
details.push({componentType:ct,occurrence:i+1,weight:w,multiplier:m,passed:!!passed,score:s,typeLabel:exp.typeLabel||ct});
});
var rate=fullScore>0?Math.round(totalScore/fullScore*100):0;
var thresh=MASTERY_WEIGHTS.masteryThresholds;
var grade=rate>=thresh.mastered?'熟练掌握':rate>=thresh.basic?'基本掌握':rate>=thresh.partial?'部分掌握':'未掌握';
return{score:100-rate,label:'掌握度预测',masteryRate:rate,grade:grade,totalScore:totalScore,fullScore:fullScore,details:details,interpretation:'预测掌握率 '+rate+'% → '+grade};
}
// 9. Format Quality (from text output standardization rules)
function scoreFormatQuality(component,level){
var c=component||{},issues=[],l=level||'L1';
// 9.1 Title format check
var title=c.title||c.taskTitle||"";
if(title&&/-/.test(title))issues.push({check:"标题格式","msg":"标题含 '-',应用中文括号替代: "+title});
if(title&&/[a-zA-Z][\u4e00-\u9fa5]|[\u4e00-\u9fa5][a-zA-Z]/.test(title))
issues.push({check:"标题格式","msg":"标题中英文间缺少空格: "+title});
// 9.2 Markdown check
var textFields=[c.stem,c.sentence,c.interactiveContent,c.taskDesc,c.sceneDesc,c.roleBackground,c.taskBackground];
textFields.forEach(function(t,i){
if(t&&typeof t==='string'&&(/[*_]{1,2}/.test(t)||/^[#>]/.test(t)))
issues.push({check:"Markdown","msg":"文本字段含Markdown标记: '"+t.substring(0,50)+"...'"});});
// 9.3 Punctuation check (full-width in English context)
var engFields=[c.stem,c.sentence,c.interactiveContent,c.answer,c.correctSentence];
engFields.forEach(function(t){
if(t&&typeof t==='string'&&/[,。!?]/.test(t)&&/[a-zA-Z]/.test(t))
issues.push({check:"标点混用","msg":"英文文本含中文全角标点: '"+t.substring(0,40)+"...'"});});
// 9.4 Non-standard punctuation
engFields.forEach(function(t){
if(t&&typeof t==='string'&&//.test(t))
issues.push({check:"标点规范","msg":"含非标准标点 '': '"+t.substring(0,40)+"...'"});});
engFields.forEach(function(t){
if(t&&typeof t==='string'&&/!!!|/.test(t))
issues.push({check:"标点规范","msg":"含过度感叹号 '!!!' 或 '': '"+t.substring(0,40)+"...'"});});
// 9.5 British spelling check
engFields.forEach(function(t){
if(!t||typeof t!=='string')return;
var words=t.split(/\s+/);words.forEach(function(w){
var clean=w.replace(/[^a-zA-Z]/g,''),lower=clean.toLowerCase();
if(BRITISH_SPELLING[lower])issues.push({check:"英式拼写","msg":"检测到美式拼写 '"+clean+"' → 应为 '"+BRITISH_SPELLING[lower]+"'"});
});});
// 9.6 Negative self-evaluation check
var allText=(c.stem||"")+" "+(c.sentence||"")+" "+(c.interactiveContent||"")+" "+(c.taskDesc||"")+" "+(c.sceneDesc||"");
var lowerAll=allText.toLowerCase();NEGATIVE_WORDS.forEach(function(w){
if(lowerAll.indexOf(w)>=0)issues.push({check:"价值观","msg":"含负面评价词汇 '"+w+"'(避免负面评价自己或他人)"});});
// 9.7 Title Chinese paren convention
if(title&&/-1|-2|-3/.test(title))issues.push({check:"标题规范","msg":"标题用了'-1'编号,应改为中文括号'(一)'"});
var sc=Math.min(Math.round(issues.length/8*100),100);
var interp=issues.length===0?'格式规范全部通过 ✓':issues.length<=2?'少量格式问题 ('+issues.length+'个)':'格式问题较多 ('+issues.length+'个)';
return{score:sc,label:'格式规范',issues:issues,issueCount:issues.length,interpretation:interp};
}
// 10. Grammar Issue Detection (from audit_l1_config check 5 + dialogue rules)
function scoreGrammarIssues(component,level){
var c=component||{},issues=[],l=level||'L1';
// 10.1 Third-person singular check
var stem=c.stem||c.sentence||c.interactiveContent||"",stemLower=stem.toLowerCase();
var hasThirdSubj=THIRD_SINGULAR.some(function(s){return new RegExp('\\b'+s+'\\b').test(stemLower);});
if(c.dialogueRole){var roleLower=c.dialogueRole.toLowerCase();
if(THIRD_SINGULAR.indexOf(roleLower)>=0)hasThirdSubj=true;}
if(hasThirdSubj){
var words=stemLower.split(/\s+/),hasNeed=words.indexOf("need")>=0,hasNeeds=words.indexOf("needs")>=0;
if(hasNeed&&!hasNeeds)
issues.push({check:"三单语法","msg":"三单主语但使用 'need' 而非 'needs': '"+stem+"'"});
}
// 10.2 Subject-verb agreement
if(stemLower.match(/\b(he|she|it|Otis|Maeve|Mum|Dad)\b.*\b(go|do|have|make|say|tell|like|play|run|come|eat|sing|want|get|put|see|give|try|look|use|take)\b/)&&
!stemLower.match(/\b(goes|does|has|makes|says|tells|likes|plays|runs|comes|eats|sings|wants|gets|puts|sees|gives|tries|looks|uses|takes)\b/))
issues.push({check:"主谓一致","msg":"疑似三单主语 + 动词原形: '"+stem+"'"});
// 10.3 Double negation in L1
if(l==='L1'){
var negWords=["don't","doesn't","no","not","never"];
var negCount=negWords.filter(function(w){return stemLower.indexOf(w)>=0;}).length;
if(negCount>=2)issues.push({check:"双重否定","msg":"L1级别含双重否定难度不适配: '"+stem+"'"});}
var sc=Math.min(Math.round(issues.length/3*100),100);
var interp=issues.length===0?'语法检测通过 ✓':'检测到 '+issues.length+' 个语法问题';
return{score:sc,label:'语法检测',issues:issues,issueCount:issues.length,interpretation:interp};
}
// ═══════════ BEHAVIORAL CORRELATION ═══════════
function correlateBehavioral(qualityScores,item,lvl){
var p=Number(item.perfect)||0,o=Number(item.oops)||0,g=Number(item.good)||0,vs=qualityScores.vocabAlignment||{},insights=[];
if(!p&&!o&&!g)return{insights:[],summary:'无行为数据,无法关联分析'};
if(vs.overLevelCount>0&&p<80)
insights.push({dimension:'vocabAlignment',correlation:'negative',strength:vs.overLevelCount/vs.totalCount>0.3?'strong':'moderate',
insight:'词汇匹配度低('+vs.score+'分)'+vs.overLevelCount+'/'+vs.totalCount+'词超纲可能解释Perfect率'+p+'%偏低。超纲词:'+vs.details.filter(function(d){return d.status!=='in_level'}).map(function(d){return d.word}).join('、'),
action:'建议检查并替换超纲词汇确保在L1/L2词库范围内'});
var ks=qualityScores.knowledgeDensity||{};
if(ks.score>70&&p<70)
insights.push({dimension:'knowledgeDensity',correlation:'negative',strength:'strong',
insight:'知识密度过高('+ks.score+'分)'+ks.targetCount+'个目标词/'+ks.dialogRounds+'轮对话学生吸收压力大Perfect率'+p+'%与此相关',
action:'建议减少目标词数或增加对话轮数,降低单轮知识密度'});
var es=qualityScores.knowledgeExposure||{};
if(es.zeroExposureCount>0&&p<80)
insights.push({dimension:'knowledgeExposure',correlation:'negative',strength:es.zeroExposureCount>1?'strong':'moderate',
insight:'知识点曝光不足('+es.score+'分)'+es.zeroExposureCount+'个目标词零暴露,学生未在语境中充分接触目标语言',
action:'建议在对话中增加目标词的显性出现确保每个目标词至少出现2次'});
// v4 extension: component compliance → behavior correlation
var cc=qualityScores.componentCompliance||{};
if(cc.issueCount>0&&p<80)
insights.push({dimension:'componentCompliance',correlation:'negative',strength:cc.issueCount>3?'strong':'moderate',
insight:'组件合规问题('+cc.issueCount+'个)配置不规范可能影响用户体验Perfect率'+p+'%与此相关',
action:'建议修复组件配置中的合规问题'});
// v4 extension: format quality → Oops correlation
var fq=qualityScores.formatQuality||{};
if(fq.issueCount>2&&o>=10)
insights.push({dimension:'formatQuality',correlation:'negative',strength:'moderate',
insight:'格式规范问题('+fq.issueCount+'个)文本质量问题可能增加学生困惑Oops率'+o+'%相关',
action:'建议修复格式规范问题'});
var gi=qualityScores.grammarIssues||{};
if(gi.issueCount>0&&o>=5)
insights.push({dimension:'grammarIssues',correlation:'negative',strength:'moderate',
insight:'语法问题('+gi.issueCount+'个)语法错误可能导致学生操作困惑Oops率'+o+'%相关',
action:'建议修复语法错误'});
return{insights:insights,summary:insights.length+'条行为关联洞察'};
}
// ═══════════ COMPONENT COMPARISON ═══════════
function compareComponents(compA,compB,lvl){
function qs(c){return evaluate(c.summary,c.item,lvl).qualityScores;}
var a=qs(compA),b=qs(compB),diff={};
['vocabAlignment','sentenceComplexity','knowledgeDensity','knowledgeExposure','contentCoverage','componentCompliance','configIntegrity','formatQuality','grammarIssues'].forEach(function(k){
if(a[k]&&b[k]&&a[k].score!==undefined&&b[k].score!==undefined)
diff[k]={a:a[k].score,b:b[k].score,delta:b[k].score-a[k].score,direction:b[k].score>a[k].score?'B优于A':a[k].score>b[k].score?'A优于B':'持平'};
});
return{componentA:{id:compA.item&&compA.item.cId||'',type:compA.item&&compA.item.cType||''},componentB:{id:compB.item&&compB.item.cId||'',type:compB.item&&compB.item.cType||''},diffs:diff};
}
// ═══════════ RISK ANALYSIS ═══════════
function analyzeRisks(qualityScores,item,lvl){
var flags=[],p=Number(item.perfect)||0,o=Number(item.oops)||0;
// v3 risk flags
if(qualityScores.vocabAlignment&&qualityScores.vocabAlignment.score>50)
flags.push({risk:'vocab_overreach',severity:qualityScores.vocabAlignment.score>80?'high':'medium',
detail:qualityScores.vocabAlignment.overLevelCount+'/'+qualityScores.vocabAlignment.totalCount+'词超纲',impact:'超纲词汇 → 学习困难 → Perfect↓'});
if(qualityScores.sentenceComplexity&&qualityScores.sentenceComplexity.overRate>25)
flags.push({risk:'complex_sentences',severity:qualityScores.sentenceComplexity.overRate>50?'high':'medium',
detail:qualityScores.sentenceComplexity.overCount+'/'+qualityScores.sentenceComplexity.totalCount+'句偏长',impact:'句子过长 → 阅读困难 → Perfect↓'});
if(qualityScores.knowledgeDensity&&qualityScores.knowledgeDensity.score>70)
flags.push({risk:'over_dense',severity:qualityScores.knowledgeDensity.score>85?'high':'medium',
detail:qualityScores.knowledgeDensity.targetCount+'词/'+qualityScores.knowledgeDensity.dialogRounds+'轮',impact:'知识密度过高 → 学生吸收困难 → Perfect↓'});
if(qualityScores.contentCoverage&&qualityScores.contentCoverage.score<60)
flags.push({risk:'thin_content',severity:qualityScores.contentCoverage.score<40?'high':'medium',
detail:'内容完整度仅'+qualityScores.contentCoverage.score+'分',impact:'缺少关键教学环节 → 教学效果难以保证'});
// v4 extension risk flags
var cc=qualityScores.componentCompliance||{},ci=qualityScores.configIntegrity||{},fq=qualityScores.formatQuality||{},gi=qualityScores.grammarIssues||{},mp=qualityScores.masteryPrediction||{};
if(cc.issueCount>=5)flags.push({risk:'component_noncompliant',severity:'high',detail:cc.issueCount+'个合规问题',impact:'组件配置严重不规范 → 影响交互体验'});
if(ci.issueCount>=3)flags.push({risk:'config_incomplete',severity:ci.issueCount>=5?'high':'medium',detail:ci.issueCount+'个字段完整性问题',impact:'配置字段缺失 → 可能导致运行时错误'});
if(fq.issueCount>=4)flags.push({risk:'format_quality',severity:'medium',detail:fq.issueCount+'个格式规范问题',impact:'文本质量不达标 → 影响学生理解'});
if(gi.issueCount>=2)flags.push({risk:'grammar_error',severity:gi.issueCount>=3?'high':'medium',detail:gi.issueCount+'个语法问题',impact:'语法错误 → 错误语言输入'});
if(mp.masteryRate!==undefined&&mp.masteryRate<MASTERY_WEIGHTS.masteryThresholds.partial)
flags.push({risk:'mastery_risk',severity:mp.masteryRate<30?'high':'medium',detail:'预测掌握率'+mp.masteryRate+'%',impact:'知识点掌握风险 → 需强化巩固设计'});
// Behavioral red flags
if(p<70&&o>=10)flags.push({risk:'critical_performance',severity:'high',detail:'Perfect='+p+'%/Oops='+o+'%',impact:'关键内容组件表现严重不达标,需优先排查'});
else if(p<70)flags.push({risk:'low_perfect',severity:'medium',detail:'Perfect='+p+'%',impact:'学习吸收不足,建议复核内容质量和难度'});
else if(o>=10)flags.push({risk:'high_oops',severity:'medium',detail:'Oops='+o+'%',impact:'操作或理解阻塞,建议复核交互设计和内容清晰度'});
return{flags:flags,severityCounts:{high:flags.filter(function(f){return f.severity==='high'}).length,medium:flags.filter(function(f){return f.severity==='medium'}).length},summary:flags.length+'个风险项('+flags.filter(function(f){return f.severity==='high'}).length+'高/'+flags.filter(function(f){return f.severity==='medium'}).length+'中)'};
}
// ═══════════ MAIN API ═══════════
function evaluate(summary,item,lvl,options){
var l=lvl||'L1',s=summary||{},it=item||{},opts=options||{};
// Merge dialogs from both sources
var allDialogs=(s.realContent&&s.realContent.dialogs)||[];
if(!allDialogs.length&&s.dialogLines)allDialogs=s.dialogLines.filter(function(l){return typeof l==='string'}).map(function(l){return l});
var qs={};
// v3 dimensions (always scored)
qs.vocabAlignment=scoreVocabAlignment(s.keyPreview,l);
qs.sentenceComplexity=scoreSentenceComplexity(allDialogs,l);
qs.knowledgeDensity=scoreKnowledgeDensity(s.keyPreview,s.dialogRounds,l);
qs.knowledgeExposure=scoreKnowledgeExposure(s.keyPreview,allDialogs,l);
qs.contentCoverage=scoreContentCoverage(s,l);
// v4 dimensions (scored when data available)
if(opts.component||s.component||it.componentType||it.cType)
qs.componentCompliance=scoreComponentCompliance(opts.component||s.component||{type:it.cType||it.componentType,title:s.title||it.title,stem:s.stem,options:it.options||s.options,answer:it.answer||s.answer,taskDesc:s.taskDesc||it.taskDesc,requirement:it.requirement||s.requirement,feedback:it.feedback||s.feedback,wordBank:s.wordBank,answerSentence:s.answerSentence,readingSentence:s.readingSentence||s.sentence,dialogueRole:s.dialogueRole||it.dialogueRole,knowledge:s.knowledge||it.knowledge,taskBackground:s.taskBackground,id:s.id||it.cId||it.componentId},l);
if(opts.component||s.component||s.id||it.cId)
qs.configIntegrity=scoreConfigIntegrity(opts.component||s.component||{id:s.id||it.cId||it.componentId,cType:it.cType||it.componentType,title:s.title||it.title,taskDesc:s.taskDesc,cambridgeLevel:s.cambridgeLevel,cefrLevel:s.cefrLevel,options:it.options,answer:it.answer,wordBank:s.wordBank,answerSentence:s.answerSentence,title_zh:s.title_zh,desc_zh:s.desc_zh},l);
if(opts.componentExposures&&opts.componentExposures.length)
qs.masteryPrediction=scoreMasteryPrediction(opts.componentExposures,l);
if(opts.component||s.component||s.title||it.title)
qs.formatQuality=scoreFormatQuality(opts.component||s.component||{title:s.title||it.title,taskTitle:s.taskTitle||it.taskTitle,stem:s.stem,interactiveContent:s.interactiveContent,sentence:s.sentence,taskDesc:s.taskDesc,sceneDesc:s.sceneDesc,roleBackground:s.roleBackground,taskBackground:s.taskBackground,answer:s.answer||it.answer,correctSentence:s.correctSentence||s.answer},l);
if(opts.component||s.component||s.stem||s.sentence)
qs.grammarIssues=scoreGrammarIssues(opts.component||s.component||{stem:s.stem,dialogueRole:s.dialogueRole||it.dialogueRole,sentence:s.sentence,interactiveContent:s.interactiveContent},l);
// Weighted overall score (expanded for v4)
// Lower = better (0=perfect, 100=worst)
var weights={vocabAlignment:0.20,sentenceComplexity:0.10,knowledgeDensity:0.08,knowledgeExposure:0.20,contentCoverage:0.07,componentCompliance:0.10,configIntegrity:0.10,masteryPrediction:0.05,formatQuality:0.05,grammarIssues:0.05};
var posMetrics={knowledgeExposure:true,contentCoverage:true};
var overall=0,wsum=0;
Object.keys(weights).forEach(function(k){
if(qs[k]&&qs[k].score!==undefined&&qs[k].score!==null&&!qs[k].skip){
var sc=posMetrics[k]?(100-qs[k].score):qs[k].score;
overall+=sc*weights[k];wsum+=weights[k];
}
});
// Renormalize weights for available dimensions
if(wsum>0)overall=Math.round(overall/wsum);else overall=null;
// Behavioral boost
var p=Number(it.perfect)||0,o=Number(it.oops)||0;
if(p>0&&p<60&&overall!==null)overall=Math.round(overall*0.7+15);
if(o>15&&overall!==null)overall=Math.round(overall*0.7+15);
var bi=correlateBehavioral(qs,it,l);
var ra=analyzeRisks(qs,it,l);
return{
version:VERSION,
level:l,
componentType:it.cType||it.type||'',
componentId:it.cId||'',
qualityScores:qs,
overallQualityScore:overall,
overallLabel:overall===null?'无法计算':overall<15?'优(内容质量高)':overall<30?'良(质量较好)':overall<50?'中(有提升空间)':'差(需优化)',
behavioralInsights:bi,
riskAnalysis:ra,
timestamp:new Date().toISOString()
};
}
function score(summary,lvl){return evaluate(summary,null,lvl).qualityScores;}
function benchmarks(lvl){return BM[lvl||'L1']||BM.L1;}
function listComponentTypes(){return Object.keys(COMPONENT_RULES).map(function(k){return{type:k,label:COMPONENT_RULES[k].label,family:COMPONENT_RULES[k].family};});}
function getComponentRules(type){return COMPONENT_RULES[type]||null;}
// ── Export ──
g.PedagogyRules={VERSION:VERSION,
evaluate:evaluate,score:score,benchmarks:benchmarks,compare:compareComponents,
// v3 scorers
scoreVocabAlignment:scoreVocabAlignment,scoreSentenceComplexity:scoreSentenceComplexity,
scoreKnowledgeDensity:scoreKnowledgeDensity,scoreKnowledgeExposure:scoreKnowledgeExposure,
scoreContentCoverage:scoreContentCoverage,
// v4 scorers (NEW)
scoreComponentCompliance:scoreComponentCompliance,scoreConfigIntegrity:scoreConfigIntegrity,
scoreMasteryPrediction:scoreMasteryPrediction,scoreFormatQuality:scoreFormatQuality,
scoreGrammarIssues:scoreGrammarIssues,
// v4 utilities
listComponentTypes:listComponentTypes,getComponentRules:getComponentRules,correlateBehavioral:correlateBehavioral,analyzeRisks:analyzeRisks,
// Data (NEW: BRITISH_SPELLING, COMPONENT_RULES, MASTERY_WEIGHTS)
WORD_LISTS:W,PATTERNS:P,BENCHMARKS:BM,BRITISH_SPELLING:BRITISH_SPELLING,COMPONENT_RULES:COMPONENT_RULES,MASTERY_WEIGHTS:MASTERY_WEIGHTS,NEGATIVE_WORDS:NEGATIVE_WORDS
};
console.log('[PedagogyRules v4] L1 words:'+Object.keys(W.L1).length+' L2 words:'+Object.keys(W.L2).length+' L1 pat:'+P.L1.length+' L2 pat:'+P.L2.length+' component types:'+listComponentTypes().length+' spelling map:'+Object.keys(BRITISH_SPELLING).length);
})(typeof window!=='undefined'?window:this);