# ThreadAuto 뉴스 인사이트 파이프라인 — 구현 계획서
**작업 ID**: task-1189.1 | **작성일**: 2026-03-28
**프로젝트 경로**: /home/jay/projects/ThreadAuto/

---

## Phase 1: 인프라 + 크롤링 (예상 1주)

### 마이크로태스크 1-1: ContentType Enum 신설
- **대상 파일**: `/home/jay/projects/ThreadAuto/constants.py` (신규)
- **변경 내용**: ContentType Enum 정의 (text_empathy, text_data, text_story, text_insight, text_news_insight, text_cta_soft, text_cta_hard, cardnews, video), EntityRole Enum (SUBJECT, CONTEXT)
- **테스트**: import 확인, 기존 문자열과 값 일치 검증
- **커밋**: "feat: add ContentType and EntityRole enums"

### 마이크로태스크 1-2: 매일경제 HTML 파서 구현
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/rss_fetcher.py` (수정 — MKInsuranceFetcher 함수 추가)
- **변경 내용**: `fetch_mk_insurance()` — requests+BS4로 https://www.mk.co.kr/news/financial/insurance 파싱. 제목, URL, 발행일, 리드문 2문장 추출. User-Agent 헤더 설정. 실패 시 빈 리스트 반환.
- **테스트**: 실제 URL 접근 테스트 (requests.get → 200 확인), 파싱 결과 필드 존재 검증
- **커밋**: "feat: add MK insurance HTML scraper to rss_fetcher"

### 마이크로태스크 1-3: 보험저널 S2 전체 섹션 파서
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/rss_fetcher.py` (수정)
- **변경 내용**: INSJOURNAL_SECTIONS에 S2 전체 섹션 URL 추가. 기존 `section#section-list > ul.type1` 셀렉터가 `ul.type3`으로 변경되었을 수 있으므로, 양쪽 모두 시도하는 fallback 로직 추가.
- **테스트**: 실제 S2 페이지 접근 + 파싱, 기존 하위 섹션과 중복 기사 여부 확인
- **커밋**: "feat: add insjournal S2 full section parser with CSS selector fallback"

### 마이크로태스크 1-4: Scrapling fallback 래퍼
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/scrapling_adapter.py` (신규)
- **변경 내용**: `ScraplingFetcher` 클래스 — requests 실패(403/429) 시 Scrapling으로 재시도. 인터페이스는 rss_fetcher와 동일 (title, link, summary, published 반환).
- **테스트**: mock으로 requests 403 응답 → Scrapling fallback 실행 확인
- **커밋**: "feat: add Scrapling fallback adapter for WAF-blocked sites"

### 마이크로태스크 1-5: 통합 뉴스 크롤러
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/news_crawler.py` (신규)
- **변경 내용**: `crawl_news()` — fetch_all_feeds() + fetch_mk_insurance() 통합 호출. 각 소스별 실패 격리 (한 소스 실패해도 나머지 계속). 결과를 news_cache.json schema v2로 저장.
- **테스트**: 전체 소스 크롤링 E2E 테스트, 실패 격리 테스트
- **커밋**: "feat: add unified news_crawler with source isolation"

### 마이크로태스크 1-6: 크롤링 스케줄러
- **대상 파일**: `/home/jay/projects/ThreadAuto/scheduler/news_scheduler.py` (신규)
- **변경 내용**: APScheduler 기반 6시간 주기 크롤링 트리거. 30분 주기 TTL 만료 체크. 실행 시각 로깅.
- **테스트**: 스케줄러 등록 확인, 트리거 시각 검증 (mock time)
- **커밋**: "feat: add news crawling scheduler (6h crawl + 30min TTL check)"

---

## Phase 2: 필터링 + 다양성 (예상 1주)

### 마이크로태스크 2-1: 중복 제거 모듈
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/dedup_checker.py` (신규)
- **변경 내용**: `DeduplicationChecker` — URL 기반 + 제목 Jaccard 유사도 0.85 병행 중복 제거. difflib.SequenceMatcher 사용.
- **테스트**: 동일 URL 제거, 유사 제목(0.86) 제거, 다른 제목(0.50) 통과 테스트
- **커밋**: "feat: add dedup_checker with URL + title similarity"

### 마이크로태스크 2-2: 특정 회사 페널티 키워드
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/keyword_filter.py` (수정)
- **변경 내용**: COMPANY_PENALTY_KEYWORDS 추가 (인사발령, 순이익, 영업이익, 대표이사 취임, 실적 발표 등). calculate_score()에 company_penalty 반영. 최종 점수 < 0 → 드랍.
- **테스트**: 페널티 키워드 포함 기사 → 점수 감소, "삼성화재 실적 발표" → 드랍 확인
- **커밋**: "feat: add company penalty keywords to filter"

### 마이크로태스크 2-3: LLM 보조 필터
- **대상 파일**: `/home/jay/projects/ThreadAuto/crawler/llm_filter.py` (신규)
- **변경 내용**: `classify_article()` — Claude Haiku로 "업계 전반 / 특정 회사" 분류. 배치 처리 (최대 10건 동시). ai_category_hint 필드 반환. 최종 pass/fail 권한 없음.
- **테스트**: mock LLM 응답으로 필드 저장 확인
- **커밋**: "feat: add LLM auxiliary filter for article classification"

### 마이크로태스크 2-4: 도메인 다양성 점수
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/domain_diversity_scorer.py` (신규)
- **변경 내용**: `domain_diversity_score()` — tldextract로 eTLD+1 추출. `1 - (최다도메인비율)` 계산. 0.5 미달 시 최다 도메인 기사 1개 교체.
- **테스트**: 단일 도메인 5건 → 0.0, 3개 도메인 고른 분포 → 0.67 등 검증
- **커밋**: "feat: add domain diversity scorer with eTLD+1"

### 마이크로태스크 2-5: topic_selector.py 수정
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/topic_selector.py` (수정)
- **변경 내용**: DAILY_MIX_V3에 text_news_insight 추가. select_trend_topics() 내부에서 뉴스 최대 4개, evergreen 최소 2개 보장 로직. select_single_topic(category="업계동향")에서 뉴스 토픽 반환 지원. 도메인 다양성 점수 0.5 체크.
- **테스트**: 뉴스 0건 → evergreen 6개, 뉴스 5건 → 뉴스 4개+evergreen 2개 등
- **커밋**: "feat: integrate news topics into topic_selector with diversity check"

---

## Phase 3: 인사이트 생성 (예상 1.5주)

### 마이크로태스크 3-1: news_context_injector 구현
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/news_context_injector.py` (신규)
- **변경 내용**: `inject_news_context()` — 뉴스 토픽의 제목+리드문+출처URL을 프롬프트 컨텍스트로 구성. locked_fact 변수 추출 (수치 정규식으로 자동 감지). visual_hint 기본값 생성.
- **테스트**: 뉴스 토픽 입력 → locked_fact 리스트 추출, 프롬프트 컨텍스트 포맷 검증
- **커밋**: "feat: add news_context_injector with locked_fact extraction"

### 마이크로태스크 3-2: 프롬프트 작성
- **대상 파일**: `/home/jay/projects/ThreadAuto/prompts/news_insight_v1.txt` (신규)
- **변경 내용**: 아테나 초안 기반 프롬프트. SYSTEM 지시(원문 인용 금지, locked_fact 보존, 금융 조언 금지) + USER 템플릿 ({article_title}, {lead_text}, {locked_facts_json}, {slide_count}).
- **테스트**: 프롬프트 렌더링 후 변수 치환 정상 확인
- **커밋**: "feat: add news_insight_v1 prompt template"

### 마이크로태스크 3-3: FiveStagePipeline mode 분기
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/five_stage_pipeline.py` (수정)
- **변경 내용**: generate() 메서드에 mode 파라미터 추가. mode="news_insight" 시 news_context_injector 전처리 → 뉴스 프롬프트 적용. 기존 evergreen 동작 (mode=None) 변경 없음.
- **테스트**: mode=None → 기존 동작 회귀 테스트, mode="news_insight" → 뉴스 컨텍스트 포함 확인
- **커밋**: "feat: add news_insight mode to FiveStagePipeline"

### 마이크로태스크 3-4: locked_fact_manager 구현
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/locked_fact_manager.py` (신규)
- **변경 내용**: `validate_locked_facts()` — writing 단계 출력에서 locked_fact 수치가 원형 보존되었는지 정규식 검증. 불일치 시 재생성 트리거. 3회 재시도 후 human review 큐.
- **테스트**: 수치 보존 → PASS, 수치 변조 → FAIL+재생성, 3회 실패 → human review
- **커밋**: "feat: add locked_fact_manager for numeric integrity"

### 마이크로태스크 3-5: fact_guard 뉴스 인용 경로
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/fact_guard.py` (수정)
- **변경 내용**: validate_numbers()에 news_source_url 파라미터 추가. 출처 URL이 있는 수치는 fact_db 미등재라도 "뉴스 인용" 플래그로 통과. 단, locked_fact_manager의 정합성 검증은 별도 수행.
- **테스트**: fact_db 미등재 수치 + 출처 URL → 통과 + "뉴스 인용" 플래그
- **커밋**: "feat: add news citation path to fact_guard"

---

## Phase 4: 검증 + 발행 (예상 1주)

### 마이크로태스크 4-1: compliance_filter entity_role
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/compliance_filter.py` (수정)
- **변경 내용**: entity_role 분류 5단계 규칙 추가. entity_blacklist.json 로드. SUBJECT → 콘텐츠 드랍, CONTEXT → 기업명 익명화. filter_content()에 뉴스 모드 분기 추가.
- **테스트**: 제목에 기업명 → SUBJECT, 1회 부수적 언급 → CONTEXT, 블랙리스트 매칭 → SUBJECT
- **커밋**: "feat: add entity_role classification to compliance_filter"

### 마이크로태스크 4-2: 기업 블랙리스트
- **대상 파일**: `/home/jay/projects/ThreadAuto/data/entity_blacklist.json` (신규)
- **변경 내용**: 초기 50개 보험사/GA 기업명 (삼성생명, 한화생명, 교보생명, DB손보, 메리츠화재 등 + 주요 GA사)
- **테스트**: JSON 유효성, 중복 없음 확인
- **커밋**: "feat: add initial entity blacklist (50 companies)"

### 마이크로태스크 4-3: 원문 유사도 체크
- **대상 파일**: `/home/jay/projects/ThreadAuto/content/content_qa.py` (신규)
- **변경 내용**: `check_originality()` — difflib.SequenceMatcher로 생성 콘텐츠와 원문 리드문 유사도 계산. 0.6 초과 시 FAIL + 재생성 또는 수동 큐.
- **테스트**: 원문 그대로 → 1.0 FAIL, 완전 다른 텍스트 → 0.1 PASS, 경계값 0.59 → PASS
- **커밋**: "feat: add originality check with difflib similarity"

### 마이크로태스크 4-4: 출처 표기 슬라이드 렌더러
- **대상 파일**: `/home/jay/projects/ThreadAuto/renderer/card_source_slide.py` (신규)
- **변경 내용**: `SourceSlideRenderer` — 단색 배경(#F5F5F5), 도메인명+제목+발행일, "본 콘텐츠는 원문을 바탕으로 재구성되었습니다" 고지 문구. QR코드 선택적 (URL 화이트리스트 검증).
- **테스트**: 렌더링 결과 이미지 생성 확인, QR URL 화이트리스트 미등록 도메인 → QR 생략
- **커밋**: "feat: add SourceSlideRenderer for news attribution"

### 마이크로태스크 4-5: run_text_post.py / run_card_post.py 수정
- **대상 파일**: `/home/jay/projects/ThreadAuto/run_text_post.py` (수정), `/home/jay/projects/ThreadAuto/run_card_post.py` (수정)
- **변경 내용**: text_news_insight content_type 지원. ContentType Enum 참조. 뉴스 토픽 선택 시 mode="news_insight"로 파이프라인 호출. 카드뉴스 마지막에 출처 슬라이드 자동 추가.
- **테스트**: text_news_insight 토픽 → 올바른 파이프라인 호출, 출처 슬라이드 포함
- **커밋**: "feat: add text_news_insight support to run_text/card_post"

---

## Phase 5: 모니터링 + 운영 (예상 1주)

### 마이크로태스크 5-1: 메트릭 수집
- **대상 파일**: `/home/jay/projects/ThreadAuto/monitor/metrics_collector.py` (신규)
- **변경 내용**: 크롤링 성공/실패율, 필터링 통과율, 콘텐츠 품질 점수, 도메인 다양성 수집
- **커밋**: "feat: add metrics_collector for pipeline monitoring"

### 마이크로태스크 5-2: 알림 발송
- **대상 파일**: `/home/jay/projects/ThreadAuto/monitor/alert_dispatcher.py` (신규)
- **변경 내용**: locked_fact 실패, 유사도 초과, circuit breaker 발동, 크롤러 장애 알림 (Telegram)
- **커밋**: "feat: add alert_dispatcher for pipeline failures"

### 마이크로태스크 5-3: human review 큐 + 대시보드
- **대상 파일**: `/home/jay/projects/ThreadAuto/monitor/review_queue.py` (신규)
- **변경 내용**: human review 큐 관리, 30분 타임아웃 자동 evergreen 대체, 승인/거부 API
- **커밋**: "feat: add human review queue with 30min timeout"

---

## 의존성 추가 (requirements.txt)

```
tldextract>=5.0.0    # Phase 2: eTLD+1 도메인 추출
scrapling>=0.2.0     # Phase 1: WAF fallback (optional)
apscheduler>=3.10.0  # Phase 1: 크롤링 스케줄러
```

## 테스트 전략

| 구분 | 대상 | 기준 |
|------|------|------|
| 단위 | rule_filter, dedup_checker, entity_role | 커버리지 90%+ |
| 통합 | 크롤링→필터링→저장 | 실 URL 20개 |
| 유사도 | locked_fact + originality | 원문 대비 0.6 이하 100% |
| 회귀 | run_card_post.py content_type | Enum 불일치 0건 |
| E2E | 전체 파이프라인 | 발행 성공률 95%+ |
