1182 lines
54 KiB
Markdown
1182 lines
54 KiB
Markdown
# 범용 온톨로지 구축 플랫폼 통합 설계서
|
||
|
||
작성일: 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 없이 독립적으로 테스트 가능해야 한다.**
|
||
|
||
이 원칙을 지키면, 더 안정적이고, 빠르고, 확장 가능한 온톨로지 구축 플랫폼을 만들 수 있다.
|