전통 RAG의 한계
전통적인 RAG 아키텍처는 벡터 데이터베이스(VectorDB)를 중심으로 의미적 유사성 컨텍스트를 검색하여 대규모 언어 모델(LLM)이 재학습 없이 최신 지식을 얻을 수 있게 합니다. 이 아키텍처는 현재 다양한 AI 비즈니스 시나리오에서 널리 사용되고 있으며, 예를 들어 질담봇, 지능형 고객 서비스, 사설 지식库 검색 등이 있습니다.
RAG는 지식 강화를 통해 LLM 환각 문제를 일부 완화할 수 있지만, 여전히 정확도가 높지 않은 문제에 직면해 있습니다. 이는 정보 검색 프로세스의 고유한 제약(예: 정보 검색 관련성, 검색 알고리즘 효율성, 임베딩 모델)과 LLM 능력에 대한 큰 의존성으로 인해 결과 생성 과정에서 더 많은 불확실성이 발생하기 때문입니다.
아래 이미지는 RAG 분야의 유명한 논문에서 가져온 것으로: 《Seven Failure Points When Engineering a Retrieval Augmented Generation System》, 이는 RAG 아키텍처 사용 시 자주 발생하는 문제들을 요약한 것입니다(빨간색 상자 부분, 원작자는 7가지 실패 지점이라고 부릅니다).
이에는 다음과 같은 문제들이 포함됩니다:
- 내용 누락(Missing Content): 사용자가 원하는 답변이 지식库에 없을 경우, LLM이 의미 없는 답변을 제공합니다.
- 상위 순위 문서 누락(Missed Top Ranked): 텍스트 분할 방식, 크기, 임베딩 모델 등의 영향으로 벡터 데이터베이스에서 검색된 top-k가 가장 정확한 답변이 아닐 수 있습니다.
- 컨텍스트 내 없음(Not in Context): 이전 항목과 유사하게, 다양한 처리 후에 궁극적으로 추출된 컨텍스트의 질이 낮습니다.
- 형식 오류(Wrong Format): 검색 결과가 특정 형식이 아니며, LLM이 이를 잘 인식하고 분석하지 못합니다.
- 미추출(Not Extracted): 프롬프트에 관련 없는 정보가 많이 포함되어 LLM 판단에 영향을 미치며, 즉 컨텍스트에 많은 노이즈가 포함됩니다.
- 답변 불완전(Incomplete): 생성된 답변이 충분히 완전하지 않습니다.
- 부정확한 구체성(Incorrect Specificity): 답변이 응답에 반환되지만, 사용자의 요구를 충족시키기에 구체적이지 않거나 너무 구체적입니다.
이러한 문제들에 대한 최적화 방법은 현재 존재하지만, 여전히 우리의 기대치와는 차이가 있습니다.
따라서 의미적 텍스트 블록 기반 검색 외에도, 업계는 데이터 간의 연관성에 초점을 맞춘 새로운 데이터 검색 형식을 탐색하고 있습니다. 이러한 연관성은 관계형 모델의 논리적 강한 의존성과는 달리, 일정程度的 의미적 연관성을 가집니다. 예를 들어, 사람과 휴대폰은 두 개의 독립된 엔티티이며 벡터 데이터베이스에서의 유사성은 매우 낮을 것입니다. 하지만 실제 생활 시나리오를 고려할 때, 사람과 휴대폰의 관계는 매우 밀접합니다. 만약 한 사람의 정보를 검색한다면, 그의 주변 정보(예: 어떤 모델의 휴대폰을 사용하는지, 사진을 찍는 것을 좋아하는지 게임을 하는지 등)에도 관심이 있을 가능성이 높습니다.
이러한 관계형 모델을 기반으로, 지식库에서 검색된 컨텍스트는 특정 시나리오에서 더 효과적일 수 있습니다. 예를 들어, "회사에서 휴대폰으로 사진을 찍는 것을 좋아하는 사람은 누구인가"와 같은 질문입니다.
지식 그래프를 통한 데이터 관계 표현
지식库의 추상적 데이터 관계를 효과적으로 설명하기 위해 지식 그래프 개념을 도입합니다. 이는 더 이상 2차원 표로 데이터를 구성하지 않고, 그래프 구조를 사용하여 관계를 설명합니다. 여기서의 관계는 고정된 패러다임 제약이 없으며, 다음과 같은 인물 관계도와 유사합니다:

위 그림에는 가장 중요한 세 가지 요소가 있습니다: 객체(인물), 속성(신분), 관계입니다. 이러한 데이터를 저장하기 위해 전용 데이터베이스 제품이 지원되며, 이것이 바로 그래프 데이터베이스입니다.
그래프 데이터베이스(GraphDB)
그래프 데이터베이스는 NoSQL의 일종으로, 비교적 유연한 스키마 정의를 가지며 실제 세계의 임의의 연관 관계를 간단하고 효율적으로 표현할 수 있습니다. 그래프 데이터베이스의 주요 개념은 다음과 같습니다:
- 엔티티(Entity): 또는 정점(vertex) 또는 노드(node)라고도 하며, 그래프 구조의 객체입니다.
- 엣지(Edge): 두 엔티티를 연결하는 경로로, 엔티티 간의 관계입니다.
- 속성(Property): 엔티티 또는 엣지의 특징을 구체적으로 설명하는 데 사용됩니다.
이러한 개념들은 앞서 언급한 인물 관계도에 해당합니다.
스키마 정의 및 데이터 작업 수준에서, 인기 있는 그래프 데이터베이스 NebulaGraph를 예로 들어보겠습니다:
# 그래프 공간 생성
CREATE SPACE IF NOT EXISTS test(vid_type=FIXED_STRING(256), partition_num=1, replica_factor=1);
USE test;
# 노드와 엣지 생성
CREATE TAG IF NOT EXISTS entity(name string);
CREATE EDGE IF NOT EXISTS relationship(relationship string);
CREATE TAG INDEX IF NOT EXISTS entity_index ON entity(name(256));
# 데이터 쓰기
INSERT VERTEX entity (name) VALUES "1":("神州数码");
INSERT VERTEX entity (name) VALUES "2":("云基地");
INSERT EDGE relationship (relationship) VALUES "1"->"2":("建立了");
...
그래프 데이터베이스의 쿼리 구문은 Cypher 표준을 따릅니다:
MATCH p=(n)-[*1..2]-() RETURN p LIMIT 100;
위 구문은 NebulaGraph에서 특정 노드에서 시작하여 1에서 2개의 엣지로 연결된 경로를 최대 100개 찾아 반환합니다.
시각화된 그래프 구조로 표현하면 다음과 같습니다:
GraphRAG
VectorRAG의 개념을 차용하여 그래프 데이터베이스를 통해 검색 증강을 구현하면 GraphRAG 아키텍처가 발전합니다. 전체 프로세스는 VectorRAG와 차이가 없으며, 새로운 지식의 저장 및 검색 모두 지식 그래프를 통해 구현하여 VectorRAG의 추상적 관계 이해력이 약한 문제를 해결합니다.
일부 AI 프레임워크 도구를 사용하면 GraphRAG를 쉽게 구현할 수 있습니다. 다음은 LlamaIndex와 그래프 데이터베이스 NebulaGraph를 사용하여 구현한 간단한 GraphRAG 애플리케이션입니다:
import os
from llama_index.core import KnowledgeGraphIndex, SimpleDirectoryReader
from llama_index.core import StorageContext
from llama_index.graph_stores.nebula import NebulaGraphStore
from llama_index.llms.openai import OpenAI
from pyvis.network import Network
from llama_index.core import (
Settings,
SimpleDirectoryReader,
KnowledgeGraphIndex,
)
# 매개변수 준비
os.environ['OPENAI_API_KEY'] = "sk-proj-xxx"
os.environ["NEBULA_USER"] = "root"
os.environ["NEBULA_PASSWORD"] = "nebula"
os.environ["NEBULA_ADDRESS"] = "10.3.xx.xx:9669"
space_name = "heao"
edge_types, rel_prop_names = ["relationship"], ["relationship"]
tags = ["entity"]
llm = OpenAI(temperature=0, model="gpt-3.5-turbo")
Settings.llm = llm
Settings.chunk_size = 512
# Nebula 데이터베이스 인스턴스에 연결
graph_store = NebulaGraphStore(
space_name=space_name,
edge_types=edge_types,
rel_prop_names=rel_prop_names,
tags=tags,
)
storage_context = StorageContext.from_defaults(graph_store=graph_store)
hosts = graph_store.query("SHOW HOSTS")
print(hosts)
# 지식 그래프 추출 및 데이터베이스에 쓰기
documents = SimpleDirectoryReader(
"data"
).load_data()
kg_index = KnowledgeGraphIndex.from_documents(
documents,
storage_context=storage_context,
max_triplets_per_chunk=2,
space_name=space_name,
edge_types=edge_types,
rel_prop_names=rel_prop_names,
tags=tags,
max_knowledge_sequence=15,
)
# 정보 검색 및 답변 생성
query_engine = kg_index.as_query_engine()
response = query_engine.query("神州数码云基地在哪里?")
print(response)
VectorRAG는 일정 수준의 사실적 질문을 처리하는 데 강점이 있지만, 복잡한 관계 이해에는 약점이 있습니다. 반면 GraphRAG는 이 점을 보완합니다. 이 둘을 결합하면 이론적으로 더 나은 결과를 얻을 수 있습니다.
HybridRAG — 차세대 RAG 아키텍처
우리는 데이터를 VectorDB와 GraphDB에 각각 저장할 수 있습니다. 문제와 관련된 결과를 각각 벡터화 검색과 그래프 검색을 통해 얻은 후, 이러한 결과를 연결하여 통합된 컨텍스트를 형성하고, 마지막으로 조합된 컨텍스트를 대규모 언어 모델에 전달하여 응답을 생성합니다. 이것이 바로 HybridRAG 아키텍처입니다.
최근에 NVIDIA 팀의 Benika Hall 등이 발표한 논문 《HybridRAG: Integrating Knowledge Graphs and Vector Retrieval Augmented Generation for Efficient Information Extraction》에서 이 아이디어를 제안하고, 금융 서비스 업계의 데이터 통합, 위험 관리, 예측 분석 등의 시나리오에서 비교 검증을 수행했습니다. 관련 문서에서 컨텍스트를 추출하여 의미 있고 일관된 응답을 생성해야 할 때 VectorRAG가 뛰어난 성능을 보이는 반면, GraphRAG는 금융 문서에서 구조화된 정보를 추출하여 더 정확하고 컨텍스트 인지 응답을 생성할 수 있습니다. 그러나 GraphRAG는 추상적인 질문 작업이나 명시적으로 엔티티가 언급되지 않은 질문에서는 일반적으로 성능이 좋지 않습니다.
이 논문은 마지막으로 VectorRAG, GraphRAG 및 HybridRAG의 테스트 결과를 제공했습니다:
표에서 F는 충실도(faithfulness)를 나타내며, 생성된 답변이 제공된 컨텍스트에서 얼마程度的 추론될 수 있는지 측정합니다. AR은 답변 관련성(answer relevance)으로, 생성된 답변이 원래 문제를 얼마 degrees 해결했는지 평가합니다. CP는 컨텍스트 정확도(context precision), CR은 컨텍스트 재현율(context recall)을 의미합니다.
간단한 HybridRAG 구현
다음 예제에서는 Llama_index를 애플리케이션 프레임워크로 사용하고, TiDB Vector를 벡터 데이터베이스로, NebulaGraph를 그래프 데이터베이스로 사용하여 간단한 HybridRAG 프로세스를 구현합니다:
import os
from llama_index.core import KnowledgeGraphIndex, VectorStoreIndex, StorageContext, Settings
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.graph_stores.nebula import NebulaGraphStore
from llama_index.llms.openai import OpenAI
from llama_index.vector_stores.tidbvector import TiDBVectorStore
TIDB_CONN_STR = "mysql+pymysql://xx.root:xx@gateway01.eu-central-1.prod.aws.tidbcloud.com:4000/test?ssl_ca=isrgrootx1.pem&ssl_verify_cert=true&ssl_verify_identity=true"
os.environ["OPENAI_API_KEY"] = "sk-proj-xxx"
os.environ["NEBULA_USER"] = "root"
os.environ["NEBULA_PASSWORD"] = "nebula"
os.environ["NEBULA_ADDRESS"] = "10.3.xx.xx:9669"
llm = OpenAI(temperature=0, model="gpt-3.5-turbo")
Settings.llm = llm
Settings.chunk_size = 512
class HybridRAG:
def __init__(self):
# 벡터 데이터베이스 초기화
tidbvec = TiDBVectorStore(
connection_string=TIDB_CONN_STR,
table_name="semantic_embeddings",
distance_strategy="cosine",
vector_dimension=1536, # 차원은 모델에 의해 결정됨
drop_existing_table=False,
)
tidb_vec_index = VectorStoreIndex.from_vector_store(tidbvec)
self.vector_db = tidb_vec_index
# 지식 그래프 초기화
graph_store = NebulaGraphStore(
space_name="heao",
edge_types=["relationship"],
rel_prop_names=["relationship"],
tags=["entity"],
)
storage_context = StorageContext.from_defaults(graph_store=graph_store)
kg_index = KnowledgeGraphIndex.from_documents(
[],
storage_context=storage_context,
max_triplets_per_chunk=2,
max_knowledge_sequence=15,
)
self.kg = kg_index
# 언어 모델 초기화
self.llm = llm
# 임베딩 모델 초기화
self.embeddings = OpenAIEmbedding()
def vector_search(self, query):
# 벡터 데이터베이스에서 관련 텍스트 검색
results = self.vector_db.as_retriever().retrieve(query)
print(results)
return [result.node for result in results]
def graph_search(self, query):
# 지식 그래프에서 관련 정보 검색
results = self.kg.as_retriever().retrieve(query)
print(results)
return [result.node for result in results]
def generate_answer(self, query):
vector_context = self.vector_search(query)
graph_context = self.graph_search(query)
combined_context = "\n".join(vector_context) + "\n" + graph_context
prompt = f"Question: {query}\nContext: {combined_context}\nAnswer:"
return self.llm.generate(prompt)
# 사용 예시
hybrid_rag = HybridRAG()
question = "TiDB의 어떤 버전부터 자원 관리 기능을 지원하기 시작했나요?"
answer = hybrid_rag.generate_answer(question)
print(answer)
위 구현에서는 단순히 벡터 데이터베이스와 그래프 데이터베이스에서의 검색 결과를 조합했을 뿐입니다. 실제 애플리케이션에서는 관련 reranker 모델을 도입하여 추가적으로 정렬을 통해 컨텍스트 정확도를 높일 수 있습니다. 최종적으로 어떤 RAG 아키텍처를 사용하든, 우리의 목표는 대규모 언어 모델에 전달되는 컨텍스트가 더 완전하고 효과적이도록 보장하여 대규모 언어 모델이 더 정확한 답변을 제공하도록 하는 것입니다.
결론
RAG 아키텍처는 이미 대규모 언어 모델 능력을 향상시키는 효과적인 솔루션입니다. 다양한 비즈니스 시나리오에서 정확도를 안정적으로 유지하지 못하기 때문에, Advanced RAG, GraphRAG, HybridRAG, RAG2.0 등 일련의 변형된 RAG 아키텍처가 파생되었습니다. 이들 간에는 보완 관계, 최적화 및 강화, 그리고 완전히 새로운 접근 방식 등이 있습니다. 이는 LLM 애플리케이션 최적화가 여전히 빠르게 변화하고 있음을 보여줍니다.