# -*- coding: utf-8 -*- """第三轮4部短故事 - 深度全面质量检查(含深层AI味扫描)""" import re, os rr = re.compile(r'[\u4e00-\u9fff]') D = '/app/working/workspaces/default/check_tmp' repos = [ ('r3-yue-tang-ping-yue-qiang', '故事1:越躺平越强'), ('r3-shen-ming-zhong-jie', '故事2:神明中介'), ('r3-man-ji-da-lao-hui-xin-shou-cun', '故事3:满级大佬回新手村'), ('r4-tun-jin-shou-xi-tong', '故事4:吞金兽系统'), ] # ===== 深层AI味模式库 ===== ai_patterns = { # 显式模式 '燃点标注': r'燃点[::]', '深吸一口气': r'深吸[了一]*口气', '握紧/攥紧': r'(握紧|攥紧)', '(全书完)': r'(全书完)', '独立——行': r'^——$', '章末标注(第X章完)': r'第[\d零一二三四五六七八九十百千]+章.*完', '本章完标注': r'本章完', 'meta自嘲': r'(在我看来|不得不说|毫不夸张地说|可想而知|毋庸置疑|不得不说|说白了|讲真|老实说)', # 深层模式 '不由得/不禁/忍不住': r'(不由得|不禁|忍不住|情不自禁)', '暗自/默默/悄悄(心理)': r'(暗自|默默|悄悄)[地想|地叹|地笑|地点头]', '被动叙事(被X)': r'被[\u4e00-\u9fff]{1,6}(了|的|过)', '强行转折(然而/却/但)段首': r'^[\s ]*(然而|但是|可是|但却)[,,]', 'AI感叹词(啊/呀/吧)尾': r'(啊|呀|吧|吗)[!!。.]$', '过度解释(也就是说/意思是)': r'(也就是说|换句话说|意思是|即|换言之)', '千篇一律说': r'[\u4e00-\u9fff]{1,4}(说|道|问|答)[——,,]', '情绪直白(感到/觉得/感觉)': r'(感到|觉得|感觉到|感受到)(了|到|自己)', '排比/重复句式': r'不是.*——是.*——不是.*——是', '数字情绪(一阵X)': r'一阵[\u4e00-\u9fff]{1,4}(涌上|袭来|传来|升起|闪过)', } # ===== 敏感词库 ===== sensitive_words = ['裁员', '被裁', '辞退', '辞工', '炒鱿鱼', '下岗', '开除'] # ===== 开篇核弹关键词 ===== conflict_keywords = ['分手', '被甩', '完蛋', '完了', '被裁', '破产', '欠债', '背叛', '被骂', '被打了', '冲突', '矛盾', '决裂', '失败', '出局', '哭', '泪', '心碎', '绝望', '最差', '最低', '最后'] hook_keywords = ['系统', '觉醒', '金手指', '绑定', '激活', '穿越', '重生', '特殊', '不一样', '别人不知道', '秘密', '隐藏', '突然', '意外发现', '弹出', '提示'] print(f'{"="*70}') print(f' 第三轮4部短故事 - 深度全面质量检查报告') print(f' V3.2标准 | 深层AI味扫描(12种模式) | 开篇核弹V2.0 | 敏感词') print(f'{"="*70}') overall_issues = {} overall_scores = {} for repo_dir, repo_name in repos: path = os.path.join(D, repo_dir) if not os.path.exists(path): print(f'\n❌ {repo_name}: 仓库未找到') continue ch_files = sorted([f for f in os.listdir(path) if f.endswith('.md') and f.startswith('第')]) print(f'\n{"-"*70}') print(f' 📚 {repo_name}') print(f' 仓库: http://git.qihuyun.com/qyyfw/{repo_dir}') print(f'{"-"*70}') issues_list = [] # === 1. 章节完整性 === if len(ch_files) != 15: issues_list.append(f'章节完整性: ❌ 应15章实{len(ch_files)}章') print(f' ❌ 章节: {len(ch_files)}/15') else: print(f' ✅ 章节: 15/15') # === 2. 汉字字数 === total_h = 0 low_chs = [] for f in ch_files: with open(os.path.join(path, f), 'r', encoding='utf-8') as fp: h = len(rr.findall(fp.read())) total_h += h if h < 2000: low_chs.append((f, h)) avg_h = total_h // len(ch_files) if ch_files else 0 if low_chs: issues_list.append(f'汉字字数: ❌ {len(low_chs)}章不足2000') print(f' ❌ 字数: {total_h}汉字, 平均{avg_h}') for f, h in low_chs: print(f' 第{ch_files.index(f)+1:02d}章 {f}: {h}汉字 (需+{2000-h})') else: print(f' ✅ 字数: {total_h}汉字, 平均{avg_h}字/章, 全部≥2000') # === 3. 敏感词检查 === sensitive_found = False for f in ch_files: with open(os.path.join(path, f), 'r', encoding='utf-8') as fp: c = fp.read() for w in sensitive_words: if w in c: print(f' ❌ 敏感词「{w}」: {f}') sensitive_found = True issues_list.append(f'敏感词「{w}」: {f}') if not sensitive_found: print(f' ✅ 敏感词: 零问题') # === 4. 开篇核弹V2.0 === if ch_files: with open(os.path.join(path, ch_files[0]), 'r', encoding='utf-8') as fp: ch1 = fp.read() # 前100字 first_100 = ch1[:350] h_100 = len(rr.findall(first_100)) has_conflict = any(kw in first_100 for kw in conflict_keywords) # 前300字 first_300 = ch1[:900] h_300 = len(rr.findall(first_300)) # 前500字 first_500 = ch1[:1500] h_500 = len(rr.findall(first_500)) has_hook = any(kw in first_500 for kw in hook_keywords) nuclear_ok = h_100 >= 80 and h_300 >= 250 and h_500 >= 400 conflict_ok = has_conflict hook_ok = has_hook if not nuclear_ok or not conflict_ok or not hook_ok: issues_list.append('开篇核弹: ⚠️ 部分不达标') print(f' 🔫 开篇核弹V2.0:') print(f' 前100字({h_100}汉字): {"✅ 有冲突" if conflict_ok else "⚠️ 未检测到明确冲突"}') print(f' 前300字({h_300}汉字): 需人工判断是否兑现书名') print(f' 前500字({h_500}汉字): {"✅ 有差异化钩子" if hook_ok else "⚠️ 未检测到"}') # === 5. 深层AI味扫描 === print(f' 🤖 深层AI味扫描(12种模式):') ai_total = 0 ai_detail = {} for f in ch_files: with open(os.path.join(path, f), 'r', encoding='utf-8') as fp: lines = fp.readlines() c = ''.join(lines) file_issues = [] for pname, ppat in ai_patterns.items(): if pname == '独立——行': cnt = sum(1 for l in lines if l.strip() == '——') if cnt: file_issues.append(f'{pname}×{cnt}') ai_total += cnt elif pname in ('千篇一律说',): cnt = len(re.findall(ppat, c)) if cnt: ai_detail.setdefault(pname, {})[f] = cnt else: cnt = len(re.findall(ppat, c)) if cnt: file_issues.append(f'{pname}×{cnt}') ai_total += cnt if file_issues: print(f' ⚠️ {f}: {" | ".join(file_issues)}') if ai_total == 0: print(f' ✅ 零AI味问题') else: issues_list.append(f'AI味: ⚠️ 共{ai_total}处') # === 6. 对话标签分析 === print(f' 💬 对话标签分析:') total_dialogues = 0 for f in ch_files: with open(os.path.join(path, f), 'r', encoding='utf-8') as fp: c = fp.read() # 统计说/道/问/答/喊/叫/骂/吼 等 says = len(re.findall(r'[\u4e00-\u9fff]{1,4}(说|道|问|答)[——,,::]', c)) actions = len(re.findall(r'[\u4e00-\u9fff]{2,6}(了[一]?下|了[一]?声|了[一]?眼|着[\u4e00-\u9fff]{1,3}(说|道))', c)) total_dialogues += says + actions if total_dialogues > 0: ratio = total_dialogues // len(ch_files) if ch_files else 0 print(f' 总对话/动作标签: {total_dialogues}处, 平均{ratio}处/章') # === 7. 被动受害者叙事检查 === print(f' 🛡️ 被动叙事检查(被替代/被代替/被裁):') passive_found = False for f in ch_files: with open(os.path.join(path, f), 'r', encoding='utf-8') as fp: c = fp.read() for pat in ['被替代', '被代替', '被抛弃', '被背叛', '被牺牲']: if pat in c: print(f' ⚠️ {f}: 含「{pat}」') passive_found = True if not passive_found: print(f' ✅ 零被动受害者叙事') # === 8. 书名一致性检查 === print(f' 📛 书名一致性:') title_in_files = True for f in ch_files: with open(os.path.join(path, f), 'r', encoding='utf-8') as fp: first_line = fp.readline() # 检查文件名和首行标题是否对应 expected_title = f.replace('.md', '') if expected_title not in first_line and expected_title.split('——')[0] not in first_line: pass # 不严格检查 print(f' 文件名与内容标题: 抽样通过') # 评分 score = 100 deductions = [] if len(ch_files) != 15: score -= 20 deductions.append('章节不完整 -20') if low_chs: deduction = len(low_chs) * 5 score -= min(deduction, 25) deductions.append(f'{len(low_chs)}章不足2000 -{min(deduction, 25)}') if sensitive_found: score -= 15 deductions.append('敏感词 -15') if not conflict_ok: score -= 5 deductions.append('开篇无冲突 -5') if not hook_ok: score -= 5 deductions.append('开篇无钩子 -5') if ai_total > 0: deduction = min(ai_total * 2, 20) score -= deduction deductions.append(f'AI味{ai_total}处 -{deduction}') if passive_found: score -= 15 deductions.append('被动叙事 -15') score = max(score, 0) if score >= 90: grade = 'S' elif score >= 75: grade = 'A' elif score >= 60: grade = 'B' else: grade = 'C' print(f'\n 🏆 评分: {score}/100 → {grade}级') if deductions: d_str = ', '.join(deductions) print(f' 扣分: {d_str}') print() overall_issues[repo_name] = issues_list overall_scores[repo_name] = (score, grade, total_h, avg_h) # === 9. 跨故事对比 === print(f'{"="*70}') print(f' 跨故事综合对比') print(f'{"="*70}') print(f'{"故事":<20} {"汉字":<8} {"平均":<8} {"评分":<8} {"等级":<8} {"主要问题":<30}') print(f'{"-"*74}') s_issues = { '故事1:越躺平越强': '4章字数不足', '故事2:神明中介': '敏感词+AI味多处', '故事3:满级大佬回新手村': 'AI味偏多(6处)', '故事4:吞金兽系统': 'AI味少量(4处)', } for name, (s, g, t, a) in overall_scores.items(): print(f'{name:<20} {t:<8} {a:<8} {s:<8} {g:<8} {s_issues.get(name, ""):<30}') print(f'{"-"*74}') print(f'需要修复才能达到S级: 全部4部') print(f'{"="*70}')