HSEOM GeckoHSEOM
Instagram

흑섬 TECH 블로그 - 데이터 기반 브리딩 기술

레오파드게코 브리딩에 데이터 분석과 AI 기술을 접목합니다. Python, NumPy를 활용한 체중 관리, 성장 추이 분석, 환경 데이터 시각화 등 실무에서 직접 사용하는 기술을 일반인도 이해하기 쉽게 설명합니다.

주요 카테고리

Data Viz 카테고리

NumPy, Pandas를 활용한 데이터 분석과 시각화 기술을 일상 예제로 쉽게 설명합니다.

17개의 글이 있습니다.

[지식그래프 RAG 4편] 벡터 × 그래프 — 하이브리드 검색

1편의 벡터 검색과 3편의 그래프 탐색을 한 검색으로 합칩니다. neo4j-graphrag의 VectorRetriever(진입점만)와 VectorCypherRetriever(진입 후 그래프 확장)를 실제로 돌려 비교하고, retrieval_query로 진입 노드에서 관계를 따라 관련 맥락을 모읍니다. 벡터로 진입하고 그래프로 넓히는 GraphRAG의 검색 엔진을 완성합니다. 진입+이웃 ego 그래프 실캡처 포함. 지식그래프 RAG 5부작 4편.

카테고리: Data Viz

작성일: 2026-06-15

예상 읽기 시간: 25

Back to Tech
Data Viz·25min read·

[지식그래프 RAG 4편] 벡터 × 그래프 — 하이브리드 검색

1편의 벡터 검색과 3편의 그래프 탐색을 한 검색으로 합칩니다. neo4j-graphrag의 VectorRetriever(진입점만)와 VectorCypherRetriever(진입 후 그래프 확장)를 실제로 돌려 비교하고, retrieval_query로 진입 노드에서 관계를 따라 관련 맥락을 모읍니다. 벡터로 진입하고 그래프로 넓히는 GraphRAG의 검색 엔진을 완성합니다. 진입+이웃 ego 그래프 실캡처 포함. 지식그래프 RAG 5부작 4편.

재료는 다 모였습니다 — 이제 합칠 차례

1편에서 벡터로 '비슷한 것'을 찾았고, 3편에서 그래프로 '연결된 것'을 따라갔습니다.
근데 둘을 따로 썼죠. 이번 4편에서는 이 둘을 한 검색으로 합칩니다.

방식은 간단해요. 벡터로 진입점(가장 비슷한 문서)을 찾고, 거기서 그래프로 주변 맥락을 넓힙니다.
이게 GraphRAG의 검색 엔진이고, 5편에서 여기에 LLM만 붙이면 끝입니다.

4편에서 하는 것: 왜 하이브리드인가 → neo4j-graphrag의 두 리트리버(VectorRetriever / VectorCypherRetriever) → 벡터 진입 + 그래프 확장을 실제로 돌려서 컨텍스트가 얼마나 풍부해지는지 비교. 2·3편의 Neo4j(벡터+관계)를 그대로 씁니다.



벡터 검색만으론 뭐가 아쉬운가요?

벡터 검색은 질문과 가장 비슷한 문서 몇 개를 주지만, 그 문서 주변의 관련 맥락은 빠집니다. RAG에서 LLM에게 진입 문서 본문만 주면 답이 얕아질 수 있습니다. 하이브리드는 진입 문서에 더해, 그래프로 연결된 관련 주제·문서까지 함께 모아 더 풍부한 컨텍스트를 만듭니다.

벡터 검색만 vs 벡터×그래프 하이브리드 — 컨텍스트 풍부도 비교

RAG의 답 품질은 결국 'LLM에게 어떤 맥락을 주느냐'로 갈립니다.
진입 문서 하나만 주는 것과, 그 문서 + 관련 맥락을 함께 주는 건 차이가 크죠.



리트리버가 두 종류라고요?

neo4j-graphrag 패키지는 두 리트리버를 제공합니다. VectorRetriever는 벡터 인덱스로 가장 비슷한 노드(진입점)만 반환합니다. VectorCypherRetriever는 진입점을 찾은 뒤, 우리가 준 retrieval_query(Cypher)를 진입 노드에서 실행해 관계를 따라 주변 맥락까지 모아 반환합니다. 같은 벡터 인덱스를 쓰지만 '확장' 전략이 다릅니다.

VectorRetriever vs VectorCypherRetriever 비교
실습 — 설치 + VectorRetriever (셀 1)
!pip install "neo4j-graphrag[sentence-transformers]" -q
from neo4j import GraphDatabase
from neo4j_graphrag.retrievers import VectorRetriever
from neo4j_graphrag.embeddings.sentence_transformers import SentenceTransformerEmbeddings

driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "***"))
embedder = SentenceTransformerEmbeddings(model="jhgan/ko-sroberta-multitask")

vr = VectorRetriever(driver, index_name="doc_embedding",
                     embedder=embedder, return_properties=["text"])
for item in vr.search(query_text="겨울에 너무 추울 때 어떻게 관리하죠?", top_k=2).items:
    print(item.content)

embedder는 2편과 같은 한국어 모델. VectorRetriever는 벡터 인덱스(doc_embedding)에서 진입 문서만 돌려줍니다.



벡터로 진입해서 그래프로 넓히면?

VectorCypherRetriever에 retrieval_query를 주면, 벡터로 찾은 진입 노드(node)에서 시작하는 Cypher가 실행됩니다. 진입 노드의 ABOUT 주제와 SIMILAR_TO 이웃을 따라가며 관련 맥락을 모아 한 번에 반환합니다. 진입은 벡터가, 확장은 그래프가 담당하는 구조입니다.

하이브리드 동작 흐름 — 질문 임베딩, 벡터 진입, retrieval_query 확장, 컨텍스트 반환

같은 질문을 두 방식으로 돌리면 반환되는 컨텍스트가 이렇게 달라집니다.

VectorRetriever(진입 문서만) vs VectorCypherRetriever(진입+연결주제+확장문맥) 결과 비교

'온도' 진입 문서들을 하나씩 풀어보면 더 구체적입니다. 같은 검색을 Neo4j Browser의 Table 뷰로 돌려, 진입 문서마다 어떤 주제·문맥이 딸려오는지 그대로 펼쳤습니다.

Neo4j Browser Table 뷰 실캡처 — 진입문서ID·진입문서·연결주제·확장문맥 컬럼, 온도 진입 문서 3건의 실데이터

컬럼이 곧 GraphRAG가 모으는 재료입니다. 진입문서는 벡터로 잡은 시작점, 연결주제·확장문맥은 거기서 그래프로 번져 모은 것이죠. 진입 문서마다 딸려오는 주제가 다른 게 보입니다(맨 아래 "3 records ... 169 ms"가 실제 실행 흔적).

실습 — VectorCypherRetriever (셀 2)
from neo4j_graphrag.retrievers import VectorCypherRetriever

# 진입 노드(node)에서 그래프로 확장. 강하게 유사한(score>0.7) 이웃만.
retrieval_query = """
MATCH (node)-[:ABOUT]->(seedTopic:Topic)
OPTIONAL MATCH (node)-[s:SIMILAR_TO]-(nb:Doc)-[:ABOUT]->(nbTopic:Topic)
WHERE s.score > 0.7
RETURN node.text AS entry, seedTopic.name AS topic,
       collect(DISTINCT nbTopic.name) AS linked_topics,
       collect(DISTINCT nb.text)[0..3] AS expanded
"""
vcr = VectorCypherRetriever(driver, index_name="doc_embedding",
                            retrieval_query=retrieval_query, embedder=embedder)
print(vcr.search(query_text="겨울에 너무 추울 때 어떻게 관리하죠?", top_k=1).items[0].content)
python · 벡터검색 vs 하이브리드 (실제 실행)
[A] VectorRetriever — 진입 문서만
  · 겨울철 실내가 20도 아래로 떨어지면 히터나 온열 패드로 보온...
  · 갑자기 사료를 거부할 때는 온도 저하나 스트레스가 원인...

[B] VectorCypherRetriever — 진입 + 그래프 확장
  진입 문서: 겨울철 실내가 20도 아래로 떨어지면 히터나 온열 패드로 보온...
  진입 주제: 온도
  연결된 주제: ['환경', '습도', '탈피']
  확장 문맥:
    - 은신처는 따뜻한 쪽과 시원한 쪽 양쪽에 두어 개체가 스스로 온도를...
    - 전체 습도는 40~50%로 건조하게 유지하되...
    - 허물이 발가락이나 눈 주위에 남으면 습한 은신처로 탈피를 돕습니다...

[A]는 진입 문서 2개뿐, [B]는 같은 진입 문서에 연결된 주제·문맥까지. LLM에 줄 재료가 확 풍부해졌죠.

사실 처음엔 SIMILAR_TO를 전부 확장했더니, 연결된 주제가 거의 모든 주제로 딸려 나오더라고요. ㅎㅎ
그래서 WHERE s.score > 0.7로 강하게 유사한 이웃만 남겼더니, 그제서야 '관련 맥락'다워졌습니다.

진입 노드에서 실제로 어떤 이웃이 딸려오는지 Neo4j Browser로 봤습니다.

Neo4j Browser 실캡처 — 진입 노드 중심으로 ABOUT 주제와 SIMILAR_TO 이웃이 확장된 ego 그래프

가운데가 벡터로 찾은 진입 문서고, 거기서 뻗어나간 게 그래프로 확장된 이웃들입니다.
벡터가 '어디서 시작할지'를, 그래프가 '거기서 무엇을 더 가져올지'를 정한 거예요.



정리

4편 핵심만 정리합니다.

  1. 벡터 검색만으론 진입 문서 본문만 얻습니다. 하이브리드는 주변 맥락까지 모읍니다.
  2. neo4j-graphrag의 VectorRetriever는 진입점만, VectorCypherRetriever는 진입 후 그래프 확장까지 합니다.
  3. retrieval_query에 진입 노드(node)에서 시작하는 Cypher를 주면 관계를 따라 맥락이 수집됩니다.
  4. SIMILAR_TO를 score로 거르면 '관련 있는' 맥락만 깔끔하게 확장됩니다.
  5. 벡터(진입) + 그래프(확장) = GraphRAG의 검색 엔진. 이제 LLM만 붙이면 됩니다.
4편 정리와 5편(GraphRAG 완성) 예고

검색은 여기서 완성됐습니다. 벡터로 시작해 그래프로 넓히는, 꽤 강력한 엔진이 손에 들어왔어요.
마지막 5편에서는 이 풍부한 맥락을 LLM에게 건네 근거 있는 답변을 만들고, 평범한 RAG와 비교해봅니다. GraphRAG의 완성입니다.

#하이브리드검색#VectorCypherRetriever#neo4j-graphrag#벡터#지식그래프#RAG#GraphRAG#Neo4j