This commit is contained in:
LASTA_DEV01\lasta
2026-05-22 19:07:07 +09:00
parent c89edecf8c
commit 93980da14d
23 changed files with 1211 additions and 648 deletions

View File

@@ -0,0 +1,100 @@
# PHASE INDEX - ontology_platform engine-respect roadmap
?묒꽦?? 2026-05-19
踰붿쐞: `ontology_platform` ?꾩슜. `crawler_platform`?€ ?대쾲 ?묒뾽 踰붿쐞?먯꽌 ?쒖쇅?쒕떎.
湲곗? 臾몄꽌:
- `ontology_platform/docs/?듯빀?ㅺ퀎??md`
- `ontology_platform/README.md`
- `ontology_platform/docs/phases/PHASE0_ACCEPTANCE_GATE.md`
- `ontology_platform/docs/phases/PHASE1_NEXT_STEPS.md`
- `ontology_platform/docs/phases/PHASE1_ACCEPTANCE_GATE.md`
- `ontology_platform/docs/phases/PHASE2_ACCEPTANCE_GATE.md`
?듭떖 ?먯튃:
- OntoCast??Base ?붿쭊?쇰줈 議댁쨷?쒕떎.
- vendored OntoCast 肄붿뼱???듯빀?ㅺ퀎?쒓? ?덉슜??踰붿쐞 ?몄뿉???섏젙?섏? ?딅뒗??
- Trafilatura, Crawl4AI, Guardrails, Neo4j GraphRAG??吏곸젒 ?ш뎄?꾪븯吏€ ?딄퀬 ?뉗? adapter/facade濡?媛먯떬??
- Firecrawl, OpenDeepResearcher 肄붾뱶???ы븿?섏? ?딅뒗??
- Acceptance Gate瑜??듦낵?섍린 ???ㅼ쓬 ?듯빀?쇰줈 ?섏뼱媛€吏€ ?딅뒗??
---
PHASE 0. ?붿쭊 寃쎄퀎 媛먯궗 諛?Phase Gate 蹂듦뎄
FILE: ./26_05_19_engine_respect_plan/phase_00_001_engine_boundary_gate.md
1) ?꾩옱 `ont_platform` 紐⑤뱢??Base/Adapter/Draft/Excluded 梨낆엫?쇰줈 遺꾨쪟 [?꾨즺]
2) Phase 0?먯꽌 誘몃옒 Phase ?섏〈?깆씠 import?섏뼱 ???쒖옉??源⑥? ?딅룄濡?寃뚯씠???뺣━ [?꾨즺]
3) Phase 0 unit/integration 寃€利??덉감 怨좎젙 [?꾨즺]
4) `PHASE0_ACCEPTANCE_GATE.md` 媛깆떊 湲곗? ?뺣━ [?꾨즺]
---
PHASE 1. Trafilatura 湲곕컲 URL/HTML ?낅젰 ?뺣젹
FILE: ./26_05_19_engine_respect_plan/phase_01_001_trafilatura_ingestion.md
1) `web_extractor.py`瑜?Trafilatura adapter 梨낆엫?쇰줈 ?뺣━ [?꾨즺]
2) `SourceDocument`, `EvidenceSpan`, Content metadata ?€??寃쎄퀎 ?곌껐 [?꾨즺]
3) `/process/url` ?먮뒗 ?숇벑??URL ?낅젰 API ?ㅺ퀎 [?꾨즺]
4) ?쒓뎅??URL/HTML fixture 湲곕컲 異붿텧 ?뚯뒪?몄? dedup 湲곗? ?묒꽦 [?꾨즺]
---
PHASE 2. Candidate Storage 諛?Review 梨낆엫 寃쎄퀎
FILE: ./26_05_19_engine_respect_plan/phase_02_001_candidate_review_boundary.md
1) `storage/models.py`???꾨낫 紐⑤뜽???뺤떇 Review Queue 怨꾩빟?쇰줈 ?뺤젙 [?꾨즺]
2) OntoCast 寃곌낵?€ lightweight extraction 寃곌낵???€??寃쎈줈 遺꾨━ [?꾨즺]
3) ?뱀씤/諛섎젮/?먮룞?뱀씤 ?곹깭 ?꾩씠 洹쒖튃 ?뺤쓽 [?꾨즺]
4) evidence ?녿뒗 ?꾨낫媛€ ?뺤젙 graph濡??ㅼ뼱媛€吏€ 紐삵븯寃?李⑤떒 [?꾨즺]
---
PHASE 3. Crawl4AI ?섏쭛 怨꾩링 諛?Job Orchestration
FILE: ./26_05_19_engine_respect_plan/phase_03_001_crawl4ai_acquisition_jobs.md
1) `crawl4ai_adapter.py`瑜??숈쟻/?€???섏쭛 adapter濡??쒗븳 [?꾨즺]
2) crawler profile, robots policy, cache policy瑜??ㅼ젙 湲곕컲?쇰줈 遺꾨━ [?꾨즺]
3) Job ?곹깭 紐⑤뜽怨?progress API/WebSocket 寃쎄퀎 ?뺣━ [?꾨즺]
4) Trafilatura ?꾩쿂由ъ? SourceDocument ?€?μ쑝濡??곌껐 [?꾨즺]
---
PHASE 4. Guardrails Validation Gate
FILE: ./26_05_19_engine_respect_plan/phase_04_001_guardrails_validation_gate.md
1) `core/validation`??Pydantic lightweight?€ Guardrails facade濡?遺꾨━ [?꾨즺]
2) OntoCast LLM 異쒕젰 ?섑븨 吏€?먯쓣 vendored ?섏젙 ?놁씠 ?곗꽑 ?ㅺ퀎 [?꾨즺]
3) schema violation, endpoint missing, confidence range ?뚯뒪???묒꽦 [?꾨즺]
4) Guard ?ㅽ뙣 寃곌낵瑜?candidate/review issue濡??€??[?꾨즺]
---
PHASE 5. Neo4j Projection 諛?GraphRAG 寃€??FILE: ./26_05_19_engine_respect_plan/phase_05_001_neo4j_projection_graphrag.md
1) RDF/Fuseki瑜?canonical store, Neo4j瑜?projection/search store濡?怨좎젙 [?꾨즺]
2) `core/graph` 湲곗〈 紐⑤뱢??projection/search adapter 梨낆엫?쇰줈 ?щ텇瑜?[?꾨즺]
3) read-only Text2Cypher?€ vector/hybrid retriever API ?ㅺ퀎 [?꾨즺]
4) provenance媛€ search result源뚯? ?댁뼱吏€??寃€利?湲곗? ?묒꽦 [?꾨즺]
---
PHASE 6. Maintenance Loop 諛??댁쁺 湲곕뒫 ?뺣━
FILE: ./26_05_19_engine_respect_plan/phase_06_001_maintenance_loop_operations.md
1) Knowledge Agent??肄붾뱶媛€ ?꾨땲???꾨\?꾪듃/?뚰겕?뚮줈???⑦꽩留?李⑥슜 [?꾨즺]
2) Analyst/Researcher/Curator/Auditor/Fixer/Advisor 梨낆엫 ?뺤쓽 [?꾨즺]
3) `auth`, `audit`, `billing`, `realtime` 珥덉븞 紐⑤뱢???댁쁺 寃쎄퀎 ?뺣━ [?꾨즺]
4) destructive fix???щ엺 ?뱀씤 寃뚯씠?몃? 諛섎뱶???듦낵?섎룄濡??ㅺ퀎 [?꾨즺]
---
PHASE 7. Hybrid Rule + LLM Extraction
FILE: ./26_05_19_engine_respect_plan/phase_07_001_hybrid_rule_llm_extraction.md
1) rule baseline, LLM extraction, fallback, validation, Review UI 흐름을 기준선으로 고정 [신규]
2) `rule_only`, `llm_only`, `hybrid`, `compare` extraction mode 계약 정의 [신규]
3) product backend에 명시적 HybridExtractor와 rule/LLM agreement metadata 추가 [신규]
4) confidence breakdown에 rule agreement와 conflict/review 정책 반영 [신규]
5) Crawl/Research UI에서 mode/provider/model/base URL 선택 지원 [신규]

View File

@@ -0,0 +1,51 @@
# PHASE 1. 현재 흐름 기준선 고정 및 영향 범위 정리
## 목표
`Semantic Page Understanding Layer`를 얹기 전에 현재 page classification 흐름과 page_type 문자열 의존 지점을 정확히 고정한다.
## 기준 문서
- `README.md`
- `ontology_platform/README.md`
- `ontology_platform/docs/semantic_page_classification_codex_spec.md`
## 작업 범위
1. `crawler_platform/app/core/crawler/page_classifier.py`의 현재 public API 확인
- `classify_page(...)`
- `normalize_page_type(...)`
- `should_analyze_page(...)`
- `relation_allowed_for_page_type(...)`
- `claim_allowed_for_context(...)`
2. crawler 호출부 확인
- `site_crawler.py`
- `pipeline.py`
- `page_cleaner.py`
- `domain_discovery.py`
- `relevance_engine.py`
3. extractor 연결 확인
- `ExtractionPageContext.page_type`
- `HybridExtractor.llm_skip_reason(...)`
- `LLM_PAGE_TYPES`, `SKIP_LLM_PAGE_TYPES`, `RULE_ONLY_PAGE_TYPES`
- validation metadata의 `page_type`
4. 기존 page_type 문자열 기대 코드 목록화
- config `analyze_page_types`
- ontology relation rule `allowed_page_types`
- frontend display
- tests
- adapters
## 수정 금지
- 이 phase에서는 구현 변경을 하지 않는다.
- 기존 page_type 문자열 의미를 변경하지 않는다.
- 기존 테스트 기대값을 바꾸지 않는다.
## 완료 기준
- 현재 흐름과 의존 지점이 다음 phase 구현의 기준선으로 정리되어 있어야 한다.
- 신규 semantic API를 추가할 때 깨뜨리면 안 되는 legacy contract가 명확해야 한다.

View File

@@ -0,0 +1,276 @@
# Phase 1 Current Flow Boundary Report
작성일: 2026-05-22
범위: Semantic Page Classification Layer 구현 전, 현재 `page_type` 문자열 흐름과 의존 지점을 고정한다.
## 1. page_classifier.py public API
파일: `ontology_platform/crawler_platform/app/core/crawler/page_classifier.py`
현재 public API와 contract:
- `classify_page(url, title=None, text="", html=None, source_zones=None) -> str`
- 단일 문자열 page_type을 반환한다.
- 호출부는 반환값이 `PageClassificationResult`가 아니라 `str`이라고 가정한다.
- `normalize_page_type(value: str | None) -> str`
- 짧은 alias를 legacy page_type 문자열로 변환한다.
- 현재 alias 예: `product -> ProductPage`, `listing/category -> CategoryPage`, `community/board -> BoardPage`.
- `should_analyze_page(page_type: str, analyze_page_types: set[str]) -> bool`
- page_type과 allowlist를 각각 normalize한 뒤 포함 여부만 본다.
- `relation_allowed_for_page_type(page_type, predicate) -> bool`
- `ProductPage`는 product detail predicate를 모두 허용한다.
- `UnknownPage`, `SearchPage`, `CategoryPage`, `BoardPage`는 non-merge page로 간주한다.
- `BrandStoryPage`, `NoticePage`, `EventPage`, `PromotionPage`는 content page로 간주한다.
- `claim_allowed_for_context(page_type, predicate, zone_type) -> bool`
- page_type relation policy와 source zone policy를 함께 적용한다.
현재 legacy page_type 문자열:
- `ProductPage`
- `CategoryPage`
- `SearchPage`
- `BoardPage`
- `NoticePage`
- `BrandStoryPage`
- `PromotionPage`
- `EventPage`
- `ReviewPage`
- `UnknownPage`
- 보조/외부 상태 문자열: `unknown`, `external`, `entity`
Phase 2 이후에도 기존 `classify_page(...) -> str` contract는 유지해야 한다. 신규 semantic result는 별도 API로 추가하거나 compatibility wrapper 뒤에 두어야 한다.
## 2. crawler 호출 흐름
### site_crawler.py
파일: `ontology_platform/crawler_platform/app/core/crawler/site_crawler.py`
흐름:
1. fetcher가 HTML을 가져온다.
2. parser가 `analysis_html`을 parse한다.
3. `classify_page(final_url or url, title, raw_text or text, analysis_html, source_zones)`를 호출한다.
4. 반환된 `page_type` 문자열을 page metadata와 `SiteCrawlPageResult`에 저장한다.
5. `should_analyze_page(page_type, analyze_page_types)`가 true일 때만 `ExtractionPageContext`를 만들고 extractor를 실행한다.
6. context의 `page_type` 문자열이 HybridExtractor, validation, repository 저장까지 이어진다.
현재 기본 analyze allowlist:
```txt
ProductPage
BrandStoryPage
ReviewPage
```
따라서 현재 기본 흐름에서는 `CategoryPage`, `SearchPage`, `BoardPage`, `UnknownPage`가 extractor 실행에서 빠진다.
### pipeline.py
파일: `ontology_platform/crawler_platform/app/core/crawler/pipeline.py`
흐름:
1. 단일 URL fetch/parse 후 `classify_page(...)`를 호출한다.
2. `metadata["page_type"]`에 문자열을 저장한다.
3. fetch/extraction 실패가 아니면 `ExtractionPageContext.page_type`에 같은 문자열을 넣고 extractor를 실행한다.
주의:
- `pipeline.py`에는 `should_analyze_page()` gate가 없다.
- 단일 URL pipeline은 현재 모든 정상 parse page를 extractor로 보낸다.
### page_cleaner.py / html_cleaner.py / plugins.py
파일:
- `ontology_platform/crawler_platform/app/core/crawler/page_cleaner.py`
- `ontology_platform/crawler_platform/app/core/crawler/html_cleaner.py`
- `ontology_platform/crawler_platform/app/core/crawler/plugins.py`
흐름:
- `PageCleaner.clean()`은 page_type이 없으면 내부에서 `classify_page()`를 호출한다.
- `ZONE_PRIORITY_BY_PAGE_TYPE`가 legacy page_type 문자열에 의존한다.
- `ProductPage`, `BrandStoryPage`, `NoticePage`, `BoardPage`, `EventPage`, `PromotionPage`, `CategoryPage`별로 source zone 우선순위가 다르다.
Phase 6에서 semantic type을 추가할 때 `ProductDetailPage -> ProductPage`, `CategoryListingPage -> CategoryPage` 같은 zone compatibility가 필요하다.
## 3. research/discovery 호출 흐름
### graph_research_loop.py
파일: `ontology_platform/crawler_platform/app/core/research/graph_research_loop.py`
흐름:
1. 기본 analyze allowlist는 `ProductPage`, `BrandStoryPage`, `ReviewPage`다.
2. explored page에서 `classify_page(...)`를 호출한다.
3. metadata에 `page_type`을 저장한다.
4. relevance score와 `should_analyze_page(page_type, analyze_page_types)`를 모두 통과해야 extractor를 실행한다.
5. link 후보 metadata에도 relevance engine이 산출한 `score.page_type`을 저장한다.
### relevance_engine.py
파일: `ontology_platform/crawler_platform/app/core/research/relevance_engine.py`
흐름:
- `score_url()` 내부에서 `classify_page(...)`를 호출한다.
- `HIGH_VALUE_PAGE_TYPES` 점수표가 legacy page_type 문자열에 의존한다.
- 신규 semantic page type이 들어오면 점수표 또는 legacy normalization이 필요하다.
현재 주요 점수:
- `ProductPage`: 0.95
- `BrandStoryPage`: 0.88
- `ReviewPage`: 0.82
- `CategoryPage`: 0.34
- `SearchPage`: 0.22
- `BoardPage`: 0.18
- `UnknownPage`: 0.25
### domain_discovery.py
파일: `ontology_platform/crawler_platform/app/core/ontology/domain_discovery.py`
흐름:
- discovery job에서 `classify_page(...)`를 호출한다.
- `mine_schema_candidates(..., page_type=page_type)`로 page_type을 evidence metadata에 넣는다.
- `entity_type_from_page_type()`가 legacy mapping을 사용한다.
현재 mapping:
- `ProductPage -> Product`
- `CategoryPage -> Category`
- `BrandStoryPage -> Brand`
- `NoticePage -> Notice`
- `PromotionPage -> Promotion`
- `ReviewPage -> Review`
- `BoardPage -> Article`
## 4. Extractor / HybridExtractor 연결 흐름
### ExtractionPageContext
파일: `ontology_platform/crawler_platform/app/core/extractor/base.py`
현재 `ExtractionPageContext.page_type: str`는 필수 문자열 필드다. `to_payload()``page_type`을 그대로 내보낸다.
Phase 6에서 이 필드는 유지해야 하며, semantic 정보는 `metadata`에 추가하는 방식이 가장 안전하다.
### HybridExtractor
파일: `ontology_platform/crawler_platform/app/core/extractor/hybrid.py`
현재 LLM routing 상수:
- `LLM_PAGE_TYPES = {"ProductPage", "BrandStoryPage", "ReviewPage"}`
- `SKIP_LLM_PAGE_TYPES = {"CategoryPage", "SearchPage", "ListingPage"}`
- `RULE_ONLY_PAGE_TYPES = {"BoardPage", "CommunityPage", "UnknownPage"}`
현재 `llm_skip_reason()` 판단 순서:
1. mode가 `hybrid`가 아니거나 context가 없으면 skip 판단 없음.
2. page_type이 `SKIP_LLM_PAGE_TYPES`면 LLM skip.
3. page_type이 `RULE_ONLY_PAGE_TYPES`면 rule-only.
4. page_type이 `LLM_PAGE_TYPES`에 없으면 LLM allowlist 밖으로 skip.
5. clean text 길이가 너무 짧거나 길면 skip.
Phase 5/6에서 `LLMPolicy`가 있으면 이를 우선하고, 없으면 이 legacy fallback을 유지해야 한다.
### ai_provider.py
파일: `ontology_platform/crawler_platform/app/core/extractor/ai_provider.py`
LLM prompt에 page_type semantics가 직접 들어간다.
현재 prompt contract:
- `ProductPage`는 product detail claim 가능
- `CategoryPage`는 detailed product claim 제한
- `BrandStoryPage`는 product price/note claim 제한
- `UnknownPage`는 ontology claim을 반환하지 않도록 지시
신규 semantic type을 추가할 때 prompt가 `ProductDetailPage`, `CategoryListingPage`를 이해하도록 하거나, prompt에는 legacy page_type을 계속 넘기는 compatibility가 필요하다.
### validation.py
파일: `ontology_platform/crawler_platform/app/core/extractor/validation.py`
흐름:
- `attach_page_context()`가 entity/claim metadata에 `page_type`을 저장한다.
- `claim_status_for_bundle()``UnknownPage`, `SearchPage`, `CategoryPage`, `BoardPage`를 candidate claim으로 낮춘다.
- `invalid_claim_reason()`은 relation schema compatibility에 `page_type`을 넘긴다.
신규 semantic type은 validation status와 relation schema의 allowed page type 검증에 영향을 준다.
## 5. 기존 page_type 문자열 기대 지점
### API request/response
파일: `ontology_platform/crawler_platform/app/api/routes.py`
- 여러 request model의 기본 `analyze_page_types``["ProductPage", "BrandStoryPage", "ReviewPage"]`다.
- relation/quality 관련 request에 `allowed_page_types`가 있다.
- API response 및 progress payload에서 `page_type` 문자열을 그대로 노출한다.
### Config
파일: `ontology_platform/configs/perfume_subscription.yaml`
- `analyze_page_types` 또는 ontology rule에서 legacy page type이 쓰일 수 있다.
- target entity/ontology에는 `ProductPage`, `BrandStoryPage`, `ListingPage`, `PromotionPage`, `ReviewPage` 같은 page entity type이 포함되어 있다.
### Adapters
파일: `ontology_platform/crawler_platform/app/adapters/ecommerce/perfume.py`
- relation rules가 `page_types={"ProductPage"}` 또는 `{"ProductPage", "ReviewPage"}` 같은 legacy set에 의존한다.
### Ontology relation schema
파일: `ontology_platform/crawler_platform/app/core/ontology/relation_schema.py`
- configured relation rule의 `allowed_page_types`/`page_types`를 set으로 읽는다.
- `relation_schema_compatible()``page_type not in rule.page_types`면 reject한다.
- semantic type 도입 시 legacy alias expansion 없이는 기존 relation rules가 거부될 수 있다.
### Frontend
주요 파일:
- `ontology_platform/web/frontend/src/pages/CrawlPage.tsx`
- `ontology_platform/web/frontend/src/pages/ReviewPage.tsx`
- `ontology_platform/web/frontend/src/pages/BuildPipelinePage.tsx`
- `ontology_platform/web/frontend/src/pages/QualityInspectorPage.tsx`
- `ontology_platform/web/frontend/src/lib/api/crawl.ts`
- `ontology_platform/web/frontend/src/lib/api/claims.ts`
- `ontology_platform/web/frontend/src/lib/api/platform.ts`
- legacy JS files under `web/frontend/src/legacy`
현재 frontend는 `page_type`을 optional string으로 표시하거나, allowed page types mismatch 검사에 사용한다.
### Tests
주요 테스트:
- `ontology_platform/tests/unit/test_phase7_hybrid_extraction.py`
- `CategoryPage`가 LLM skip 되는 기존 behavior를 검증한다.
- integration/e2e tests는 API response의 `page_type` 문자열 contract에 간접 의존한다.
Phase 8에서 semantic classifier 전용 테스트를 추가하되, 기존 문자열 contract 회귀 테스트도 유지해야 한다.
## 6. Phase 2 이후 유지해야 할 legacy contract
1. `classify_page(...)`는 계속 `str`을 반환해야 한다.
2. 신규 API는 `classify_page_semantic(...) -> PageClassificationResult`처럼 분리하는 편이 안전하다.
3. `normalize_page_type()``PageClassificationResult`도 받을 수 있게 확장하되, 기존 string 입력 결과를 바꾸면 안 된다.
4. `should_analyze_page(str, set)` 기존 호출은 계속 동작해야 한다.
5. API/metadata의 `page_type` 필드는 legacy string으로 유지하고, semantic 결과는 별도 `page_classification` payload로 저장한다.
6. `ExtractionPageContext.page_type`은 legacy string으로 유지하고, `analyze_strategy`/`llm_policy`/evidence는 `metadata`로 전달한다.
7. relation schema, adapter page_types, frontend allowed page type 검증에는 legacy/semantic alias compatibility가 필요하다.
8. `CategoryPage`, `SearchPage`, `BoardPage`, `UnknownPage`의 기존 skip/rule-only 동작은 Phase 5에서 strategy 기반으로 확장하되, LLM 호출이 늘지 않도록 `LLMPolicy`를 우선한다.

View File

@@ -0,0 +1,53 @@
# PHASE 2. Taxonomy와 Classification Result 모델 추가
## 목표
기존 단일 문자열 page_type을 대체하지 않고, 그 위에 semantic classification result 모델을 추가한다.
## 작업 범위
1. 신규 taxonomy 모듈 추가
- 권장 위치: `crawler_platform/app/core/crawler/page_type_taxonomy.py`
- 기존 프로젝트 구조와 import 경계를 우선한다.
2. 다음 상수 또는 enum 정의
- `PageDomain`
- `PageArchetype`
- `PageType`
- `EntityType`
- `ActionIntent`
- `GraphRole`
- `AnalyzeStrategy`
- `LLMPolicy`
3. classification result 모델 추가
- `EvidenceItem`
- `PageClassificationResult`
4. legacy compatibility helper 추가
- `normalize_page_type(result_or_page_type)`
- `get_legacy_page_type(result_or_page_type)`
- `classify_page_semantic(...)`
- 기존 `classify_page(...) -> str` 유지
5. 기존 page type alias 유지
- `ProductPage`
- `CategoryPage`
- `SearchPage`
- `BoardPage`
- `NoticePage`
- `BrandStoryPage`
- `PromotionPage`
- `UnknownPage`
- `ReviewPage`
## 수정 금지
- 기존 `classify_page(...)` 호출부를 한 번에 모두 semantic result 기반으로 바꾸지 않는다.
- legacy 문자열을 제거하지 않는다.
## 완료 기준
- 기존 호출부가 string page_type을 그대로 받을 수 있어야 한다.
- semantic result API를 신규 테스트에서 직접 호출할 수 있어야 한다.
- legacy alias mapping이 테스트로 보호되어야 한다.

View File

@@ -0,0 +1,93 @@
# PHASE 3. Raw Snapshot 및 Signal Extraction 레이어 추가
## 목표
URL substring 중심 분류를 줄이고, HTML/DOM/metadata/link/form/text 기반 signal을 별도 레이어에서 추출한다.
## 작업 범위
1. `RawPageSnapshot` 모델 추가
* `url`, `final_url`, `status_code`, `content_type`
* `title`, `text`, `html`, `rendered_html`
* metadata, structured data, headings, links, images, forms, buttons, inputs, tables
* source_zones, screenshot_path
2. `PageSignals` 모델 추가
* structured data signals
* commerce signals
* listing signals
* editorial signals
* community signals
* knowledge/docs signals
* corporate/legal signals
* transaction/protected signals
* graph/link signals
* text/layout keyword signals
* visual/layout block signals
* external collector signals
3. signal extractor 추가
* 권장 위치: `crawler_platform/app/core/crawler/page_signal_extractor.py`
* BeautifulSoup 사용 가능 시 DOM parsing
* BeautifulSoup 미설치/HTML 깨짐 시 regex/text fallback
4. 다국어 확장 고려
* 한국어/영어 키워드 dictionary를 분리 가능한 구조로 둔다.
* 인코딩 깨짐이 있어도 예외 없이 동작한다.
5. visual/layout signal 세부화 고려
* screenshot 기반 정밀 분석은 이번 phase의 필수 구현 범위가 아니지만, 향후 visual block classification을 붙일 수 있도록 signal 구조를 열어둔다.
* DOM class/id/role/aria/heading 구조와 반복 레이아웃을 통해 가능한 범위에서 visual/layout block 후보를 추출한다.
* visual/layout block signal 예시는 다음과 같다.
* hero block
* product card grid
* article body block
* left filter sidebar
* top navigation
* footer navigation
* sticky buy box
* review/comment block
* FAQ accordion
* media player area
* map area
* calendar/availability grid
* dashboard card grid
* form wizard / stepper
* pricing table
* comparison table
* 초기 구현은 실제 computer vision까지 요구하지 않는다.
* 다만 `PageSignals`에는 visual/layout 후보를 담을 수 있는 필드를 둔다.
* 예시 필드:
* `layout_blocks: list[str]`
* `has_hero_block: bool`
* `has_card_grid: bool`
* `has_filter_sidebar: bool`
* `has_sticky_action_box: bool`
* `has_media_player_area: bool`
* `has_map_area: bool`
* `has_calendar_grid: bool`
* `has_pricing_table: bool`
* `has_comparison_table: bool`
* visual/layout signal은 page type을 단독 확정하지 않고, evidence scoring의 보조 신호로 사용한다.
## 수정 금지
* signal extractor에서 page_type을 확정 반환하지 않는다.
* 이 phase에서 extractor나 crawler의 분석 여부 정책을 바꾸지 않는다.
* Crawl4AI, Firecrawl, Trafilatura 같은 외부 도구에 핵심 classifier가 강하게 종속되도록 만들지 않는다.
* screenshot 또는 visual analysis가 없다는 이유로 기본 signal extraction이 실패하면 안 된다.
## 완료 기준
* HTML이 비어도 `PageSignals`가 생성되어야 한다.
* JSON-LD/OpenGraph/form/input/button/link/text signal이 evidence scorer에서 사용할 수 있는 형태로 정리되어야 한다.
* 외부 수집 도구 결과를 `RawPageSnapshot`에 매핑할 수 있는 구조 또는 adapter hook이 있어야 한다.
* visual/layout block 후보를 담을 수 있는 `PageSignals` 필드가 있어야 한다.
* visual/layout signal이 없어도 기존 signal extraction과 scoring 흐름은 정상 동작해야 한다.

View File

@@ -0,0 +1,99 @@
# PHASE 4. Evidence Scoring 기반 Semantic Classification 구현
## 목표
단일 if-return 방식이 아니라 signal별 evidence weight를 합산해 semantic page type을 결정한다.
## 작업 범위
1. `page_type_scorer.py` 추가
* score accumulator
* evidence item 생성 helper
* normalize 및 confidence 계산
* alternatives 산출
2. 최소 구현 page type
* `ProductDetailPage`
* `CategoryListingPage`
* `SearchResultsPage`
* `ArticlePage`
* `BlogPostPage`
* `QAPage`
* `FAQPage`
* `ForumBoardPage`
* `ForumThreadPage`
* `BrandStoryPage`
* `AboutPage`
* `ContactPage`
* `DocumentationPage`
* `APIReferencePage`
* `JobPostingPage`
* `PricingPage`
* `LoginPage`
* `CheckoutPage`
* `PaymentPage`
* `TermsPage`
* `PrivacyPolicyPage`
* `SitemapPage`
* `RSSFeedPage`
* `ErrorPage`
* `AccessDeniedPage`
* `CaptchaPage`
* `UnknownPage`
3. 범용 taxonomy 확장 기준
* 위의 최소 구현 page type은 1차 구현 범위로 본다.
* 장기 목표는 인터넷에 존재하는 다양한 페이지를 포괄할 수 있는 전체 taxonomy catalog를 유지하는 것이다.
* 따라서 `page_type_scorer.py`와 taxonomy 정의는 아래 계열을 나중에 확장할 수 있는 구조로 작성한다.
* Site / Navigation
* Commerce / Marketplace
* Editorial / Article
* Community / UGC
* Knowledge / Documentation
* Corporate / Organization
* Local / Place / Travel / Real Estate
* Education / Learning
* Jobs / Career
* Media / Entertainment
* Software / SaaS / App
* Finance / Legal / Government
* Healthcare / Medical
* Transaction / Account / Protected
* System / Technical / Machine-readable
* 현재 phase에서는 위 전체 계열을 모두 scoring 구현하지 않아도 된다.
* 다만 enum, alias, mapping, scorer 구조는 특정 몇 개 타입에 고정하지 말고, 전체 taxonomy catalog가 추가되어도 깨지지 않도록 확장 가능해야 한다.
* Phase 4의 최소 구현 page type은 1차 안정화 대상이며, 전체 taxonomy catalog는 별도 taxonomy 문서 또는 후속 phase에서 보강한다.
* `UnknownPage`는 전체 taxonomy에 아직 포함되지 않은 신규 페이지 패턴을 발견하기 위한 후보로 유지한다.
4. result enrichment
* `domain`
* `archetype`
* `main_entity_type`
* `action_intents`
* `graph_roles`
* `confidence`
* `alternatives`
* `evidence`
5. low confidence 처리
* threshold 아래는 `UnknownPage`
* alternatives/evidence는 유지
## 수정 금지
* URL 문자열 조건만 추가해서 바로 return하지 않는다.
* Product/Category/Search/Board만 처리하는 구조로 고정하지 않는다.
* 1차 최소 구현 page type만을 전체 taxonomy의 전부로 간주하지 않는다.
## 완료 기준
* classification result가 항상 evidence를 포함해야 한다.
* 보호 페이지가 commerce/detail page로 오분류되지 않아야 한다.
* category/search/board 계열은 skip 여부가 아니라 semantic type과 graph role이 남아야 한다.
* 최소 구현 page type은 동작해야 하며, 전체 taxonomy catalog를 후속 확장할 수 있는 구조여야 한다.

View File

@@ -0,0 +1,45 @@
# PHASE 5. Analyze Strategy 및 LLM Policy 분리
## 목표
분석 여부를 단순 boolean allowlist에서 page type별 strategy와 LLM policy로 분리한다.
## 작업 범위
1. `page_analysis_policy.py` 추가
- `decide_analyze_strategy(result)`
- `decide_llm_policy(result)`
- `is_protected_strategy(strategy)`
- `is_noise_strategy(strategy)`
2. strategy mapping
- `ProductDetailPage`, `ArticlePage`, `FAQPage`, `QAPage`, `BrandStoryPage` -> `AnalyzeFull`
- `CategoryListingPage`, `ForumBoardPage` -> `AnalyzeRelationsOnly`
- `SearchResultsPage`, `SitemapPage` -> `AnalyzeDiscoveryOnly`
- `TermsPage`, `PrivacyPolicyPage` -> `AnalyzeDocumentOnly`
- `LoginPage`, `CheckoutPage`, `PaymentPage`, `CaptchaPage`, `AccessDeniedPage` -> `SkipProtected`
- `ErrorPage`, `NotFoundPage` -> `SkipNoise`
- `UnknownPage` -> `AnalyzeMetadataOnly`
3. LLM policy mapping
- full content page -> `LLMFull` 또는 `LLMLight`
- listing/search/board -> `LLMForAmbiguityOnly` 또는 `RuleOnly`
- sitemap/rss/robots/protected -> `RuleOnly`, `NoLLM`, 또는 `Skip`
4. compatibility
- `should_analyze_page(result_or_page_type, analyze_page_types=None)`
- 기존 allowlist가 들어오면 legacy behavior를 최대한 유지하되 protected/noise는 안전하게 skip
- semantic result가 들어오면 strategy 우선
## 수정 금지
- CategoryPage/SearchPage/BoardPage를 무조건 skip하지 않는다.
- protected page에서 extractor/LLM이 개인정보를 추출하도록 두지 않는다.
## 완료 기준
- `CategoryListingPage.should_analyze == True`
- `SearchResultsPage.should_analyze == True`
- `ForumBoardPage.should_analyze == True`
- `LoginPage`, `CheckoutPage`, `PaymentPage``should_analyze == False`
- LLM 호출이 기존보다 불필요하게 증가하지 않도록 policy 테스트가 있어야 한다.

View File

@@ -0,0 +1,45 @@
# PHASE 6. Crawler, Cleaner, Extractor, Discovery/Relevance 통합
## 목표
Semantic classification result를 crawler와 extractor 흐름에 연결하되, 기존 page_type 문자열 기반 contract는 유지한다.
## 작업 범위
1. `site_crawler.py`
- `classify_page_semantic(...)` 호출
- 기존 `page_type` 필드는 legacy string으로 유지
- metadata에 `page_classification` 저장
- `should_analyze_page(result, analyze_page_types)` 사용
2. `pipeline.py`
- 단일 URL 처리에서도 semantic payload 저장
- `ExtractionPageContext.metadata`에 strategy/policy 전달
3. `ExtractionPageContext`
- 기존 `page_type: str`는 유지
- metadata 기반 `analyze_strategy`, `llm_policy`, evidence payload 전달
4. `HybridExtractor`
- metadata의 `llm_policy`를 우선 사용
- 없으면 기존 `LLM_PAGE_TYPES`, `SKIP_LLM_PAGE_TYPES`, `RULE_ONLY_PAGE_TYPES` fallback
- protected/skip policy는 LLM 호출 금지
5. compatibility update
- `page_cleaner.py` zone priority에 semantic aliases 추가
- `domain_discovery.py` page_type entity mapping에 semantic aliases 추가
- `relevance_engine.py` high value score mapping에 semantic aliases 추가
- relation schema allowed page type 검증에서 legacy/semantic alias 고려
## 수정 금지
- extractor 전체를 새 구조로 갈아엎지 않는다.
- DB schema 변경을 필수로 만들지 않는다.
- frontend 표시용 기존 `page_type` 필드를 제거하지 않는다.
## 완료 기준
- 기존 API response의 `page_type`은 문자열로 유지된다.
- semantic classification payload가 metadata에 남는다.
- HybridExtractor가 LLMPolicy에 따라 LLM 호출을 줄일 수 있다.
- 기존 page_type 문자열 기반 relation rule이 신규 semantic type 때문에 깨지지 않는다.

View File

@@ -0,0 +1,47 @@
# PHASE 7. Unknown Pattern 저장 기반 추가
## 목표
`UnknownPage`를 분석 실패나 폐기 대상으로 두지 않고, taxonomy 확장 후보로 저장 가능한 evidence payload를 만든다.
## 작업 범위
1. unknown payload 정의
- url
- title
- text sample
- html fingerprint
- dom fingerprint
- schema types
- link pattern summary
- button labels
- forms summary
- top keywords
- alternatives
- evidence
2. fingerprint hook
- text fingerprint
- html/dom fingerprint
- link pattern fingerprint
3. 저장 위치
- 초기 구현은 `metadata_json["page_classification"]["unknown_pattern"]`
- DB migration 없이 시작
4. 향후 확장 hook
- embedding input 생성 함수
- cluster candidate payload 생성 함수
- 실제 clustering은 이번 phase 범위에서 제외
## 수정 금지
- UnknownPage를 무조건 extractor 대상에서 제외하지 않는다.
- low confidence 결과의 evidence와 alternatives를 버리지 않는다.
- 이 phase에서 clustering 알고리즘을 새로 도입하지 않는다.
## 완료 기준
- UnknownPage result가 evidence를 가진다.
- metadata에 unknown pattern summary가 저장 가능하다.
- 향후 clustering/관리자 검토 UI로 넘길 수 있는 구조다.

View File

@@ -0,0 +1,59 @@
# PHASE 8. 테스트 Fixture 및 회귀 검증
## 목표
Semantic Page Understanding Layer가 기존 흐름을 깨지 않고, page type별 strategy와 LLM policy를 정확히 산출하는지 검증한다.
## 작업 범위
1. fixture 추가
- `tests/fixtures/pages/product_detail.html`
- `tests/fixtures/pages/category_listing.html`
- `tests/fixtures/pages/search_results.html`
- `tests/fixtures/pages/article.html`
- `tests/fixtures/pages/qapage.html`
- `tests/fixtures/pages/faq.html`
- `tests/fixtures/pages/forum_thread.html`
- `tests/fixtures/pages/documentation.html`
- `tests/fixtures/pages/job_posting.html`
- `tests/fixtures/pages/login.html`
- `tests/fixtures/pages/checkout.html`
- `tests/fixtures/pages/terms.html`
- `tests/fixtures/pages/sitemap.xml`
- `tests/fixtures/pages/unknown.html`
2. unit test 추가
- primary_page_type 확인
- confidence 최소 기준 확인
- evidence non-empty 확인
- analyze_strategy 확인
- llm_policy 확인
- protected page skip 확인
- UnknownPage 예외 없는 처리 확인
3. compatibility test 추가
- 기존 `classify_page(...) -> str`
- 신규 `classify_page_semantic(...) -> PageClassificationResult`
- `should_analyze_page(str, set)`
- `should_analyze_page(result, set)`
- legacy aliases
4. extractor policy 회귀 테스트
- HybridExtractor가 metadata `llm_policy`를 우선 사용
- `Skip`, `NoLLM`, `RuleOnly`에서 LLM 호출 금지
- 기존 legacy page_type fallback 유지
5. 회귀 테스트 실행
- 기본 명령: `pytest`
- 필요 시 변경 범위 우선: `pytest tests/unit`
## 수정 금지
- 기존 테스트 기대값을 불필요하게 변경하지 않는다.
- LLM live 호출이 필요한 테스트를 기본 회귀 테스트에 포함하지 않는다.
## 완료 기준
- 새 semantic classifier 테스트가 통과한다.
- 기존 unit/integration 테스트가 통과한다.
- 테스트 결과와 미실행 사유가 작업 완료 보고에 명확히 기록된다.

View File

@@ -1,132 +0,0 @@
# Phase 0 — Acceptance Gate 결과
본 문서는 통합설계서 §5 Phase 0의 Acceptance Gate를 객관적으로 점검한 결과다. Phase 1 진입 전에 모든 체크가 통과되어야 한다.
## 결과 요약
| # | Acceptance Gate 항목 | 상태 | 검증 방법 |
|---|---|---|---|
| 1 | 단일 PDF/JSON 입력 → ontology TTL + facts TTL이 filesystem에 생성됨 | ⚠️ **e2e 검증 대기** (로컬 LLM/API 키 필요) | `tests/e2e/test_phase0_full_pipeline.py` |
| 2 | `/health`, `/info`, `/process` (FastAPI) 정상 동작 | ✅ **통합 테스트 11/11 통과** (2026-05-19) | `tests/integration/test_api_smoke.py` |
| 3 | BudgetTracker가 LLM call/triple count를 정확히 기록 | ⚠️ **e2e 검증 대기** (mock 검증은 통합 테스트로 통과) | e2e 테스트가 실제 검증 |
| 4 | LangGraph 워크플로우 (CONVERT→CHUNK→...→SERIALIZE) 전 노드 traceable | ✅ **OntoCast 원본 워크플로우 무수정 채택** | `vendored/ontocast/ontocast/stategraph/` 그대로 사용 |
추가로 **단위 테스트 16/16 통과** (test_convert_document 7, test_platform_config 5, test_select_ontology 4).
자동 검증 기준으로는 **unit + integration 27/27 통과**가 현재 Phase 0 기본선이다.
**현재 진척 (2026-05-19)**:
- Python 3.14.5 `.venv` 환경에서 unit + integration 27/27 통과
- `python-multipart`를 Phase 0 FastAPI multipart upload 필수 의존성으로 추가
- Phase 0 production app에서 Phase 1 Trafilatura route가 기본 mount되지 않도록 lazy phase route gate 적용
- `pip install -e ".[dev]"` 또는 동등한 의존성 설치 필요
- `pip install -e vendored/ontocast` 로 OntoCast 의존성 설치 완료
- 패키지 이름 충돌 수정: `platform/``ont_platform/` (Python 내장 `platform` 모듈과 충돌)
- **남은 작업**: e2e 테스트 (Acceptance Gate #1, #3) 실행 — 로컬 Ollama 또는 OpenAI 키 필요
## 다음 작업자가 실행할 검증 절차
### 1) 환경 준비
```powershell
# Python 3.12+ 설치 (예: https://www.python.org/downloads/)
python --version # Python 3.12.x 이상 확인
cd C:\Users\lasta\MyProject\AI\ontology_platform
# 가상환경 + 의존성 설치
python -m venv .venv
.venv\Scripts\activate
pip install --upgrade pip
pip install -e ".[dev]"
# .env 생성 (실제 LLM 키 채우기)
Copy-Item .env.example .env
# 그 다음 .env 파일을 편집하여 LLM_API_KEY 등 채움
```
### 2) 자동 검증 (Acceptance Gate #2)
```powershell
# 단위 + 통합 테스트만 (LLM 호출 없음, 빠름)
.venv\Scripts\python.exe -m pytest tests/unit tests/integration -v
```
**기대 결과**: 모든 케이스 PASS.
- `tests/unit/test_select_ontology.py` (4 케이스) — Phase 0.2 검증
- `tests/unit/test_convert_document.py` (7 케이스) — Phase 0.3 검증
- `tests/unit/test_platform_config.py` (5 케이스) — Phase 0.5 검증
- `tests/integration/test_api_smoke.py` (11 케이스) — Phase 0.4 + 0.6 mock 검증, Phase 0 future dependency route gate 검증
Windows에서 `%TEMP%` 권한 문제 또는 `.pytest_cache` 쓰기 문제가 발생하면 아래처럼 pytest temp/cache 위치를 workspace 내부로 고정한다.
```powershell
$env:TMP=(Join-Path (Resolve-Path '.').Path 'pytest_tmp')
$env:TEMP=$env:TMP
New-Item -ItemType Directory -Force -Path $env:TMP | Out-Null
.venv\Scripts\python.exe -m pytest tests/unit tests/integration -v --basetemp "$env:TMP\basetemp" -o cache_dir="$env:TMP\cache"
```
### 3) End-to-end 검증 (Acceptance Gate #1, #3, #4)
LLM 호출이 실제로 일어남. OpenAI는 비용 발생, Ollama는 로컬에서 무료.
```powershell
# (A) Ollama 로컬 사용 (권장 — 비용 무료)
# 사전: Ollama 설치 후 `ollama pull qwen2.5`
$env:LLM_PROVIDER = "ollama"
$env:LLM_MODEL_NAME = "qwen2.5"
$env:LLM_BASE_URL = "http://localhost:11434"
pytest tests/e2e -m e2e -v
# (B) OpenAI 사용
$env:LLM_PROVIDER = "openai"
$env:LLM_MODEL_NAME = "gpt-4o-mini"
$env:LLM_API_KEY = "sk-..."
pytest tests/e2e -m e2e -v
```
**기대 결과**:
- `test_full_pipeline_writes_ontology_and_facts` PASS
- 응답에서 ontology TTL과 facts TTL이 비어 있지 않음
- `metadata.budget.calls_count > 0`
- `metadata.budget.ontology_triples_generated > 0` 또는 `facts_triples_generated > 0`
- `tmp_path / "work"` 아래 `.ttl` 또는 `.rdf` 파일 생성됨
### 4) 수동 smoke (선택)
```powershell
# 서버 기동
uvicorn ont_platform.api.main:app --reload
# 다른 셸에서
curl http://localhost:8000/health
curl http://localhost:8000/info
curl -X POST http://localhost:8000/process `
-H "Content-Type: application/json" `
-d '{"text":"Alice works at Acme in Berlin."}'
```
## 통과 시 처리
위 모든 검증을 통과하면 **이 문서의 표 상태 컬럼을 ✅로 갱신**하고 git에 commit한다.
이후 Phase 1 작업은 [PHASE1_NEXT_STEPS.md](PHASE1_NEXT_STEPS.md)를 따른다.
## 실패 시 처리
- **단위 테스트 실패**: 어느 케이스가 실패했는지 확인. Phase 0.2/0.3/0.5의 vendored 수정 또는 platform/ 코드에 회귀가 발생했을 가능성. PR 단위로 롤백 후 재시도.
- **통합 테스트 실패**: FastAPI 라우팅/의존성 주입 문제. `platform/api/main.py` 또는 `platform/api/deps.py` 확인.
- **E2E 테스트 실패**:
- `LLM_API_KEY`, `LLM_PROVIDER`, `LLM_MODEL_NAME` 환경변수 확인
- 워크플로우가 timeout: `ServerConfig.base_recursion_limit` 조정 검토
- OntoCast `select_ontology.py` 또는 `convert_document.py` 수정에 회귀가 있는지 점검 (VENDORED_MODIFICATIONS.md 참조)
## 검증 이력
| 일자 | 검증자 | 결과 |
|---|---|---|
| 2026-05-13 | (코드 작성: ontology-platform agent) | 코드 준비 완료. 실 환경 검증 보류. |
| 2026-05-14 | lasta + Claude | **unit 16/16, integration 10/10 통과** (Gate #2 ✅). 패키지 이름 충돌 수정 (`platform``ont_platform`). e2e는 LLM 필요로 대기. |
| 2026-05-19 | Codex | **unit 16/16, integration 11/11, 총 27/27 통과**. Phase 0 route gate 추가로 Trafilatura route는 PHASE>=1에서만 lazy mount. e2e는 LLM 필요로 대기. |
| ____-__-__ | ________________ | __________________________________ |

View File

@@ -1,124 +0,0 @@
# Phase 0 — 다음 작업자 핸드오프
본 문서는 이 프로젝트를 이어받는 AI 에이전트 또는 개발자가 즉시 작업을 시작하기 위한 핸드오프 노트다.
## 현재 상태 (지금까지 완료된 것)
- [x] **0.1 일부**: 폴더 골격, `pyproject.toml`, `README.md`, `.env.example`, `.gitignore`, `NOTICE` 생성
- [x] **설계 문서**: [../통합설계서.md](../통합설계서.md) 배치 완료 (모든 작업의 기준)
## 즉시 시작할 작업 (순서대로)
### 0.1 (잔여): OntoCast vendored copy
**근거**: 통합설계서 §9.1, OntoCast 분석 §13.1
```powershell
# 1. 원본을 vendored/ontocast로 복사 (.git 제외)
Copy-Item -Path "C:\Users\lasta\MyProject\AI\참고\ontocast-main\*" `
-Destination "C:\Users\lasta\MyProject\AI\ontology_platform\vendored\ontocast\" `
-Recurse -Exclude ".git",".github",".venv","node_modules"
# 2. 원본 LICENSE 및 NOTICE를 vendored/ontocast/ 안에 그대로 유지
# 3. NOTICE 파일의 "(원본 저장소 URL 기입)" 부분을 실제 URL로 채우기
# 4. git init (아직 안 했다면)
cd C:\Users\lasta\MyProject\AI\ontology_platform
git init
git add .
git commit -m "Initial scaffold: folder skeleton, design doc, NOTICE"
```
**확인 사항**:
- [ ] `vendored/ontocast/` 안에 원본 LICENSE 파일이 있어야 한다
- [ ] `vendored/ontocast/pyproject.toml`은 그대로 두되, 우리 `pyproject.toml`이 우선
- [ ] NOTICE 파일의 OntoCast 항목에 실제 source URL 기입
### 0.2: `select_ontology.py` 버그 수정
**근거**: 통합설계서 §5 Phase 0, OntoCast 분석 §13.1 / §9.1 (1번)
**문제**: `vendored/ontocast/ontocast/agent/select_ontology.py`에서 None 선택 인덱스 불일치.
- 코드는 `answer_index == 0`을 None으로 처리
- 그러나 dynamic model은 `1..num_ontologies+1` 범위 사용
- 실제 None 선택은 `num_ontologies + 1`이어야 자연스러움
**조치**:
1. 해당 함수의 분기 로직을 `answer_index == num_ontologies + 1` 또는 동등한 표현으로 수정
2. **수정 사실을 파일 상단 주석으로 명시** (Apache 2.0 의무): 예) `# MODIFIED 2026-MM-DD: Fixed None index inconsistency, see docs/통합설계서.md §5 Phase 0`
3. 회귀 테스트 작성: `tests/unit/test_select_ontology.py`
- 케이스 1: ontology가 0개일 때 → None 반환
- 케이스 2: ontology가 N개, LLM이 1~N 선택 → 해당 ontology 반환
- 케이스 3: ontology가 N개, LLM이 N+1 선택 → None 반환
### 0.3: `convert_document.py` 다중 파일 처리 확장
**근거**: 통합설계서 §5 Phase 0, OntoCast 분석 §13.1 (3번) / §21.1 (4번)
**문제**: `convert_document()`가 "processing only one file"로 주석 처리되어 있고, 다중 파일 처리 시 마지막 파일 기준으로만 상태가 업데이트됨.
**조치**:
1. 입력 파일 목록을 순회하며 각 파일을 독립 `ContentUnit`으로 만들어 `AgentState.content_units`에 누적
2. 동일 corpus 내 파일들이 함께 처리되도록 보장 (각 파일이 별도 doc IRI를 가짐)
3. 회귀 테스트: 2개 PDF를 한 번에 처리 → 둘 다 처리되어야 함
### 0.4: Robyn → FastAPI 재작성
**근거**: 통합설계서 §11 (기술 스택), OntoCast 분석 §13 (API 명세)
**조치**:
1. `platform/api/main.py` 생성 (FastAPI app 인스턴스)
2. OntoCast 분석 §13.1~§13.4의 4개 endpoint를 FastAPI로 동일 시맨틱 재작성:
- `GET /health`
- `GET /info`
- `POST /process` (JSON + multipart)
- `POST /flush` (관리자 권한 + confirmation token, 분석 §21.1 #6)
3. OntoCast의 `ToolBox` 의존성 주입은 FastAPI `Depends`로 변환
4. `uvicorn platform.api.main:app --reload`로 기동 가능해야 함
**중요**: OntoCast 코어 모듈(`stategraph/`, `agent/`, `onto/`, `tool/`)은 **건드리지 않는다**. API 레이어만 재작성.
### 0.5: Pydantic Settings 정리
**근거**: 통합설계서 §5 Phase 0 (5번), OntoCast 분석 §15
**조치**:
1. `platform/config.py` 생성
2. `pydantic-settings``BaseSettings``.env` 로딩
3. **Phase 0에서는 filesystem 모드만 활성화** (Fuseki/Neo4j는 Phase 4에서):
- `STORAGE_BACKEND=filesystem` 강제
- Neo4j/Fuseki 변수가 채워져 있어도 무시
4. OntoCast의 기존 `Config` 클래스는 우리 `Settings`에서 만들어 주입
### 0.6: End-to-end 통합 테스트
**근거**: 통합설계서 §5 Phase 0 Acceptance Gate
**조치**:
1. `vendored/ontocast/data/`의 예제 JSON 또는 PDF 1개를 fixture로 복사 → `tests/fixtures/`
2. `tests/integration/test_phase0_e2e.py` 작성:
- FastAPI `TestClient``/process` 호출
- 응답에 `ontology` TTL과 `facts` TTL 둘 다 포함
- `working_directory/`에 ontology/facts 파일 생성 확인
- BudgetTracker가 LLM call/triple count를 0보다 큰 값으로 기록
### 0.7: Acceptance Gate 0 체크
통합설계서 §5 Phase 0 Acceptance Gate의 4개 체크박스를 PR에 인용하며 모두 확인:
- [ ] 단일 PDF 또는 JSON 입력 → ontology TTL + facts TTL이 filesystem에 생성됨
- [ ] `/health`, `/info`, `/process` (FastAPI 버전) 정상 동작
- [ ] BudgetTracker가 LLM call/triple count를 정확히 기록
- [ ] LangGraph 워크플로우(CONVERT→CHUNK→...→SERIALIZE) 전 노드가 traceable
## 작업 시 준수사항
1. **PR 단위**: 위의 0.1~0.7 각각을 별도 PR/커밋으로 분리. 하나의 PR에 여러 단계를 섞지 않는다.
2. **PR 설명에 근거 인용**: 예) "통합설계서 §5 Phase 0 (3번)에 따라 다중 파일 처리 확장. OntoCast 분석 §13.1 인용."
3. **vendored/ 수정 시 라이선스 의무**:
- 수정한 파일 상단에 `# MODIFIED YYYY-MM-DD: <한 줄 설명>` 주석 추가
- 원본 LICENSE/NOTICE 파일은 절대 삭제하지 않는다
4. **Phase 1로 넘어가지 말 것**: Acceptance Gate 0 통과 전까지 Trafilatura/Crawl4AI/Guardrails/Neo4j GraphRAG 의존성을 활성화하거나 import하지 않는다. (`pyproject.toml`에 명시되어 있더라도 코드에서 사용 금지)
## Phase 1 이후 핸드오프
Phase 0 완료 후, 본 폴더에 `PHASE1_NEXT_STEPS.md`를 작성하여 다음 작업자에게 동일한 형식으로 핸드오프한다. 통합설계서 §12 Phase 1 작업 단위(1.1~1.7)를 참조.

View File

@@ -1,33 +0,0 @@
# Phase 1 Acceptance Gate 결과
작성일: 2026-05-19
범위: Trafilatura 기반 URL/HTML 입력 정렬, SourceDocument/EvidenceSpan 계약, URL 입력 API, fixture 기반 dedup 검증.
## 결과 요약
| # | Acceptance Gate 항목 | 상태 | 검증 방법 |
|---|---|---|---|
| 1 | URL/HTML 입력이 정제 문서로 변환됨 | 통과 | `tests/unit/test_web_extractor.py` |
| 2 | source URL, title, language, content hash, fingerprint 보존 | 통과 | `test_extract_from_korean_html_preserves_document_contract` |
| 3 | `SourceDocument`, `EvidenceSpan`, Content metadata 경계 연결 | 통과 | `test_extracted_content_maps_to_source_document_and_evidence_spans`, `test_content_unit.py` |
| 4 | `/process/url`, `/api/v1/extract/url` URL 입력 API 제공 | 통과 | `tests/integration/test_url_ingest.py` |
| 5 | 같은 본문 중복 입력은 fingerprint 기반으로 skip | 통과 | `test_same_clean_body_gets_same_hash_and_fingerprint`, `test_process_url_skips_duplicate_payload_by_fingerprint` |
| 6 | Phase 0 회귀 없음 | 통과 | `python -m pytest tests/unit tests/integration -q` |
## 검증 이력
| 일자 | 검증자 | 결과 |
|---|---|---|
| 2026-05-19 | Codex | Phase 1 신규 테스트 7/7 통과. 전체 unit/integration 34/34 통과. |
## 구현 메모
- `ont_platform/core/extractors/web_extractor.py`는 Trafilatura 2.x `bare_extraction`을 사용하되, local HTML fixture에서 Trafilatura fingerprint가 비어 있는 경우 normalized text 기반 `sha1:` fingerprint를 생성한다.
- `ont_platform/storage/models.py`의 SQLAlchemy 예약어 충돌을 피하기 위해 DB 컬럼명은 `metadata`로 유지하고 Python attribute는 `metadata_`로 정리했다.
- `/process/url`, `/api/v1/process/url`, `/api/v1/extract/url`은 같은 Phase 1 응답 계약을 사용한다.
- OntoCast vendored core는 수정하지 않았다.
## 다음 Gate
Phase 2는 Candidate Storage 및 Review 책임 경계를 다룬다. 진행 전 `PHASE_INDEX.md`에서 Phase 2 항목만 명시적으로 선택해 작업한다.

View File

@@ -1,162 +0,0 @@
# 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)를 참조.

View File

@@ -1,35 +0,0 @@
# Phase 2 Acceptance Gate 결과
작성일: 2026-05-19
범위: Candidate Storage 및 Review 책임 경계. Lightweight/OntoCast 후보 저장 경로, review 상태 전이, audit trail, evidence 기반 promotion gate.
## 결과 요약
| # | Acceptance Gate 항목 | 상태 | 검증 방법 |
|---|---|---|---|
| 1 | extraction 결과가 candidate로 저장됨 | 통과 | `tests/unit/test_candidate_repository.py` |
| 2 | lightweight와 OntoCast 저장 경로가 분리됨 | 통과 | `test_repository_saves_lightweight_candidates_with_evidence`, `test_repository_saves_ontocast_candidates_on_separate_source_path` |
| 3 | 승인/반려/자동승인 상태 변경 이력이 남음 | 통과 | `tests/unit/test_review_service.py` |
| 4 | evidence 없는 항목은 승인 및 graph commit 대상이 아님 | 통과 | `test_candidate_without_evidence_cannot_be_approved`, `test_promotion_plan_blocks_approved_candidate_without_evidence` |
| 5 | Review API가 ingest/list/detail/approve/reject/promote 흐름을 제공함 | 통과 | `tests/integration/test_review_api.py` |
| 6 | Phase 0-1 회귀 없음 | 통과 | `python -m pytest tests/unit tests/integration -q` |
## 검증 이력
| 일자 | 검증자 | 결과 |
|---|---|---|
| 2026-05-19 | Codex | Phase 2 신규 테스트 9/9 통과. 전체 unit/integration 43/43 통과. 변경 파일 대상 ruff 통과. |
## 구현 메모
- `CandidateEntity`, `CandidateRelation``source_type`, `created_by`, `validation_passed`, `promoted_at`을 추가해 review queue 계약을 명확히 했다.
- `ReviewDecision`으로 상태 변경 audit trail을 남긴다.
- `CandidateRepository.save_lightweight_result()``save_ontocast_result()`를 분리해 두 입력 경로가 같은 candidate contract로 정규화되되, 출처는 유지된다.
- `ReviewService``pending -> approved/rejected/auto_approved`, `approved/auto_approved -> rejected`만 허용한다.
- `CandidatePromotionService``approved` 또는 `auto_approved`이면서 evidence가 실제 존재하는 후보만 commit plan에 포함한다.
- OntoCast vendored core는 수정하지 않았다.
## 다음 Gate
Phase 3은 Crawl4AI 수집 계층 및 Job Orchestration이다. 진행 전 `PHASE_INDEX.md`에서 Phase 3 항목만 명시적으로 선택해 작업한다.

View File

@@ -1,27 +0,0 @@
# Phase 2 — Candidate Storage 및 Review 책임 경계
본 문서는 Phase 1 완료 후 다음 작업자가 Phase 2를 시작할 때 참고할 핸드오프 노트다. 자동으로 Phase 2를 진행하지 않는다.
## 시작 전 확인
- `PHASE_INDEX.md`에서 Phase 2 진행 요청이 명시되어 있는지 확인한다.
- `PHASE1_ACCEPTANCE_GATE.md`의 unit/integration 34/34 통과 상태를 기준선으로 삼는다.
- vendored OntoCast core는 계속 직접 수정하지 않는다.
## Phase 2 목표
추출 결과를 바로 확정 그래프로 보내지 않고, 사람이 검토할 수 있는 candidate/review queue 계약으로 분리한다. SourceDocument와 EvidenceSpan이 없는 후보는 확정 graph로 들어가지 못하게 한다.
## 작업 범위
1. `storage/models.py``CandidateEntity`, `CandidateRelation`을 review queue 계약으로 확정한다.
2. OntoCast 결과와 lightweight extraction 결과의 저장 경로를 분리한다.
3. `pending`, `approved`, `auto_approved`, `rejected` 상태 전이 규칙을 문서와 테스트로 고정한다.
4. evidence 없는 후보가 확정 graph로 승격되지 못하도록 validation boundary를 둔다.
## 권장 테스트
- 후보 생성 시 `document_id``evidence_ids`가 필수로 연결되는지 검증한다.
- 승인/반려/자동승인 상태 전이가 허용된 경로로만 움직이는지 검증한다.
- evidence 없는 entity/relation이 commit 단계에 도달하지 못하는지 검증한다.
- Phase 1 URL/HTML ingestion 테스트가 계속 통과하는지 회귀 검증한다.

View File

@@ -1,28 +0,0 @@
# Phase 3 — Crawl4AI 수집 계층 및 Job Orchestration
본 문서는 Phase 2 완료 후 다음 작업자가 Phase 3을 시작할 때 참고할 핸드오프 노트다. 자동으로 Phase 3을 진행하지 않는다.
## 시작 전 확인
- `PHASE_INDEX.md`에서 Phase 3 진행 요청이 명시되어 있는지 확인한다.
- `PHASE2_ACCEPTANCE_GATE.md`의 unit/integration 43/43 통과 상태를 기준선으로 삼는다.
- 수집 계층은 SourceDocument 생성 전 단계까지만 책임진다. Candidate 저장과 Review Queue는 Phase 2 계약을 사용한다.
- vendored OntoCast core는 계속 직접 수정하지 않는다.
## Phase 3 목표
정적 URL 1건 처리를 넘어 동적 페이지와 대량 수집을 job 단위로 관리한다. Crawl4AI는 acquisition adapter로 감싸고, 본문 정제는 Phase 1 Trafilatura adapter, 후보 저장은 Phase 2 Review Queue로 넘긴다.
## 작업 범위
1. `crawl4ai_adapter.py`를 동적/대량 수집 adapter로 제한한다.
2. crawler profile, robots policy, cache policy를 설정 기반으로 분리한다.
3. Job 상태 모델과 progress API/WebSocket 경계를 정리한다.
4. 수집 결과를 Trafilatura 후처리와 SourceDocument 저장으로 연결한다.
## 권장 테스트
- 정적 HTML/동적 페이지 profile이 같은 SourceDocument 계약으로 이어지는지 검증한다.
- robots/cache policy가 설정값에 따라 선택되는지 검증한다.
- job 상태가 pending/running/completed/failed로 전이되는지 검증한다.
- Phase 1 extraction 및 Phase 2 review queue 테스트가 계속 통과하는지 회귀 검증한다.

View File

@@ -1,100 +1,101 @@
# PHASE INDEX - ontology_platform engine-respect roadmap
# PHASE INDEX - Semantic Page Classification Layer
?묒꽦?? 2026-05-19
작성일: 2026-05-22
踰붿쐞: `ontology_platform` ?꾩슜. `crawler_platform`?€ ?대쾲 ?묒뾽 踰붿쐞?먯꽌 ?쒖쇅?쒕떎.
범위: `ontology_platform` `crawler_platform.app.core.crawler.page_classifier` 및 page classification과 직접 연결된 crawler/extractor 흐름.
湲곗? 臾몄꽌:
- `ontology_platform/docs/?듯빀?ㅺ퀎??md`
기준 문서:
- `README.md`
- `docs/PHASE_PLANNING.md`
- `ontology_platform/README.md`
- `ontology_platform/docs/phases/PHASE0_ACCEPTANCE_GATE.md`
- `ontology_platform/docs/phases/PHASE1_NEXT_STEPS.md`
- `ontology_platform/docs/phases/PHASE1_ACCEPTANCE_GATE.md`
- `ontology_platform/docs/phases/PHASE2_ACCEPTANCE_GATE.md`
- `ontology_platform/docs/semantic_page_classification_codex_spec.md`
?듭떖 ?먯튃:
- OntoCast??Base ?붿쭊?쇰줈 議댁쨷?쒕떎.
- vendored OntoCast 肄붿뼱???듯빀?ㅺ퀎?쒓? ?덉슜??踰붿쐞 ?몄뿉???섏젙?섏? ?딅뒗??
- Trafilatura, Crawl4AI, Guardrails, Neo4j GraphRAG??吏곸젒 ?ш뎄?꾪븯吏€ ?딄퀬 ?뉗? adapter/facade濡?媛먯떬??
- Firecrawl, OpenDeepResearcher 肄붾뱶???ы븿?섏? ?딅뒗??
- Acceptance Gate瑜??듦낵?섍린 ???ㅼ쓬 ?듯빀?쇰줈 ?섏뼱媛€吏€ ?딅뒗??
핵심 원칙:
- 기존 엔진을 폐기하거나 대규모로 교체하지 않는다.
- 기존 `classify_page(...) -> str` 호출부가 깨지지 않도록 legacy compatibility를 유지한다.
- 기존 `ProductPage`, `CategoryPage`, `SearchPage`, `BoardPage`, `BrandStoryPage`, `UnknownPage` 문자열은 alias 또는 compatibility mapping으로 유지한다.
- URL substring 중심 if-return 확장이 아니라 signal extraction -> evidence scoring -> classification result -> analyze strategy -> LLM policy 구조로 확장한다.
- protected page는 안전하게 skip하고, UnknownPage는 evidence와 confidence를 남긴다.
- pytest 또는 현재 프로젝트 테스트 명령으로 회귀 검증한다.
---
PHASE 0. ?붿쭊 寃쎄퀎 媛먯궗 諛?Phase Gate 蹂듦뎄
FILE: ./26_05_19_engine_respect_plan/phase_00_001_engine_boundary_gate.md
PHASE 1. 현재 흐름 기준선 고정 및 영향 범위 정리
FILE: ./26_05_22_semantic_page_classification/phase_01_001_current_flow_boundary.md
1) ?꾩옱 `ont_platform` 紐⑤뱢??Base/Adapter/Draft/Excluded 梨낆엫?쇰줈 遺꾨쪟 [?꾨즺]
2) Phase 0?먯꽌 誘몃옒 Phase ?섏〈?깆씠 import?섏뼱 ???쒖옉??源⑥? ?딅룄濡?寃뚯씠???뺣━ [?꾨즺]
3) Phase 0 unit/integration 寃€利??덉감 怨좎젙 [?꾨즺]
4) `PHASE0_ACCEPTANCE_GATE.md` 媛깆떊 湲곗? ?뺣━ [?꾨즺]
1) `page_classifier.py`의 현재 public API와 legacy page_type 문자열 목록 고정 [완료]
2) `should_analyze_page()` 호출부와 crawler의 `classify_page()` 호출 위치 문서화 [완료]
3) Extractor/HybridExtractor에서 page_type과 LLM skip 정책이 연결되는 흐름 정리 [완료]
4) 기존 page_type 문자열을 기대하는 테스트, config, adapter, ontology rule 경로 목록화 [완료]
---
PHASE 1. Trafilatura 湲곕컲 URL/HTML ?낅젰 ?뺣젹
FILE: ./26_05_19_engine_respect_plan/phase_01_001_trafilatura_ingestion.md
PHASE 2. Taxonomy와 Classification Result 모델 추가
FILE: ./26_05_22_semantic_page_classification/phase_02_001_taxonomy_result_model.md
1) `web_extractor.py`瑜?Trafilatura adapter 梨낆엫?쇰줈 ?뺣━ [?꾨즺]
2) `SourceDocument`, `EvidenceSpan`, Content metadata ?€??寃쎄퀎 ?곌껐 [?꾨즺]
3) `/process/url` ?먮뒗 ?숇벑??URL ?낅젰 API ?ㅺ퀎 [?꾨즺]
4) ?쒓뎅??URL/HTML fixture 湲곕컲 異붿텧 ?뚯뒪?몄? dedup 湲곗? ?묒꽦 [?꾨즺]
1) PageDomain/PageArchetype/PageType/EntityType/ActionIntent/GraphRole/AnalyzeStrategy/LLMPolicy 상수 또는 enum 추가 [완료]
2) `EvidenceItem`, `PageClassificationResult` dataclass 추가 [완료]
3) legacy alias 및 normalize helper 추가 [완료]
4) 기존 `classify_page()` 문자열 반환 호환을 유지하면서 semantic result API 추가 [완료]
---
PHASE 2. Candidate Storage 諛?Review 梨낆엫 寃쎄퀎
FILE: ./26_05_19_engine_respect_plan/phase_02_001_candidate_review_boundary.md
PHASE 3. Raw Snapshot 및 Signal Extraction 레이어 추가
FILE: ./26_05_22_semantic_page_classification/phase_03_001_signal_extraction_layer.md
1) `storage/models.py`???꾨낫 紐⑤뜽???뺤떇 Review Queue 怨꾩빟?쇰줈 ?뺤젙 [?꾨즺]
2) OntoCast 寃곌낵?€ lightweight extraction 寃곌낵???€??寃쎈줈 遺꾨━ [?꾨즺]
3) ?뱀씤/諛섎젮/?먮룞?뱀씤 ?곹깭 ?꾩씠 洹쒖튃 ?뺤쓽 [?꾨즺]
4) evidence ?녿뒗 ?꾨낫媛€ ?뺤젙 graph濡??ㅼ뼱媛€吏€ 紐삵븯寃?李⑤떒 [?꾨즺]
1) `RawPageSnapshot``PageSignals` 모델 추가 [완료]
2) JSON-LD, OpenGraph, Twitter Card, meta, headings, links, forms, buttons, inputs 추출 [완료]
3) commerce/listing/editorial/community/docs/corporate/protected/system signal 추출 [완료]
4) HTML 일부가 깨지거나 필드가 누락되어도 예외 없이 빈 값으로 처리 [완료]
---
PHASE 3. Crawl4AI ?섏쭛 怨꾩링 諛?Job Orchestration
FILE: ./26_05_19_engine_respect_plan/phase_03_001_crawl4ai_acquisition_jobs.md
PHASE 4. Evidence Scoring 기반 Semantic Classification 구현
FILE: ./26_05_22_semantic_page_classification/phase_04_001_evidence_scoring_classifier.md
1) `crawl4ai_adapter.py`瑜??숈쟻/?€???섏쭛 adapter濡??쒗븳 [?꾨즺]
2) crawler profile, robots policy, cache policy瑜??ㅼ젙 湲곕컲?쇰줈 遺꾨━ [?꾨즺]
3) Job ?곹깭 紐⑤뜽怨?progress API/WebSocket 寃쎄퀎 ?뺣━ [?꾨즺]
4) Trafilatura ?꾩쿂由ъ? SourceDocument ?€?μ쑝濡??곌껐 [?꾨즺]
1) 주요 page type별 scoring function과 evidence recording 구조 추가 [완료]
2) 최소 20개 semantic page type 분류 구현 [완료]
3) confidence, alternatives, secondary_page_types 산출 [완료]
4) low confidence 또는 모호한 결과를 evidence 포함 UnknownPage로 처리 [완료]
---
PHASE 4. Guardrails Validation Gate
FILE: ./26_05_19_engine_respect_plan/phase_04_001_guardrails_validation_gate.md
PHASE 5. Analyze Strategy 및 LLM Policy 분리
FILE: ./26_05_22_semantic_page_classification/phase_05_001_analysis_llm_policy.md
1) `core/validation`??Pydantic lightweight?€ Guardrails facade濡?遺꾨━ [?꾨즺]
2) OntoCast LLM 異쒕젰 ?섑븨 吏€?먯쓣 vendored ?섏젙 ?놁씠 ?곗꽑 ?ㅺ퀎 [?꾨즺]
3) schema violation, endpoint missing, confidence range ?뚯뒪???묒꽦 [?꾨즺]
4) Guard ?ㅽ뙣 寃곌낵瑜?candidate/review issue濡??€??[?꾨즺]
1) PageClassificationResult 기반 `decide_analyze_strategy()` 추가 [TODO]
2) PageClassificationResult 기반 `decide_llm_policy()` 추가 [TODO]
3) `should_analyze_page(result_or_page_type, analyze_page_types=None)` compatibility 구현 [TODO]
4) Category/Search/Board 계열을 무조건 skip하지 않고 strategy 기반으로 처리 [TODO]
5) Login/Checkout/Payment/Captcha/AccessDenied 계열은 SkipProtected/Skip 정책으로 처리 [TODO]
---
PHASE 5. Neo4j Projection 諛?GraphRAG 寃€??FILE: ./26_05_19_engine_respect_plan/phase_05_001_neo4j_projection_graphrag.md
PHASE 6. Crawler, Cleaner, Extractor, Discovery/Relevance 통합
FILE: ./26_05_22_semantic_page_classification/phase_06_001_pipeline_integration.md
1) RDF/Fuseki瑜?canonical store, Neo4j瑜?projection/search store濡?怨좎젙 [?꾨즺]
2) `core/graph` 湲곗〈 紐⑤뱢??projection/search adapter 梨낆엫?쇰줈 ?щ텇瑜?[?꾨즺]
3) read-only Text2Cypher?€ vector/hybrid retriever API ?ㅺ퀎 [?꾨즺]
4) provenance媛€ search result源뚯? ?댁뼱吏€??寃€利?湲곗? ?묒꽦 [?꾨즺]
1) `site_crawler.py``pipeline.py` metadata에 semantic classification payload 저장 [TODO]
2) `ExtractionPageContext` 또는 metadata를 통해 analyze_strategy/llm_policy 전달 [TODO]
3) `HybridExtractor`가 LLMPolicy를 우선 사용하고 legacy page_type fallback을 유지하도록 수정 [TODO]
4) `page_cleaner.py`, `domain_discovery.py`, `relevance_engine.py`의 legacy page_type 기대 경로와 신규 semantic type을 호환 [TODO]
---
PHASE 6. Maintenance Loop 諛??댁쁺 湲곕뒫 ?뺣━
FILE: ./26_05_19_engine_respect_plan/phase_06_001_maintenance_loop_operations.md
PHASE 7. Unknown Pattern 저장 기반 추가
FILE: ./26_05_22_semantic_page_classification/phase_07_001_unknown_pattern_storage.md
1) Knowledge Agent??肄붾뱶媛€ ?꾨땲???꾨\?꾪듃/?뚰겕?뚮줈???⑦꽩留?李⑥슜 [?꾨즺]
2) Analyst/Researcher/Curator/Auditor/Fixer/Advisor 梨낆엫 ?뺤쓽 [?꾨즺]
3) `auth`, `audit`, `billing`, `realtime` 珥덉븞 紐⑤뱢???댁쁺 寃쎄퀎 ?뺣━ [?꾨즺]
4) destructive fix???щ엺 ?뱀씤 寃뚯씠?몃? 諛섎뱶???듦낵?섎룄濡??ㅺ퀎 [?꾨즺]
1) UnknownPage 또는 low confidence 페이지의 evidence payload 정의 [TODO]
2) text/html/link/schema/button/form summary와 fingerprint hook 추가 [TODO]
3) DB schema 변경 없이 metadata_json에 저장 가능한 초기 구조 구현 [TODO]
4) 향후 clustering/embedding 확장을 위한 hook만 추가하고 실제 clustering은 이번 범위에서 제외 [TODO]
---
PHASE 7. Hybrid Rule + LLM Extraction
FILE: ./26_05_19_engine_respect_plan/phase_07_001_hybrid_rule_llm_extraction.md
PHASE 8. 테스트 Fixture 및 회귀 검증
FILE: ./26_05_22_semantic_page_classification/phase_08_001_tests_regression.md
1) rule baseline, LLM extraction, fallback, validation, Review UI 흐름을 기준선으로 고정 [신규]
2) `rule_only`, `llm_only`, `hybrid`, `compare` extraction mode 계약 정의 [신규]
3) product backend에 명시적 HybridExtractor와 rule/LLM agreement metadata 추가 [신규]
4) confidence breakdown에 rule agreement와 conflict/review 정책 반영 [신규]
5) Crawl/Research UI에서 mode/provider/model/base URL 선택 지원 [신규]
1) 최소 10개 이상의 HTML fixture 추가 [TODO]
2) ProductDetailPage, CategoryListingPage, SearchResultsPage, ArticlePage, QAPage, FAQPage, ForumThreadPage, DocumentationPage, JobPostingPage, LoginPage, CheckoutPage, TermsPage, SitemapPage, UnknownPage 단위 테스트 추가 [TODO]
3) legacy `classify_page()``should_analyze_page()` 호환성 테스트 추가 [TODO]
4) HybridExtractor LLMPolicy 회귀 테스트 추가 [TODO]
5) pytest 또는 현재 프로젝트 테스트 명령 실행 및 결과 기록 [TODO]