# Phase 1 — Trafilatura 통합 (다음 작업자 핸드오프) 본 문서는 Phase 0이 완료된 시점에서 Phase 1 작업을 이어받는 AI 에이전트 또는 개발자가 즉시 작업을 시작하기 위한 핸드오프 노트다. ## 시작 전 확인 사항 - [ ] **Phase 0 Acceptance Gate**가 모두 ✅인가? [PHASE0_ACCEPTANCE_GATE.md](PHASE0_ACCEPTANCE_GATE.md) 참조. 통과 전에는 Phase 1 진행 금지. - [ ] `tests/unit`와 `tests/integration` 전체가 PASS인가? - [ ] git log에 Phase 0 commit들이 PR 단위로 분리되어 있는가? (0.1 vendored / 0.2 bug fix / 0.3 multi-file / 0.4 FastAPI / 0.5 config / 0.6 e2e tests / 0.7 gate) ## Phase 1 목표 URL이 입력일 때 원본 페이지에서 본문, 제목, 저자, 날짜, 언어, canonical URL을 정확히 뽑아 OntoCast의 `ContentUnit` metadata에 채워 넣는다. **근거**: 통합설계서 §5 Phase 1, Trafilatura 분석 §11~§17. **왜 Trafilatura를 가장 먼저 통합하는가**: 가장 작은 통합 — 단일 함수 호출(`bare_extraction`)만으로 끝남. 의존성도 명확하며 라이선스 동일 (Apache 2.0). ## 작업 단위 (PR 분해) ### 1.1: Trafilatura 의존성 활성화 `pyproject.toml`에 이미 `trafilatura[all]>=2.0.0`이 명시되어 있다. 활성화 절차: ```powershell pip install -e ".[dev]" # 의존성 재설치 시 trafilatura 자동 설치 python -c "import trafilatura; print(trafilatura.__version__)" ``` **확인**: `2.0.0` 이상이 출력되어야 한다. ### 1.2: `web_extractor.py` 어댑터 작성 **위치**: `platform/core/extractors/web_extractor.py` **근거**: Trafilatura 분석 §17의 `extract_for_ontology` 함수를 거의 그대로 사용. **필수 동작**: - 입력: `html: str`, `url: str`, `lang: str | None = None` - 출력: `ExtractedWebDocument` (dataclass) - `url`, `title`, `author`, `date`, `sitename`, `description` - `text` (정제 본문) - `body_xml` (Trafilatura `Document.body`) - `metadata` (raw dict) - `fingerprint` (SimHash) - 실패 시 `None` 반환 **호출 옵션** (Trafilatura 분석 §12 권장값 그대로): ```python Extractor( output_format="python", url=url, with_metadata=True, comments=False, tables=True, formatting=True, links=True, images=True, dedup=True, lang=lang, ) ``` ### 1.3: `ContentUnit` 모델 확장 OntoCast의 `vendored/ontocast/ontocast/onto/content_unit.py`를 **직접 수정하지 말고**, 우리 쪽에 wrapper 모델을 만든다. **위치**: `platform/models/content_unit.py` **필드** (통합설계서 §7.1 참조): - 기존 OntoCast 필드 (`text`, `index`, `doc_iri`, `graph`, `type`, `iri`) 유지/위임 - 추가: `source_url`, `title`, `author`, `publish_date`, `language`, `sitename`, `fingerprint`, `content_hash`, `metadata`, `retrieved_at`, `extracted_by` **호환성**: 기존 OntoCast 코드가 받는 `ContentUnit`과 인터페이스 호환되도록 `as_ontocast()` 메서드 제공. ### 1.4: OntoCast `ConverterTool` 분기 추가 (URL/HTML 입력) **문제**: OntoCast `ConverterTool`은 PDF/DOCX/MD만 처리. URL 또는 HTML 입력은 처리 못 함. **조치 옵션**: - **옵션 A (권장)**: OntoCast의 `convert_document.py` 모듈에 새 분기 추가 — `.html`, `.htm` 확장자 또는 `state.source_url`이 있으면 Trafilatura로 처리. **vendored 수정이지만 매우 작음**. - **옵션 B**: API 레이어(`platform/api/`)에서 입력이 URL이면 미리 fetch + Trafilatura 처리한 뒤 그 결과를 JSON envelope로 ToolBox에 넘김. **권장**: 옵션 B. vendored 수정을 늘리지 않고 platform 코드로 끝낼 수 있음. 새 endpoint: - `POST /process/url` — body: `{"url": "...", "ontology_user_instruction": "...", ...}` — 내부적으로 `web_extractor`로 본문 추출 후 OntoCast workflow 실행. ### 1.5: Fingerprint 기반 dedup - `tests/fixtures/`에 같은 본문의 두 URL fixture 만들기 - `web_extractor` 결과의 `fingerprint`가 일치하면 OntoCast 처리 skip - 저장 위치: 일단 in-memory set (`platform/storage/dedup_cache.py`), Phase 2에서 Redis로 이전 ### 1.6: 한국어 페이지 3종 추출 검증 **테스트 fixture 수집**: - 한국어 뉴스 1개 (예: 연합뉴스/조선/한겨레) - 한국어 블로그 1개 (예: 네이버 블로그) - 한국어 쇼핑 페이지 1개 (예: 쿠팡 상품 페이지) 각각 raw HTML을 `tests/fixtures/korean/`에 저장 (실제 fetch는 운영 환경에서 한 번만, 그 결과를 fixture로 박제). **테스트**: `tests/integration/test_web_extractor_korean.py` - 본문 길이 > 200자 - title 추출 성공 - language 감지: `ko` - author 또는 date 중 하나 이상 추출 ### 1.7: Acceptance Gate 1 체크 통합설계서 §5 Phase 1 Acceptance Gate 4개 항목: - [ ] URL 입력 → 본문/메타데이터가 정확히 추출되어 `ContentUnit`에 저장됨 - [ ] 한국어 뉴스/블로그/쇼핑 페이지 각각 1개씩 본문 추출 정확도 수동 검증 - [ ] 동일 URL 재입력 시 fingerprint 기반 dedup으로 skip - [ ] Phase 0의 모든 기능이 여전히 정상 동작 (회귀 없음) Phase 0의 `tests/unit/`, `tests/integration/` 전체가 여전히 PASS여야 함. ## Phase 1에서 만들 새 산출물 ``` platform/ core/ extractors/ web_extractor.py ← 1.2 models/ content_unit.py ← 1.3 storage/ dedup_cache.py ← 1.5 api/ routes/ url_ingest.py ← 1.4 (POST /process/url) tests/ fixtures/ korean/ ← 1.6 news_yonhap.html blog_naver.html shop_coupang.html unit/ test_web_extractor.py ← 1.2 test_dedup_cache.py ← 1.5 integration/ test_url_ingest.py ← 1.4 test_web_extractor_korean.py ← 1.6 docs/ phases/ PHASE1_ACCEPTANCE_GATE.md ← 1.7 (PHASE0과 동일 형식) PHASE2_NEXT_STEPS.md ← 다음 작업자에게 넘김 ``` ## 작업 시 준수사항 (PHASE0과 동일) 1. **PR 단위 분리**: 1.1~1.7 각각 별도 PR/커밋. 2. **PR 설명에 근거 인용**: 예) "통합설계서 §5 Phase 1 (1.2)에 따라 Trafilatura adapter 작성. Trafilatura 분석 §17 인용." 3. **vendored/ontocast/** 수정 최소화. 본 Phase에서는 옵션 B 사용 시 vendored 수정 0건이 목표. 4. **Phase 2로 넘어가지 말 것**: Acceptance Gate 1 통과 전까지 Crawl4AI 의존성을 코드에서 import하지 않는다. ## Phase 2 이후 핸드오프 Phase 1 완료 후 다음 작업자에게 동일한 형식의 `PHASE2_NEXT_STEPS.md`를 작성한다. 통합설계서 §12 Phase 2 작업 단위(2.1~2.8)를 참조.