Files
AI/RESPONSIBILITY_REFACTOR_REPORT_2026-05-11.md
2026-05-11 22:55:01 +09:00

8.4 KiB

crawler_platform 책임 분리 리포트 (AI=Semantic Extractor)

작성일: 2026-05-11
대상: C:\Users\lasta\MyProject\AI\crawler_platform

1. 결론 요약

현재 구조는 동작은 하지만, 제어 책임이 API Route / SiteCrawler / CrawlPipeline / Repository에 분산되어 있습니다.
특히 SiteCrawlerCrawlPipeline이 탐색, 분석 판단, 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_urlPageProcessor.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_urlPageProcessor.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/병합 규칙 직접 처리 제거