Files
AI/온톨로지플랫폼_통합설계서.md
2026-05-14 23:37:21 +09:00

1182 lines
54 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 범용 온톨로지 구축 플랫폼 통합 설계서
작성일: 2026-05-13
대상: 본 문서를 받아 실제 구현을 수행할 모든 AI 에이전트 및 개발자
근거 자료: `C:\Users\lasta\MyProject\AI\오픈소스분석자료` 의 8개 분석 명세 (Crawl4AI, Firecrawl, Guardrails, Knowledge Agent, Neo4j GraphRAG, OntoCast, OpenDeepResearcher, Trafilatura)
---
## 0. 본 문서의 사용 규칙 (Agent Instruction)
본 문서를 받는 AI 에이전트는 다음을 준수한다.
1. 본 문서는 8개 분석 자료를 바탕으로 도출된 **최종 통합 설계**다. 개별 분석 자료와 본 문서가 충돌할 경우 본 문서가 우선한다.
2. 본 문서가 "그대로 사용"이라고 명시한 모듈은 원본 소스를 가급적 수정하지 않고 라이브러리 또는 vendored copy 형태로 도입한다. "어댑터 작성"이라고 명시한 부분만 우리 코드로 만든다.
3. "통합은 한 번에 하나씩"의 원칙을 지킨다. Phase N의 검수 게이트(Acceptance Gate)를 통과하기 전에는 Phase N+1로 진행하지 않는다.
4. 코드 작성 시 어느 분석 자료의 어느 절을 근거로 했는지 PR 설명에 명시한다. 예: `OntoCast 분석 §6 GraphUpdate 모델`.
5. 본 문서가 "제외(Excluded)"라고 명시한 프로젝트는 코드/의존성에 포함하지 않는다.
---
## 1. Executive Summary (한눈에 보는 결론) — 수정판
### 1.1 설계 변경 배경
초기 설계는 **OntoCast를 Phase 0 Base로 사용**하고, 이후 다른 컴포넌트를 순차 통합하는 구조였다.
그러나 **Phase 0 실제 구현 및 테스트 결과**, OntoCast의 전체 파이프라인(RDF/OWL 갱신, Renderer/Critic retry loop, Entity Aggregation)을 초기 단계부터 사용하면:
- 처리 속도가 매우 느림 (테스트 분량 축소에도 불구하고)
- 중간 결과 확인이 어려워 디버깅 복잡
- 대량 크롤링 단계에 부적합
- 비용 폭주 위험
따라서 **설계를 전면 개정**한다.
### 1.2 수정된 핵심 전략
| 항목 | 기존 설계 | 수정 설계 |
|---|---|---|
| **기본 엔진** | OntoCast 중심 | 가벼운 JSON Extraction |
| **OntoCast 역할** | 입력 → 처리 → RDF 출력 | 승인된 후보 → 정밀 RDF/OWL 갱신 |
| **처리 흐름** | 모든 문서 → OntoCast → RDF | 문서 → 후보 추출 → 검수 → 선택적 OntoCast → RDF |
| **Phase 0 목표** | OntoCast 안정화 | Fast Extraction MVP (RDF 없이) |
| **총 Phase 수** | 6개 (Phase 0~5) | 6개 (구조 변경) |
**한 문장 요약**: OntoCast는 기본 엔진에서 정밀 갱신 엔진으로 역할을 낮춘다.
### 1.3 최종 구조
```
Trafilatura / Crawl4AI
→ Fast JSON Extraction (가벼움)
→ Candidate Store (SQLite/PostgreSQL)
→ Validation / Guardrails
→ Review Queue (사람 검수)
→ Optional OntoCast (고신뢰/승인된 후보만)
→ Fuseki RDF (Canonical)
→ Neo4j Projection (검색/RAG)
```
| 항목 | 결정 |
|---|---|
| **기본 수집** | Trafilatura (정적, 빠름) + Crawl4AI (동적, 선택) |
| **초기 추출** | Lightweight LLM JSON Extractor (RDF 아님) |
| **검증** | Pydantic + Guardrails |
| **정밀 RDF 갱신** | OntoCast (batch 후보 단위, 선택적) |
| **통합 대상** | ① Trafilatura ② Crawl4AI ③ Guardrails ④ Neo4j GraphRAG ⑤ OntoCast (용도 변경) |
| **제외** | Firecrawl, OpenDeepResearcher (기존과 동일) |
---
## 기존 설계 요약 (참고용) — Phase 0 문제 분석
### 기존 Phase 0: OntoCast Base 안정화 (⚠️ 폐기됨)
기존 설계에서는 다음과 같이 계획되었다:
```
1. OntoCast 저장소를 platform/core/ontocast/ 에 배치
2. select_ontology.py 버그 수정
3. convert_document.py 다중 파일 처리 확장
4. Robyn → FastAPI 재작성
5. 전체 파이프라인 end-to-end 테스트
→ RDF/OWL 생성 목표
```
**실제 테스트 결과 발견된 문제**:
- 처리 속도: 단순 샘플도 5분 이상 소요 (목표: 30초)
- 디버깅 어려움: RDF 중심이라 중간 상태 확인 곤란
- 비용 폭주: Critic retry loop 반복될수록 LLM 호출 증가
- 확장성 부족: 대량 문서 처리에 부적합
**결론**: OntoCast 중심 구조는 **정밀도는 높지만 초기 단계에는 부적합**. 따라서 기존 Phase 0을 폐기하고, 새로운 Phase 0 (Fast Extraction MVP)으로 변경.
**기존 계획 중 여전히 유용한 부분**:
- OntoCast 버그 수정 (§9.1 참고) → Phase 4에서 활용
- Pydantic Settings 패턴 → 현재 설계에 적용
- BudgetTracker 개념 → Phase 4에서 재사용
---
## 2. 8개 오픈소스 강점 매트릭스 (Strength Matrix)
각 소스가 "가장 잘하는 영역" 하나씩만 추려서 기능 중복을 정리한 표.
| # | 프로젝트 | 카테고리 | 대체 불가 강점 | 라이선스 | 언어 | 채택 여부 |
|---|---|---|---|---|---|---|
| 1 | **OntoCast** | 온톨로지 코어 | **GraphUpdate 기반 SPARQL 증분 갱신** + Renderer/Critic retry loop + Entity aggregation(embedding + URI 정규화 + owl:sameAs) | Apache 2.0 | Python 3.12+ | **선택적 Precision RDF/OWL Engine** |
| 2 | **Trafilatura** | 본문 추출 | 본문/메타데이터/날짜/저자/언어 추출의 **산업 표준 정밀도**. XML body tree 보존, SimHash fingerprint, feed/sitemap discovery | Apache 2.0 | Python | **통합** |
| 3 | **Crawl4AI** | 크롤링 | **동적 페이지(Playwright)+LLM 친화 Markdown** 변환의 결정판. Deep crawl(BFS/DFS/Best-First) + URL Seeder + Adaptive crawler + browser pool/dispatcher/cache | Apache 2.0 | Python 3.10+ | **통합** |
| 4 | **Guardrails** | 검증 | **Pydantic 기반 LLM 출력 강제 + on_fail 정책(reask/fix/filter/refrain) + JSON path field validator** | Apache 2.0 | Python 3.10+ | **통합** |
| 5 | **Neo4j GraphRAG** | KG 저장/검색 | **GraphSchema + GraphPruning + Neo4jWriter + EntityResolver + Vector/Hybrid/Text2Cypher Retriever + GraphRAG** | Apache 2.0 | Python 3.10+ | **통합** |
| 6 | Knowledge Agent | 멀티에이전트 | LangGraph 기반 Analyst→Researcher→Curator→Auditor→Fixer→Advisor 패턴 + LightRAG 엔티티/관계 추출 프롬프트 | 비명시 | Python | **패턴/프롬프트만 차용** |
| 7 | Firecrawl | 크롤링 API | scrape/map/crawl/search/parse API 명세 우수, fire-engine fallback | AGPL 의심 | TypeScript | **제외** (스택 분리 부담 + 라이선스 리스크 + Crawl4AI와 중복) |
| 8 | OpenDeepResearcher | 검색 루프 | LLM 기반 검색어 생성 + 자기확장 루프 + `<done>` 판단 | MIT | Python (notebook) | **제외** (`eval()` 보안 문제 + Knowledge Agent의 Researcher가 더 구조화됨) |
---
## 3. OntoCast를 선택적 Precision RDF/OWL Engine으로 유지하는 근거
| 비교 항목 | OntoCast | Knowledge Agent | Neo4j GraphRAG |
|---|---|---|---|
| 사용자 목표 적합성("온톨로지 구축") | ◎ RDF/OWL 중심 | △ 지식그래프 보조 도구 | ○ Property Graph 중심 |
| 핵심 자산의 대체 난이도 | ◎ GraphUpdate 증분 갱신은 다른 어디서도 못 구함 | △ LangGraph 패턴은 재작성 용이 | ○ Library 형태로 갖다 쓰면 됨 |
| Renderer/Critic retry loop | ◎ 내장 | △ Refiner 비슷한 개념만 | × 없음 |
| Entity Aggregation/URI 정규화 | ◎ 내장 (`tool/agg/`) | × 없음 | △ Resolver 있으나 단순 |
| LangGraph 워크플로우 | ◎ 내장 (`stategraph/`) | ◎ 내장 | × 없음 |
| Triple Store 추상화 (Fuseki/Neo4j/FS) | ◎ 내장 | × LightRAG에 종속 | △ Neo4j만 |
| ToolBox dependency container | ◎ 내장 | × | × |
| 라이선스 명확성 | ◎ Apache 2.0 | × 비명시 | ◎ Apache 2.0 |
| 코드 안정성 | ○ 일부 버그 (§13.1) | × 다수 버그 (Curator/Auditor/Fixer/Advisor) | ◎ 테스트 광범위 |
**결론**: OntoCast의 "GraphUpdate 증분 갱신 + Renderer/Critic 루프 + Entity Aggregation"은 다른 소스로 대체 불가능한 차별 자산이다. 이를 선택적 Precision RDF/OWL Engine으로 유지하고, Fast Pipeline과 분리하여 사용한다.
---
## 4. 통합 아키텍처 (Layered Architecture) — 수정판
### 4.0 기존 vs 수정 아키텍처 비교
**기존 설계 (Phase 0에서 발견된 문제)**:
```
입력(URL/File)
→ L4 OntoCast 전체 파이프라인 (Converter/Chunker/Renderer/Critic...)
→ RDF/OWL 생성
→ L1 Fuseki 저장
```
❌ 문제: 초기 단계부터 무거운 RDF 처리, 디버깅 어려움, 테스트 속도 느림
**수정 설계 (분리된 두 경로)**:
```
Fast Path (대부분): Precision Path (선택):
입력 → 본문 추출 승인 후보 → OntoCast
→ 가벼운 JSON 추출 → RDF/OWL 갱신
→ 검증 (Pydantic) → Fuseki 저장
→ 후보 저장
→ 검수/자동승인
```
✅ 이점: 빠른 피드백, 명확한 단계, OntoCast는 필요할 때만
### 4.1 수정된 계층 구조
```
┌──────────────────────────────────────────────────────────────────┐
│ L7. UI Layer (Ontology Studio, Review Console, Search UI) │
│ — 새로 작성 (React/Next.js 등 자유 선택) │
├──────────────────────────────────────────────────────────────────┤
│ L6. Platform API (FastAPI) │
│ — 새로 작성. /projects, /jobs, /sources, /extract, │
│ /candidates, /review, /search, /admin │
├──────────────────────────────────────────────────────────────────┤
│ L5. Job Orchestration (Job Queue + Worker) │
│ — 새로 작성. Arq/Celery, Redis/Postgres state │
├──────────────────────────────────────────────────────────────────┤
│ L4a. Fast Extraction Engine (기본 경로) │
│ ★ Trafilatura (본문/메타 추출) │
│ ★ Crawl4AI (동적 크롤링, 선택) │
│ ★ Lightweight JSON Extractor (가벼운 entity/relation 추출) │
│ — 입력: 정제 본문 → 출력: JSON (RDF 아님) │
├──────────────────────────────────────────────────────────────────┤
│ L4b. OntoCast RDF Engine (정밀 경로, 선택적) │
│ ★ OntoCast (승인 후보 → RDF/OWL 갱신, Batch 처리) │
│ — Renderer/Critic retry loop, GraphUpdate, Entity Agg. │
│ — 호출: batch 후보 단위 (문서 단위 아님) │
├──────────────────────────────────────────────────────────────────┤
│ L3. Quality & Validation Gate │
│ ★ Pydantic (JSON 스키마 검증) │
│ ★ Guardrails (LLM 출력 강제 검증) │
│ — CandidateEntity/Relation 검증 │
├──────────────────────────────────────────────────────────────────┤
│ L2. Temporary Knowledge Store │
│ • SQLite / PostgreSQL (초기) │
│ • CandidateEntity, CandidateRelation, ReviewStatus │
│ — RDF 아님, 검수 전 임시 저장소 │
│ — Neo4j draft graph (선택, Phase 3) │
├──────────────────────────────────────────────────────────────────┤
│ L1. Canonical Storage (검수/승인 후) │
│ • Fuseki (Canonical RDF Store) │
│ • Neo4j (Projection, Vector Index, Fulltext) │
│ ★ Neo4j GraphRAG (검색/RAG Retriever) │
│ • PostgreSQL (Job/User/Project 메타데이터) │
│ • Object Storage (raw HTML, PDF artifacts) │
└──────────────────────────────────────────────────────────────────┘
```
### 4.2 데이터 흐름 (수정판) — Fast Path + Precision Path
#### Fast Path (기본, 대부분의 문서)
```
[Source URL/File]
→ [L4a 수집] Trafilatura bare_extraction (본문 + metadata)
또는 Crawl4AI (JS 필요 시)
→ [L4a 정제] 정제된 본문 텍스트 + title/author/date/language
→ [L4a 추출] Lightweight LLM JSON Extractor
입력: 정제 본문
출력: {entities, relations, claims, evidence, confidence}
→ [L3 검증] Pydantic + Guardrails
EntityIdFormatValidator
UniqueEntityIdValidator
RelationEndpointExistsValidator
ConfidenceRangeValidator
EvidenceExistsValidator
→ [L2 저장] PostgreSQL / SQLite
CandidateEntity (id, label, type, confidence, evidence_ids)
CandidateRelation (source_id, predicate, target_id, confidence)
EvidenceSpan (chunk_id, text, start_offset, end_offset)
ReviewStatus (pending/approved/rejected)
→ [L6 API] /candidates 조회
사람이 결과 확인 및 검수
```
**특징**:
- ✅ 10~30초 내 처리 (OntoCast 없음)
- ✅ JSON 기반 (RDF 아님)
- ✅ 중간 결과 명확함
- ✅ 대량 문서 적합
#### Precision Path (선택, 승인된 후보만)
```
[Approved CandidateEntity/Relation (Batch)]
→ [L4b OntoCast] OntoCast Adapter
Candidate JSON → OntoCast internal format 변환
batch_size: 10~50개 단위
→ [L4b 처리] OntoCast RENDER_ONTOLOGY_UPDATE + Critic
GraphUpdate 생성
Entity Aggregation (owl:sameAs)
→ [L3 검증] Guardrails (OntoCast 출력)
최종 GraphUpdate schema 검증
→ [L1 저장]
Fuseki: Canonical RDF (via GraphUpdate SPARQL)
Postgres: Job metadata, processing time, cost
→ [L1 투영] Neo4j Async Worker
Fuseki RDF → Neo4j Property Graph
Chunk embedding + Vector index
Lexical graph (Document/Chunk/Entity)
→ [L6 API] /search/{type} 호출 시 Neo4j GraphRAG의
VectorRetriever + HybridRetriever + GraphRAG
```
**특징**:
- ✅ RDF/OWL 정밀 반영 (OntoCast의 강점)
- ✅ GraphUpdate 증분 갱신 (대체 불가 기능)
- ✅ Entity Aggregation (중복 병합)
- ⚠️ 처리 시간 길음 (batch 단위로 완화)
- ⚠️ 비용 높음 (선택적 호출로 제어)
### 4.3 저장소 전략 (Fast Path vs Precision Path)
#### Fast Path 저장소
| 저장소 | 역할 | 특징 |
|---|---|---|
| **PostgreSQL / SQLite** | 임시 지식 저장소. Candidate 후보 보관 | 검수 전 상태, RDF 아님 |
| **CandidateEntity** | id, label, type, confidence, source_doc_id, evidence_ids | 사람 검수 대기 |
| **CandidateRelation** | source_id, predicate, target_id, confidence, evidence | 사람 검수 대기 |
| **ReviewStatus** | pending → approved/rejected/auto_approved | 승인 상태 기록 |
#### Precision Path 저장소
| 저장소 | 역할 | 근거 |
|---|---|---|
| **Fuseki (Canonical RDF)** | 진실 원본 (source of truth). OntoCast의 GraphUpdate가 SPARQL UPDATE로 직접 반영. OWL/SHACL 추론 가능 | OntoCast GraphUpdate는 SPARQL 기반 |
| **Neo4j (Projection)** | 검색/시각화/RAG 전용. Fuseki 변경 시 비동기로 동기화. Lexical graph(Document/Chunk) + Entity 노드 + embedding | Neo4j GraphRAG + Vector search |
**중요 원칙**:
- Fast Path는 RDF를 생성하지 않음 (JSON만)
- Precision Path만 RDF 생성 (OntoCast 호출 시)
- RDF가 진실, Neo4j는 사본
- Fuseki commit → Worker가 async로 Neo4j 동기화
---
## 5. 단계별 통합 로드맵 (Phased Integration Plan) — 수정판
각 Phase의 마지막에 **Acceptance Gate**(검수 게이트)가 있다. Gate를 통과해야 다음 Phase 진행.
---
### Phase 0: Fast Extraction MVP ⭐ (새로운 시작점)
**목표**: OntoCast를 붙이기 전에, 웹페이지나 파일에서 의미 있는 JSON 형태의 온톨로지 후보가 빠르게 추출되는지 검증한다. **RDF/OWL, SPARQL, GraphUpdate, Critic loop를 사용하지 않는다.**
**배경**: 기존 설계에서 OntoCast를 Phase 0 Base로 사용했으나, 실제 구현 결과 처리 속도가 지나치게 느렸다. 따라서 보다 가벼운 추출 엔진을 우선 구성하고, OntoCast는 선택적으로 호출하는 구조로 변경했다. (자세한 배경은 §1.1 참조)
**작업**:
1. **`platform/core/extractors/web_extractor.py` 작성**
- Trafilatura 기반 본문 추출
- 출력: title, author, publish_date, language, canonical_url, fingerprint, body_text
- 참고: 기존 설계 Phase 1 내용 일부 활용
2. **`platform/core/crawler/crawl4ai_adapter.py` 작성** (최소 버전)
- 기본: HTTP fetch만 (Trafilatura로 후처리)
- 선택: Crawl4AI dynamic profile (JS 렌더링 필요 시)
- 참고: 기존 설계 Phase 2 내용 단순화
3. **`platform/core/extraction/lightweight_extractor.py` 작성**
- 입력: 정제된 본문 텍스트
- 출력: JSON 형태
```json
{
"entities": [
{"id": "E1", "label": "...", "type": "...", "confidence": 0.9, "evidence_ids": ["EV1"]}
],
"relations": [
{"source_id": "E1", "predicate": "...", "target_id": "E2", "confidence": 0.85}
],
"claims": [],
"evidence": [
{"id": "EV1", "text": "...", "start_offset": 100, "end_offset": 150}
],
"warnings": []
}
```
- LLM: OpenAI/Claude (선택), 프롬프트는 간단한 JSON extraction만
4. **`CandidateStore` (SQLite 또는 PostgreSQL)**
- 테이블: CandidateEntity, CandidateRelation, EvidenceSpan, SourceDocument, ReviewStatus
- RDF 변환 금지 (후보만 저장)
5. **간단한 FastAPI 작성**
```
POST /extract/url # URL 입력
POST /extract/file # 파일 업로드
GET /candidates # 후보 목록 조회
GET /candidates/{id} # 후보 상세
```
**Acceptance Gate 0**:
- [ ] 단일 URL 입력 시 **10~30초 이내** 후보 JSON 생성 (기존 OntoCast 방식과 비교해 5배 이상 빠름)
- [ ] 본문 추출 결과를 사람이 확인 가능 (metadata 포함)
- [ ] entity/relation 후보가 SQLite/PostgreSQL에 저장됨
- [ ] **OntoCast 없이도** 추출 품질을 평가할 수 있음
- [ ] 동일 URL 재입력 시 fingerprint 기반 중복 감지 가능
- [ ] Trafilatura + Crawl4AI (선택) 초기 통합 완료
---
### Phase 1: Fast Pipeline 품질 개선
**목표**: 가벼운 추출 파이프라인의 결과 품질을 안정화하고, 무의미한 후보를 필터링한다.
**작업**:
1. **Pydantic 스키마 정의**
```python
class CandidateEntity(BaseModel):
id: str
label: str
type: Literal["concept", "person", "org", ...] # 도메인별 설정
description: str | None = None
confidence: float # 0.0 ~ 1.0
evidence_ids: list[str]
class CandidateRelation(BaseModel):
source_id: str
predicate: str
target_id: str
confidence: float
evidence: list[str]
```
2. **Lightweight JSON 검증 강화**
- "value", "keyword" 같은 무의미한 label 필터링
- confidence ≤ 0 또는 ≥ 1.0인 값 정정
- evidence 없는 relation은 저장 금지
- entity와 relation endpoint 매칭 검사
3. **도메인별 entity type/predicate 설정**
- 프로젝트별 기본 타입 팩 정의
- 사용자 정의 타입 추가 가능
- 프롬프트에 반영
4. **Confidence 점수 부여 개선**
- 초기: LLM 직접 추출값
- 개선: evidence 길이, 문맥 유사도, type 신뢰도 등 반영
**Acceptance Gate 1**:
- [ ] "value", "keyword", 빈 문자열 같은 무의미한 후보가 저장되지 않음
- [ ] evidence 없는 relation은 저장 금지
- [ ] entity와 relation endpoint가 서로 일치함 (존재하는 entity만 참조)
- [ ] 후보 품질을 사람이 빠르게 검토 가능 (metadata 포함)
- [ ] Phase 0 성능 유지 (10~30초)
---
### Phase 2: Guardrails 검증 강화
**목표**: Lightweight Extractor가 생성한 JSON 후보를 Guardrails로 강제 검증한다. 잘못된 값이 저장소에 들어가는 것을 방지한다.
**배경**: 기존 설계 Phase 3에서 OntoCast Renderer 출력을 Guardrails로 검증했으나, 여기서는 더 초기 단계인 Lightweight JSON extraction 결과를 검증한다.
**작업**:
1. **의존성 추가**: `guardrails-ai>=0.x` (Hub/telemetry 비활성화 필수)
2. **Validator 작성** (Guardrails 분석 §15.4 참고, 단순화)
- `EntityIdFormatValidator`: id는 알파벳+숫자만
- `UniqueEntityIdValidator`: 동일 id 중복 없음
- `RelationEndpointExistsValidator`: source/target entity 존재 확인
- `ConfidenceRangeValidator`: confidence는 0.0~1.0
- `EvidenceExistsValidator`: evidence_ids가 실제로 존재하는 지 확인
- `NoSelfRelationValidator`: source ≠ target
3. **Guardrails Guard 설정**
```python
guard = Guard.for_pydantic(
CandidateEntity,
validators=[...],
num_reasks=1,
on_fail=OnFailAction.FILTER
)
```
4. **Lightweight Extractor 후 검증 적용**
- LLM JSON 출력 → Guardrails Guard → CandidateStore
- 검증 실패 시: fix/filter/refrain 정책 적용
- 실패 후보는 review queue로 이동 (삭제 금지)
**Acceptance Gate 2**:
- [ ] confidence > 1.0 같은 오류 자동 fix 또는 filter
- [ ] 존재하지 않는 entity를 참조하는 relation 차단
- [ ] evidence 없는 후보 자동 필터링
- [ ] 검증 실패 결과도 감사 로그에 기록됨
- [ ] Guardrails Hub/telemetry 외부 통신 없음 (환경변수 확인)
- [ ] Phase 0~1 성능 유지
---
### Phase 3: Review & Approval 시스템
**목표**: 검증된 후보에 대해 사람이 검수하고, 자동 승인 정책을 적용할 수 있도록 한다.
**작업**:
1. **Review UI (간단한 버전)**
- 후보 목록 표시
- entity/relation별 evidence 하이라이트 (원문으로부터)
- 승인/반려/수정 버튼
2. **자동 승인 정책 정의**
```python
if confidence >= 0.95 and source_trust >= 0.9:
status = "auto_approved"
else:
status = "pending" # 사람 검수 대기
```
3. **ReviewStatus 테이블 관리**
- pending: 사람 검수 대기
- approved: 사람이 승인
- auto_approved: 정책으로 자동 승인
- rejected: 반려
4. **승인된 후보만 다음 단계로**
- Precision Path (OntoCast) 또는
- 최종 저장소 (RDF/Neo4j)로 전달
**Acceptance Gate 3**:
- [ ] Review 큐에 100개 후보를 5분 내 표시
- [ ] evidence 원문 추적 가능
- [ ] 자동 승인 정책이 정확히 작동
- [ ] 승인/반려 이력이 저장됨
- [ ] Phase 0~2 기능 회귀 없음
---
### Phase 4: OntoCast 선택적 통합 ⭐ (역할 변경)
**목표**: OntoCast를 전체 파이프라인의 Base에서 **승인된 후보를 RDF/OWL로 정밀 반영하는 선택적 엔진**으로 통합한다.
**배경**: Phase 0 테스트 결과, OntoCast 전체 파이프라인을 초기부터 사용하는 것은 비효율적임. 따라서 검증된 후보만 batch 단위로 OntoCast에 전달하여 처리한다.
**처리 흐름**:
```
Approved CandidateEntity/Relation (batch)
→ OntoCast Adapter (JSON → internal format 변환)
→ OntoCast RENDER_ONTOLOGY_UPDATE + Critic loop
→ GraphUpdate 생성
→ Guardrails (최종 검증)
→ Fuseki RDF 저장 (SPARQL UPDATE)
```
**작업**:
1. **`platform/core/ontocast_adapter.py` 작성**
- CandidateEntity/Relation → OntoCast 입력 포맷 변환
- batch_size: 10~50개 단위 (문서 단위 아님)
- 처리 실패 시 후보는 보존, review queue로 이동
2. **OntoCast 호출 원칙**
- ❌ 모든 문서를 직접 OntoCast에 보내지 않음
- ✅ 검수된 후보 또는 고신뢰 후보만 전달
- ✅ batch 단위로 묶어 호출 (문서 단위 호출 금지)
- ✅ 실패해도 Fast Pipeline은 계속 동작
3. **OntoCast 처리 선택지**
- **필수 OntoCast**: confidence ≥ 0.95 + approved by human
- **선택 OntoCast**: 신뢰도 낮은 후보 batch 처리 (비용 증가)
- **Skip OntoCast**: 임시 저장소(Neo4j draft)에만 유지
4. **GraphUpdate 결과 처리**
- OntoCast GraphUpdate → SPARQL 문장
- Fuseki에 commit
- Worker가 async로 Neo4j projection 갱신
5. **Cost & Time Tracking**
- OntoCast 호출당 비용/시간 기록
- BudgetTracker에 반영
- 운영자가 비용 제어 가능
**Acceptance Gate 5**:
- [ ] 승인된 후보 10~50개를 batch로 OntoCast 처리 가능
- [ ] 처리 시간: 각 batch마다 1~5분 (문서 단위 처리보다 효율)
- [ ] 처리 실패 시 후보 데이터는 보존됨 (손실 없음)
- [ ] **OntoCast 없이도 Fast Pipeline은 계속 독립 동작**
- [ ] OntoCast 처리 결과가 RDF/OWL로 Fuseki에 저장됨
- [ ] GraphUpdate는 SPARQL이므로 증분 갱신 가능 (기존 RDF와 merge)
### Phase 5: Neo4j GraphRAG 통합 (Projection + 검색/RAG)
**목표**: Fuseki에 commit된 RDF를 Neo4j Property Graph로 projection하고, Neo4j GraphRAG의 Retriever/GraphRAG로 검색/QA 기능을 추가한다.
**작업**:
1. 의존성 추가: `neo4j-graphrag[openai,experimental]==1.16.0` (버전 고정)
2. APOC core 설치된 Neo4j 5.18.1+ 배포 (Docker compose 추가)
3. `platform/core/projection/rdf_to_neo4j.py` 신설:
- Fuseki의 `Ontology` + `Facts` graph → `Neo4jGraph(nodes, relationships)` 변환
- `KGWriter`로 `Neo4jWriter` 사용해 upsert
- Lexical graph(Document/Chunk) + Entity 노드를 `LexicalGraphBuilder` 표준에 맞춰 생성
4. Chunk embedding:
- OntoCast의 청킹 결과(`ContentUnit.text`)에 `OpenAIEmbeddings` 또는 `SentenceTransformerEmbeddings` 적용
- Neo4j chunk vector index 자동 생성 (`indexes.py`)
5. 검색 API 신설:
- `POST /search/vector` → `VectorRetriever`
- `POST /search/hybrid` → `HybridRetriever`
- `POST /search/text2cypher` → `Text2CypherRetriever` (read-only 강제)
- `POST /search/graphrag` → `GraphRAG` 답변 생성
6. Text2Cypher 보안 (Neo4j GraphRAG 분석 §13.4):
- read-only 검사 + 허용 schema 제한 + query timeout + result limit
7. Entity Resolver:
- `SinglePropertyExactMatchResolver` 기본 활성화
- OntoCast Entity Aggregation 결과(`owl:sameAs`)와 함께 사용해 중복 병합
**Acceptance Gate 4**:
- [ ] Fuseki commit 후 5초 이내 Neo4j projection 동기화 완료
- [ ] Lexical graph(Document/Chunk/Entity)와 provenance(`FROM_CHUNK`) 정상 생성
- [ ] Vector 검색 결과의 chunk → entity provenance 추적 가능
- [ ] Text2Cypher가 write/delete 쿼리를 차단
- [ ] GraphRAG 답변에 evidence chunk URL 포함
- [ ] Phase 0~3 기능 회귀 없음
---
### Phase 5: Neo4j GraphRAG 통합 & 검색/RAG
**목표**: 최종 RDF (또는 검증된 후보)를 Neo4j에 projection하고, Neo4j GraphRAG의 retriever/QA 기능으로 검색/RAG을 제공한다.
**작업**:
1. **의존성 추가**: `neo4j-graphrag==1.16.0` (버전 고정)
2. **Projection 어댑터** (`platform/core/projection/rdf_to_neo4j.py`)
- Fuseki RDF → Neo4j Property Graph 변환
- Node: Entity (label, type, confidence)
- Relationship: Predicate (confidence, evidence)
- Chunk/Document: Lexical graph builder 활용
3. **Embedding & Vector Index**
- 최종 chunk → OpenAI/SentenceTransformer embedding
- Neo4j vector index 자동 생성
4. **검색 API 추가**
```
POST /search/vector # Vector 검색
POST /search/hybrid # Vector + fulltext
POST /search/graphrag # QA (답변 + evidence)
```
5. **Text2Cypher 보안** (Phase 4 기존 내용 적용)
- read-only 강제
- allowlist schema
- timeout + result limit
**Acceptance Gate 5**:
- [ ] Fuseki commit 후 5초 이내 Neo4j projection 동기화
- [ ] Vector 검색으로 chunk 찾고, entity/predicate 추적 가능
- [ ] GraphRAG 답변에 evidence chunk + URL 포함
- [ ] Text2Cypher가 write/delete 차단
- [ ] Phase 0~4 기능 회귀 없음
---
## 6. 성능 최적화 전략
### 6.1 Fast Path vs Precision Path 분리
| 단계 | Fast Path | Precision Path (Optional) |
|---|---|---|
| 추출 엔진 | Lightweight JSON | OntoCast RDF |
| 처리 속도 | 10~30초 | 1~5분 (batch) |
| 저장소 | PostgreSQL/SQLite | Fuseki RDF |
| 용도 | 초벌 후보, 사람 검수 | 최종 canonical graph |
| 비용 | 낮음 (LLM 1회) | 높음 (Critic loop) |
### 6.2 OntoCast 호출 최적화
```
❌ 하지 말아야 할 것:
- 모든 문서를 OntoCast에 넣기
- 문서 단위 호출
- 저신뢰 후보도 OntoCast 처리
✅ 해야 할 것:
- 검증된 후보만 선택
- batch 단위 호출 (10~50개)
- 사람 검수를 거친 데이터
- 병렬 배치 처리로 대기 시간 단축
```
### 6.3 메모리/비용 관리
- **LLM 캐싱**: OntoCast의 기존 Cacher 활용
- **BudgetTracker**: 월간/일일 한도 + 자동 차단
- **Worker 풀**: Arq/Celery로 병렬 처리 (느린 OntoCast는 background로)
---
## 7. 이전 설계와의 차이점 요약
| 항목 | 기존 설계 | 수정 설계 |
|---|---|---|
| **Phase 0** | OntoCast 전체 파이프라인 | Fast JSON Extraction |
| **초기 결과** | RDF/OWL | JSON (후보) |
| **중간 단계** | RDF → Fuseki 직접 저장 | JSON → 사람 검수 → 선택적 OntoCast |
| **OntoCast 역할** | 입력 처리의 중심 | 정밀 RDF 갱신의 선택지 |
| **처리 속도** | 느림 (전체 파이프라인) | 빠름 (Phase 0~2) |
| **실패 시 영향** | 전체 job 실패 | Fast Pipeline은 계속 동작 |
| **테스트 용이성** | 어려움 (RDF 중심) | 쉬움 (JSON 중간결과 명확) |
| **대량 처리** | 부적합 | 적합 (비동기, 배치) |
---
## 6. 상용 제품 수준 기능 명세 (Functional Spec)
기능 ID 체계: `[영역코드]-[순번]`. 영역코드: PROJ, ACQ, ONT, REV, SRCH, OPS, GOV.
### 6.1 프로젝트/멀티테넌트 관리 (PROJ)
| ID | 기능명 | 설명 | 우선순위 |
|---|---|---|---|
| PROJ-001 | 온톨로지 프로젝트 생성 | 이름, 도메인, 기본 언어, 기본 타입 팩, Neo4j DB/Fuseki dataset 매핑 | P0 |
| PROJ-002 | 멤버/권한 관리 | Owner/Admin/Editor/Reviewer/Viewer 역할 | P0 |
| PROJ-003 | LLM Profile 선택 | provider/model/api_key/temperature/max_tokens (Neo4j GraphRAG `LLMConfig` 차용) | P0 |
| PROJ-004 | Embedding Profile 선택 | provider/model/dimension (cohere/openai/sentence-transformers) | P0 |
| PROJ-005 | 비용 예산 설정 | LLM call/token/검색 호출 일일·월간 한도 (OntoCast BudgetTracker 확장) | P1 |
| PROJ-006 | 프로젝트 fork/복제 | 기존 프로젝트의 스키마/설정만 복제 | P2 |
| PROJ-007 | 프로젝트 export/import | 전체 RDF + 설정을 zip으로 export, import | P1 |
### 6.2 데이터 수집 (ACQ)
| ID | 기능명 | 사용 컴포넌트 | 우선순위 |
|---|---|---|---|
| ACQ-001 | 단일 URL scrape | Crawl4AI `fast_static` + Trafilatura | P0 |
| ACQ-002 | 동적 페이지 scrape | Crawl4AI `dynamic_page` | P0 |
| ACQ-003 | 사이트 deep crawl | Crawl4AI `deep_discovery` (BFS/DFS/Best-First) | P1 |
| ACQ-004 | sitemap/feed 발견 | Trafilatura `sitemap_search`, `find_feed_urls` | P1 |
| ACQ-005 | URL seeding 미리보기 | Crawl4AI `AsyncUrlSeeder` | P1 |
| ACQ-006 | 파일 업로드 (PDF/DOCX/MD) | Lightweight Extractor + docling (선택적 OntoCast 후처리 가능) | P0 |
| ACQ-007 | 배치 URL import (CSV) | URL 목록 업로드 → 큐잉 | P1 |
| ACQ-008 | 중복 문서 dedup | Trafilatura SimHash fingerprint | P0 |
| ACQ-009 | 도메인 allow/block list | 프로젝트별 정책 DB | P0 |
| ACQ-010 | robots.txt 준수 모드 | strict/respect/ignore (감사 로그 필수) | P0 |
| ACQ-011 | 변경 감지 (changeTracking) | 주기 재크롤 + fingerprint 비교 | P2 |
| ACQ-012 | screenshot/PDF archive | Crawl4AI `full_capture` Profile | P2 |
### 6.3 온톨로지 구축 (ONT)
| ID | 기능명 | 사용 컴포넌트 | 우선순위 |
|---|---|---|---|
| ONT-001 | 수동 스키마 작성 | Neo4j GraphRAG `GraphSchema` 모델 그대로 사용 + UI | P0 |
| ONT-002 | 자동 스키마 추출 | Neo4j GraphRAG `SchemaFromTextExtractor` 기반 + 선택적 OntoCast 보조 | P0 |
| ONT-003 | 자유 추출 | OntoCast `schema="FREE"` 모드 | P1 |
| ONT-004 | 온톨로지 증분 갱신 | OntoCast `GraphUpdate` SPARQL (핵심 차별 기능) | P0 |
| ONT-005 | 온톨로지 비평/재시도 | OntoCast Critic loop + Guardrails | P0 |
| ONT-006 | 사실(facts) 추출 | OntoCast `RENDER_FACTS` + Guardrails | P0 |
| ONT-007 | Entity Aggregation | OntoCast `tool/agg/` (embedding clustering + URI 정규화 + owl:sameAs) | P0 |
| ONT-008 | Entity Resolver (사후) | Neo4j GraphRAG `FuzzyMatchResolver` + 검수 큐 | P1 |
| ONT-009 | 스키마 버전 관리 | OntoCast `GraphVersionManager` + draft/published/deprecated | P1 |
| ONT-010 | 스키마 diff 뷰어 | 버전 간 class/property/pattern 변경 | P1 |
| ONT-011 | 스키마 마이그레이션 | rename/merge/split + facts auto-migration | P2 |
| ONT-012 | SHACL/OWL 검증 | rdflib + owlready2 | P2 |
| ONT-013 | 다국어 label 관리 | rdfs:label + lang tag (ko/en/...) | P1 |
### 6.4 검수/승인 (REV)
| ID | 기능명 | 설명 | 우선순위 |
|---|---|---|---|
| REV-001 | 추출 후보 큐 | Renderer 결과를 Fuseki commit 전 검토용으로 저장 | P0 |
| REV-002 | Evidence 하이라이트 | entity/relation → `FROM_CHUNK` → 원문 텍스트 표시 (Neo4j GraphRAG lexical graph) | P0 |
| REV-003 | 단위 승인/반려 | node/relationship/property 단위 | P0 |
| REV-004 | Pruned 후보 검토 | Neo4j GraphRAG `GraphPruning`에서 제거된 후보를 schema 후보로 제안 | P1 |
| REV-005 | Merge 후보 검토 | Resolver 후보 그룹의 시각화 + 승인/반려 | P1 |
| REV-006 | 일괄 승인 정책 | confidence ≥ X + source 신뢰도 ≥ Y → 자동 승인 | P1 |
| REV-007 | 변경 이력 (audit) | 누가/언제/무엇을/왜 변경 (RDF reification 또는 별도 audit log) | P0 |
| REV-008 | 롤백 | 특정 시점의 RDF graph로 복원 | P2 |
### 6.5 검색/RAG (SRCH)
| ID | 기능명 | 사용 컴포넌트 | 우선순위 |
|---|---|---|---|
| SRCH-001 | Vector 검색 | Neo4j GraphRAG `VectorRetriever` | P0 |
| SRCH-002 | Hybrid 검색 (vector+fulltext) | `HybridRetriever` | P0 |
| SRCH-003 | Graph 확장 검색 | `VectorCypherRetriever` (chunk → entity → neighbor) | P0 |
| SRCH-004 | Text2Cypher (NL → Cypher) | `Text2CypherRetriever` (read-only) | P1 |
| SRCH-005 | SPARQL 직접 질의 | Fuseki SPARQL endpoint (관리자 권한) | P1 |
| SRCH-006 | GraphRAG QA | `GraphRAG` + evidence URL 포함 | P0 |
| SRCH-007 | Faceted 탐색 | entity type/predicate별 filter | P1 |
| SRCH-008 | Entity 상세 페이지 | 모든 property + 인입/인출 relation + evidence | P0 |
| SRCH-009 | Subgraph 시각화 | Neo4j Browser embed 또는 Cytoscape.js | P1 |
| SRCH-010 | 저장된 질의 (saved query) | 즐겨찾기 + 알림 | P2 |
### 6.6 운영/관측 (OPS)
| ID | 기능명 | 설명 | 우선순위 |
|---|---|---|---|
| OPS-001 | Job Queue/Worker | 모든 비동기 작업의 큐잉/재시도/취소 | P0 |
| OPS-002 | Job 진행률 스트리밍 | WebSocket/SSE로 실시간 진행 | P0 |
| OPS-003 | BudgetTracker | LLM call/token/검색/크롤 호출 비용 추적 | P0 |
| OPS-004 | LLM Response Cache | OntoCast `Cacher` 그대로 사용 | P0 |
| OPS-005 | Prometheus metrics | Crawl4AI 분석 §16.4 패턴 | P1 |
| OPS-006 | 감사 로그 | 모든 RDF 변경 + 사용자 액션 | P0 |
| OPS-007 | 에러 알림 | webhook + email + slack | P1 |
| OPS-008 | 백업/복구 | Fuseki + Neo4j + Postgres 일관성 있는 백업 | P1 |
| OPS-009 | 멀티 환경 | dev/staging/prod 분리 | P0 |
### 6.7 거버넌스/보안 (GOV)
| ID | 기능명 | 설명 | 우선순위 |
|---|---|---|---|
| GOV-001 | 인증 (OAuth2/OIDC) | Google/GitHub/Azure AD | P0 |
| GOV-002 | RBAC | 프로젝트별 역할 (PROJ-002) | P0 |
| GOV-003 | API Key | 외부 시스템 연동용 | P0 |
| GOV-004 | Rate Limiting | 사용자별/IP별 | P0 |
| GOV-005 | Text2Cypher 샌드박스 | read-only + timeout + result limit + allowlist | P0 |
| GOV-006 | 비밀(secret) vault | LLM API key 암호화 저장 | P0 |
| GOV-007 | 데이터 보존 정책 | raw HTML/PDF 보존 기간 + zero-retention 모드 | P1 |
| GOV-008 | 라이선스/출처 표기 | source별 license metadata 저장 + 결과에 노출 | P1 |
---
## 7. 데이터 모델 표준 (Canonical Data Models)
본 절은 5개 소스를 잇기 위한 공통 데이터 계약이다. 모든 어댑터는 이 모델로 변환한다.
### 7.1 ContentUnit (문서 청크의 표준 표현)
```python
class ContentUnit(BaseModel):
id: str # UUID
project_id: str
source_id: str
source_url: str | None # canonical URL (Trafilatura)
file_path: str | None
document_type: Literal["html","pdf","markdown","docx","inline_text"]
text: str # 정제된 본문
body_xml: bytes | None # Trafilatura Document.body (lxml serialized)
markdown: str | None # Crawl4AI markdown 또는 변환본
chunk_index: int
total_chunks: int
title: str | None
author: str | None
publish_date: str | None # ISO-8601
language: str | None
sitename: str | None
fingerprint: str | None # Trafilatura SimHash
content_hash: str # SHA-256 of text
metadata: dict # raw provider metadata
retrieved_at: str # ISO-8601
extracted_by: str # "crawl4ai+trafilatura"
```
### 7.2 OntologyExtractionResult (Guardrails 검증 대상)
Guardrails §15.5 그대로 채택. 위 §5 Phase 3 참조.
### 7.3 GraphUpdate (OntoCast 그대로)
OntoCast `sparql_models.GraphUpdate`를 그대로 사용. 변경 금지.
### 7.4 Job
```python
class Job(BaseModel):
id: str
project_id: str
type: Literal["scrape","crawl","extract","project","resolve","maintenance"]
status: Literal["queued","running","paused","completed","failed","cancelled"]
progress: float # 0.0 ~ 1.0
input: dict # 작업 입력 (URL, options, etc.)
output: dict | None # 결과 요약
started_at: str | None
finished_at: str | None
budget: BudgetTracker # OntoCast 그대로
error: str | None
audit_log_id: str
```
### 7.5 ReviewItem
```python
class ReviewItem(BaseModel):
id: str
project_id: str
job_id: str
target_type: Literal["entity","relation","property","merge_group","prune_candidate"]
target_data: dict # 후보 데이터
evidence: list[dict] # chunk_id + text span
confidence: float
source_trust: float
status: Literal["pending","approved","rejected","auto_approved"]
decided_by: str | None # user_id
decided_at: str | None
reason: str | None
```
---
## 8. API 표준 (RESTful, 일부 WebSocket)
전체는 OpenAPI 3.1 spec으로 별도 관리. 여기서는 핵심 endpoint만 명시.
| Method | Path | 설명 | 인용 근거 |
|---|---|---|---|
| POST | `/projects` | 프로젝트 생성 | PROJ-001 |
| GET | `/projects/{id}` | 프로젝트 조회 | - |
| POST | `/projects/{id}/sources` | 데이터 소스 등록 | ACQ |
| POST | `/projects/{id}/sources/{sid}/scrape` | 단일 URL scrape | ACQ-001 |
| POST | `/projects/{id}/sources/{sid}/crawl` | deep crawl | ACQ-003 |
| POST | `/projects/{id}/sources/{sid}/seed` | URL seeding preview | ACQ-005 |
| POST | `/projects/{id}/upload` | 파일 업로드 | ACQ-006 |
| GET | `/projects/{id}/schemas` | 스키마 목록 | ONT-001 |
| POST | `/projects/{id}/schemas` | 스키마 작성 | ONT-001 |
| POST | `/projects/{id}/schemas/extract` | 자동 스키마 추출 | ONT-002 |
| POST | `/projects/{id}/schemas/{sid}/publish` | 스키마 발행 | ONT-009 |
| POST | `/projects/{id}/process` | 문서 처리 (전체 OntoCast 워크플로우) | OntoCast §13.3 |
| GET | `/jobs/{job_id}` | Job 조회 | OPS-001 |
| GET | `/jobs/{job_id}/progress` (WS) | 진행률 스트리밍 | OPS-002 |
| POST | `/jobs/{job_id}/cancel` | Job 취소 | OPS-001 |
| GET | `/projects/{id}/review` | 검수 큐 | REV-001 |
| POST | `/projects/{id}/review/{rid}/approve` | 승인 | REV-003 |
| POST | `/projects/{id}/review/{rid}/reject` | 반려 | REV-003 |
| POST | `/projects/{id}/search/vector` | Vector 검색 | SRCH-001 |
| POST | `/projects/{id}/search/hybrid` | Hybrid 검색 | SRCH-002 |
| POST | `/projects/{id}/search/text2cypher` | NL → Cypher | SRCH-004 |
| POST | `/projects/{id}/search/graphrag` | QA | SRCH-006 |
| GET | `/projects/{id}/entities/{eid}` | Entity 상세 | SRCH-008 |
| POST | `/projects/{id}/maintenance/run` | 유지보수 루프 | Phase 5 |
---
## 9. 어떤 코드를 어디서 가져오는가 (Module Map)
각 5개 소스에서 가져올 모듈을 정확히 명시. **"그대로"=수정 금지, "어댑터"=얇은 래퍼만 작성**.
### 9.1 OntoCast (Optional Precision RDF/OWL Engine)
| 가져올 모듈 | 방식 | 수정 사항 |
|---|---|---|
| `ontocast/onto/state.py` (AgentState) | 그대로 | - |
| `ontocast/onto/unit_states.py` | 그대로 | - |
| `ontocast/onto/sparql_models.py` (GraphUpdate) | 그대로 | - |
| `ontocast/onto/rdfgraph.py` | 그대로 | - |
| `ontocast/onto/ontology.py` | 그대로 | - |
| `ontocast/stategraph/` | 그대로 | Phase 5에서 노드 추가만 |
| `ontocast/agent/render_*.py` | 그대로 | - |
| `ontocast/agent/criticise_*.py` | 그대로 | - |
| `ontocast/agent/select_ontology.py` | **버그 수정** | None index 불일치 (분석 §13.1) |
| `ontocast/agent/convert_document.py` | **확장** | 다중 파일 처리 (분석 §13.1) |
| `ontocast/tool/agg/` | 그대로 | - |
| `ontocast/tool/triple_manager/` | 그대로 | - |
| `ontocast/tool/llm.py` | **래핑** | Guardrails Guard 통과 (Phase 3) |
| `ontocast/tool/cache.py` | 그대로 | - |
| `ontocast/toolbox.py` | 그대로 | - |
| `ontocast/cli/serve.py` (Robyn) | **재작성** | FastAPI로 (Phase 0) |
### 9.2 Trafilatura
| 가져올 함수 | 방식 |
|---|---|
| `bare_extraction(output_format="python")` | 그대로 import |
| `extract_metadata` | 그대로 import |
| `sitemap_search` | 그대로 import |
| `find_feed_urls` | 그대로 import |
| `content_fingerprint`, `Simhash` | 그대로 import |
| `trafilatura.meta.reset_caches` | 그대로 import (배치 종료 시 호출) |
신규 어댑터: `platform/core/extractors/web_extractor.py` (§17 Trafilatura 분석의 `extract_for_ontology` 그대로)
### 9.3 Crawl4AI
| 가져올 클래스 | 방식 |
|---|---|
| `AsyncWebCrawler` | 그대로 |
| `BrowserConfig`, `CrawlerRunConfig`, `CacheMode` | 그대로 |
| `CrawlResult` | 그대로 |
| `LLMExtractionStrategy`, `JsonCssExtractionStrategy` | 그대로 |
| `BFSDeepCrawlStrategy`, `BestFirstCrawlingStrategy` | 그대로 |
| `AsyncUrlSeeder`, `SeedingConfig` | 그대로 |
| `MemoryAdaptiveDispatcher` | 그대로 |
| Docker FastAPI 서버 코드 | **사용 안 함** (자체 FastAPI 사용) |
신규 어댑터: `platform/core/crawler/crawl4ai_adapter.py` (Crawl4AI 분석 §21.1의 권장 계층 구조)
### 9.4 Guardrails
| 가져올 모듈 | 방식 |
|---|---|
| `guardrails.Guard.for_pydantic` | 그대로 |
| `guardrails.AsyncGuard` | 그대로 |
| `guardrails.classes.validation_outcome.ValidationOutcome` | 그대로 |
| `guardrails.actions.*` | 그대로 |
| `guardrails.types.on_fail.OnFailAction` | 그대로 |
| `guardrails.validator_base.Validator` (커스텀 validator 작성용 base) | 그대로 |
| `guardrails.hub.*` | **사용 안 함** (외부 통신 차단) |
| `guardrails.telemetry.*` | **사용 안 함** |
| `guardrails.cli.*` | **사용 안 함** |
신규 작성: `platform/core/validation/validators.py` (Guardrails 분석 §15.4의 12개 validator)
### 9.5 Neo4j GraphRAG
| 가져올 클래스 | 방식 |
|---|---|
| `GraphSchema`, `NodeType`, `RelationshipType`, `Pattern`, `ConstraintType` | 그대로 |
| `SimpleKGPipeline` | **사용 안 함** (OntoCast 워크플로우가 우선) |
| `LLMEntityRelationExtractor` | **사용 안 함** (OntoCast Renderer가 우선) |
| `Neo4jWriter`, `Neo4jGraph`, `Neo4jNode`, `Neo4jRelationship` | 그대로 (Projection 용) |
| `LexicalGraphBuilder` | 그대로 |
| `SinglePropertyExactMatchResolver`, `FuzzyMatchResolver` | 그대로 |
| `VectorRetriever`, `HybridRetriever`, `VectorCypherRetriever` | 그대로 |
| `Text2CypherRetriever` | 그대로 (read-only 강제) |
| `GraphRAG` | 그대로 |
| `embeddings/*`, `llm/*` | 그대로 (선택적) |
신규 어댑터: `platform/core/projection/rdf_to_neo4j.py` (Fuseki RDF → `Neo4jGraph` 변환)
### 9.6 Knowledge Agent (패턴/프롬프트만)
| 가져올 자산 | 방식 |
|---|---|
| `prompts/analyst_prompt.txt` | **수정 후 사용** (OntoCast `OntologyExtractionResult` 스키마에 맞춤) |
| `prompts/planner_prompt.txt` | 수정 후 사용 |
| `prompts/refiner_prompt.txt` | 수정 후 사용 |
| `prompts/summarizer_prompt.txt` | 수정 후 사용 |
| `prompts/search_ranker_prompt.txt` | 수정 후 사용 |
| `prompts/ingester_prompt.txt` | 수정 후 사용 |
| `lightrag/prompt.py` (엔티티/관계 추출 프롬프트) | **참고만** (OntoCast Renderer가 이미 있음) |
| 코드 전체 | **사용 안 함** (다수 버그 — 분석 §9.1) |
---
## 10. 예상 리스크와 대응
| 리스크 | 영향 | 대응 |
|---|---|---|
| OntoCast `select_ontology.py` 버그 | 워크플로우 실패 | Phase 0에서 즉시 수정 |
| Crawl4AI/Playwright 메모리 누수 | 운영 장애 | `max_pages_before_recycle` 설정 + browser pool monitor |
| Trafilatura 전역 LRU cache 충돌 | 다중 테넌트에서 결과 오염 | 작업 단위 `reset_caches()` |
| Guardrails Hub 외부 통신 | 보안/네트워크 의존 | Hub/telemetry 비활성화 환경변수 강제 |
| Neo4j GraphRAG `experimental` API 변경 | upstream 호환성 | 버전 고정 (`==1.16.0`) + 우리 코드는 어댑터로만 접근 |
| Fuseki ↔ Neo4j 동기화 지연 | 검색 결과와 진실 불일치 | "Last sync at" 표시 + 강제 동기화 API |
| LLM 비용 폭주 | 운영 비용 | OPS-003 BudgetTracker 한도 + 자동 차단 |
| Text2Cypher 인젝션 | 보안 | read-only 강제 + allowlist + timeout (GOV-005) |
| robots.txt 위반 | 법적 리스크 | ACQ-010 strict 모드 기본 + 감사 로그 |
| 라이선스 (특히 AGPL 회피) | 배포 제약 | Apache/MIT만 채택. Firecrawl 제외 결정 근거 |
---
## 11. 기술 스택 요약 (전체)
| 영역 | 선택 |
|---|---|
| 언어 | Python 3.12+ (OntoCast 요구사항이 가장 높음) |
| API | FastAPI (모든 통합 소스가 OpenAPI 친화) |
| LangGraph 워크플로우 | OntoCast 기존 사용 |
| Validation | Pydantic v2 + Guardrails |
| 비동기 | asyncio (Crawl4AI/Trafilatura 모두 지원) |
| Job Queue | Arq (Redis 기반, 가벼움) 또는 Celery (대규모) |
| 메타데이터 DB | PostgreSQL 16+ |
| Canonical RDF Store | Apache Jena Fuseki 5+ |
| Property Graph | Neo4j 5.18.1+ with APOC core |
| Object Storage | S3 호환 (MinIO 로컬, AWS S3 운영) |
| 캐시 | Redis 7+ |
| 관측 | OpenTelemetry + Prometheus + Grafana |
| 컨테이너 | Docker Compose (개발) / Kubernetes (운영) |
| 인증 | Authlib + OAuth2/OIDC |
| 프론트엔드 | (자유 선택, 권장 Next.js 14 + shadcn/ui) |
---
## 12. 작업 단위 분해 (PR 단위)
각 Phase 내부에서 PR 단위로 쪼갠 예시.
### Phase 0: Fast Extraction MVP
- **0.1** Trafilatura 의존성 + `web_extractor.py` (본문/메타 추출)
- **0.2** Crawl4AI 최소 adapter (정적 수집 우선)
- **0.3** `lightweight_extractor.py` + 프롬프트 (JSON 출력)
- **0.4** CandidateStore 테이블 정의 (PostgreSQL/SQLite)
- **0.5** FastAPI `/extract/url`, `/extract/file`, `/candidates` endpoint
- **0.6** 단일 URL 테스트 (10~30초 내 결과)
- **0.7** Acceptance Gate 0 확인
### Phase 1: Fast Pipeline 품질 개선
- **1.1** Pydantic 스키마 정의 (CandidateEntity/Relation)
- **1.2** 무의미한 후보 필터링 로직
- **1.3** confidence/evidence 검증
- **1.4** entity/relation endpoint 매칭 검사
- **1.5** 도메인별 type/predicate 설정 지원
- **1.6** 한국어 페이지 3종 테스트 (뉴스/블로그/쇼핑)
- **1.7** Acceptance Gate 1 확인
### Phase 2: Guardrails 검증 강화
- **2.1** `guardrails-ai` 의존성 + 환경변수 설정 (Hub/telemetry 비활성화)
- **2.2** Validator 작성 6종 (EntityIdFormat, UniqueEntityId, RelationEndpoint, ConfidenceRange, Evidence, NoSelfRelation)
- **2.3** Guardrails Guard 통합 (Lightweight Extractor 후 검증)
- **2.4** 검증 실패 후보 → review queue로 이동
- **2.5** Guardrails 로그 + 감사 기록
- **2.6** Acceptance Gate 2 확인
### Phase 3: Review & Approval 시스템
- **3.1** Review UI (후보 목록 + evidence 하이라이트)
- **3.2** 승인/반려/수정 버튼
- **3.3** ReviewStatus 테이블 관리
- **3.4** 자동 승인 정책 정의 (confidence ≥ 0.95)
- **3.5** 승인 이력 저장 (감사 로그)
- **3.6** Acceptance Gate 3 확인
### Phase 4: OntoCast 선택적 통합 ⭐
- **4.1** `ontocast_adapter.py` (Candidate JSON → OntoCast 입력 포맷)
- **4.2** batch 단위 호출 (10~50개)
- **4.3** OntoCast 처리 실패 → review queue (보존, 손실 없음)
- **4.4** GraphUpdate → Fuseki commit (SPARQL UPDATE)
- **4.5** BudgetTracker 연동 (OntoCast 호출 비용/시간 기록)
- **4.6** Fast Pipeline 독립 검증 (OntoCast 없이도 동작)
- **4.7** Acceptance Gate 4 확인
### Phase 5: Neo4j GraphRAG & 검색
- **5.1** `neo4j-graphrag==1.16.0` + Docker compose
- **5.2** `rdf_to_neo4j.py` (Fuseki RDF → Neo4j projection)
- **5.3** Embedding + Vector index
- **5.4** `/search/vector`, `/search/hybrid` endpoint
- **5.5** `/search/graphrag` (QA + evidence)
- **5.6** Text2Cypher 보안 (read-only)
- **5.7** Fuseki commit hook → Neo4j async 동기화
- **5.8** Acceptance Gate 5 확인
---
## 13. 다른 AI 에이전트를 위한 체크리스트
본 설계서를 받은 AI 에이전트가 작업을 시작하기 전 확인할 항목.
### 필수 확인
- [ ] 본 문서의 §1~§12를 모두 읽었는가?
- [ ] **설계 변경 배경** (§1.1)을 이해했는가? — OntoCast를 Phase 0 Base에서 선택적 엔진으로 변경한 이유
- [ ] 작업할 Phase의 **Acceptance Gate를 명확히 이해**했는가?
- [ ] **Fast Path와 Precision Path의 차이**를 이해했는가?
### Phase별 확인
#### Phase 0: Fast Extraction MVP
- [ ] OntoCast를 사용하지 않는다는 것을 확인했는가?
- [ ] RDF/OWL 변환을 하지 않고 JSON만 생성하는 것을 확인했는가?
- [ ] 처리 속도 목표 (10~30초)를 이해했는가?
#### Phase 1~3: Fast Pipeline 안정화
- [ ] 이 단계에서도 OntoCast는 사용하지 않는가?
- [ ] JSON 검증 및 사람 검수 과정을 이해했는가?
#### Phase 4: OntoCast 통합
- [ ] OntoCast는 **batch 후보 단위**로 호출한다는 것을 확인했는가?
- [ ] OntoCast 처리 실패 시에도 Fast Pipeline이 계속 동작한다는 것을 확인했는가?
- [ ] GraphUpdate → Fuseki commit이 SPARQL 기반임을 확인했는가?
#### Phase 5: 검색/RAG
- [ ] Neo4j는 Fuseki의 projection이라는 것을 확인했는가?
- [ ] Text2Cypher는 read-only만 허용한다는 것을 확인했는가?
### 일반 원칙
- [ ] "그대로 사용" 모듈을 수정하려 하고 있지 않은가?
- [ ] "제외(Excluded)" 프로젝트의 코드를 가져오려 하고 있지 않은가? (Firecrawl, OpenDeepResearcher)
- [ ] PR 설명에 어느 분석 자료의 어느 절을 근거로 했는지 명시할 준비가 되었는가?
- [ ] 이전 Phase의 회귀 테스트가 통과하는지 확인할 계획이 있는가?
- [ ] License 고지(Apache 2.0, MIT 등)가 포함되는가?
---
## 14. 마지막 한 마디
### 이번 설계 수정의 핵심
기존 설계는 이론적으로 완벽했지만, **실제 구현 결과는 다음 문제가 있었다**:
```
OntoCast 중심 아키텍처
→ 처리 속도 느림 (초기 테스트부터 지연)
→ 중간 결과 확인 어려움 (RDF 중심)
→ 대량 처리에 부적합 (비용 폭주)
→ 버그 가능성 높음 (무거운 파이프라인)
```
따라서 설계를 다음과 같이 수정했다:
```
Fast Path (기본) Precision Path (선택)
가벼운 JSON 추출 → 승인된 후보만
Pydantic 검증 → OntoCast 정밀화
사람 검수 → RDF/OWL 저장
```
### 최종 구조 한 문장
> **OntoCast는 입력 처리 엔진이 아니라, 검증된 후보를 RDF로 정밀 반영하는 고급 엔진이다.**
### 이점
| 기존 | 수정 |
|---|---|
| ❌ 모든 문서 → OntoCast | ✅ 검증된 후보만 → OntoCast |
| ❌ 초기부터 느림 | ✅ Fast Path로 빠른 피드백 |
| ❌ 중간 과정 불명확 | ✅ JSON 후보로 명확한 단계 |
| ❌ OntoCast 실패 = 전체 실패 | ✅ Fast Pipeline은 독립 동작 |
| ❌ 대량 처리 어려움 | ✅ batch 단위 + 비동기 처리 |
### 작업 방향
다음 Phase부터 이 수정된 설계를 따른다. 특히:
1. **Phase 0에서 OntoCast를 사용하지 않는다.**
2. **JSON 기반 후보 추출에 집중한다.**
3. **검증된 후보만 OntoCast에 전달한다.**
4. **각 Phase는 이전 Phase 없이 독립적으로 테스트 가능해야 한다.**
이 원칙을 지키면, 더 안정적이고, 빠르고, 확장 가능한 온톨로지 구축 플랫폼을 만들 수 있다.