|
|
@@ -0,0 +1,160 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+"""
|
|
|
+美业护肤科普口播 · 敏感词 / 合规机器初筛
|
|
|
+整合自 content-reviewer 与 medical-content-compliance-auditor 两技能,按美业场景裁剪。
|
|
|
+输出 markdown 审查报告(风险等级 + 法规依据 + 合规替换 + 结论三选一)。
|
|
|
+
|
|
|
+用法:
|
|
|
+ python compliance_scan.py --text "口播全文"
|
|
|
+ python compliance_scan.py --file path/to/script.md
|
|
|
+ python compliance_scan.py --file path/to/script.md --output report.md
|
|
|
+"""
|
|
|
+import argparse
|
|
|
+import sys
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+# 词库: (词, 类别, 风险符号, 法规依据, 合规替换)
|
|
|
+WORD_LIST = [
|
|
|
+ # 绝对化用语
|
|
|
+ ("最好", "绝对化用语", "🟠", "《广告法》第9条", "适合 / 值得考虑"),
|
|
|
+ ("最佳", "绝对化用语", "🟠", "《广告法》第9条", "合适"),
|
|
|
+ ("第一", "绝对化用语", "🟠", "《广告法》第9条", "代表性 / 较早"),
|
|
|
+ ("顶级", "绝对化用语", "🟠", "《广告法》第9条", "优质"),
|
|
|
+ ("唯一", "绝对化用语", "🟠", "《广告法》第9条", "特色"),
|
|
|
+ ("国家级", "绝对化用语", "🟠", "《广告法》第9条", "(删除)"),
|
|
|
+ ("最安全", "绝对化用语", "🟠", "《广告法》第9条", "较安全"),
|
|
|
+ ("最有效", "绝对化用语", "🟠", "《广告法》第9条", "较有效"),
|
|
|
+ ("全球领先", "绝对化用语", "🟠", "《广告法》第9条", "行业先进"),
|
|
|
+ # 治愈承诺
|
|
|
+ ("根治", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "改善"),
|
|
|
+ ("治愈", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "改善"),
|
|
|
+ ("根除", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "减少"),
|
|
|
+ ("彻底消除", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "明显缓解"),
|
|
|
+ ("永不复发", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "减少反复"),
|
|
|
+ ("包治", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "辅助改善"),
|
|
|
+ ("治好", "治愈承诺", "🔴", "《广告法》第17条 / 《医疗广告管理办法》", "改善"),
|
|
|
+ # 保证性承诺
|
|
|
+ ("保证", "保证性承诺", "🟠", "《广告法》第9、25条", "有助于"),
|
|
|
+ ("承诺", "保证性承诺", "🟠", "《广告法》第9、25条", "预期(疗效类承诺删除)"),
|
|
|
+ ("100%", "保证性承诺", "🟠", "《广告法》第9、25条", "多数情况下"),
|
|
|
+ ("无效退款", "保证性承诺", "🟠", "《广告法》第25条", "(删除)"),
|
|
|
+ ("签约治疗", "保证性承诺", "🟠", "《广告法》第25条", "(删除)"),
|
|
|
+ # 功效夸大·即时
|
|
|
+ ("一夜回春", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "逐步改善"),
|
|
|
+ ("秒变", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "逐渐"),
|
|
|
+ ("瞬间", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "逐渐"),
|
|
|
+ ("奇效", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "效果"),
|
|
|
+ ("神效", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "效果"),
|
|
|
+ ("立竿见影", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "循序渐进、需要坚持"),
|
|
|
+ ("立马见效", "功效夸大·即时", "🟡", "《广告法》第28条(虚假宣传)", "逐渐见效"),
|
|
|
+ # 医疗功效越界(护肤品禁宣称)
|
|
|
+ ("治疗", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "护理 / 改善"),
|
|
|
+ ("药用", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "(删除)"),
|
|
|
+ ("消炎", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "舒缓"),
|
|
|
+ ("杀菌", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "洁净"),
|
|
|
+ ("抗炎", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "舒缓"),
|
|
|
+ ("抗菌", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "清洁"),
|
|
|
+ ("医用", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "(删除)"),
|
|
|
+ ("药效", "医疗功效越界", "🟠", "《化妆品监督管理条例》/《广告法》", "(删除)"),
|
|
|
+ # 患者证言·对比
|
|
|
+ ("术前术后", "患者证言·对比", "🔴", "《医疗广告管理办法》", "(删除,改用示意图)"),
|
|
|
+ ("对比图", "患者证言·对比", "🔴", "《医疗广告管理办法》", "(删除,改用示意图)"),
|
|
|
+ ("疗效见证", "患者证言·对比", "🔴", "《医疗广告管理办法》", "(删除)"),
|
|
|
+ ("患者案例", "患者证言·对比", "🔴", "《医疗广告管理办法》", "(删除 / 脱敏)"),
|
|
|
+ # 平台诱导风险
|
|
|
+ ("加微信", "平台诱导风险", "🟡", "抖音/小红书审核规则", "进店了解"),
|
|
|
+ ("私信我", "平台诱导风险", "🟡", "抖音/小红书审核规则", "(删除)"),
|
|
|
+ ("点击下方链接", "平台诱导风险", "🟡", "抖音/小红书审核规则", "(删除)"),
|
|
|
+ ("限时优惠", "平台诱导风险", "🟡", "抖音/小红书审核规则", "活动期间优惠"),
|
|
|
+ ("最低价", "平台诱导风险", "🟡", "抖音/小红书审核规则", "优惠价"),
|
|
|
+ ("免费领", "平台诱导风险", "🟡", "抖音/小红书审核规则", "(删除)"),
|
|
|
+ ("扫码", "平台诱导风险", "🟡", "抖音/小红书审核规则", "(删除)"),
|
|
|
+]
|
|
|
+
|
|
|
+RISK_RANK = {"🔴": 4, "🟠": 3, "🟡": 2, "🟢": 1}
|
|
|
+CONCLUSION = {
|
|
|
+ 4: "不予通过(存在极高违规,必须删除/替换后重检)",
|
|
|
+ 3: "修改后通过(高)—— 须替换 🟠 项方可进入生成",
|
|
|
+ 2: "修改后通过(中)—— 建议替换 🟡 项",
|
|
|
+ 1: "修改后通过(低)—— 建议酌情优化",
|
|
|
+ 0: "通过(未检出违禁词)",
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+def scan(text: str):
|
|
|
+ hits = []
|
|
|
+ seen = set()
|
|
|
+ for word, cat, risk, law, repl in WORD_LIST:
|
|
|
+ if word in text and word not in seen:
|
|
|
+ seen.add(word)
|
|
|
+ hits.append({
|
|
|
+ "word": word, "cat": cat, "risk": risk,
|
|
|
+ "law": law, "repl": repl,
|
|
|
+ "count": text.count(word),
|
|
|
+ })
|
|
|
+ hits.sort(key=lambda h: -RISK_RANK[h["risk"]])
|
|
|
+ return hits
|
|
|
+
|
|
|
+
|
|
|
+def build_report(text: str, hits: list) -> str:
|
|
|
+ lines = []
|
|
|
+ lines.append("## 敏感词 / 合规检测报告\n")
|
|
|
+ lines.append("### 内容类型")
|
|
|
+ lines.append("抖音短视频口播文案(美业护肤科普)\n")
|
|
|
+ lines.append("### 检测条目")
|
|
|
+ if not hits:
|
|
|
+ lines.append("\n> ✅ 未检出高危违禁词。\n")
|
|
|
+ else:
|
|
|
+ lines.append("")
|
|
|
+ lines.append("| 违规点 | 原表述 | 风险等级 | 法规依据 | 合规替换 |")
|
|
|
+ lines.append("|--------|--------|----------|----------|----------|")
|
|
|
+ for h in hits:
|
|
|
+ cnt = f"(×{h['count']})" if h["count"] > 1 else ""
|
|
|
+ lines.append(
|
|
|
+ f"| {h['cat']} | 「{h['word']}」{cnt} | {h['risk']} {('极高' if h['risk']=='🔴' else '高' if h['risk']=='🟠' else '中' if h['risk']=='🟡' else '低')} | {h['law']} | {h['repl']} |"
|
|
|
+ )
|
|
|
+ lines.append("")
|
|
|
+ max_rank = max((RISK_RANK[h["risk"]] for h in hits), default=0)
|
|
|
+ lines.append("### 审查结论")
|
|
|
+ lines.append(CONCLUSION[max_rank] + "\n")
|
|
|
+ if hits:
|
|
|
+ lines.append("### 建议")
|
|
|
+ for i, h in enumerate(hits, 1):
|
|
|
+ lines.append(f"{i}. 「{h['word']}」({h['cat']})→ 替换为「{h['repl']}」,规避对应风险。")
|
|
|
+ lines.append(f"{len(hits)+1}. 修改后建议再跑一次本脚本确认无新增违禁词。")
|
|
|
+ return "\n".join(lines)
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ ap = argparse.ArgumentParser(description="美业口播敏感词/合规机器初筛")
|
|
|
+ g = ap.add_mutually_exclusive_group(required=True)
|
|
|
+ g.add_argument("--text", help="直接传入口播文本")
|
|
|
+ g.add_argument("--file", help="传入文本/脚本文件路径(.txt/.md)")
|
|
|
+ ap.add_argument("--output", help="将报告写入该文件(可选)")
|
|
|
+ args = ap.parse_args()
|
|
|
+
|
|
|
+ if args.text:
|
|
|
+ text = args.text
|
|
|
+ else:
|
|
|
+ p = Path(args.file)
|
|
|
+ if not p.exists():
|
|
|
+ print(f"[错误] 文件不存在: {args.file}", file=sys.stderr)
|
|
|
+ sys.exit(2)
|
|
|
+ text = p.read_text(encoding="utf-8")
|
|
|
+
|
|
|
+ hits = scan(text)
|
|
|
+ report = build_report(text, hits)
|
|
|
+ print(report)
|
|
|
+
|
|
|
+ if args.output:
|
|
|
+ Path(args.output).write_text(report, encoding="utf-8")
|
|
|
+ print(f"\n[已写入报告] {args.output}", file=sys.stderr)
|
|
|
+
|
|
|
+ # 退出码:极高/高违规 → 非0,便于流程判断
|
|
|
+ max_rank = max((RISK_RANK[h["risk"]] for h in hits), default=0)
|
|
|
+ sys.exit(0 if max_rank <= 2 else 1)
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|