이제 개념을 코드로 옮겨봅니다. 세 줄짜리 문장을 지식 그래프로 만들고, "김민수와 보안 팀은 어떤 관계야?"라는 질문에 관계를 따라 답하게 만드는 것이 목표입니다. 먼저 지식 그래프가 무엇으로 이루어지는지부터 뜯어봅시다.
지식 그래프의 3요소 — 노드·엣지·속성
| 요소 | 쉬운 비유 | 무엇이 들어가나 |
|---|---|---|
| 노드 (Node) | 명사 | 사람·팀·프로젝트 같은 개체/개념 (김민수, 보안 팀) |
| 엣지 (Edge) | 동사 | 노드 사이의 관계 (담당한다, 포함된다, 공동진행) |
| 속성 (Property) | 상세 정보 | 노드·엣지에 붙는 세부값 (나이=34, 언어=파이썬, 출처=문서A) |
예를 들어 "김민수는 결제 시스템 리팩터링을 담당한다"를 분해하면, 명사 '김민수'와 '결제 시스템 리팩터링'이 각각 노드가 되고, 동사 '담당한다'가 둘을 잇는 엣지가 됩니다. 문장이 늘어나면 그래프는 이렇게 뻗어 나갑니다.
세 문장이 하나의 그래프로
- 김민수 사람
- 결제 시스템 리팩터링 담당한다 →
- 장애율 개선 프로젝트 포함된다 →
- 보안 팀 · 플랫폼 팀 공동진행 →
노드(개체)를 엣지(관계)가 잇습니다. 각 노드에는 나이·언어 같은 속성이, 각 엣지에는 출처 문서 같은 속성이 붙을 수 있습니다.
지식 그래프는 어디에 저장하나 — 그래프 DB
벡터를 벡터 DB에 저장했듯, 지식 그래프는 그래프 DB에 저장합니다. 종류가 여럿이지만 대표적인 것은 다음과 같고, 이 실습에서는 랭체인과 통합이 잘 되어 있는 Neo4j를 씁니다.
| 그래프 DB | 특징 |
|---|---|
| Neo4j | 가장 널리 쓰이는 그래프 DB, 랭체인 통합·Cypher 쿼리 (실습에 사용) |
| Amazon Neptune | AWS 관리형 그래프 DB, 대규모 운영에 적합 |
| TigerGraph | 대규모 분산 그래프 분석에 강점 |
| Ontotext GraphDB | RDF·온톨로지 표준(SPARQL) 중심 |
실습 준비
사전 준비 4단계
- Neo4j Desktop 앱을 설치하고 테스트 인스턴스를 실행 (로컬 서버 역할, URI는 보통 bolt://localhost:7687)
- 파이썬 가상환경을 만들고 활성화
- 필요한 패키지 설치 (아래 명령)
- .env 파일에 OpenAI 키와 Neo4j 접속 정보 입력
패키지 설치
pip install langchain langchain-openai langchain-neo4j langchain-experimental neo4j python-dotenv
.env
OPENAI_API_KEY=sk-...
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=여기에_인스턴스_생성시_만든_비밀번호
1) 연결 테스트
1_test_connection.py
import os
from dotenv import load_dotenv
from langchain_neo4j import Neo4jGraph
load_dotenv()
graph = Neo4jGraph(
url=os.environ["NEO4J_URI"],
username=os.environ["NEO4J_USERNAME"],
password=os.environ["NEO4J_PASSWORD"],
)
# 간단한 쿼리로 연결 확인
print(graph.query("RETURN 1 AS ok")) # [{'ok': 1}] 이면 연결 성공
2) 텍스트 → 지식 그래프 (LLM이 자동 추출)
노드와 엣지를 사람이 하나씩 손으로 그리는 대신, LLM에게 '이 문서에서 개체와 관계를 뽑아 그래프로 만들어줘'라고 시킵니다. 랭체인의 LLMGraphTransformer가 이 역할을 합니다.
2_build_graph.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_neo4j import Neo4jGraph
from langchain_experimental.graph_transformers import LLMGraphTransformer
from langchain_core.documents import Document
load_dotenv()
graph = Neo4jGraph(
url=os.environ["NEO4J_URI"],
username=os.environ["NEO4J_USERNAME"],
password=os.environ["NEO4J_PASSWORD"],
)
text = (
"김민수는 결제 시스템 리팩터링을 담당했다. "
"이 결제 시스템 리팩터링은 장애율을 낮추기 위한 프로젝트였다. "
"이 장애율 개선 프로젝트는 보안 팀과 플랫폼 팀이 공동으로 진행했다."
)
# LLM이 문서에서 노드·엣지를 추출
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
transformer = LLMGraphTransformer(llm=llm)
graph_docs = transformer.convert_to_graph_documents([Document(page_content=text)])
# 추출 결과 확인
for node in graph_docs[0].nodes:
print("NODE:", node.id, node.type)
for rel in graph_docs[0].relationships:
print("EDGE:", rel.source.id, "-", rel.type, "->", rel.target.id)
# Neo4j에 저장
graph.add_graph_documents(graph_docs)
print("그래프 생성 완료")
LLMGraphTransformer에 allowed_nodes=["Person", "Project", "Team"], allowed_relationships=["담당", "포함", "공동진행"] 처럼 허용 목록을 넘기면, LLM이 제멋대로 스키마를 만드는 것을 막고 일관된 그래프를 얻을 수 있습니다. 실무에서는 이 스키마 통제가 품질의 핵심입니다.
3) 질문하기 (질문 → Cypher → 답변)
GraphCypherQAChain은 자연어 질문을 Cypher 쿼리로 자동 번역해 그래프를 조회하고, 그 결과를 LLM에 넣어 최종 답변을 만듭니다. Cypher는 그래프 DB의 SQL이라고 생각하면 됩니다.
3_ask_graph.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_neo4j import Neo4jGraph, GraphCypherQAChain
load_dotenv()
graph = Neo4jGraph(
url=os.environ["NEO4J_URI"],
username=os.environ["NEO4J_USERNAME"],
password=os.environ["NEO4J_PASSWORD"],
)
graph.refresh_schema() # 현재 그래프 스키마를 LLM에 알려줌
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = GraphCypherQAChain.from_llm(
llm=llm,
graph=graph,
verbose=True, # 생성된 Cypher와 조회 결과를 출력
allow_dangerous_requests=True, # 로컬 학습용. 운영에선 읽기 전용 계정 권장
)
result = chain.invoke({"query": "김민수와 보안 팀은 어떤 관계야?"})
print(result["result"])
# 예: 김민수는 결제 시스템 리팩터링을 담당했고, 이 작업에서 보안 팀과 협업한 관계입니다.
실습 파이프라인 한눈에
- 연결 테스트 Neo4jGraph로 접속 확인
- 그래프 구축 LLMGraphTransformer로 텍스트 → 노드·엣지
- 질의 GraphCypherQAChain: 질문 → Cypher → 조회 → 답변
핵심은 2단계입니다 — LLM이 그래프를 '만들고', 또 다른 LLM 체인이 질문을 Cypher로 '번역'해 조회합니다.
Cypher 맛보기
Neo4j Desktop의 쿼리 창에서 직접 Cypher를 실행해 그래프를 눈으로 확인할 수 있습니다. 관계는 (노드)-[관계]->(노드) 문법으로 표현합니다.
전체 그래프 조회 + 특정 관계 탐색
// 만들어진 모든 노드와 관계를 시각화
MATCH (n)-[r]->(m)
RETURN n, r, m;
// 김민수에서 출발해 연결된 경로 따라가기
MATCH path = (p {id: "김민수"})-[*1..3]->(x)
RETURN path;
Neo4j Desktop에서 그래프가 안 보이면 인스턴스가 실행 중인지(재생 버튼), .env의 URI·비밀번호가 맞는지부터 확인하세요. 대부분의 연결 오류는 인스턴스 미실행 또는 비밀번호 불일치입니다.
LLM이 자동 추출한 그래프는 완벽하지 않습니다. 같은 개체가 다른 이름으로 중복 노드가 되거나(예: '보안팀'과 '보안 팀'), 관계 방향이 뒤집힐 수 있습니다. 운영에 쓰려면 allowed_nodes/relationships로 스키마를 고정하고, 추출 결과를 사람이 검수·정규화하는 과정이 반드시 필요합니다. 또한 그래프 구축과 질의 모두 LLM을 호출하므로 API 비용이 발생합니다.
여기까지 랭체인과 Neo4j로 지식 그래프를 만들고, 관계를 따라 답을 찾는 GraphRAG의 전체 흐름을 손으로 돌려봤습니다. 다음 레슨에서는 그래프 DB의 언어인 Cypher를 CRUD 수준으로 익히고, 그다음 레슨에서 실제 PDF 문서로 처음부터 끝까지 GraphRAG 파이프라인을 구축합니다.