Files
AI/RESPONSIBILITY_REFACTOR_REPORT_2026-05-11.md

264 lines
8.4 KiB
Markdown
Raw Normal View History

2026-05-11 22:55:01 +09:00
# crawler_platform 책임 분리 리포트 (AI=Semantic Extractor)
작성일: 2026-05-11
대상: `C:\Users\lasta\MyProject\AI\crawler_platform`
## 1. 결론 요약
현재 구조는 동작은 하지만, **제어 책임이 API Route / SiteCrawler / CrawlPipeline / Repository에 분산**되어 있습니다.
특히 `SiteCrawler``CrawlPipeline`이 탐색, 분석 판단, AI 호출, 저장까지 동시에 수행하고 있어 확장성과 테스트 경계가 약합니다.
핵심 정리:
- AI(`LLMJsonExtractor`)는 현재 DB/크롤 흐름을 직접 알지 않으며, 대체로 “의미 추출기” 역할을 수행 중.
- 하지만 시스템 전체 orchestration 주체가 부재하여 Route/Crawler/Pipeline에 제어가 분산됨.
- Repository는 저장소를 넘어 일부 정책(신뢰도 결합/병합 전략)을 포함.
---
## 2. 구성요소별 실제 책임 진단
## API Route (`app/api/routes.py`)
현재 책임:
- 요청/응답 처리 외에 다음까지 수행
- config 로딩 및 의존 객체 생성 (`CrawlPipeline`, `SiteCrawler`)
- 근거: 149-195
- crawl 파라미터 정책 적용(`max_depth/max_pages` 보정)
- 근거: 188-191
- discovery 흐름 직접 수행(robots/fetch/discover)
- 근거: 197-212
- claim confidence 갱신 정책(0~1 clamp)
- 근거: 274-284
- entity merge 비즈니스 로직 직접 수행(Claim/Relation 재매핑)
- 근거: 286-308
- 추천 태그 집계 규칙 직접 수행
- 근거: 310-343
판단:
- Route가 단순 진입점을 넘어 **서비스/도메인 로직 조립 및 정책 수행자** 역할까지 맡음.
- “얇은 Route + Service 호출” 원칙과 불일치.
---
## SiteCrawler (`app/core/crawler/site_crawler.py`)
현재 책임:
- 사이트 탐색(queue/depth/visited/discover)
- 근거: 70-117
- same-domain 필터, robots 차단 판단
- 근거: 89-100
- 페이지 유형 분류 및 분석 여부 판단
- 근거: 105, 119-120, 201-224
- AI/Extractor 호출
- 근거: 120
- 저장 호출(page/claim/evidence/log)
- 근거: 121-135
- crawl_jobs 상태 생성/완료 처리
- 근거: 83, 172-188
판단:
- `SiteCrawler`가 “웹 탐색기”를 넘어 **페이지 처리기 + 저장 오케스트레이터**까지 수행.
- 요청하신 기준(탐색 전용) 대비 책임 과다.
분리 후보:
- `classify_page`, `analyze_page_types` 판단 로직
- `extractor.extract(...)` 호출
- `repository.upsert_page/save_extraction_bundle(...)` 저장 호출
- `_create_job/_finish_job` 실행 추적
---
## CrawlPipeline (`app/core/crawler/pipeline.py`)
현재 책임:
- robots 정책 판단
- 근거: 34-35
- fetch + parse + extract
- 근거: 37-41
- 프로젝트/소스 동기화 및 페이지/추출 결과 저장
- 근거: 43-55
판단:
- 단일 URL 처리기 역할을 하면서도 저장 정책까지 포함.
- 이름은 Pipeline이지만 사실상 **PageProcessor + 저장 orchestration**을 동시에 수행.
분리 후보:
- `crawl_url``PageProcessor.process(url)``CrawlService.persist(processed_page)`로 분리
- robots 허용/재시도/저장 여부 판단은 Service 계층으로 이동
---
## Extractor (`app/core/extractor/ai_provider.py`, `factory.py`)
현재 책임:
- LLM 호출 및 JSON 파싱/복구
- ontology predicate normalize
- 실패 시 rule-based fallback
주요 근거:
- AI 추출 핵심: 42-107, 148-247
- ontology normalize: 75-78
- fallback: 109-131
- provider 선택(facade): `factory.py` 11-17
판단:
- 크롤링 큐, 링크 탐색, DB 저장을 직접 알지 않음(좋음).
- 다만 `LLMJsonExtractor` 내부 fallback은 “추출 품질 보완” 범주로는 허용 가능하나, 책임을 더 엄격히 분리하려면 fallback도 외부 orchestration(Service)로 이동 가능.
요약:
- **치명적 위반 없음**(crawl/storage/pipeline orchestration은 알지 않음).
---
## Repository (`app/core/database/repository.py`)
현재 책임:
- pages/entities/claims/evidence/extraction_logs 저장 및 upsert
- claim hash 기반 dedup/merge
- relation upsert 및 support_count 증가
- confidence 결합 규칙(extraction + source trust)
주요 근거:
- 저장/병합 중심: 140-226, 228-306
- 신뢰도 결합 규칙: 163, 322-323
- claim hash 전략: 164-176, 326-342
판단:
- 저장 인터페이스 역할은 수행하지만, **정책성 로직(신뢰도 결합 비율 0.7/0.3, max merge, relation support 전략)**이 포함됨.
- “Repository는 저장소” 원칙을 엄격히 적용하면, 정책 계산은 Service(또는 Domain Policy)로 이동하는 것이 바람직.
분리 후보:
- `combine_confidence`
- claim update 시 `max(confidence)` 전략
- relation `support_count` 증가 규칙
---
## session_scope (`app/core/database/session.py`)
현재 책임:
- commit/rollback/close 트랜잭션 경계
근거: 40-51
판단:
- 요청하신 기준과 일치. 변경 우선순위 낮음.
---
## 3. 현재 가장 큰 책임 혼재 지점
1. `SiteCrawler`가 탐색기 + 처리기 + 저장 오케스트레이터를 모두 수행
2. `CrawlPipeline`이 처리기 + 저장기를 동시에 수행
3. `API Route`가 서비스 조립/정책/집계/병합 로직을 직접 수행
4. `Repository`가 저장소를 넘어 정책 일부까지 포함
---
## 4. 목표 아키텍처 제안
권장 호출 구조:
`FastAPI Route -> CrawlService -> SiteCrawler -> PageProcessor -> Fetcher -> Parser -> Extractor -> Repository`
역할 재정의:
- Route: request 검증, service 호출, response 변환
- CrawlService: 전체 orchestration/정책 판단/재시도/저장 여부 결정
- SiteCrawler: 링크 탐색(queue/depth/domain/link discovery)만 수행
- PageProcessor: 단일 URL의 fetch/parse/extract만 수행
- Extractor: 텍스트 -> 구조화 JSON/Entity/Claim 변환만 수행
- Repository: 저장/upsert 인터페이스만 수행 (정책 계산 제외)
---
## 5. 리팩터링 설계(코드 대규모 변경 전)
## Phase 0: 인터페이스 고정
- `PageProcessorResult` DTO 정의
- `url/final_url/status_code/title/clean_text/page_type/entities/claims/raw_output/errors`
- `CrawlDecisionPolicy`(분석 여부/저장 여부 판단) 초안 분리
## Phase 1: Service 계층 도입
- `app/core/services/crawl_service.py` 신설
- Route는 `CrawlService.crawl_url(...)`, `CrawlService.crawl_site(...)`만 호출
- 기존 로직은 내부적으로 재사용하되 외부 인터페이스 먼저 고정
## Phase 2: SiteCrawler 축소
- `SiteCrawler` 반환을 “발견된 URL 작업 목록” 중심으로 전환
- 페이지 분류/분석 여부/AI 호출/저장은 `CrawlService`로 이동
## Phase 3: CrawlPipeline -> PageProcessor 전환
- `CrawlPipeline.crawl_url``PageProcessor.process`로 대체
- PageProcessor는 fetch/parse/extract까지만 수행, DB 접근 제거
## Phase 4: Repository 정책 분리
- `combine_confidence`, merge rule을 `app/core/services/policies/*.py`로 이동
- Repository는 저장/조회/upsert만 수행
## Phase 5: Route 슬림화
- `/crawl`, `/crawl-site`, `/discover`, `/entities/merge`, `/claims/{id}/confidence`를 Service 호출형으로 변환
---
## 6. 안전한 단위 리팩터링 파일 목록과 변경 순서
1) `app/core/services/crawl_service.py` (신규)
2) `app/core/services/crawl_dto.py` (신규)
3) `app/core/services/policies.py` (신규; confidence/merge 정책)
4) `app/core/crawler/pipeline.py` (PageProcessor 역할로 축소 또는 `page_processor.py`로 분리)
5) `app/core/crawler/site_crawler.py` (탐색 전용으로 축소)
6) `app/core/database/repository.py` (정책 제거, 저장 전용화)
7) `app/api/routes.py` (Service 호출만 남기기)
8) `app/cli/main.py` (Route와 동일 Service 재사용)
9) `tests/` (서비스 단위/계층 경계 테스트 추가)
---
## 7. 테스트 전략(리팩터링 안전장치)
- 계약 테스트: `Extractor` 입력/출력 계약 유지
- 단위 테스트:
- `SiteCrawler`: URL discovery/queue/depth/domain 필터만 검증
- `PageProcessor`: fetch/parse/extract 파이프만 검증(저장 없음)
- `CrawlService`: 분석 여부 판단/저장 호출/재시도 정책 검증
- `Repository`: pure upsert/조회만 검증
- 회귀 테스트:
- `/crawl`, `/crawl-site` API 응답 필드 변화 없음
- claim/entity 수 및 dedup 결과 일관성 확인
---
## 8. 즉시 적용 가능한 최소 원칙
- AI는 `clean_text -> structured data`만 담당
- “저장 여부, 재시도, 정책 판단”은 Service가 담당
- Repository에서 정책 계산 로직 분리
- Route에서 SQL/병합 규칙 직접 처리 제거