# 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만 우리 코드베이스에 추가한다.