Neo4j로 LLM 기반 지식 그래프 구축 시도
요약
본 기사는 Neo4j GraphRAG for Python의 SimpleKGPipeline을 활용하여 지식 그래프(Knowledge Graph)를 구축하는 방법을 다룹니다. 특히 스키마 지정 여부에 따라 FREE, EXTRACTED, SCHEMA 세 가지 패턴으로 동일한 텍스트에서 그래프를 자동 구축하고 그 차이점을 비교 분석합니다.
핵심 포인트
- Neo4j는 노드와 리레이션 기반의 네이티브 그래프 데이터베이스입니다.
- GraphRAG for Python은 청크 분할, LLM 추출, Neo4j 쓰기를 통합한 파이프라인을 제공합니다.
- 스키마 지정 방식(FREE/EXTRACTED/SCHEMA)에 따라 지식 그래프 구축 결과가 달라집니다.
서론
안녕하세요, 데이터 엔지니어 Maru입니다.
최근에는 LLM을 활용한 검색 시스템뿐만 아니라, '정보들 간에 어떤 관계가 있는지'를 고려한 검색 활용이 주목받고 있습니다. 그 수단 중 하나가 지식 그래프(Knowledge Graph)입니다.
반면, 지식 그래프를 처음부터 사람이 직접 구축하는 데는 큰 비용이 듭니다. 그래서 LLM을 이용하여 문서에서 엔티티(Entity)나 리레이션(Relation)을 추출하고, 이를 통해 지식 그래프를 자동으로 구축하는 접근 방식이 있습니다.
본 기사에서는 Neo4j GraphRAG for Python의 SimpleKGPipeline을 사용하여, 스키마를 지정하지 않는 FREE, LLM이 스키마를 추정하는 EXTRACTED, 그리고 스키마를 수동으로 정의하는 SCHEMA의 3가지 패턴으로 동일한 텍스트에서 그래프를 LLM 기반으로 구축하고, 각각의 차이점을 비교합니다.
본 기사의 대상자
- 지식 그래프에 관심 있는 분
- Neo4j에서 LLM을 활용한 지식 그래프 구축을 시도해 보고 싶은 분
1. Neo4j GraphRAG for Python 개요
1.1 Neo4j란
Neo4j는 노드(Node)와 리레이션(Relation)으로 데이터를 표현하는 네이티브 그래프 데이터베이스입니다.
예를 들어, 사람과 그 관계를 관계형 데이터베이스(RDB)의 테이블 조인 방식이 아닌, 다음과 같은 그래프로 저장하고 탐색할 수 있습니다.
(사람 A)-[:USES]->(사물 A)<-[:USES]-(사람 B)
이렇게 그래프로 관계성을 표현함으로써 여러 관계를 따라가는 검색을 구현하기가 용이합니다.
1.2 Neo4j GraphRAG for Python이란
Neo4j GraphRAG for Python은 Neo4j에서 제공하는 Python용 공식 GraphRAG 라이브러리입니다. 텍스트의 청크 분할(Chunk Splitting), LLM을 이용한 엔티티/리레이션 추출, Neo4j에 쓰기 등 지식 그래프 구축에 필요한 기능을 제공합니다.
SimpleKGPipeline은 텍스트로부터 지식 그래프를 구축하기 위한 처리 과정을 통합한 파이프라인입니다.
자세한 내용은 Neo4j 공식 문서를 참조해 주십시오.
1.3 스키마 지정 방법
SimpleKGPipeline에서는 지식 그래프를 구축할 때 스키마를 제공하는 세 가지 방법, 즉 FREE, EXTRACTED, 그리고 SCHEMA가 있습니다.
| 패턴 | 스키마 생성자 | 특징 |
|---|---|---|
| FREE | 없음 | 스키마에 의한 제약 없이 노드와 리레이션을 자유롭게 추출합니다. |
| ... | ||
| 본 기사에서는 이 3가지 방법으로 지식 그래프를 구축하고, 생성된 그래프의 구조를 비교합니다. |
2. 검증 개요
2.1 대상 데이터
검증용으로 직접 만든 요리 레시피 Markdown 파일 cookbook.md을 사용합니다.
데이터에는 '카레라이스'와 '육개장(肉じゃが)' 두 가지 레시피가 수록되어 있습니다. 두 레시피 모두 소고기, 양파, 당근, 감자 등 공통 재료를 포함하고 있어, 레시피, 재료, 조리 과정 등의 관계를 지식 그래프로 어떻게 추출할 수 있는지 확인합니다.
cookbook.md
# 레시피: 카레라이스
카레라이스는 소고기와 채소를 볶아 커리 루로 끓여 만드는 요리입니다.
이 레시피에서는 소고기, 양파, 당근, 감자를 사용합니다.
...
2.2 검증 환경
검증은 Neo4j를 Docker 컨테이너로 구동한 로컬 환경에서 실시했습니다.
| 항목 | 내용 |
|---|---|
| Neo4j | 2026.07.1 |
| ... | |
청크 분할에는 이번 검증 데이터의 구조에 맞춰 직접 만든 H1MarkdownTextSplitter를 이용했습니다. |
neo4j-graphrag에는 FixedSizeSplitter가 준비되어 있지만, 이번 cookbook.md에서는 '레시피' 단위로 청크를 분할하고 싶었기 때문에 Markdown의 H1 (#)을 구분자로 하여 분할하는 독자적인 Splitter를 구현했습니다.
import re
from neo4j_graphrag.experimental.components.text_splitters.langchain import (
LangChainTextSplitterAdapter,
...
이 Splitter를 통해, # 레시피: 카레라이스
예시로, 각 레시피를 하나의 청크(chunk)로 간주합니다.
2.3 검증 방법
동일한 텍스트, LLM, 청크 분할 설정에서 스키마 지정 방식만 변경하여 지식 그래프(Knowledge Graph)를 구축합니다. 각 패턴별로 Neo4j Browser의 그래프 표시와 노드/관계 목록을 확인합니다.
2.3.1 FREE
FREE는 스키마를 사전에 정의하지 않고, LLM이 엔티티와 관계를 자유롭게 추출하는 방식입니다.
kg_builder = SimpleKGPipeline(
llm=llm,
driver=driver,
...
이 방식은 사전 준비가 필요 없다는 장점이 있지만, 노드 타입, 관계 타입, 관계의 방향성은 LLM의 해석에 맡겨집니다. 따라서 대상 텍스트에 어떤 개념이나 관계가 포함되어 있는지 탐색하는 용도에 적합합니다.
2.3.2 EXTRACTED
EXTRACTED는 먼저 LLM이 입력된 전체 텍스트에서 스키마를 추정합니다. 이 추정된 스키마를 후속 모든 청크(chunk)에 대한 추출 가이드로 사용합니다.
kg_builder = SimpleKGPipeline(
llm=llm,
driver=driver,
...
FREE와 달리, 청크마다 완전히 자유로운 타입을 선택하는 것이 아니라 문서 전체에서 얻은 공통 스키마에 맞춰 추출합니다. 이 덕분에 문서 내의 표현을 통일하기 쉽습니다. 반면, 스키마의 세분성(granularity)이나 관계명은 LLM이 결정하므로, 활용 목적에 필요한 의미나 방향성이 반드시 반영된다고 할 수는 없습니다.
2.3.3 SCHEMA
SCHEMA는 노드 타입, 관계 타입, 연결 패턴 등을 사람이 사전에 정의하는 방식입니다. FREE나 EXTRACTED처럼 고정된 지정값을 사용하는 것이 아니라, 정의한 스키마를 schema 파라미터에 전달합니다.
이번 스키마에서는 레시피, 재료, 조리 과정 등의 요소 외에도 이들 간의 관계성을 명시적으로 정의했습니다.
SCHEMA = {
"node_types": [
{
...
정의된 스키마를 SimpleKGPipeline에 전달하여 지식 그래프를 구축합니다.
kg_builder = SimpleKGPipeline(
llm=llm,
driver=driver,
...
사전 정의된 스키마 덕분에 노드 타입이나 관계 타입뿐만 아니라, 이들을 어떻게 연결할 수 있는지까지 명시할 수 있습니다.
예를 들어, Recipe와 Ingredient를 REQUIRES_INGREDIENT로 연결하여 레시피 전체에서 사용되는 재료를 표현할 수 있습니다. 또한, Step끼리 NEXT_STEP으로 연결함으로써
、STEP_USES_INGREDIENT
、USES
와 같이 재료와의 관계나 이용 관계를 나타내는 리レーション 타입에 대해서도, 의미가 유사함에도 불구하고 서로 다른 표현이 생성되고 있습니다.

육전밥(肉じゃが)
노드 주변의 그래프 모습. Dish 노드의 「육전밥」과 Recipe 노드의 「육전밥」 두 가지가 존재합니다.
이처럼, FREE 방식에서는 사전에 타입을 정의하지 않기 때문에, 같은 도메인 내에서 유사한 개념이나 관계를 서로 다른 노드 타입/리レーション 타입으로 표현하는 결과가 확인되었습니다.
이 결과로부터, FREE 방식으로 구축된 그래프를 Text2Cypher의 검색 대상으로 삼을 경우, 쿼리를 생성할 때 어떤 노드 타입이나 리レーション 타입을 선택해야 할지 불안정해질 가능성이 있습니다.
예를 들어, 「육전밥 레시피를 검색한다」는 요구사항에 대해 Recipe와 Dish 중 어느 것을 사용해야 하는지, 또는 「레시피에서 사용하는 재료」를 검색할 때 HAS_INGREDIENT나 RECIPE_HAS_INGREDIENT, USES 등의 어떤 리レーション 타입을 사용해야 할지를 Text2Cypher 측에서 판단해야 합니다.
이는 그래프 구축 시 자유도가 높은 반면, 후속 검색 처리에서는 스키마의 모호성이 과제가 될 수 있음을 보여줍니다.
3.2 EXTRACTED 결과
구축된 전체 그래프 및 노드 타입과 리レーション 타입은 다음과 같습니다.

EXTRACTED 방식일 경우의 그래프 전체 모습
| 항목 | 타입 수 | 타입 목록 |
|---|---|---|
| 노드 타입 | 7 | Recipe, Step, Ingredient, Chunk, __Entity__, __KGBuilder__, * |
| 리レーション 타입 | 6 | HAS_STEP, HAS_INGREDIENT, NEXT_CHUNK, FROM_DOCUMENT, FROM_CHUNK, * |
EXTRACTED 방식에서는 LLM이 입력 텍스트 전체로부터 스키마를 추출한 후 엔티티와 리レーション을 추출하기 때문에, FREE 방식과 비교하여 노드 타입이나 리レーション 타입의 표기 및 분류가 통일되어 있으며, 유사한 타입이 난립하는 경우도 억제되었습니다.
반면, 스키마 자체를 LLM이 생성하기 때문에 이용 목적에 필요한 관계성이 반드시 정의되는 것은 아닙니다.
이번 예시에서는 예를 들어 「어떤 조리 과정에서 어떤 재료를 사용하는지」나 「조리 과정 간의 순서」와 같은 관계성이 충분히 모델링되지 않았습니다. 따라서 단순히 「레시피에 포함된 재료」를 검색할 수는 있지만, 「재료 X를 사용하는 과정은 어디인지」「과정 1 다음에 진행하는 과정은 무엇인지」와 같이 프로세스를 의식한 관계성의 탐색이 불가능할 수 있습니다.

양파 노드로부터 1홉(hop)의 그래프 모습. 각 레시피의 공통 재료 간의 관계성은 있지만, 각 조리 과정에서의 재료 필요성 관계성이 형성되어 있지 않습니다.

카레라이스 조리 과정의 그래프 모습. 레시피로부터의 관계성만 존재하며, 조리 과정 간의 전후 관계는 표현하고 있지 않습니다.
이로 미루어 볼 때, EXTRACTED 방식은 FREE 방식과 비교하여 스키마의 일관성을 확보하기 쉽지만, LLM에게 스키마 정의를 맡기는 것만으로는 이용 목적에 필요한 관계성이 반드시 그래프로 표현된다고 할 수 없습니다.
3.3 SCHEMA 결과
구축된 전체 그래프 및 노드 타입과 리レーション 타입은 다음과 같습니다.

SCHEMA 방식일 경우의 그래프 전체 모습
| 항목 | 타입 수 | 타입 목록 |
|---|---|---|
| 노드 타입 | 9 | Recipe, Ingredient, Step, CookingMethod, Document, Chunk, __Entity__, __KGBuilder__, * |
| 리レーション 타입 | 9 | REQUIRES_INGREDIENT, HAS_STEP, NEXT_STEP, REQUIRES_INGREDIENT_FOR_STEP, USES_METHOD, NEXT_CHUNK, FROM_DOCUMENT, FROM_CHUNK, * |
그렇다면, 사전에 정의한 스키마를 가이드로 삼아 예상되는 노드 타입이나 리레이션 타입을 그래프 위에 표현할 수 있었습니다. EXTRACTED와 비교했을 때, 미리 검색하거나 활용하고 싶은 관계성을 스키마로 정의함으로써 의도한 구조를 그래프 위에 명시적으로 표현할 수 있다는 것을 확인할 수 있었습니다.
예를 들어, 두 레시피에서 공통으로 사용되는 재료를 관계성으로 추적하거나, 조리 절차 간의 순서를 리레이션으로 표현함으로써, '양파를 사용하는 여러 레시피를 추적한다', '어떤 절차 다음에 수행할 절차를 추적한다'와 같이 목적에 맞는 관계성의 탐색이 가능해졌습니다.

양파
노드에서 1홉(hop) 떨어진 그래프의 모습. 각 레시피의 공통 재료 관계성이나 각 조리 절차에서의 재료 필요성이 표현되어 있습니다.

카레라이스 조리 절차의 그래프 모습. 조리 절차 간의 전후 관계도 표현할 수 있습니다.
한편, 이번에 동일한 스키마와 같은 형식으로 여러 번 실행했을 때, 노드 표기명 등에 흔들림이 보이는 것을 확인했습니다.
이는 스키마가 노드 타입이나 리레이션 타입과 같은 그래프의 구조는 가이드할 수 있지만, 입력 텍스트에서 구체적으로 어떤 정보를 엔티티나 리레이션으로 추출하고 그것을 어떤 명칭으로 표현할지는 LLM의 판단에 맡겨지기 때문입니다. 따라서 동일한 입력이라 하더라도 실행마다 추출 결과가 완전히 일치하지 않을 경우가 있습니다.
이처럼 스키마를 통해 그래프 구조를 어느 정도 제어할 수 있지만, LLM에 의한 정보 추출에는 일정 수준의 흔들림(variability)이 남아있습니다. 이는 이번에 비교한 모든 방식에 공통적으로 존재하는, LLM 기반 그래프 구축에서의 제약 중 하나입니다.
3.4 요약
각 패턴의 결과를 정리합니다.
| 관점 | FREE | EXTRACTED | SCHEMA |
|---|---|---|---|
| 스키마 결정자 | 없음 | LLM | 인간 |
| ... | |||
FREE |
에서는 사전 정의가 필요 없어 탐색하기 쉽지만, 유사한 노드/리레이션이 생성되는 등 구조의 통일성 측면에서 과제가 보였습니다.
EXTRACTED
에서는 LLM에게 스키마를 생성하게 함으로써 통일성은 향상되지만, 활용 목적에 필요한 관계성이 부족할 수 있습니다.
SCHEMA
에서는 노드 타입이나 리레이션 타입, 연결 패턴을 사전에 정의함으로써, 의도한 관계성을 그래프로 표현하기 쉬워진다는 것을 확인할 수 있었습니다.
한편, 처음부터 모든 스키마를 사람이 설계하는 것은 쉽지 않습니다. 또한, 필요한 관계성을 모두 포괄하려 하면 실제 검색/분석에서는 이용하지 않을 관계까지 과잉으로 정의해 버릴 가능성도 있습니다.
따라서 우선 FREE나 EXTRACTED로 대상 데이터에 포함된 개념/관계성을 탐색하고, 그 결과와 실제로 하고 싶은 검색 요건을 바탕으로 필요한 것을 SCHEMA로 정의하는 진행 방식이 고려될 수 있습니다.
4. 마지막으로
본 기사에서는 Neo4j GraphRAG for Python의 SimpleKGPipeline에서 FREE, EXTRACTED, SCHEMA 세 가지 패턴을 비교했습니다.
우선 FREE나 EXTRACTED로 대상 데이터에 포함된 개념/관계성을 탐색하고, 실제로 필요한 쿼리를 기점으로 SCHEMA를 설계함으로써, 용도에 맞는 지식 그래프를 구축하기 쉬워질 것이라고 생각합니다.
지식 그래프는 단순한 정보 검색뿐만 아니라, 정보 간의 관계성을 바탕으로 판단 및 액션하는 AI 에이전트 등 앞으로 활용 범위가 더욱 넓어질 것이라 생각합니다.
그 한편, 지식 그래프를 처음부터 사람의 손으로 구축하려면 큰 비용이 듭니다. 향후에는 LLM 등을 활용하면서 어떻게 효율적이고 품질 높은 지식 그래프를 구축할 수 있는지가 중요한 포인트가 될 것 같습니다.
다음 회차에서는 이번에 구축한 지식 그래프를 대상으로 어떤 검색 방식들이 있는지, 각 방식에서 검색 결과에 어떤 차이가 발생하는지 확인해 보겠습니다.
끝까지 읽어주셔서 감사합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기