AI 콘텐츠 모더레이션: LLM 기반 유해 콘텐츠 분류 시스템 구축

AI 기술

콘텐츠 모더레이션AI 분류LLM 분류기유해 콘텐츠안전한 AI

이 글은 누구를 위한 것인가

  • UGC(사용자 생성 콘텐츠) 플랫폼에서 유해 콘텐츠 관리가 필요한 팀
  • 규칙 기반 필터를 LLM 기반으로 업그레이드하려는 팀
  • 모더레이션 자동화와 인간 검토의 최적 비율을 찾는 운영팀

들어가며

"씨발"이 욕설인지, 친구 사이의 격의 없는 표현인지는 맥락에 따라 다르다. 단어 매칭 필터는 이것을 구분할 수 없다. LLM 기반 모더레이션은 문맥을 이해하고 의도를 판단한다.

이 글은 bluefoxdev.kr의 AI 안전성 시스템 설계 를 참고하여 작성했습니다.


1. 모더레이션 분류 체계

[콘텐츠 카테고리별 처리 전략]

카테고리 1 - 즉시 삭제 (신뢰도 무관):
  아동 성착취물 (CSAM)
  테러리즘 관련
  
카테고리 2 - 자동 삭제 (신뢰도 > 0.95):
  명백한 스팸
  욕설/혐오 표현
  개인정보 노출

카테고리 3 - 인간 검토 대기 (신뢰도 0.7-0.95):
  맥락 의존적 혐오 표현
  성적 암시
  허위정보 의심

카테고리 4 - 자동 통과 (신뢰도 < 0.3):
  명백히 무해한 콘텐츠

[모더레이션 결정 트리]
  콘텐츠 입력
    → LLM 1차 분류 (빠른 분류, 저렴한 모델)
    → 신뢰도 < 0.7: LLM 2차 검토 (더 정확한 모델)
    → 여전히 불확실: 인간 검토 큐
    → 명백 위반: 즉시 처리

2. LLM 모더레이션 파이프라인

import anthropic
from dataclasses import dataclass
from enum import Enum

class ModerationCategory(Enum):
    SAFE = "safe"
    SPAM = "spam"
    HATE_SPEECH = "hate_speech"
    SEXUAL = "sexual"
    VIOLENCE = "violence"
    PERSONAL_INFO = "personal_info"
    MISINFORMATION = "misinformation"

@dataclass
class ModerationResult:
    category: ModerationCategory
    confidence: float
    action: str  # auto_approve, auto_reject, human_review
    reasoning: str
    severity: str  # low, medium, high, critical

client = anthropic.Anthropic()

async def moderate_content(content: str, content_type: str = "text") -> ModerationResult:
    """LLM 기반 콘텐츠 모더레이션"""
    
    # 1단계: 빠른 분류 (Haiku - 저렴하고 빠름)
    quick_response = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=200,
        messages=[{
            "role": "user",
            "content": f"""다음 {content_type} 콘텐츠를 모더레이션하세요.

콘텐츠: {content[:500]}

JSON으로만 응답:
{{
  "category": "safe|spam|hate_speech|sexual|violence|personal_info|misinformation",
  "confidence": 0.0-1.0,
  "severity": "low|medium|high|critical"
}}"""
        }]
    )
    
    import json
    quick_result = json.loads(quick_response.content[0].text)
    confidence = quick_result["confidence"]
    
    # 낮은 신뢰도면 더 정확한 모델로 재검토
    if 0.4 < confidence < 0.85:
        detailed_response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=500,
            messages=[{
                "role": "user",
                "content": f"""다음 콘텐츠를 상세하게 모더레이션하세요.
한국 문화 맥락을 고려하여 판단하세요.

콘텐츠: {content}

JSON으로만 응답:
{{
  "category": "safe|spam|hate_speech|sexual|violence|personal_info|misinformation",
  "confidence": 0.0-1.0,
  "severity": "low|medium|high|critical",
  "reasoning": "판단 이유 (한 문장)"
}}"""
            }]
        )
        final_result = json.loads(detailed_response.content[0].text)
    else:
        final_result = quick_result
        final_result["reasoning"] = ""
    
    # 액션 결정
    cat = ModerationCategory(final_result["category"])
    conf = final_result["confidence"]
    sev = final_result.get("severity", "low")
    
    if cat == ModerationCategory.SAFE and conf > 0.7:
        action = "auto_approve"
    elif sev == "critical" or (conf > 0.95 and cat != ModerationCategory.SAFE):
        action = "auto_reject"
    else:
        action = "human_review"
    
    return ModerationResult(
        category=cat,
        confidence=conf,
        action=action,
        reasoning=final_result.get("reasoning", ""),
        severity=sev,
    )

async def bulk_moderate(contents: list[dict], batch_size: int = 50) -> list[dict]:
    """대량 콘텐츠 일괄 모더레이션"""
    import asyncio
    
    results = []
    for i in range(0, len(contents), batch_size):
        batch = contents[i:i + batch_size]
        batch_results = await asyncio.gather(*[
            moderate_content(c["text"], c.get("type", "text"))
            for c in batch
        ])
        
        for content, result in zip(batch, batch_results):
            results.append({
                "content_id": content["id"],
                "action": result.action,
                "category": result.category.value,
                "confidence": result.confidence,
            })
        
        # 레이트 리밋 방지
        await asyncio.sleep(0.5)
    
    return results

마무리

LLM 모더레이션의 핵심은 "신뢰도에 따른 자동/수동 분기"다. 신뢰도 95% 이상인 경우만 자동 처리하고, 나머지는 인간 검토 큐에 넣는다. 오탐(False Positive) 발생 시 이의 신청 프로세스가 반드시 있어야 한다. 모더레이션 결정은 항상 기록하고, 이의 신청 결과로 모델을 개선하는 피드백 루프가 핵심이다.