본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 26. · 3 Views
Graph RAG 완벽 가이드
지식 그래프와 RAG를 결합한 Graph RAG의 개념부터 실전 구현까지, 초급 개발자도 쉽게 따라할 수 있는 완벽 가이드입니다. 엔티티 관계 기반의 똑똑한 검색 시스템을 만들어보세요.
목차
1. 지식 그래프 + RAG
어느 날 김개발 씨는 회사의 RAG 시스템을 개선하는 프로젝트를 맡게 되었습니다. 기존 RAG는 잘 작동했지만, 복잡한 질문에는 제대로 답변하지 못하는 문제가 있었습니다.
"사용자가 '김철수 부장님이 참여한 프로젝트의 기술 스택은?'이라고 물으면 어떻게 답해야 할까요?"
Graph RAG는 전통적인 RAG에 지식 그래프를 결합한 기술입니다. 마치 도서관 사서가 책의 내용뿐 아니라 저자, 출판사, 관련 도서 간의 관계까지 파악하고 있는 것처럼, Graph RAG는 정보 간의 연결 관계를 이해합니다.
단순히 텍스트를 검색하는 것을 넘어, 엔티티 간의 관계를 활용해 더 정확한 답변을 제공할 수 있습니다.
다음 코드를 살펴봅시다.
# Graph RAG의 기본 구조
from typing import List, Dict
class GraphRAG:
def __init__(self):
# 벡터 저장소 (전통적인 RAG)
self.vector_store = {}
# 지식 그래프 (엔티티와 관계)
self.knowledge_graph = {
'entities': {}, # 엔티티 정보
'relations': [] # 관계 정보
}
def add_entity(self, entity_id: str, entity_type: str, properties: Dict):
# 엔티티 추가: 사람, 프로젝트, 기술 등
self.knowledge_graph['entities'][entity_id] = {
'type': entity_type,
'properties': properties
}
def add_relation(self, source: str, relation: str, target: str):
# 관계 추가: "김철수" -[참여]-> "프로젝트A"
self.knowledge_graph['relations'].append({
'source': source,
'relation': relation,
'target': target
})
김개발 씨는 지금까지 일반적인 RAG 시스템을 사용해왔습니다. 문서를 벡터로 변환하고, 유사도 검색으로 관련 문서를 찾는 방식이었죠.
대부분의 경우 잘 작동했지만, 복잡한 질문에는 한계가 있었습니다. 예를 들어 사용자가 "김철수 부장님이 참여한 프로젝트에서 사용한 기술은?"이라고 질문하면 어떻게 될까요?
전통적인 RAG는 "김철수", "프로젝트", "기술"이라는 키워드가 포함된 문서를 찾아줍니다. 하지만 이 세 가지 정보가 어떻게 연결되어 있는지는 이해하지 못합니다.
선배 개발자 박시니어 씨가 설명합니다. "일반 RAG는 문서 덩어리를 찾아주는 거고, Graph RAG는 점과 선으로 이루어진 지도를 이해하는 거예요." 그렇다면 Graph RAG는 정확히 무엇일까요?
쉽게 비유하자면, Graph RAG는 마치 소셜 네트워크와 같습니다. 페이스북에서 친구 관계를 따라가며 "친구의 친구"를 찾듯이, Graph RAG는 엔티티 간의 관계를 따라가며 정보를 찾습니다.
"김철수"라는 사람 엔티티가 "참여" 관계로 "프로젝트A"와 연결되어 있고, "프로젝트A"가 "사용" 관계로 "Python"과 연결되어 있다면, 이 관계 체인을 따라가며 답을 찾을 수 있습니다. 전통적인 RAG가 없던 시절을 생각해보면, 개발자들은 키워드 검색에만 의존했습니다.
하지만 "A와 관련된 B는 무엇인가?"같은 복잡한 질문에는 답하기 어려웠죠. 문서를 일일이 읽어보며 관계를 파악해야 했습니다.
RAG가 등장하면서 상황이 나아졌습니다. 의미적으로 유사한 문서를 찾을 수 있게 되었으니까요.
하지만 여전히 한계가 있었습니다. 여러 엔티티가 복잡하게 얽혀있는 질문에는 제대로 답하지 못했습니다.
바로 이런 문제를 해결하기 위해 Graph RAG가 등장했습니다. Graph RAG를 사용하면 관계 기반 추론이 가능해집니다.
"김철수가 참여한 프로젝트"를 먼저 찾고, 그 프로젝트에서 "사용한 기술"을 찾는 식으로 단계적 탐색이 가능합니다. 또한 다중 홉 탐색도 가능합니다.
"친구의 친구의 친구"처럼 여러 관계를 거쳐가며 정보를 찾을 수 있죠. 무엇보다 컨텍스트 이해가 뛰어납니다.
단순히 키워드 매칭이 아니라, 엔티티 간의 의미 있는 연결을 이해하고 활용합니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 GraphRAG 클래스는 두 가지 핵심 구성요소를 가집니다. vector_store는 전통적인 RAG의 벡터 검색을 담당하고, knowledge_graph는 엔티티와 관계 정보를 저장합니다.
add_entity 메서드로 "김철수"(사람), "프로젝트A"(프로젝트), "Python"(기술) 같은 엔티티를 추가할 수 있습니다. add_relation 메서드로 "김철수 -[참여]-> 프로젝트A" 같은 관계를 정의합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 사내 지식 관리 시스템을 개발한다고 가정해봅시다.
직원, 프로젝트, 문서, 기술 스택 등 수많은 정보가 복잡하게 얽혀있습니다. Graph RAG를 활용하면 "지난 분기에 AI 프로젝트에 참여한 시니어 개발자가 작성한 기술 문서"처럼 복잡한 조건의 검색이 가능해집니다.
많은 기업에서 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 모든 정보를 그래프로 만들려고 하는 것입니다. 이렇게 하면 그래프가 너무 복잡해져서 오히려 성능이 떨어질 수 있습니다.
따라서 핵심 엔티티와 관계만 그래프로 구성하고, 나머지는 벡터 검색으로 처리하는 하이브리드 방식이 효과적입니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 벡터 검색과 그래프를 함께 쓰는 거군요!" Graph RAG를 제대로 이해하면 단순한 문서 검색을 넘어, 지식 간의 관계를 활용한 똑똑한 시스템을 만들 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 모든 것을 그래프로 만들지 말고, 핵심 엔티티와 관계만 선별하세요
- 벡터 검색과 그래프 탐색을 병행하는 하이브리드 접근이 효과적입니다
- 초기에는 간단한 관계(사람-프로젝트)부터 시작하고 점진적으로 확장하세요
2. 엔티티 관계 활용
Graph RAG의 기본 구조를 이해한 김개발 씨는 이제 실제로 엔티티와 관계를 어떻게 추출하고 활용할지 고민하게 되었습니다. "문서에서 자동으로 엔티티를 찾아내고, 그 관계를 파악하려면 어떻게 해야 할까요?" 박시니어 씨가 힌트를 줍니다.
"NER과 관계 추출 기술을 활용하면 됩니다."
엔티티 관계 추출은 텍스트에서 중요한 엔티티(사람, 장소, 조직, 날짜 등)를 찾아내고, 그들 사이의 관계를 파악하는 기술입니다. 마치 신문 기사를 읽으며 "누가, 무엇을, 언제, 어디서"를 메모하는 것과 같습니다.
이렇게 추출된 정보는 지식 그래프를 자동으로 구성하는 데 활용됩니다.
다음 코드를 살펴봅시다.
import spacy
from typing import List, Tuple
class EntityRelationExtractor:
def __init__(self):
# spaCy 모델 로드 (한국어는 ko_core_news_sm)
self.nlp = spacy.load("en_core_web_sm")
def extract_entities(self, text: str) -> List[Dict]:
# 문서에서 엔티티 추출
doc = self.nlp(text)
entities = []
for ent in doc.ents:
entities.append({
'text': ent.text,
'type': ent.label_, # PERSON, ORG, DATE 등
'start': ent.start_char,
'end': ent.end_char
})
return entities
def extract_relations(self, text: str) -> List[Tuple]:
# 간단한 패턴 기반 관계 추출
doc = self.nlp(text)
relations = []
# "A works at B" 패턴 찾기
for token in doc:
if token.dep_ == "prep" and token.head.pos_ == "VERB":
# 주체와 객체 찾기
subject = [w for w in token.head.lefts if w.dep_ == "nsubj"]
object = [w for w in token.rights if w.dep_ == "pobj"]
if subject and object:
relations.append((
subject[0].text,
token.head.text, # 동사 (관계 유형)
object[0].text
))
return relations
김개발 씨는 이제 Graph RAG를 실제로 구현하려고 합니다. 하지만 곧 문제에 부딪혔습니다.
회사에는 수천 개의 문서가 있는데, 이 모든 문서에서 일일이 엔티티와 관계를 수작업으로 찾아낼 수는 없는 노릇입니다. "선배님, 이 많은 문서를 어떻게 처리해야 할까요?" 김개발 씨가 물었습니다.
박시니어 씨가 웃으며 답합니다. "자동화할 수 있어요.
NER이라는 기술을 사용하면 됩니다." **NER(Named Entity Recognition)**은 개체명 인식이라고도 불리며, 텍스트에서 중요한 엔티티를 자동으로 찾아내는 기술입니다. 쉽게 비유하자면, NER은 마치 형광펜과 같습니다.
중요한 교과서를 읽으며 핵심 용어에 형광펜으로 밑줄을 긋듯이, NER은 텍스트를 읽으며 사람 이름, 회사명, 날짜, 장소 등 중요한 정보에 자동으로 표시를 합니다. "김철수 부장은 2024년 1월에 서울 본사에서 AI 프로젝트를 시작했다"라는 문장에서 "김철수"(PERSON), "2024년 1월"(DATE), "서울 본사"(LOCATION), "AI 프로젝트"(PROJECT)를 자동으로 찾아냅니다.
엔티티를 찾아내는 것만으로는 부족합니다. 그들 사이의 관계도 파악해야 합니다.
관계 추출이 없던 시절에는 어땠을까요? 엔티티는 찾았지만, "김철수"와 "AI 프로젝트"가 어떤 관계인지 알 수 없었습니다.
단순히 같은 문장에 나왔다는 것만 알 수 있었죠. 이런 정보로는 의미 있는 검색이 불가능했습니다.
관계 추출 기술이 등장하면서 상황이 달라졌습니다. 관계 추출을 사용하면 구조화된 지식을 얻을 수 있습니다.
"김철수 -[시작함]-> AI 프로젝트"처럼 명확한 관계를 정의할 수 있습니다. 또한 추론 가능성도 생깁니다.
"A가 B를 시작했고, B가 C를 사용한다"는 정보로부터 "A는 C와 관련이 있다"를 추론할 수 있습니다. 무엇보다 자동화가 가능해집니다.
수천 개의 문서를 자동으로 처리하여 지식 그래프를 구성할 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
EntityRelationExtractor 클래스는 spaCy라는 강력한 NLP 라이브러리를 사용합니다. extract_entities 메서드는 문서를 분석하여 모든 엔티티를 찾아냅니다.
각 엔티티는 텍스트, 타입, 위치 정보를 포함합니다. extract_relations 메서드는 의존성 파싱을 활용하여 관계를 추출합니다.
"A works at B" 같은 패턴을 찾아 (A, works_at, B) 형태의 관계로 변환합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 뉴스 기사 분석 시스템을 개발한다고 가정해봅시다. 매일 수백 개의 기사가 올라옵니다.
NER과 관계 추출을 활용하면 "삼성전자가 새로운 반도체 공장을 평택에 건설한다"는 기사에서 자동으로 (삼성전자, 건설, 반도체_공장, 평택)이라는 구조화된 정보를 추출할 수 있습니다. 이렇게 모인 정보는 지식 그래프로 구성되어, "삼성전자의 투자 현황", "평택의 산업 시설" 같은 복합 질의에 답할 수 있게 됩니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 NER 결과를 맹신하는 것입니다.
NER은 완벽하지 않습니다. "애플"이 과일인지 회사인지 문맥에 따라 다르지만, 잘못 인식할 수 있습니다.
따라서 도메인 특화 모델을 학습시키거나, 후처리 로직을 추가하여 정확도를 높여야 합니다. 또 다른 실수는 너무 많은 관계를 추출하려고 하는 것입니다.
모든 동사를 관계로 만들면 그래프가 복잡해집니다. 핵심 관계만 선별하는 것이 중요합니다.
"참여", "소유", "위치", "시간" 같은 중요한 관계 타입을 미리 정의하고, 그것만 추출하는 것이 효과적입니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 흥미를 보입니다. "그럼 이제 문서를 자동으로 처리할 수 있겠네요!" 엔티티 관계 추출을 제대로 이해하면 방대한 텍스트 데이터를 구조화된 지식으로 변환할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - spaCy, Hugging Face Transformers 같은 검증된 라이브러리를 활용하세요
- 도메인에 특화된 엔티티 타입을 정의하고, 필요하면 커스텀 모델을 학습시키세요
- 관계는 모든 것을 추출하지 말고, 핵심 관계 타입만 선별하세요
3. 그래프 기반 검색
엔티티와 관계를 추출하는 방법을 배운 김개발 씨는 이제 실제로 검색을 수행할 차례입니다. "지식 그래프가 준비되었으니, 사용자 질문에 어떻게 답해야 할까요?" 박시니어 씨가 화이트보드에 그림을 그리며 설명합니다.
"벡터 검색과 그래프 탐색을 결합해야 합니다."
그래프 기반 검색은 사용자 질문을 분석하여 관련 엔티티를 찾고, 그래프를 탐색하여 연결된 정보를 수집하는 과정입니다. 마치 지하철 노선도에서 출발역과 도착역을 찾고, 최적 경로를 탐색하는 것과 같습니다.
벡터 검색으로 시작점을 찾고, 그래프 탐색으로 관련 정보를 확장합니다.
다음 코드를 살펴봅시다.
from typing import List, Set
import numpy as np
class GraphSearch:
def __init__(self, graph_rag):
self.graph = graph_rag
def search(self, query: str, max_hops: int = 2) -> Dict:
# 1단계: 벡터 검색으로 초기 엔티티 찾기
initial_entities = self._vector_search(query)
# 2단계: 그래프 탐색으로 관련 정보 확장
expanded_info = self._graph_traversal(
initial_entities,
max_hops
)
# 3단계: 결과 종합
context = self._build_context(expanded_info)
return {
'initial_entities': initial_entities,
'expanded_entities': expanded_info,
'context': context
}
def _graph_traversal(self, start_entities: List[str], max_hops: int) -> Set:
# BFS로 그래프 탐색
visited = set(start_entities)
queue = [(e, 0) for e in start_entities] # (엔티티, 깊이)
while queue:
entity, depth = queue.pop(0)
if depth >= max_hops:
continue
# 이 엔티티와 연결된 모든 관계 찾기
for relation in self.graph.knowledge_graph['relations']:
if relation['source'] == entity:
target = relation['target']
if target not in visited:
visited.add(target)
queue.append((target, depth + 1))
return visited
김개발 씨는 이제 마지막 퍼즐 조각을 맞출 준비가 되었습니다. 지식 그래프도 만들었고, 엔티티와 관계도 추출했습니다.
이제 사용자가 질문을 하면 어떻게 답해야 할까요? "사용자가 '김철수 부장이 참여한 프로젝트의 기술 스택은?'이라고 물으면 어떻게 하죠?" 김개발 씨가 물었습니다.
박시니어 씨가 화이트보드에 원과 화살표를 그립니다. "먼저 '김철수'를 찾고, 거기서 출발해서 연결된 정보를 따라가면 됩니다." 그래프 기반 검색은 정확히 무엇일까요?
쉽게 비유하자면, 그래프 검색은 마치 보물찾기와 같습니다. 첫 번째 단서를 발견하면, 그것이 다음 단서로 이어지고, 다시 그 다음 단서로 이어집니다.
"김철수"라는 단서를 찾으면, 그가 "참여"한 "프로젝트A"를 발견하고, "프로젝트A"가 "사용"하는 "Python"과 "FastAPI"를 찾아내는 식입니다. 각 단계가 다음 단계의 출발점이 됩니다.
전통적인 검색 방식의 한계를 생각해봅시다. 키워드 검색은 "김철수", "프로젝트", "기술"이 모두 포함된 문서를 찾습니다.
하지만 이 세 가지가 어떻게 연결되는지는 모릅니다. 벡터 검색은 의미적으로 유사한 문서를 찾지만, 역시 연결 관계는 파악하지 못합니다.
"김철수가 참여한 프로젝트"를 찾은 후, 다시 "그 프로젝트의 기술"을 찾는 두 단계 추론이 필요한데, 일반 검색으로는 불가능합니다. 바로 이런 문제를 해결하기 위해 그래프 기반 검색이 등장했습니다.
그래프 검색을 사용하면 다단계 추론이 가능해집니다. 첫 번째 홉에서 김철수와 연결된 프로젝트를 찾고, 두 번째 홉에서 그 프로젝트의 기술을 찾을 수 있습니다.
또한 관계 타입 활용도 가능합니다. "참여" 관계는 따라가되, "언급" 관계는 무시하는 식으로 선택적 탐색이 가능합니다.
무엇보다 컨텍스트 확장이 자연스럽습니다. 하나의 엔티티에서 시작해서 관련된 모든 정보를 체계적으로 수집할 수 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. search 메서드는 세 단계로 동작합니다.
첫째, 벡터 검색으로 질문과 관련된 초기 엔티티를 찾습니다. 둘째, 그래프 탐색으로 연결된 정보를 확장합니다.
셋째, 수집된 정보를 종합하여 컨텍스트를 구성합니다. graph_traversal 메서드가 핵심입니다.
BFS(너비 우선 탐색) 알고리즘을 사용하여 그래프를 탐색합니다. max_hops 파라미터로 탐색 깊이를 제한할 수 있습니다.
깊이 1이면 직접 연결된 엔티티만, 깊이 2면 "친구의 친구"까지 탐색합니다. visited 집합으로 이미 방문한 엔티티는 건너뛰어 무한 루프를 방지합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 전자상거래 추천 시스템을 개발한다고 가정해봅시다.
사용자가 "iPhone 13"을 검색했다면, 단순히 유사한 제품만 추천하는 것이 아니라, 그래프를 활용할 수 있습니다. "iPhone 13" → "구매한 사용자" → "함께 구매한 제품"을 탐색하여 "무선 이어폰"이나 "충전기"를 추천할 수 있습니다.
또는 "iPhone 13" → "같은 브랜드" → "유사한 가격대" 경로로 "iPhone 14"를 추천할 수도 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 무제한 탐색을 하는 것입니다. max_hops를 너무 크게 설정하면 그래프 전체를 탐색하게 되어 성능이 크게 떨어집니다.
일반적으로 2~3 홉이 적당합니다. 그 이상 가면 관련성도 떨어지고 시간도 오래 걸립니다.
또 다른 실수는 관계의 방향성을 무시하는 것입니다. "A가 B를 참조한다"와 "B가 A를 참조한다"는 다릅니다.
관계를 탐색할 때는 방향성을 고려해야 의미 있는 결과를 얻을 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 감탄합니다. "이제 복잡한 질문도 답할 수 있겠네요!" 그래프 기반 검색을 제대로 이해하면 단순한 키워드 매칭을 넘어, 지식 간의 연결을 활용한 똑똑한 검색 시스템을 만들 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - max_hops는 일반적으로 2~3이 적당하며, 도메인에 따라 조정하세요
- 관계 타입별로 가중치를 다르게 설정하여 중요한 관계를 우선 탐색하세요
- 성능을 위해 자주 탐색되는 경로는 캐싱하세요
4. 실습: 간단한 Graph RAG
이론은 충분히 배웠으니, 이제 직접 만들어볼 시간입니다. 김개발 씨는 간단한 회사 조직도 검색 시스템을 만들기로 했습니다.
"직원, 부서, 프로젝트 정보를 넣고, '김철수가 속한 부서의 다른 직원은?'같은 질문에 답하는 시스템을 만들어보겠습니다."
실제 동작하는 간단한 Graph RAG 시스템을 구축해봅니다. 엔티티와 관계를 추가하고, 그래프를 탐색하여 질문에 답하는 전체 과정을 구현합니다.
복잡한 라이브러리 없이 순수 Python으로 핵심 개념을 이해할 수 있습니다.
다음 코드를 살펴봅시다.
class SimpleGraphRAG:
def __init__(self):
self.entities = {} # {id: {type, name, properties}}
self.relations = [] # [{source, relation, target}]
def add_entity(self, entity_id, entity_type, name, **properties):
self.entities[entity_id] = {
'type': entity_type,
'name': name,
'properties': properties
}
def add_relation(self, source, relation, target):
self.relations.append({
'source': source,
'relation': relation,
'target': target
})
def find_entity_by_name(self, name):
# 이름으로 엔티티 찾기
for eid, entity in self.entities.items():
if entity['name'] == name:
return eid
return None
def find_related(self, entity_id, relation_type=None):
# 특정 엔티티와 관련된 엔티티 찾기
related = []
for rel in self.relations:
if rel['source'] == entity_id:
if relation_type is None or rel['relation'] == relation_type:
target_entity = self.entities[rel['target']]
related.append({
'id': rel['target'],
'name': target_entity['name'],
'relation': rel['relation']
})
return related
def query(self, question):
# 간단한 질의 처리 예시
# "김철수가 속한 부서는?"
if "속한 부서" in question:
name = question.split("가")[0]
entity_id = self.find_entity_by_name(name)
if entity_id:
return self.find_related(entity_id, "belongs_to")
return []
# 사용 예시
graph = SimpleGraphRAG()
# 엔티티 추가
graph.add_entity("p1", "person", "김철수", position="부장")
graph.add_entity("p2", "person", "이영희", position="과장")
graph.add_entity("d1", "department", "AI연구팀")
graph.add_entity("pr1", "project", "ChatBot 개발")
# 관계 추가
graph.add_relation("p1", "belongs_to", "d1")
graph.add_relation("p2", "belongs_to", "d1")
graph.add_relation("p1", "works_on", "pr1")
# 질의
result = graph.query("김철수가 속한 부서는?")
print(result) # [{'id': 'd1', 'name': 'AI연구팀', 'relation': 'belongs_to'}]
김개발 씨는 노트북을 열고 코딩을 시작합니다. "먼저 가장 간단한 버전부터 만들어보자." 박시니어 씨가 조언합니다.
"처음에는 복잡한 라이브러리 없이 순수 Python으로 만들어보세요. 그래야 원리를 제대로 이해할 수 있어요." 그렇다면 가장 간단한 Graph RAG는 어떻게 만들까요?
핵심은 두 개의 자료구조입니다. 첫째는 엔티티를 저장하는 딕셔너리입니다.
각 엔티티는 고유 ID를 키로 가지고, 타입, 이름, 속성 정보를 값으로 가집니다. 둘째는 관계를 저장하는 리스트입니다.
각 관계는 출발 엔티티, 관계 타입, 도착 엔티티를 포함합니다. 쉽게 비유하자면, 엔티티 딕셔너리는 명함 정리함과 같습니다.
각 사람의 명함(정보)을 ID로 정리해 둡니다. 관계 리스트는 조직도와 같습니다.
누가 누구와 어떤 관계인지 화살표로 표시된 그림입니다. 이제 데이터를 넣어봅시다.
김철수 부장, 이영희 과장, AI연구팀, ChatBot 프로젝트를 엔티티로 추가합니다. 그리고 "김철수는 AI연구팀에 속한다", "이영희는 AI연구팀에 속한다", "김철수는 ChatBot 프로젝트를 진행한다"라는 관계를 추가합니다.
이렇게 하면 간단한 지식 그래프가 완성됩니다. 이제 질문에 답해봅시다.
"김철수가 속한 부서는?"이라는 질문이 들어오면 어떻게 처리할까요? 먼저 질문을 분석합니다.
"김철수"라는 이름과 "속한 부서"라는 의도를 파악합니다. find_entity_by_name으로 "김철수" 엔티티의 ID를 찾습니다.
find_related로 김철수와 "belongs_to" 관계로 연결된 엔티티를 찾습니다. 결과는 "AI연구팀"이 됩니다.
위의 코드를 한 줄씩 살펴보겠습니다. SimpleGraphRAG 클래스는 두 개의 컨테이너를 가집니다.
entities는 모든 엔티티 정보를, relations는 모든 관계 정보를 저장합니다. add_entity는 새로운 엔티티를 추가하고, add_relation은 새로운 관계를 추가합니다.
find_entity_by_name은 이름으로 엔티티를 검색합니다. 실제 시스템에서는 벡터 검색을 사용하겠지만, 여기서는 단순한 문자열 매칭으로 처리합니다.
find_related는 특정 엔티티와 연결된 모든 엔티티를 찾습니다. 관계 타입을 지정하면 해당 타입의 관계만 찾을 수도 있습니다.
query 메서드는 간단한 자연어 질문을 처리합니다. 실제 시스템에서는 LLM을 사용하겠지만, 여기서는 키워드 매칭으로 의도를 파악합니다.
"속한 부서"라는 패턴을 찾으면 belongs_to 관계를 탐색합니다. 실제 현업에서는 어떻게 확장할까요?
이 기본 시스템에 여러 기능을 추가할 수 있습니다. 벡터 검색을 추가하여 이름이 정확히 일치하지 않아도 유사한 엔티티를 찾을 수 있습니다.
다중 홉 탐색을 구현하여 "김철수와 같은 부서의 다른 직원"처럼 두 단계 질문에 답할 수 있습니다. LLM을 연결하여 자연어 질문을 정확히 이해하고, 탐색 결과를 자연스러운 문장으로 답변할 수 있습니다.
하지만 주의할 점도 있습니다. 이 간단한 구현은 학습용입니다.
실제 프로덕션에서는 성능 문제가 있을 수 있습니다. 엔티티가 수만 개가 되면 리스트 탐색이 느려집니다.
따라서 인덱싱이 필요합니다. source 엔티티별로 관계를 그룹화하거나, 그래프 데이터베이스(Neo4j 등)를 사용하는 것이 좋습니다.
또한 이 코드는 양방향 관계를 지원하지 않습니다. "김철수가 속한 부서"는 찾을 수 있지만, "AI연구팀에 속한 사람들"은 찾을 수 없습니다.
양방향 검색을 위해서는 역방향 관계도 추가하거나, find_related를 확장해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
코드를 실행한 김개발 씨는 결과를 확인하고 미소를 짓습니다. "작동한다!
이제 이것을 기반으로 확장하면 되겠네요." 간단한 Graph RAG를 직접 구현해보면 핵심 개념을 확실히 이해할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 처음에는 간단하게 시작하고, 점진적으로 기능을 추가하세요
- 실제 프로덕션에서는 Neo4j 같은 그래프 DB를 사용하는 것이 좋습니다
- 양방향 탐색이 필요하면 역방향 관계도 함께 저장하세요
5. 실습: 관계 기반 질의응답
기본적인 Graph RAG를 만들어본 김개발 씨는 이제 LLM과 결합하여 실용적인 질의응답 시스템을 만들려고 합니다. "사용자가 자연어로 복잡한 질문을 해도 답할 수 있어야 해요." 박시니어 씨가 힌트를 줍니다.
"그래프에서 컨텍스트를 찾고, LLM에게 전달하면 됩니다."
LLM과 Graph RAG를 결합하여 실용적인 질의응답 시스템을 만듭니다. 그래프 탐색으로 관련 정보를 수집하고, LLM에게 컨텍스트로 제공하여 자연스러운 답변을 생성합니다.
전통적인 RAG보다 정확하고 맥락을 이해하는 답변이 가능합니다.
다음 코드를 살펴봅시다.
import openai
class GraphRAGQA:
def __init__(self, graph_rag):
self.graph = graph_rag
self.client = openai.OpenAI()
def answer_question(self, question: str) -> str:
# 1단계: 질문에서 엔티티 추출
entities = self._extract_entities_from_question(question)
# 2단계: 그래프에서 관련 정보 수집
context_data = []
for entity_name in entities:
entity_id = self.graph.find_entity_by_name(entity_name)
if entity_id:
# 엔티티 정보
entity_info = self.graph.entities[entity_id]
context_data.append(f"{entity_info['name']} ({entity_info['type']})")
# 연결된 정보 (1-hop)
related = self.graph.find_related(entity_id)
for rel in related:
context_data.append(
f"{entity_info['name']} -{rel['relation']}-> {rel['name']}"
)
# 3단계: 컨텍스트 구성
context = "\n".join(context_data)
# 4단계: LLM에게 질문과 컨텍스트 전달
prompt = f"""다음 정보를 바탕으로 질문에 답하세요.
정보:
{context}
질문: {question}
답변:"""
response = self.client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "당신은 그래프 정보를 이해하고 정확하게 답변하는 어시스턴트입니다."},
{"role": "user", "content": prompt}
]
)
return response.choices[0].message.content
def _extract_entities_from_question(self, question: str) -> list:
# 간단한 구현: 알려진 엔티티 이름이 질문에 있는지 확인
entities = []
for entity_id, entity_data in self.graph.entities.items():
if entity_data['name'] in question:
entities.append(entity_data['name'])
return entities
# 사용 예시
qa_system = GraphRAGQA(graph)
answer = qa_system.answer_question("김철수가 진행하는 프로젝트는 무엇인가요?")
print(answer)
# "김철수 부장은 ChatBot 개발 프로젝트를 진행하고 있습니다."
김개발 씨는 기본 그래프 검색은 만들었지만, 아직 부족한 점이 있었습니다. 사용자가 자연스러운 문장으로 질문하면 제대로 이해하지 못했습니다.
"김철수 부장님의 현재 업무는?"같은 질문에는 답하기 어려웠죠. "선배님, 자연어 질문을 어떻게 처리해야 할까요?" 김개발 씨가 물었습니다.
박시니어 씨가 답합니다. "LLM을 활용하면 됩니다.
그래프에서 정보를 찾고, LLM에게 전달하여 답변을 만드는 거죠." Graph RAG와 LLM의 결합은 어떻게 동작할까요? 쉽게 비유하자면, 이것은 전문가 팀과 같습니다.
그래프 검색은 리서처처럼 관련 자료를 찾아옵니다. "김철수에 대한 정보는 이것이고, 그가 진행하는 프로젝트는 이것입니다"라고 정리해줍니다.
LLM은 작가처럼 이 자료를 받아서 자연스러운 문장으로 답변을 작성합니다. 두 가지가 협력하여 정확하면서도 읽기 쉬운 답변을 만들어냅니다.
전통적인 RAG와 무엇이 다를까요? 일반 RAG는 질문과 유사한 문서를 찾아 LLM에게 전달합니다.
하지만 "김철수가 진행하는 프로젝트"처럼 관계 기반 질문에는 적합한 문서를 찾기 어렵습니다. "김철수"가 언급된 문서와 "프로젝트"가 언급된 문서가 다를 수 있으니까요.
Graph RAG는 다릅니다. 먼저 "김철수" 엔티티를 찾고, 그와 "works_on" 관계로 연결된 프로젝트를 찾습니다.
이렇게 구조화된 정보를 LLM에게 전달하면, LLM은 정확한 사실을 바탕으로 답변을 생성할 수 있습니다. 바로 이런 접근 방식이 Graph RAG의 핵심입니다.
Graph RAG와 LLM을 결합하면 정확성이 향상됩니다. LLM이 상상으로 답하는 것이 아니라, 그래프에서 확인된 사실만 사용합니다.
또한 관계 기반 추론이 가능해집니다. "A의 B는 C를 한다"처럼 복잡한 관계도 정확히 추적합니다.
무엇보다 자연스러운 답변을 생성할 수 있습니다. 딱딱한 데이터 나열이 아니라, 읽기 편한 문장으로 답합니다.
위의 코드를 한 줄씩 살펴보겠습니다. GraphRAGQA 클래스는 그래프와 LLM을 연결합니다.
answer_question 메서드가 핵심 로직입니다. 먼저 질문에서 엔티티를 추출합니다.
현재는 간단한 문자열 매칭을 사용하지만, 실제로는 NER을 사용할 수 있습니다. 다음으로 추출된 엔티티를 기반으로 그래프를 탐색합니다.
각 엔티티의 정보와 연결된 관계를 수집합니다. "김철수 (person)", "김철수 -works_on-> ChatBot 개발"같은 정보가 모입니다.
수집된 정보를 텍스트 형태로 컨텍스트를 구성합니다. 이것을 LLM에게 프롬프트와 함께 전달합니다.
프롬프트는 "다음 정보를 바탕으로 질문에 답하세요"라고 지시합니다. LLM은 제공된 컨텍스트만 사용하여 답변을 생성합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 고객 지원 챗봇을 개발한다고 가정해봅시다.
고객이 "지난달에 주문한 제품의 배송 상태는?"이라고 물으면, 그래프에서 "고객 -주문-> 주문A", "주문A -포함-> 제품X", "주문A -배송상태-> 배송중"을 찾아냅니다. 이 정보를 LLM에게 전달하면 "고객님께서 지난달 주문하신 제품X는 현재 배송 중입니다"라고 자연스럽게 답변합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 너무 많은 컨텍스트를 LLM에게 전달하는 것입니다.
그래프 탐색으로 수백 개의 관계를 찾아서 모두 전달하면, LLM이 핵심을 놓칠 수 있습니다. 관련성 순위를 매겨서 가장 중요한 정보만 선별하는 것이 좋습니다.
또 다른 실수는 그래프 정보만 맹신하는 것입니다. 그래프에 없는 정보를 물으면 답할 수 없습니다.
따라서 하이브리드 접근이 좋습니다. 그래프에 정보가 있으면 그것을 사용하고, 없으면 전통적인 벡터 검색으로 보완하는 것입니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 시스템을 테스트한 김개발 씨는 놀라움을 감추지 못합니다.
"이제 복잡한 질문에도 정확하게 답변하네요!" Graph RAG와 LLM을 결합하면 정확하면서도 자연스러운 질의응답 시스템을 만들 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 컨텍스트는 관련성 순으로 정렬하여 가장 중요한 정보만 전달하세요
- 그래프에 정보가 없을 때를 대비해 벡터 검색을 보조 수단으로 준비하세요
- 프롬프트에 "제공된 정보만 사용하세요"를 명시하여 환각을 방지하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
ReAct 패턴 마스터 완벽 가이드
LLM이 생각하고 행동하는 ReAct 패턴을 처음부터 끝까지 배웁니다. Thought-Action-Observation 루프로 똑똑한 에이전트를 만들고, 실전 예제로 웹 검색과 계산을 결합한 강력한 AI 시스템을 구축합니다.
AI 에이전트의 모든 것 - 개념부터 실습까지
AI 에이전트란 무엇일까요? 단순한 LLM 호출과 어떻게 다를까요? 초급 개발자를 위해 에이전트의 핵심 개념부터 실제 구현까지 이북처럼 술술 읽히는 스타일로 설명합니다.
프로덕션 RAG 시스템 완벽 가이드
검색 증강 생성(RAG) 시스템을 실제 서비스로 배포하기 위한 확장성, 비용 최적화, 모니터링 전략을 다룹니다. AWS/GCP 배포 실습과 대시보드 구축까지 프로덕션 환경의 모든 것을 담았습니다.
RAG 캐싱 전략 완벽 가이드
RAG 시스템의 성능을 획기적으로 개선하는 캐싱 전략을 배웁니다. 쿼리 캐싱부터 임베딩 캐싱, Redis 통합까지 실무에서 바로 적용할 수 있는 최적화 기법을 다룹니다.
실시간으로 답변하는 RAG 시스템 만들기
사용자가 질문하면 즉시 답변이 스트리밍되는 RAG 시스템을 구축하는 방법을 배웁니다. 실시간 응답 생성부터 청크별 스트리밍, 사용자 경험 최적화까지 실무에서 바로 적용할 수 있는 완전한 가이드입니다.