Files
AI/오픈소스분석자료/Neo4j_GraphRAG_분석_및_기능명세.md

1079 lines
39 KiB
Markdown
Raw Permalink Normal View History

2026-05-13 19:57:34 +09:00
# Neo4j GraphRAG Python 분석 및 기능명세
분석 대상: `C:\Users\lasta\MyProject\AI\참고\neo4j-graphrag-python-main`
분석일: 2026-05-13
프로젝트 버전: `neo4j-graphrag 1.16.0`
라이선스: Apache License 2.0 계열(`LICENSE.APACHE2.txt`, `LICENSE.txt`)
목표: 범용 온톨로지 구축 플랫폼의 기본 소스로 거의 변형 없이 재사용 가능한 기능과, 플랫폼 레이어에서 감싸야 할 기능을 식별한다.
## 1. 총평
`neo4j-graphrag-python`은 Neo4j가 공식 제공하는 GraphRAG Python 패키지이며, 단순 질의응답 RAG보다 “비정형 문서 → 스키마/온톨로지 후보 → 엔티티/관계 추출 → 그래프 정제 → Neo4j 저장 → 검색/질의응답” 흐름에 더 강하다.
우리 프로젝트가 지향하는 “범용 온톨로지 구축 플랫폼” 관점에서는 다음 모듈이 가장 중요하다.
| 영역 | 재사용 판단 | 핵심 소스 |
|---|---:|---|
| KG 구축 파이프라인 | 높음 | `experimental/pipeline/kg_builder.py`, `config/template_pipeline/simple_kg_builder.py` |
| 컴포넌트형 파이프라인 엔진 | 높음 | `experimental/pipeline/pipeline.py`, `component.py`, `orchestrator.py` |
| 온톨로지/그래프 스키마 모델 | 매우 높음 | `experimental/components/schema.py` |
| 스키마 자동 추출 | 높음 | `experimental/components/schema.py`, `graph_schema_extraction.py` |
| 엔티티/관계 추출 | 높음 | `experimental/components/entity_relation_extractor.py` |
| 그래프 정제/스키마 준수 | 높음 | `experimental/components/graph_pruning.py` |
| Neo4j 저장 | 높음 | `experimental/components/kg_writer.py`, `neo4j_queries.py` |
| 엔티티 중복 해소 | 중간~높음 | `experimental/components/resolver.py` |
| 검색/RAG | 높음 | `retrievers/*`, `generation/graphrag.py` |
| LLM/임베딩 어댑터 | 높음 | `llm/*`, `embeddings/*` |
| 문서 로더 | 중간 | `experimental/components/data_loader.py` |
| 외부 벡터DB 연동 | 선택 | `retrievers/external/*` |
단, `experimental` 네임스페이스의 KG 구축 기능은 공식 문서상 API 변경 가능성이 있는 실험 기능이다. “그대로 복사”보다는 패키지 의존성으로 고정 버전을 사용하고, 우리 플랫폼의 안정 API를 별도 래퍼로 제공하는 방식이 안전하다.
## 2. 프로젝트 구조
```text
src/neo4j_graphrag/
embeddings/ # OpenAI, Azure OpenAI, Ollama, VertexAI, Cohere, Bedrock, Mistral, SentenceTransformer 임베딩
llm/ # OpenAI, Azure, Ollama, VertexAI, Anthropic, Cohere, Bedrock, Mistral LLM
retrievers/ # Vector, Hybrid, Text2Cypher, Tools, Vector+Cypher 검색기
generation/ # GraphRAG, 프롬프트 템플릿, RAG 결과 타입
experimental/
components/ # KG 구축 컴포넌트: loader, splitter, schema, extractor, pruner, writer, resolver
pipeline/ # 비동기 DAG 파이프라인 엔진, 설정 파일 실행기
indexes.py # Neo4j 벡터/풀텍스트 인덱스 생성, 벡터 upsert
schema.py # 기존 Neo4j DB 스키마 조회/포맷팅/Text2Cypher용 스키마 생성
filters.py # 검색 필터 DSL → Cypher 변환
message_history.py # InMemory/Neo4j 대화 기록
tool.py # LLM tool schema 추상화
```
테스트 구조는 `tests/unit`, `tests/e2e`가 분리되어 있고, Neo4j/Weaviate/Pinecone/Qdrant 연동 E2E가 있다. 핵심 동작은 테스트가 비교적 넓게 잡혀 있어 베이스 소스로 신뢰도가 높다.
## 3. 의존성 및 실행 조건
### 3.1 기본 요구사항
- Python: `>=3.10,<3.15`
- Neo4j Python driver: `neo4j>=5.17,<7`
- Pydantic v2
- `pypdf`, `fsspec`, `json-repair`, `pyyaml`, `numpy`, `scipy`, `tenacity`
### 3.2 Neo4j 요구사항
- Neo4j `>=5.18.1`
- Neo4j Aura `>=5.18.0`
- Neo4j `2026.01+`: `SEARCH` clause 기반 in-index filtering 지원
- KG writer 및 entity resolver 일부 기능은 APOC 필요
- README의 KG construction 예시는 APOC core 설치를 요구한다.
- `SinglePropertyExactMatchResolver`, similarity resolver는 `apoc.refactor.mergeNodes`를 사용한다.
### 3.3 선택 의존성
| Extra | 용도 |
|---|---|
| `openai` | OpenAI/Azure OpenAI LLM 및 임베딩 |
| `ollama` | 로컬 Ollama LLM/임베딩 |
| `google` | Vertex AI |
| `cohere` | Cohere |
| `anthropic` | Anthropic |
| `mistralai` | Mistral AI |
| `bedrock` | AWS Bedrock |
| `sentence-transformers` | 로컬 임베딩 |
| `experimental` | KG 구축 파이프라인, LlamaIndex/LangChain splitter, Parquet |
| `nlp` | spaCy resolver. Python 3.14에서는 미지원 |
| `fuzzy-matching` | RapidFuzz 기반 중복 해소 |
| `weaviate`, `pinecone`, `qdrant` | 외부 벡터DB retriever |
## 4. 핵심 아키텍처
### 4.1 전체 흐름
```mermaid
flowchart LR
A["문서/텍스트 입력"] --> B["DataLoader"]
B --> C["TextSplitter"]
C --> D["TextChunkEmbedder"]
C --> E["SchemaBuilder 또는 SchemaFromTextExtractor"]
E --> F["LLMEntityRelationExtractor"]
C --> F
F --> G["GraphPruning"]
G --> H["KGWriter: Neo4jWriter 또는 ParquetWriter"]
H --> I["EntityResolver"]
I --> J["Neo4j Knowledge Graph"]
J --> K["Retriever: Vector/Hybrid/Text2Cypher"]
K --> L["GraphRAG"]
```
### 4.2 설계 철학
이 프로젝트는 “하나의 거대한 KG builder”가 아니라 비동기 컴포넌트 DAG를 조립하는 방식이다.
- 각 컴포넌트는 `Component`를 상속한다.
- 컴포넌트의 `run`은 Pydantic `DataModel`을 반환한다.
- `Pipeline.connect()`로 이전 컴포넌트 출력 필드를 다음 컴포넌트 입력으로 매핑한다.
- `PipelineRunner`는 JSON/YAML 설정 파일로 파이프라인을 복원하고 실행한다.
- `SimpleKGPipeline`은 대표적인 템플릿 파이프라인이다.
우리 플랫폼에서는 이 구조를 그대로 “워크플로우 엔진”으로 활용할 수 있다. 별도 GUI나 API 서버에서는 `SimpleKGPipeline` 설정을 생성하고 실행 결과를 추적하는 레이어를 만들면 된다.
## 5. 데이터 모델 명세
### 5.1 문서 모델
`DocumentInfo`
| 필드 | 타입 | 설명 |
|---|---|---|
| `path` | `str` | 파일 경로 또는 inline text 식별자 |
| `metadata` | `dict[str,str] \| None` | 문서 메타데이터. Document 노드 property로 저장 |
| `uid` | `str` | UUID 기본 생성. Document id |
| `document_type` | `pdf`, `markdown`, `inline_text` | 문서 유형 |
`LoadedDocument`
| 필드 | 타입 | 설명 |
|---|---|---|
| `text` | `str` | 추출된 원문 |
| `document_info` | `DocumentInfo` | 문서 식별/메타데이터 |
### 5.2 텍스트 청크 모델
`TextChunk`
| 필드 | 타입 | 설명 |
|---|---|---|
| `text` | `str` | 청크 텍스트 |
| `index` | `int` | 원문 내 순서 |
| `metadata` | `dict[str,Any] \| None` | 청크 메타데이터. `embedding`이 있으면 별도 embedding property로 분리 |
| `uid` | `str` | UUID 기본 생성 |
`TextChunks`
| 필드 | 타입 | 설명 |
|---|---|---|
| `chunks` | `list[TextChunk]` | 청크 목록 |
### 5.3 그래프 모델
`Neo4jNode`
| 필드 | 타입 | 설명 |
|---|---|---|
| `id` | `str` | 내부 관계 연결용 id |
| `label` | `str` | Neo4j label |
| `properties` | `dict[str, PropertyValue]` | Neo4j property |
| `embedding_properties` | `dict[str, list[float]]` | 벡터 property |
`Neo4jRelationship`
| 필드 | 타입 | 설명 |
|---|---|---|
| `start_node_id` | `str` | 시작 노드 id |
| `end_node_id` | `str` | 끝 노드 id |
| `type` | `str` | relationship type |
| `properties` | `dict[str, PropertyValue]` | relationship property |
| `embedding_properties` | `dict[str, list[float]]` | relationship vector property |
`Neo4jGraph`
| 필드 | 타입 | 설명 |
|---|---|---|
| `nodes` | `list[Neo4jNode]` | 노드 목록 |
| `relationships` | `list[Neo4jRelationship]` | 관계 목록 |
### 5.4 Lexical graph 설정
`LexicalGraphConfig` 기본값:
| 설정 | 기본값 | 의미 |
|---|---|---|
| `document_node_label` | `Document` | 문서 노드 label |
| `chunk_node_label` | `Chunk` | 청크 노드 label |
| `chunk_to_document_relationship_type` | `FROM_DOCUMENT` | Chunk → Document |
| `next_chunk_relationship_type` | `NEXT_CHUNK` | Chunk → 다음 Chunk |
| `node_to_chunk_relationship_type` | `FROM_CHUNK` | Entity → Chunk |
| `chunk_id_property` | `id` | chunk id property |
| `chunk_index_property` | `index` | chunk 순서 property |
| `chunk_text_property` | `text` | chunk 본문 property |
| `chunk_embedding_property` | `embedding` | chunk embedding property |
온톨로지 플랫폼에서는 이 설정을 테넌트/프로젝트 단위로 고정하거나, 사용자가 “문서 그래프 모델”을 커스터마이즈할 수 있게 노출하면 된다.
## 6. 온톨로지/스키마 모델 명세
핵심 파일: `src/neo4j_graphrag/experimental/components/schema.py`
### 6.1 PropertyType
| 필드 | 타입 | 설명 |
|---|---|---|
| `name` | `str` | property 이름 |
| `type` | Neo4j property type literal | `STRING`, `INTEGER`, `FLOAT`, `BOOLEAN`, `DATE`, `LOCAL_DATETIME`, `POINT`, `LIST` 등 |
| `description` | `str` | LLM 추출 가이드 |
| `required` | `bool` | deprecated. 존재 제약은 `ConstraintType(EXISTENCE)` 권장 |
### 6.2 NodeType
| 필드 | 타입 | 설명 |
|---|---|---|
| `label` | `str` | 노드 label |
| `description` | `str` | 의미 설명 |
| `properties` | `list[PropertyType]` | 허용 property. 최소 1개 |
| `additional_properties` | `bool` | 스키마 외 property 허용 여부 |
특이 동작:
- 문자열 `"Person"`으로 입력하면 `{label:"Person", properties:[{name:"name", type:"STRING"}], additional_properties:true}`로 자동 변환된다.
- label이 `__`로 시작하거나 끝나면 내부 예약 label로 보고 거부한다.
### 6.3 RelationshipType
| 필드 | 타입 | 설명 |
|---|---|---|
| `label` | `str` | relationship type |
| `description` | `str` | 의미 설명 |
| `properties` | `list[PropertyType]` | 관계 property |
| `additional_properties` | `bool` | 스키마 외 property 허용 여부 |
문자열 `"WORKS_AT"` 입력도 허용된다.
### 6.4 ConstraintType
지원 제약:
| 타입 | 의미 |
|---|---|
| `UNIQUENESS` | 노드 property unique. 복합 가능 |
| `EXISTENCE` | 노드/관계 property 필수. 단일 property |
| `KEY` | Neo4j node key/relationship key. 필수 + unique. 복합 가능 |
범용 온톨로지 플랫폼에서는 이 모델을 “온톨로지 제약조건”의 기본 표현으로 재사용할 수 있다. 다만 OWL/RDFS의 class hierarchy, domain/range, cardinality, inverse property, equivalent class 같은 의미론적 제약은 별도 확장이 필요하다.
### 6.5 Pattern
`Pattern``(source node label, relationship label, target node label)` 구조를 표현한다. 예:
```python
("Person", "WORKS_AT", "Organization")
```
이것은 ontology의 relationship domain/range 후보로 직접 매핑 가능하다.
### 6.6 GraphSchema
`GraphSchema`는 다음 정보를 묶는다.
- `node_types`
- `relationship_types`
- `patterns`
- `constraints`
- `additional_node_types`
- `additional_relationship_types`
- `additional_patterns`
스키마가 제공되면 LLM 추출 프롬프트의 grounding 정보가 되고, 이후 `GraphPruning`이 이 스키마에 맞지 않는 노드/관계/property를 제거한다.
## 7. KG 구축 기능명세
### 7.1 SimpleKGPipeline
파일: `experimental/pipeline/kg_builder.py`
`SimpleKGPipeline`은 비정형 텍스트나 PDF/Markdown 파일에서 KG를 만들기 위한 고수준 API이다.
#### 생성자 입력
| 파라미터 | 필수 | 설명 |
|---|---:|---|
| `llm` | 예 | 엔티티/관계 추출용 LLM |
| `driver` | 예 | Neo4j driver |
| `embedder` | 예 | chunk embedding 생성기 |
| `schema` | 아니오 | `GraphSchema`, dict, `"FREE"`, `"EXTRACTED"`, `None` |
| `from_file` | 아니오 | `True`: file path 입력. `False`: text 입력 |
| `text_splitter` | 아니오 | 기본 `FixedSizeSplitter` |
| `file_loader` | 아니오 | 기본 extension 기반 PDF/Markdown loader |
| `kg_writer` | 아니오 | 기본 `Neo4jWriter` |
| `on_error` | 아니오 | `"IGNORE"` 또는 `"RAISE"` |
| `perform_entity_resolution` | 아니오 | 기본 `True` |
| `prompt_template` | 아니오 | 추출 프롬프트 템플릿 |
| `lexical_graph_config` | 아니오 | Document/Chunk 그래프 label/relationship 커스터마이즈 |
| `neo4j_database` | 아니오 | Neo4j database 이름 |
#### 실행 입력
`run_async(file_path=None, text=None, document_metadata=None)`
| 입력 | 조건 | 설명 |
|---|---|---|
| `file_path` | `from_file=True`일 때 필요 | PDF/Markdown 파일 경로 |
| `text` | `from_file=False`일 때 필요 | 직접 입력 텍스트 |
| `document_metadata` | 선택 | Document node property로 저장 |
#### 스키마 모드
| 모드 | 설정 | 동작 |
|---|---|---|
| 자동 추출 | `schema=None` 또는 `"EXTRACTED"` | 입력 텍스트에서 LLM으로 스키마를 한 번 추출한 뒤 전체 chunk 추출에 사용 |
| 자유 추출 | `schema="FREE"` 또는 empty schema | 스키마 없이 엔티티/관계 추출 |
| 고정 스키마 | dict 또는 `GraphSchema` | 사용자가 정의한 온톨로지 구조에 맞춰 추출 |
범용 온톨로지 플랫폼에서는 세 모드를 다음 UI/API로 제공하는 것이 적합하다.
- “자동 온톨로지 초안 생성”
- “스키마 없이 자유 그래프 생성”
- “승인된 온톨로지에 맞춰 인스턴스 추출”
### 7.2 DataLoader
파일: `experimental/components/data_loader.py`
제공 구현:
- `PdfLoader`: PDF 텍스트 추출
- `MarkdownLoader`: Markdown 텍스트 로드
- 내부 extension 기반 loader: `.pdf`, `.md`, `.markdown`
기능명세:
| 기능 | 입력 | 출력 | 비고 |
|---|---|---|---|
| PDF 로드 | `filepath`, `metadata` | `LoadedDocument` | `pypdf` 사용 |
| Markdown 로드 | `filepath`, `metadata` | `LoadedDocument` | plain text로 처리 |
| 문서 메타데이터 생성 | path, metadata | `DocumentInfo` | Document node에 연결 |
확장 필요:
- HTML, DOCX, PPTX, XLSX, CSV, 웹 크롤링 결과, API 문서 등 우리 프로젝트 입력 소스에 맞춘 loader 추가
- 이미 우리 프로젝트에 crawler가 있으므로 crawler output을 `LoadedDocument`로 변환하는 adapter 필요
### 7.3 TextSplitter
제공 구현:
- `FixedSizeSplitter`
- `LangChainTextSplitterAdapter`
- `LlamaIndexTextSplitterAdapter`
기능명세:
| 기능 | 입력 | 출력 | 비고 |
|---|---|---|---|
| 고정 길이 chunking | text, chunk_size, overlap | `TextChunks` | `approximate=True`이면 단어 중간 절단 회피 |
| LangChain splitter 감싸기 | LangChain splitter | `TextChunks` | 기존 생태계 활용 |
| LlamaIndex splitter 감싸기 | LlamaIndex splitter | `TextChunks` | 기존 생태계 활용 |
온톨로지 플랫폼에서는 도메인별 chunking 전략이 중요하다.
- 법령/규정: 조문 단위
- 논문: section/paragraph 단위
- 사내 문서: heading hierarchy 유지
- 웹 문서: URL, heading, DOM 경로 메타데이터 유지
따라서 기본 splitter는 재사용하되, “구조 보존 splitter”를 별도 컴포넌트로 추가하는 것이 좋다.
### 7.4 TextChunkEmbedder
파일: `experimental/components/embedder.py`
기능:
- `TextChunks`의 각 chunk text를 embedder로 임베딩한다.
- 임베딩을 chunk metadata의 `embedding`에 저장한다.
- `LexicalGraphBuilder``embedding` metadata를 chunk node의 `embedding_properties`로 분리한다.
재사용 판단: 높음. 단, 대량 문서 처리에서는 batch embedding, rate limit, retry, cache가 플랫폼 레이어에 필요하다.
### 7.5 SchemaBuilder / SchemaFromTextExtractor
파일: `experimental/components/schema.py`, `graph_schema_extraction.py`
기능:
- 수동 schema dict 또는 `GraphSchema`를 검증한다.
- 텍스트에서 자동으로 node type, relationship type, pattern, constraint 후보를 추출한다.
- OpenAI/VertexAI 등 structured output 지원 LLM에서는 JSON schema 기반 구조화 출력을 사용한다.
- 기존 Neo4j graph에서 schema를 읽어 schema 후보로 만들 수 있다.
기능명세:
| 기능 | 입력 | 출력 |
|---|---|---|
| 수동 스키마 검증 | node types, relationship types, patterns, constraints | `GraphSchema` |
| 자동 스키마 추출 | text/chunks, LLM, prompt | `GraphSchema` |
| 기존 graph 스키마 추출 | Neo4j driver | `GraphSchema` 후보 |
| schema visualization | `GraphSchema` | 시각화 graph |
우리 플랫폼 확장 포인트:
- 스키마 버전 관리
- 자동 추출 schema의 승인/반려 workflow
- label/property 표준화 규칙
- 한국어 label/영문 label alias 관리
- 온톨로지 class hierarchy 확장
### 7.6 LLMEntityRelationExtractor
파일: `experimental/components/entity_relation_extractor.py`
기능:
- 각 chunk에 대해 LLM으로 `Neo4jGraph(nodes, relationships)`를 추출한다.
- chunk별 node id에 chunk UUID prefix를 붙여 충돌을 방지한다.
- `create_lexical_graph=True`이면 Document/Chunk graph와 Entity → Chunk provenance 관계를 함께 생성한다.
- JSON repair를 사용해 깨진 JSON 응답을 복구한다.
- `on_error=IGNORE`이면 실패 chunk는 빈 graph로 처리한다.
- `on_error=RAISE`이면 LLM/JSON 오류를 예외로 올린다.
- `max_concurrency`로 LLM 호출 동시성을 제한한다.
- structured output 지원 LLM이면 `Neo4jGraph` Pydantic 모델을 response schema로 사용할 수 있다.
기능명세:
| 기능 | 입력 | 출력 | 중요 옵션 |
|---|---|---|---|
| chunk별 엔티티/관계 추출 | `TextChunks`, `GraphSchema`, examples | `Neo4jGraph` | `max_concurrency` |
| lexical graph 생성 | chunks, document_info | Document/Chunk 포함 graph | `create_lexical_graph` |
| 출처 연결 | extracted entity, chunk | `FROM_CHUNK` 관계 | provenance 핵심 |
| 오류 처리 | LLM JSON 오류 | 빈 graph 또는 예외 | `on_error` |
온톨로지 플랫폼에서 매우 중요한 특성:
- 모든 추출 엔티티가 chunk와 연결되므로 근거 추적이 가능하다.
- 추출 결과를 바로 DB에 쓰기 전에 `GraphPruning`으로 스키마 위반을 제거할 수 있다.
- 추출 결과를 사용자가 승인하는 “검수 큐”를 만들려면 `Neo4jWriter` 이전에 graph를 저장/표시하는 컴포넌트를 끼우면 된다.
### 7.7 GraphPruning
파일: `experimental/components/graph_pruning.py`
기능:
- 추출 graph가 `GraphSchema`를 준수하도록 노드/관계/property를 제거한다.
- lexical graph(Document/Chunk)는 별도로 보존한다.
- pruning 통계를 반환한다.
제거 사유:
| 사유 | 의미 |
|---|---|
| `NOT_IN_SCHEMA` | 스키마에 없는 node/relationship/property |
| `MISSING_REQUIRED_PROPERTY` | 필수 property 누락 |
| `NO_PROPERTY_LEFT` | 유효 property가 하나도 없음 |
| `INVALID_START_OR_END_NODE` | 관계의 양 끝 노드가 유효하지 않음 |
| `INVALID_PATTERN` | 허용 pattern이 아님 |
| `MISSING_LABEL` | label 없음 |
기능명세:
| 기능 | 입력 | 출력 |
|---|---|---|
| 노드 정제 | graph, schema | 유효 node 목록 |
| 관계 정제 | graph, schema, valid nodes | 유효 relationship 목록 |
| property 정제 | node/relationship properties | 스키마에 맞는 property만 유지 |
| 통계 생성 | pruning 결과 | `PruningStats` |
우리 플랫폼에서는 pruning 결과를 “자동 폐기”만 하지 말고, 사용자에게 “추출됐지만 온톨로지에서 거부된 후보”로 보여주는 기능이 필요하다. 이것이 온톨로지 개선 루프의 핵심 데이터가 된다.
### 7.8 LexicalGraphBuilder
파일: `experimental/components/lexical_graph.py`
생성 그래프:
```mermaid
flowchart LR
C1["Chunk 0"] -->|FROM_DOCUMENT| D["Document"]
C2["Chunk 1"] -->|FROM_DOCUMENT| D
C1 -->|NEXT_CHUNK| C2
E1["Entity"] -->|FROM_CHUNK| C1
```
기능명세:
| 기능 | 생성물 | 설명 |
|---|---|---|
| Document node | `Document` | path, createdAt, metadata, document_type |
| Chunk node | `Chunk` | text, index, metadata, embedding |
| Chunk → Document | `FROM_DOCUMENT` | 문서 소속 |
| Chunk → Chunk | `NEXT_CHUNK` | 원문 순서 |
| Entity → Chunk | `FROM_CHUNK` | 추출 근거 |
온톨로지 플랫폼에서는 이 provenance 구조를 거의 그대로 사용하면 된다. 다만 문서 소스가 crawler/web이면 Document node에 `url`, `crawl_job_id`, `source_type`, `retrieved_at`, `content_hash` 같은 property를 추가하는 것이 좋다.
### 7.9 KGWriter
파일: `experimental/components/kg_writer.py`
제공 구현:
- `Neo4jWriter`
- `ParquetWriter`
#### Neo4jWriter
기능:
- node batch upsert
- relationship batch upsert
- non-lexical node에 `__Entity__` label 추가
- 임시 내부 id index 생성
- write 이후 임시 label/property 정리
- Neo4j 버전에 따라 dynamic label 및 variable scope clause 지원 여부 분기
중요 파라미터:
| 파라미터 | 기본값 | 설명 |
|---|---:|---|
| `driver` | 필수 | Neo4j driver |
| `neo4j_database` | `None` | DB 이름 |
| `batch_size` | `1000` | batch write 크기 |
| `clean_db` | `True` | writer 내부 임시 데이터 정리 |
출력:
```json
{
"status": "SUCCESS",
"metadata": {
"statistics": {
"node_count": 0,
"relationship_count": 0,
"nodes_per_label": {},
"rel_per_type": {},
"input_files_count": 0,
"input_files_total_size_bytes": 0
},
"files": []
}
}
```
#### ParquetWriter
기능:
- node label별 Parquet 파일 생성
- `(head_label, relationship_type, tail_label)`별 relationship Parquet 파일 생성
- schema constraints를 metadata로 반영
- Neo4j bulk import 또는 데이터 레이크 연계를 위한 중간 산출물 생성
재사용 판단:
- 실시간/소규모 구축: `Neo4jWriter`
- 대량 batch/검수/승인 workflow: `ParquetWriter` 또는 custom writer 권장
### 7.10 EntityResolver
파일: `experimental/components/resolver.py`
제공 구현:
| Resolver | 방식 | 의존성 |
|---|---|---|
| `SinglePropertyExactMatchResolver` | 같은 label + 같은 property 값이면 merge | APOC |
| `SpaCySemanticMatchResolver` | property text embedding cosine similarity | spaCy, numpy, APOC |
| `FuzzyMatchResolver` | RapidFuzz string similarity | rapidfuzz, APOC |
기능명세:
| 기능 | 설명 |
|---|---|
| 대상 선택 | 기본 `MATCH (entity:__Entity__)`, `filter_query`로 scope 축소 |
| label별 그룹화 | `__Entity__`, `__KGBuilder__` 제외 |
| property 비교 | 기본 `name`, 다중 property 가능 |
| merge | `apoc.refactor.mergeNodes(..., {properties:'discard', mergeRels:true})` |
| 통계 | resolve 대상 수, 생성/merge 결과 수 |
주의:
- 기본 merge 정책은 property 충돌 시 discard이다.
- 온톨로지 플랫폼에서는 자동 merge 전 “후보 그룹 검수” 기능이 필요하다.
- 한국어/영문 alias, 약어, 조직명 변형 처리에는 custom resolver가 필요하다.
## 8. RAG 및 검색 기능명세
### 8.1 VectorRetriever
파일: `retrievers/vector.py`
기능:
- Neo4j vector index 기반 ANN 검색
- `query_text` 입력 시 embedder로 vector 생성
- `query_vector` 직접 입력 가능
- `top_k`, `effective_search_ratio`, metadata `filters` 지원
- `return_properties` 또는 `result_formatter`로 반환 형식 커스터마이즈
- Neo4j 2026.01+에서는 `SEARCH` clause와 filterable properties 활용 가능
### 8.2 VectorCypherRetriever
기능:
- vector 검색 결과를 시작점으로 custom Cypher traversal 수행
- “유사 chunk 검색 후 주변 entity/관계 확장” 패턴에 적합
온톨로지 플랫폼에서 매우 유용한 검색:
- 특정 문장과 유사한 chunk를 찾고, 해당 chunk에서 추출된 entity와 ontology class를 함께 반환
- 사용자의 질의와 관련된 provenance, source document, neighbor graph를 함께 표시
### 8.3 HybridRetriever / HybridCypherRetriever
기능:
- vector search + fulltext search 결합
- ranker 지원
- Cypher 확장형 retriever 제공
사용처:
- 고유명사/코드/제품명은 fulltext가 강하고, 의미 검색은 vector가 강하다.
- 온톨로지 탐색 UI에서는 hybrid 검색을 기본으로 두는 것이 좋다.
### 8.4 Text2CypherRetriever
파일: `retrievers/text2cypher.py`
기능:
- 자연어 질의를 LLM으로 Cypher로 변환
- 기존 Neo4j schema를 자동 조회하거나 수동 schema 입력
- few-shot examples 제공 가능
- 생성 Cypher에서 코드블록 추출, 공백 포함 label/property/type backtick 보정
- read-only query type만 허용하는 안전장치가 있다.
주의:
- 운영 환경에서는 사용자 권한별 schema 제한, allowlist, query timeout, result limit이 필요하다.
- ontology 관리 기능에 직접 연결할 경우 쓰기 쿼리는 별도 승인된 API로만 처리해야 한다.
### 8.5 ToolsRetriever
기능:
- 여러 tool/retriever를 LLM tool calling 방식으로 선택하게 한다.
- 질의 유형별로 vector/hybrid/text2cypher/custom tool을 라우팅하는 데 적합하다.
### 8.6 External Retriever
지원:
- Weaviate
- Pinecone
- Qdrant
패턴:
- 외부 vector DB에서 vector 검색
- 검색 결과 id를 Neo4j graph와 join
우리 프로젝트가 Neo4j 중심이면 초기에는 보류 가능하다. 다만 대규모 벡터 검색을 별도 인프라로 분리할 가능성이 있으면 adapter 구조는 참고 가치가 높다.
### 8.7 GraphRAG
파일: `generation/graphrag.py`
기능:
1. retriever로 context 검색
2. prompt template에 query/context/examples 주입
3. LLM 호출
4. answer 반환
5. 옵션으로 retriever context 포함 반환
6. message history가 있으면 질의에 대화 요약을 결합
입력:
| 파라미터 | 설명 |
|---|---|
| `query_text` | 사용자 질문 |
| `message_history` | 대화 기록 |
| `examples` | few-shot 예시 |
| `retriever_config` | retriever별 옵션. 예: `top_k` |
| `return_context` | 검색 결과 포함 여부 |
| `response_fallback` | 검색 결과가 없을 때 fallback 답변 |
우리 플랫폼 적용:
- “온톨로지 기반 질의응답”
- “이 entity가 어디서 나왔는가?”
- “이 class와 관련된 문서/근거/관계는?”
- “스키마에 맞지 않아 버려진 후보는?”
## 9. LLM 및 임베딩 어댑터
### 9.1 LLM
지원 구현:
- `OpenAILLM`, `AzureOpenAILLM`
- `OllamaLLM`
- `VertexAILLM`
- `AnthropicLLM`
- `CohereLLM`
- `MistralAILLM`
- `BedrockLLM`
공통 특성:
- `LLMInterface`, `LLMInterfaceV2`, `LLMBase`
- sync/async invoke
- structured output 일부 지원
- rate limit retry 기본 내장
- max attempts: 3
- min wait: 1s
- max wait: 60s
- multiplier: 2
### 9.2 임베딩
지원 구현:
- `OpenAIEmbeddings`, `AzureOpenAIEmbeddings`
- `OllamaEmbeddings`
- `VertexAIEmbeddings`
- `CohereEmbeddings`
- `MistralAIEmbeddings`
- `BedrockEmbeddings`
- `SentenceTransformerEmbeddings`
우리 프로젝트는 provider 독립성이 중요하므로 이 어댑터 계층은 거의 그대로 사용 가능하다. 플랫폼 설정에는 “LLM profile”, “Embedding profile” 개념을 두고, pipeline 실행 시 profile을 주입하는 구조가 좋다.
## 10. 설정 파일 기반 실행
`PipelineRunner.from_config_file(file_path)`로 JSON/YAML 파이프라인 실행이 가능하다.
기본 SimpleKGPipeline 설정 예:
```yaml
version_: 1
template_: SimpleKGPipeline
neo4j_config:
params_:
uri: bolt://localhost:7687
user: neo4j
password:
resolver_: ENV
var_: NEO4J_PASSWORD
llm_config:
class_: OpenAILLM
params_:
model_name: gpt-5
api_key:
resolver_: ENV
var_: OPENAI_API_KEY
model_params:
temperature: 0
max_tokens: 2000
embedder_config:
class_: OpenAIEmbeddings
params_:
model: text-embedding-3-large
schema:
node_types:
- Person
- label: Organization
properties:
- name: name
type: STRING
relationship_types:
- WORKS_AT
patterns:
- ["Person", "WORKS_AT", "Organization"]
from_file: true
perform_entity_resolution: true
on_error: IGNORE
```
우리 플랫폼에서는 이 설정을 DB에 저장하고, GUI/API에서 생성/수정/버전 관리하도록 만들면 된다.
## 11. 범용 온톨로지 구축 플랫폼 적용 설계
### 11.1 그대로 재사용할 1차 기반
| 플랫폼 기능 | 사용할 소스 |
|---|---|
| 문서 → KG 실행 | `SimpleKGPipeline` |
| 커스텀 워크플로우 | `Pipeline`, `Component` |
| 온톨로지 스키마 표현 | `GraphSchema`, `NodeType`, `RelationshipType`, `Pattern`, `ConstraintType` |
| 자동 온톨로지 초안 | `SchemaFromTextExtractor` |
| 엔티티/관계 추출 | `LLMEntityRelationExtractor` |
| 스키마 정합성 검증 | `GraphPruning` |
| Neo4j 저장 | `Neo4jWriter` |
| 출처 그래프 | `LexicalGraphBuilder` |
| 중복 엔티티 병합 | `SinglePropertyExactMatchResolver`, `FuzzyMatchResolver` |
| 검색/QA | `VectorRetriever`, `HybridRetriever`, `Text2CypherRetriever`, `GraphRAG` |
### 11.2 플랫폼에서 추가해야 할 레이어
#### A. 프로젝트/테넌트 관리
- 온톨로지 프로젝트 생성
- 데이터소스 연결
- Neo4j database 또는 namespace 매핑
- LLM/embedding profile 매핑
#### B. 온톨로지 버전 관리
현재 `GraphSchema`는 schema 객체일 뿐 버전 관리 기능은 없다.
필요 기능:
- schema draft/published 상태
- version number
- 변경 diff
- migration plan
- label/property rename 이력
- 이전 버전 추출 결과와 새 버전 비교
#### C. 추출 결과 검수
현재 pipeline은 추출 후 pruning/write까지 자동으로 갈 수 있다.
필요 기능:
- 추출 graph 임시 저장
- node/relationship/property 단위 승인/반려
- pruning된 후보 복원/스키마 반영
- confidence score 또는 LLM rationale 저장
- 근거 chunk 하이라이트
#### D. 도메인별 document loader
추가 대상:
- crawler output loader
- HTML loader
- DOCX/PPTX/XLSX loader
- CSV/DB table loader
- API response loader
- code/documentation loader
#### E. Ontology semantics 확장
`GraphSchema`는 property graph schema에 가깝다. 범용 온톨로지 플랫폼이면 다음 개념이 필요할 수 있다.
| 온톨로지 개념 | 현재 지원 | 확장 필요 |
|---|---:|---|
| Class/Entity type | 지원 | class hierarchy 추가 |
| Object property | relationship type으로 지원 | inverse/symmetric/transitive 추가 |
| Data property | property type으로 지원 | domain/range 강화 |
| Domain/Range | pattern으로 부분 지원 | 다중 domain/range, inheritance 반영 |
| Cardinality | 미지원 | min/max/exact cardinality |
| Equivalent class/property | 미지원 | alias/equivalence 모델 |
| Disjoint class | 미지원 | validation rule |
| SKOS concept | 미지원 | concept scheme, broader/narrower |
#### F. 운영 안정성
- LLM call budget 관리
- chunk/embedding cache
- 재시도/중단/재개
- job progress DB 저장
- 대량 batch queue
- Neo4j transaction timeout 설정
- Text2Cypher query sandbox
### 11.3 권장 내부 모듈 구조
우리 프로젝트에 통합할 때는 원본 소스를 직접 수정하기보다 다음 형태가 좋다.
```text
crawler_platform/
ontology/
schemas.py # GraphSchema 래퍼, 버전/상태/소유자 메타데이터
pipeline_profiles.py # LLM/embedding/Neo4j profile
jobs.py # KG build job 상태
adapters/
documents.py # crawler output → LoadedDocument
schema.py # 우리 온톨로지 모델 ↔ GraphSchema
services/
kg_builder.py # SimpleKGPipeline 실행 래퍼
review.py # 추출 결과 검수
search.py # GraphRAG/Text2Cypher 래퍼
resolver.py # merge 후보/실행
```
## 12. 상세 기능명세
### 12.1 온톨로지 프로젝트 관리
| ID | 기능 | 설명 | 우선순위 |
|---|---|---|---:|
| ONT-PROJ-001 | 프로젝트 생성 | 이름, 설명, Neo4j DB/profile, 기본 언어 설정 | P0 |
| ONT-PROJ-002 | 데이터소스 연결 | 파일, 크롤링 결과, URL, DB table 등록 | P0 |
| ONT-PROJ-003 | LLM profile 선택 | provider/model/key/params 선택 | P0 |
| ONT-PROJ-004 | Embedding profile 선택 | provider/model/dimension 설정 | P0 |
| ONT-PROJ-005 | 실행 이력 조회 | pipeline run 상태/시간/token/비용/오류 | P1 |
### 12.2 온톨로지 스키마 관리
| ID | 기능 | 설명 | 우선순위 |
|---|---|---|---:|
| ONT-SCH-001 | 수동 schema 작성 | node type, relationship type, property, pattern 작성 | P0 |
| ONT-SCH-002 | 자동 schema 추출 | 문서 샘플에서 `SchemaFromTextExtractor` 실행 | P0 |
| ONT-SCH-003 | schema 검증 | `GraphSchema` Pydantic validation + custom rule | P0 |
| ONT-SCH-004 | schema 버전 발행 | draft → published | P0 |
| ONT-SCH-005 | schema diff | version 간 label/property/pattern 변경 비교 | P1 |
| ONT-SCH-006 | constraint 관리 | uniqueness/key/existence 제약 관리 | P1 |
| ONT-SCH-007 | class hierarchy | 상위/하위 class 정의 | P2 |
### 12.3 KG 구축
| ID | 기능 | 설명 | 우선순위 |
|---|---|---|---:|
| ONT-KG-001 | 파일 기반 KG 구축 | PDF/Markdown → SimpleKGPipeline | P0 |
| ONT-KG-002 | 텍스트 기반 KG 구축 | crawler text 또는 inline text → SimpleKGPipeline | P0 |
| ONT-KG-003 | lexical graph 생성 | Document/Chunk/provenance graph 생성 | P0 |
| ONT-KG-004 | chunk embedding | Chunk embedding property 저장 | P0 |
| ONT-KG-005 | 스키마 기반 추출 | published GraphSchema로 LLM 추출 | P0 |
| ONT-KG-006 | 자동 스키마 추출 후 KG 구축 | `schema="EXTRACTED"` 사용 | P1 |
| ONT-KG-007 | 자유 추출 | `schema="FREE"` 사용 | P1 |
| ONT-KG-008 | pruning 통계 저장 | 제거 노드/관계/property 기록 | P0 |
| ONT-KG-009 | 추출 결과 임시 저장 | writer 전 검수용 graph 저장 | P1 |
| ONT-KG-010 | Parquet export | 대량 import 또는 검수 산출물 | P2 |
### 12.4 검수 및 승인
| ID | 기능 | 설명 | 우선순위 |
|---|---|---|---:|
| ONT-REV-001 | 추출 후보 목록 | node/relationship/property 후보 조회 | P1 |
| ONT-REV-002 | 근거 chunk 표시 | `FROM_CHUNK` 관계 기반 원문 근거 표시 | P1 |
| ONT-REV-003 | 승인/반려 | 후보 단위 상태 변경 | P1 |
| ONT-REV-004 | pruning 후보 검토 | 스키마 위반으로 제거된 후보 검토 | P1 |
| ONT-REV-005 | schema 개선 제안 | 반복 pruning된 후보를 schema 후보로 제안 | P2 |
### 12.5 엔티티 해소
| ID | 기능 | 설명 | 우선순위 |
|---|---|---|---:|
| ONT-RES-001 | exact match merge | label + name exact match | P0 |
| ONT-RES-002 | fuzzy merge 후보 | RapidFuzz로 후보 계산 | P1 |
| ONT-RES-003 | semantic merge 후보 | spaCy 또는 embedding similarity | P2 |
| ONT-RES-004 | merge 검수 | 후보 그룹 승인 후 APOC merge | P1 |
| ONT-RES-005 | merge 이력 | 병합 전후 node id, property 충돌 기록 | P1 |
### 12.6 검색 및 질의응답
| ID | 기능 | 설명 | 우선순위 |
|---|---|---|---:|
| ONT-SEA-001 | vector 검색 | Chunk vector index 검색 | P0 |
| ONT-SEA-002 | hybrid 검색 | vector + fulltext | P1 |
| ONT-SEA-003 | graph 확장 검색 | VectorCypher/HybridCypher로 주변 graph 반환 | P0 |
| ONT-SEA-004 | Text2Cypher | 자연어 → read-only Cypher | P1 |
| ONT-SEA-005 | GraphRAG 답변 | 검색 context 기반 답변 생성 | P1 |
| ONT-SEA-006 | provenance 포함 답변 | 답변에 문서/chunk 근거 포함 | P1 |
## 13. 통합 시 주의사항
### 13.1 원본 코드를 직접 수정하지 않는 것이 좋다
이 프로젝트의 KG builder는 `experimental`이다. 원본을 직접 수정하면 upstream 반영이 어려워진다. 권장 방식:
1. `neo4j-graphrag==1.16.0`으로 버전 고정
2. 우리 프로젝트에 adapter/wrapper 작성
3. 필요한 custom component만 우리 namespace에 구현
4. 원본 API 변경 시 wrapper만 수정
### 13.2 LLM JSON 품질
`json-repair`와 structured output이 있더라도 LLM 추출은 완전하지 않다.
필수 보완:
- 스키마 grounding prompt 강화
- examples few-shot 관리
- chunk별 실패율 기록
- pruning 결과 분석
- 사람이 승인하는 workflow
### 13.3 Neo4j/APOC 의존성
KG writer와 resolver는 Neo4j 버전/APOC에 민감하다.
운영 체크:
- Neo4j 버전 확인
- APOC core 설치 확인
- `apoc.refactor.mergeNodes` 사용 가능 여부 확인
- vector/fulltext index 생성 권한 확인
- multi database 사용 시 `neo4j_database` 일관성 유지
### 13.4 Text2Cypher 보안
`Text2CypherRetriever`는 read-only 검사를 갖지만, 운영 서비스에서는 추가 안전장치가 필요하다.
- 허용 schema 제한
- query timeout
- result limit
- 금지 키워드 검사
- 사용자별 권한 필터
- 쓰기/삭제/관리 명령 차단
## 14. 초기 적용 로드맵
### Phase 1: 최소 KG 구축
목표: crawler 결과 또는 텍스트를 Neo4j KG로 저장.
작업:
1. `neo4j-graphrag[openai,experimental]` 의존성 추가
2. Neo4j 연결 profile 모델 추가
3. LLM/embedding profile 모델 추가
4. crawler output → `LoadedDocument` adapter 작성
5. `SimpleKGPipeline(from_file=False)` 실행 service 작성
6. `GraphSchema` 수동 입력 API 작성
7. `Neo4jWriter` 결과 통계 저장
### Phase 2: 온톨로지 초안/검수
목표: 자동 schema 추출과 추출 결과 검수.
작업:
1. `schema="EXTRACTED"` 실행 지원
2. 추출 schema draft 저장
3. `GraphPruning` 결과 저장
4. pruned item 검토 화면/API
5. 승인된 schema version 발행
### Phase 3: 검색/질의응답
목표: 구축된 KG를 탐색하고 질의응답.
작업:
1. Chunk vector index 생성 자동화
2. `VectorCypherRetriever`로 chunk → entity/provenance 검색
3. `HybridRetriever` 도입
4. `GraphRAG` service 작성
5. Text2Cypher read-only 질의 API 추가
### Phase 4: 엔티티 해소/품질관리
목표: 중복 entity와 schema 품질 개선.
작업:
1. exact match resolver 실행
2. fuzzy 후보 생성
3. merge 후보 검수
4. merge 이력 저장
5. 반복 pruning 기반 schema 개선 추천
## 15. 결론
`neo4j-graphrag-python`은 우리 범용 온톨로지 구축 플랫폼의 “KG 생성 엔진”과 “GraphRAG 검색 엔진”으로 매우 적합하다. 특히 `GraphSchema`, `SimpleKGPipeline`, `LLMEntityRelationExtractor`, `GraphPruning`, `Neo4jWriter`, `VectorCypherRetriever`, `Text2CypherRetriever`는 거의 그대로 가져다 쓸 수 있다.
다만 이 프로젝트가 제공하는 것은 “라이브러리/엔진”이지 “플랫폼”은 아니다. 우리 프로젝트의 핵심 차별점은 다음 레이어에서 만들어야 한다.
- 온톨로지 프로젝트/버전 관리
- schema draft/publish workflow
- 추출 결과 검수 및 provenance UI
- crawler 결과와의 자연스러운 연결
- 대량 실행 job 관리
- entity merge 후보 검수
- Text2Cypher 보안/권한 레이어
따라서 권장 전략은 원본 소스를 복사해 수정하기보다, `neo4j-graphrag`를 고정 버전 의존성으로 두고 우리 플랫폼 서비스가 이 라이브러리를 orchestration하는 방식이다. 필요한 경우 custom loader, custom splitter, custom resolver, custom writer만 우리 코드베이스에 추가한다.