diff --git a/custom-skills/46-jamie-journal-editor/code/scripts/journal_validator.py b/custom-skills/46-jamie-journal-editor/code/scripts/journal_validator.py index 4dbe2b6..6c1e101 100644 --- a/custom-skills/46-jamie-journal-editor/code/scripts/journal_validator.py +++ b/custom-skills/46-jamie-journal-editor/code/scripts/journal_validator.py @@ -172,8 +172,136 @@ class StructureValidator: class ComplianceChecker: """Checks brand compliance rules for Jamie Clinic journal content.""" + PROHIBITED_PATTERNS = { + 'effect_guarantee': { + 'severity': 'critical', + 'patterns': [ + r'100[%%]\s*(?:만족|효과|성공)', + r'반드시\s+(?:효과|개선|만족)', + r'완벽한?\s+결과', + r'보장합니다', + r'확실한?\s+효과', + r'틀림없이?\s+(?:효과|개선)', + ], + 'message': '효과를 보장하는 표현은 의료법 제56조 제2항 제2호 위반입니다.', + 'suggestion': '"개선에 도움을 줄 수 있습니다" 또는 "개인에 따라 결과가 다를 수 있습니다"로 변경하세요.', + }, + 'comparative_superiority': { + 'severity': 'critical', + 'patterns': [ + r'최고의?', + r'(?:국내|강남|서울|압구정)\s*(?:최고|1위|최초)', + r'1위', + r'타\s*병원보다', + r'다른\s*(?:병원|의원)보다\s*(?:우수|뛰어|좋)', + r'업계\s*최초', + r'독보적', + ], + 'message': '타 의료기관과의 비교 우위 주장은 의료법 제56조 제2항 제4호 위반입니다.', + 'suggestion': '"풍부한 경험을 보유한" 또는 "저희만의 방법으로"와 같은 객관적 표현으로 변경하세요.', + }, + 'safety_guarantee': { + 'severity': 'critical', + 'patterns': [ + r'부작용\s*(?:없|無|제로|zero)', + r'(?:100[%%]|완전히?|절대)\s*안전', + r'위험\s*(?:없|無)', + r'흉터\s*(?:없|無|제로|zero)', + r'무통(?:증)?(?:\s|$|,)', + ], + 'message': '안전성을 보장하는 표현은 의료법 제56조 제2항 제7호 위반입니다.', + 'suggestion': '부작용 가능성을 명시하고 "안전한 수술을 위해 최선을 다합니다"로 변경하세요.', + }, + 'patient_testimonial': { + 'severity': 'critical', + 'patterns': [ + r'(?:환자|고객)\s*[가-힣A-Z]+\s*(?:씨|님)의?\s*(?:후기|경험)', + r'실제\s*(?:환자|고객)\s*(?:후기|리뷰|경험담)', + r'[""]\s*(?:정말|너무|진짜)\s+(?:만족|좋아요|감사)', + r'수술\s*후\s*\d+\s*(?:개월|주일|년)\s*(?:만족|경과)', + ], + 'message': '환자 치료경험담은 의료법 제56조 제2항 제2호 위반입니다.', + 'suggestion': '환자 후기를 제거하고, 일반적인 수술 과정 설명으로 대체하세요.', + }, + 'exaggeration': { + 'severity': 'high', + 'patterns': [ + r'(?:놀라운|대박|극적인)\s*(?:변화|효과|결과)', + r'마법같은?', + r'기적적인?', + r'다시\s*태어나', + r'완벽\s*변신', + ], + 'message': '과장된 표현은 의료법 제56조 제2항 제3호 위반 가능성이 있습니다.', + 'suggestion': '"자연스러운 개선", "점진적인 효과"와 같은 절제된 표현으로 변경하세요.', + }, + 'specialty_claim': { + 'severity': 'high', + 'patterns': [ + r'전문\s*병원', + r'특화\s*(?:병원|클리닉|센터)', + r'노하우', + ], + 'message': '전문병원 지정 없이 "전문", "특화" 표현 사용은 과장 광고에 해당합니다.', + 'suggestion': '"중점진료", "집중진료", "풍부한 경험"으로 변경하세요.', + }, + } + + PROCEDURE_KEYWORDS = [ + '수술', '시술', '이마거상', '쌍꺼풀', '리프팅', '보톡스', '필러', + '눈매교정', '안검하수', '지방이식', '절개', + ] + def check(self, content: str) -> List[ValidationIssue]: - return [] + issues: List[ValidationIssue] = [] + lines = content.split('\n') + + # Check prohibited patterns + for category, rule in self.PROHIBITED_PATTERNS.items(): + for raw_pat in rule['patterns']: + compiled = re.compile(raw_pat) + for m in compiled.finditer(content): + line_number = content[:m.start()].count('\n') + 1 + issues.append(ValidationIssue( + checker='ComplianceChecker', + issue_type=category, + severity=rule['severity'], + line_number=line_number, + matched_text=m.group(), + message=rule['message'], + suggestion=rule['suggestion'], + )) + + # Check for missing disclaimers when procedure keywords present + has_procedure = any(kw in content for kw in self.PROCEDURE_KEYWORDS) + if has_procedure: + side_effect_terms = ['부작용', '합병증', '붓기', '멍', '염증'] + if not any(t in content for t in side_effect_terms): + total_lines = len(lines) + issues.append(ValidationIssue( + checker='ComplianceChecker', + issue_type='missing_side_effect_notice', + severity='high', + line_number=total_lines, + matched_text='', + message='시술/수술 내용이 있으나 부작용 안내가 없습니다.', + suggestion='"부작용(출혈, 감염, 염증 등)이 있을 수 있습니다" 형태의 안내를 추가하세요.', + )) + + individual_variation_pattern = re.compile(r'개인.{0,5}(?:차이|다를 수|다릅니다)') + if not individual_variation_pattern.search(content): + total_lines = len(lines) + issues.append(ValidationIssue( + checker='ComplianceChecker', + issue_type='missing_individual_variation_notice', + severity='medium', + line_number=total_lines, + matched_text='', + message='시술/수술 내용이 있으나 개인차 고지가 없습니다.', + suggestion='"개인에 따라 결과가 다를 수 있습니다" 문구를 추가하세요.', + )) + + return issues class SpellingChecker: