여러분의 RAG 시스템이 온도 모니터링에 관한 6개의 청크를 검색해냈다고 가정해 보겠습니다. 모두 의미론적(semantic)으로는 높은 관련성을 가집니다. 하지만 그 어떤 청크도 해당 온도 센서가 실험실정보관리시스템(LIMS)으로 데이터를 전송하고, 이것이 배치 출하(batch release) 결정을 트리거하며, 따라서 센서를 변경하면 3개의 적격성평가 프로토콜(qualification protocols)이 무효화된다는 사실을 명시하지 못했습니다. 벡터 검색은 유사한 텍스트를 찾아냈지만, ’관계(relationship)’는 찾아내지 못한 것입니다.

이것이 바로 토이(toy) RAG와 프로덕션 RAG를 가르는 결정적인 실패 모드(failure mode)입니다. 의미론적 유사성은 관계적 진실(relational truth)이 아닙니다. 벡터 데이터베이스는 여러분의 질문과 비슷하게 들리는 것은 알려줄 수 있지만, 여러분이 변경하려는 대상에 의존하고 있는 것이 무엇인지는 결코 알려주지 못합니다.

해결책은 더 강력한 임베딩 모델이 아닙니다. 바로 아키텍처의 근본적인 전환입니다.

3중 데이터베이스 원칙 (The Three-Database Principle)

프로덕션 검색 시스템은 본질적으로 서로 다른 세 가지 유형의 질문에 답할 수 있어야 합니다:

질문 유형 예시 최적의 엔진
“텍스트에 무엇이 적혀 있는가?” “알람 테스트에 대해 SOP에 어떻게 기술되어 있는가?” 벡터 검색 (Vector search)
“요소들이 어떻게 연결되어 있는가?” “이 애플리케이션에 의존하는 시스템은 무엇인가?” 그래프 순회 (Graph traversal)
“현재 상태는 무엇인가?” “이 시스템은 밸리데이션되었는가? 현재 버전은 무엇인가?” 관계형 쿼리 (Relational query)

단일 데이터베이스로는 이 세 가지 영역을 모두 완벽하게 다룰 수 없습니다. pgvector를 적용한 PostgreSQL은 중간 규모의 벡터 검색을 지원하지만, 벡터 수가 1,000만 건을 초과하면 심각한 병목 현상이 발생합니다. 그래프 데이터베이스는 문서를 저장할 수는 있지만, 전문 검색(full-text search)이나 트랜잭션 메타데이터를 관리하기에는 적절한 도구가 아닙니다. 벡터 데이터베이스는 페이로드(payload)를 보관할 수는 있으나, GROUP BY, 집계(aggregation), 감사 추적(audit trail) 기능은 지원하지 못합니다.

실제 환경에서 견고하게 작동하는 해법은 3중 구조(triad) 아키텍처입니다:

                         USER QUERY                       
                              │                           
                              ▼                           
                    ┌─────────────────┐                   
                    │  Query Planner  │                   
                    │  Intent → Route │                   
                    └────────┬────────┘                   
                             │                            
          ┌──────────────────┼──────────────────┐         
          ▼                  ▼                  ▼         
    ┌───────────┐    ┌──────────────┐    ┌──────────────┐ 
    │ PostgreSQL │    │    Qdrant    │    │   Memgraph   │
    │           │    │              │    │              │ 
    │ Truth     │    │ Similarity   │    │ Relationships│ 
    │ Metadata  │    │ Embeddings   │    │ Traversals   │ 
    │ Audit     │    │ Hybrid search│    │ Communities  │ 
    └─────┬─────┘    └──────┬───────┘    └──────┬───────┘ 
          │                 │                   │         
          └────────┬────────┴───────────────────┘         
                   ▼                                      
          ┌─────────────────┐                             
          │  Result Fusion  │                             
          │  RRF + Reranking│                             
          └────────┬────────┘                             
                   ▼                                      
          ┌─────────────────┐                             
          │   LLM Generate  │                             
          └─────────────────┘                             

각 데이터베이스는 특정 유형의 진실(truth)에 대한 단일 권한 소스(authoritative source) 역할을 수행합니다:

  • PostgreSQL — 선언적 진실(declarative truth). 현재 승인된 상태는 무엇인가? 소유자는 누구인가? 마지막으로 밸리데이션된 시점은 언제인가?
  • Qdrant — 증거적 진실(evidentiary truth). 이 주장을 논의하거나 뒷받침하는 콘텐츠는 무엇인가?
  • Memgraph — 구조적 진실(structural truth). 무엇이 무엇과 어떤 경로를 통해 연결되어 있는가?

이러한 분리는 불필요한 복잡성이 아닙니다. 관계를 환각(hallucination)하는 시스템과, 자신의 추론 과정을 근거와 함께 투명하게 증명할 수 있는 시스템을 가르는 본질적인 차이입니다.

PostgreSQL: 단일 기록 시스템 (System of Record)

PostgreSQL은 스키마가 영구적으로 유지되는 유일한 저장소입니다. 그 외의 모든 것은 파생된 인덱스(derived index)에 불과합니다.

PostgreSQL에 보관해야 하는 핵심 데이터:

documents           — 원본 텍스트, 소스 메타데이터, 타임스탬프, 버전
chunks              — 오프셋, 해시, 토큰 수가 포함된 분할 텍스트
entities            — UUID가 부여된 표준 엔티티 레지스트리 (Canonical entity registry)
chunk_entities      — 청크와 엔티티를 연결하는 브리지 테이블 (Bridge table)
provenance          — 추출 방식, 신뢰도, 원본 소스 문서 (출처 정보)
audit_events        — 누가, 언제, 어떤 근거로 무엇을 수행했는지에 대한 감사 기록
permissions         — 사용자의 접근 권한 (ACL, 테넌트 격리)

PostgreSQL은 또한 tsvector와 pg_trgm을 통해 전문 검색(full-text search)을 처리합니다. 이는 사람들이 생각하는 것보다 훨씬 중요합니다. 생명과학 및 엔터프라이즈 데이터에는 부품 번호, 프로토콜 ID, 규제 조항 인용 등 정확한 식별자(exact identifiers)가 가득 차 있으며, 이러한 영역에서는 BM25 키워드 매칭이 밀집 벡터 검색(dense vector search)을 압도적인 성능 차이로 앞섭니다. 사용자가 URS-EMS-003을 검색했을 때 필요한 것은 ’사용자 요구사항 명세서’라는 개념과 의미론적으로 유사한 10개의 청크가 아니라, 정확히 일치하는 해당 문서입니다.

CREATE TABLE chunks (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    document_id UUID REFERENCES documents(id),
    content     TEXT NOT NULL,
    embedding   vector(1536),
    tsv         tsvector GENERATED ALWAYS AS
                (to_tsvector('english', content)) STORED
);

CREATE INDEX idx_chunks_tsv ON chunks USING GIN(tsv);
CREATE INDEX idx_chunks_embedding ON chunks
    USING hnsw (embedding vector_cosine_ops);

PostgreSQL의 embedding 컬럼은 보조 인덱스(secondary index)일 뿐, 기본 벡터 저장소가 아닙니다. 순수한 ANN 속도보다 동일 위치에 있는 메타데이터 필터링(co-located metadata filtering)이 더 중요한 쿼리 — 예를 들어 “지난 90일 동안 승인된 문서 중에서만 의미론적으로 유사한 청크 검색”과 같은 경우에 활용하십시오.

Qdrant: 시맨틱 메모리 (Semantic Memory)

Qdrant는 고성능 밀집 벡터 검색(dense retrieval)을 전담합니다. 하이브리드 RAG를 위한 Qdrant의 킬러 기능은 단일 컬렉션 내에서 밀집 벡터와 희소 벡터(sparse vectors)를 기본적으로 지원하고, 단 한 번의 쿼리 안에서 상호 순위 융합(Reciprocal Rank Fusion, RRF)을 통해 이를 결합할 수 있다는 점입니다.

엔터프라이즈 텍스트에는 자연어적 의미 표현과 정확한 식별자가 공존하기 때문에 이는 매우 중요합니다. 밀집 벡터는 “온도 모니터링 시스템 업그레이드 시 밸리데이션 영향도”와 같은 문장을 처리하고, 희소 벡터는 “Vaisala viewLinc URS-EMS-004 21 CFR Part 11”과 같은 식별자를 처리합니다. 두 가지 모두가 반드시 필요합니다.

results = client.query_points(
    collection_name="chunks",
    prefetch=[
        Prefetch(using="dense", query=dense_embedding, limit=50),
        Prefetch(using="sparse", query=sparse_embedding, limit=50),
    ],
    query=FusionQuery(fusion=Fusion.RRF),
    limit=20,
    query_filter=Filter(must=[
        FieldCondition(key="tenant_id",
                        match=MatchValue(value="tenant_12"))
    ])
)

Qdrant의 페이로드 필터링(payload filtering)은 검색 후가 아니라 ANN 검색 도중에(사전 필터링, pre-filtering) 적용됩니다. 대규모 데이터 환경에서 이는 3ms와 300ms라는 극적인 지연 시간 차이를 만들어냅니다. 페이로드에는 PostgreSQL로 역추적하고 Memgraph로 확장할 수 있는 브리지 식별자 — chunk_id, document_id, entity_ids — 가 담깁니다.

페이로드 스키마는 세 저장소 간의 연계를 정의하는 인터페이스 규약(contract)입니다:

{
    "chunk_id": "c1a2b3c4-...",
    "document_id": "doc_9910",
    "entity_ids": ["ent_alpha", "ent_beta"],
    "tenant_id": "tenant_12",
    "doc_type": "validation_protocol",
    "status": "approved"
}

Memgraph: 관계 엔진 (Relationship Engine)

Memgraph는 C++로 구축된 Cypher 호환 인메모리(in-memory) 그래프 데이터베이스입니다. 엔티티를 노드로, 관계를 엣지로 저장하며 양쪽 모두에 속성(property)을 부여할 수 있습니다.

이 단계에서 아키텍처는 단순한 “유사 텍스트 탐색”을 넘어 “요소들이 어떻게 연결되어 있는지 이해하는 구조”로 도약합니다.

온도 모니터링 시스템을 업그레이드하는 변경 요청(change request)을 예로 들어보겠습니다. 벡터 검색은 온도 모니터링에 관한 문서를 찾아냅니다. 반면 Memgraph는 변경의 영향 범위(blast radius)를 정확히 파악합니다:

EMS-001 (Temperature Monitoring)                              
    │                                                         
    ├── implements ──── REQ-123 (Temperature Range Monitoring)
    ├── implements ──── REQ-124 (Alarm Response Protocol)     
    ├── has_risk ────── RISK-21 (Data Integrity Risk)         
    ├── validated_by ── OQ-44 (Operational Qualification)     
    ├── produces_data_for ── QMS-01 (Batch Release)           
    ├── integrates_with ─── LIMS-01 (LabVantage)              
    └── documented_by ───── SOP-100 (Environmental Monitoring)

“온도 모니터링 업그레이드”에 대한 벡터 검색만으로는 이 시스템을 변경할 경우 3개의 적격성평가 프로토콜이 무효화되고 배치 출하 결정에 영향을 미친다는 사실을 결코 알아낼 수 없습니다. 그래프는 이러한 연결 고리를 명시적이고 순회 가능(traversable)하게 만듭니다.

이 인접 노드들을 추출하기 위한 Cypher 쿼리는 다음과 같습니다:

MATCH (seed:Entity {id: $system_id})
MATCH path = (seed)-[r:RELATION*1..2]-(neighbor:Entity)
MATCH (neighbor)<-[:MENTIONS]-(chunk:Chunk)
RETURN path, collect(DISTINCT chunk.id) AS expanded_chunks

Memgraph는 MAGE 알고리즘 라이브러리를 통한 커뮤니티 탐지(community detection)도 지원합니다. 엔티티 그래프에 루뱅 클러스터링(Louvain clustering)을 실행하고, 각 커뮤니티별로 LLM 요약을 생성한 뒤, 해당 요약을 Qdrant에 임베딩으로 저장합니다. 이제 “전체 아키텍처에 걸친 주요 위험 요소는 무엇인가?“와 같은 포괄적인 질문이 들어오면 커뮤니티 요약과 먼저 매칭한 다음 세부 구성 청크로 드릴다운(drill down)할 수 있습니다.

핵심 검색 패턴 (The Retrieval Patterns)

아키텍처의 진정한 힘은 개별 저장소가 아니라, 이들을 유기적으로 결합하는 검색 패턴에서 나옵니다.

패턴 1: 벡터 시드 기반 그래프 확장 (Vector-seed graph expansion)

가장 보편적인 패턴입니다. 의미론적 유사성으로 시작하여 관계를 통해 검색 범위를 확장합니다.

질문: "이 시스템을 뒷받침하는 밸리데이션 증거는 무엇인가?"

1단계: 질문 임베딩 생성 → Qdrant ANN 검색 → 상위 K개 청크 도출
2단계: Qdrant 페이로드에서 entity_ids 추출
3단계: Memgraph: 시드 엔티티로부터 1~2 홉(hop) 순회 수행
4단계: 그래프에서 연결된 청크 ID 수집
5단계: PostgreSQL: 식별된 모든 청크의 전문(full text) 하이드레이션(hydrate)

벡터 검색은 진입점(entry point)을 찾아냅니다. 그래프 확장은 벡터 유사도만으로는 놓칠 수밖에 없는 컨텍스트를 발견합니다. “온도 센서 → LIMS → 배치 출하”로 이어지는 연결 사슬을 포착해내는 것이 바로 이 패턴입니다.

구조적 제약 조건을 먼저 설정한 후, 정의된 경계 내에서 의미론적 검색을 수행합니다.

질문: "동일한 시스템과 관련된 유사한 일탈(deviation) 사례를 찾아라"

1단계: Memgraph: 질의 엔티티와 연결된 시스템 식별
2단계: 해당 시스템들과 연결된 모든 청크 ID 수집
3단계: Qdrant: chunk_id에 대한 MatchAny 필터를 적용하여 벡터 검색 실행
4단계: 의미론적으로 유사하면서도 구조적으로 연관된 결과 반환

이 방식은 관련 없는 정보의 검색을 획기적으로 줄여줍니다. 전체 코퍼스에서 유사한 일탈을 검색하는 대신, 연관 시스템의 서브그래프 내에서만 집중적으로 검색을 수행합니다.

패턴 3: 글로벌 커뮤니티 검색 (Global community retrieval)

포괄적이고 주제 중심적인 질문을 처리하기 위한 패턴입니다.

질문: "이번 분기의 모든 일탈에 걸친 주요 테마는 무엇인가?"

1단계: Memgraph: 커뮤니티 요약 검색 (사전 연산됨)
2단계: Qdrant: 질문 임베딩과 커뮤니티 임베딩 매칭
3단계: 상위 커뮤니티 요약을 상위 수준 컨텍스트로 반환
4단계: 필요에 따라 구성 청크로 세부 드릴다운 수행

이는 Microsoft GraphRAG의 “글로벌 검색(global search)” 패턴을 멀티 저장소 아키텍처에 맞게 변형하여 적용한 것입니다.

패턴 4: 융합 기반 병렬 하이브리드 검색 (Parallel hybrid with fusion)

어느 저장소가 가장 유용할지 사전에 알 수 없는 복잡한 질의를 위한 패턴입니다.

질문 ──┬──► Qdrant (시맨틱)    ──┐                                   
       ├──► Memgraph (그래프)  ──┼──► RRF 융합 ──► 리랭커(Reranker) ──► LLM
       └──► PostgreSQL (BM25)  ──┘                                   

세 저장소에 병렬로 쿼리를 전송하고, 수집된 결과를 상호 순위 융합(Reciprocal Rank Fusion, RRF)을 통해 결합합니다:

RRF_Score(d) = Σ  w_m / (k + r_m(d))

여기서:
  M = {Vector Rank, Graph Rank, BM25 Rank}
  k = 60 (스무딩 상수)
  w_m = 쿼리 의도에 기반한 가중치

다중 홉 관계 추론이 필요한 질의라면 그래프 가중치를 높이고, 순수한 의미론적 질문이라면 벡터 가중치를 높입니다. 쿼리 플래너(Query Planner)가 의도를 분류하고 이에 맞춰 가중치를 동적으로 조정합니다.

점수 융합(Score Fusion)은 필수적인 단계입니다

세 저장소의 결과를 단순히 연결(concatenate)해 두고 LLM이 알아서 처리해 주기를 기대해서는 안 됩니다. 융합 단계야말로 이 아키텍처가 복잡성을 감수할 만한 가치를 증명하는 지점입니다.

가장 효과적인 파이프라인은 3단계로 구성됩니다:

1단계: 광범위 검색 (Broad retrieval)
    Qdrant 밀집+희소 벡터 → 후보 100개
    Memgraph 순회        → 후보 50개
    PostgreSQL BM25      → 후보 50개

2단계: 융합 및 그래프 필터링 (Fusion + graph filtering)
    전체 후보에 RRF 적용 → 순위화된 결과 30개
    엔티티 연결 청크에 대한 그래프 관련성 부스팅(boosting)

3단계: 고비용 리랭킹 (Expensive reranking)
    Cross-encoder (ms-marco-MiniLM) → 최종 청크 10개
    인용(citation)을 포함한 컨텍스트 조립

크로스 인코더(Cross-encoder) 리랭킹 단계는 100~300ms의 비용이 소요되지만, “관련 있어 보이는 청크”와 “정확히 필요한 청크”를 가르는 결정적인 차이를 만듭니다. 프로덕션 시스템에서는 충분히 투자할 가치가 있는 지연 시간입니다.

표준 ID(Canonical ID)가 아키텍처를 결속합니다

세 저장소는 반드시 공통의 식별자 계층을 공유해야 합니다. PostgreSQL이 표준 ID를 발급하고, 다른 모든 저장소가 이를 참조합니다.

PostgreSQL:  entity_id = AST-000183

Memgraph:    (:Entity {id: "AST-000183"})

Qdrant:      payload: {"entity_id": "AST-000183"}

이는 벡터 데이터베이스나 그래프 데이터베이스를 권한 있는 신원 식별 시스템으로 결코 신뢰해서는 안 됨을 의미합니다. 다운스트림 저장소 중 하나에 데이터 손상이 발생하거나 스키마 마이그레이션이 필요하다면 언제든 PostgreSQL로부터 다시 빌드할 수 있어야 합니다. 그래프와 벡터 저장소는 프로젝션(projection)이지 진실의 원천(source of truth)이 아닙니다.

동기화: 아웃박스 패턴 (The Outbox Pattern)

세 개의 데이터베이스에 데이터를 쓰는 작업은 일관성 문제를 야기합니다. 이에 대한 해결책은 PostgreSQL을 코디네이터로 사용하는 트랜잭셔널 아웃박스 패턴(transactional outbox pattern)입니다.

CREATE TABLE outbox (
    id            BIGSERIAL PRIMARY KEY,
    aggregate     TEXT NOT NULL,
    aggregate_id  UUID NOT NULL,
    operation     TEXT NOT NULL,
    payload       JSONB NOT NULL,
    target        TEXT NOT NULL,
    processed_at  TIMESTAMPTZ
);

청크가 수집될 때, PostgreSQL은 청크 데이터와 아웃박스 엔트리를 동일한 트랜잭션 내에서 기록합니다. 별도의 워커(worker)가 아웃박스를 처리하며 Qdrant와 Memgraph에 비동기적으로 변경 사항을 전파합니다.

PostgreSQL (ACID 쓰기)                                          
    │                                                            
    ├── 아웃박스 엔트리 → Qdrant 워커 → 벡터 upsert            
    └── 아웃박스 엔트리 → Memgraph 워커 → 그래프 노드/엣지 upsert

이를 통해 쓰기 경로(write path)에서는 트랜잭션 일관성을 확보하고, 읽기 경로(read path)에서는 최종 일관성(eventual consistency)을 유지할 수 있습니다. RAG 활용 사례에서는 그래프에 몇 초 정도의 지연(staleness)이 발생하더라도 검색 품질에 거의 영향을 미치지 않습니다.

가장 중요한 규칙: Qdrant와 Memgraph는 PostgreSQL로부터 언제든 다시 빌드할 수 있는 파생 인덱스여야 합니다. 결코 그 반대가 되어서는 안 됩니다.

오래된 엣지(Stale Edge) 문제

그래프 데이터는 벡터 데이터와는 달리 노드 간 상호의존성이 매우 높습니다. 공급업체 A가 공장 Y에 납품을 중단했음에도 Memgraph에 연결 엣지가 그대로 남아 있다면, RAG 시스템은 이미 사라진 관계를 자신 있게 환각해낼 것입니다.

완화 방안:

  • 엣지 TTL(Time To Live) 설정: (:Supplier)-[:SUPPLIES {valid_until: '2026-08-01'}]->(:Factory)
  • PostgreSQL 기반 CDC(Change Data Capture): 계약 테이블에서 행이 삭제되면 Memgraph로 삭제 연쇄(cascade) 전파
  • 청크 해시 버전 관리: 세 저장소 전체에 걸쳐 일관된 청크 해시 버전을 관리하여, 제거된 페이로드를 참조하는 오래된 순회를 방지
  • 주기적 그래프 검증 배치 작업: 그래프 엣지와 PostgreSQL 소스 테이블 간의 정합성 주기적 대조

LLM이 직접 Cypher를 작성하게 두지 마십시오

규제 대상 환경에서는 이 점이 특히 중요합니다. LLM에 임의의 그래프 쿼리를 생성할 수 있는 권한을 부여해서는 안 됩니다. 대신 명확한 타입이 정의된 검색 연산 함수(typed retrieval operations)를 제공해야 합니다:

get_system_dependencies(system_id)
get_validation_coverage(system_id)
get_change_impact(change_id)
get_requirement_traceability(requirement_id)
get_related_risks(system_id)
get_tests_for_requirement(requirement_id)

각 함수는 내부적으로 사전에 제어된 안전한 Cypher를 실행합니다. 에이전트는 쿼리를 마음대로 만들어내는 대신 “지금은 밸리데이션 커버리지가 필요하다”는 판단만을 내립니다. 이 방식이 거버넌스, 감사 추적, 밸리데이션 측면에서 비교할 수 없을 정도로 훨씬 안전하고 수월합니다.

엔티티 해소(Entity Resolution)가 가장 까다로운 영역입니다

엔티티 해소 과정이 없다면 여러분의 그래프는 “Vaisala EMS”, “Vaisala system”, “Vaisala viewLinc”, “Environmental Monitoring”, “EMS”, “EMS system”을 서로 다른 6개의 독립된 노드로 저장하게 됩니다. 이는 그래프 쓰레기(graph garbage)에 불과합니다.

엔티티 해소 캐스케이드(resolution cascade):

  1. 정규화된 이름에 대한 정확한 일치 (Exact match) (소문자 변환, 공백 제거)
  2. PostgreSQL의 pg_trgm 유사도를 활용한 퍼지 매칭 (Fuzzy match) (임계값 약 0.7)
  3. 임베딩 매칭 (Embedding match) — Qdrant에서 가장 가까운 엔티티 탐색, 코사인 유사도 > 0.95일 경우 병합
  4. 모호한 케이스에 대한 LLM 명확화 (LLM disambiguation) (비용이 높으므로 신중하게 제한적으로 사용)

이 영역에 집중적으로 투자해야 합니다. 엔티티 해소의 품질이 그래프의 품질을 결정하고, 이는 곧 검색 품질과 최종 답변의 품질을 좌우합니다. 사슬의 강도는 가장 약한 연결 고리에 의해 결정됩니다.

출처 추적(Provenance): 모든 주장은 증거로 추적되어야 합니다

그래프의 모든 엣지는 출처 정보(provenance)를 포함해야 합니다:

EMS-001 ──implements──> REQ-123        
                                       
    provenance:                        
        source = URS-004               
        source_version = 3             
        evidence_location = section 4.2
        extraction_method = human      
        confidence = 1.0               

이를 통해 AI는 단순 주장이 아닌 강력한 근거를 제시할 수 있습니다: “EMS-001은 URS-004 v3, 4.2절에 근거하여 REQ-123을 구현합니다.” 이는 근거 없는 단언보다 비교할 수 없을 만큼 확실하게 방어 가능합니다.

AI가 생성한 모든 답변은 다음과 같은 증거 사슬(evidence chain)을 통해 검증될 수 있어야 합니다:

주장(Claim) → 증거(Evidence) → 원본 문서(Source Document) → 승인된 버전(Approved Version) → 감사 추적(Audit Trail)

이것이 바로 AI의 답변을 검증 가능(auditable)하게 만드는 핵심 아키텍처입니다.

성능 예산 (Performance Budget)

하이브리드 쿼리의 전체 검색 지연 시간:

단계 지연 시간
질의 임베딩 생성 ~50ms
Qdrant 밀집 + 희소 검색 ~15-30ms
PostgreSQL BM25 검색 ~20-50ms
Memgraph 순회 (2-hop) ~5-50ms
결과 융합 (RRF) ~5ms
크로스 인코더 리랭킹 ~100-300ms
전체 검색 소요 시간 ~150-500ms

그래프 강화 검색은 벡터 전용 RAG에 비해 100~300ms의 지연 시간이 추가됩니다. 그러나 요소 간 관계가 중요한 모든 질의에 있어서 이는 충분히 감수할 가치가 있는 트레이드오프입니다. 단순한 사실 확인용 질문의 경우, 쿼리 플래너가 그래프 경로를 완전히 건너뛰어 전체 응답 시간을 100ms 이내로 유지할 수 있습니다.

또한 이전 질의에 대한 시맨틱 캐싱(semantic caching, 코사인 유사도 > 0.85)을 적용하면 빈번한 질문에 대해 불필요한 그래프 순회를 효과적으로 제거할 수 있습니다.

과도한 엔지니어링(Overkill)이 되는 경우

복잡성에 따르는 비용을 객관적으로 평가해야 합니다. 세 개의 데이터베이스를 운영한다는 것은 세 개의 배포 대상, 세 개의 커넥션 풀, 세 개의 백업 전략, 그리고 세 가지의 장애 모드를 관리해야 함을 의미합니다.

이 아키텍처가 정당화되는 조건은 다음과 같습니다:

  • 풍부한 엔티티 관계를 포함하는 100,000건 이상의 문서를 보유하고 있는 경우
  • 질의가 다중 홉 추론을 요구하는 경우 (“X가 Z를 통해 Y에 어떤 영향을 미치는가?”)
  • 정밀한 키워드 매칭과 심층적인 의미론적 이해가 동시에 필요한 경우
  • 출처 추적과 감사 추적이 필수적인 규제 산업 환경인 경우

보유 문서가 50,000건 미만이고 단순한 질의응답이 주 목적이라면 pgvector를 적용한 PostgreSQL만으로도 충분합니다. 관계가 중요하지만 데이터 규모가 작다면, 자체 벡터 검색을 지원하는 단일 그래프 데이터베이스(Memgraph 또는 Neo4j)로도 요구사항을 충족할 수 있습니다.

결론 (The Bottom Line)

“데이터베이스를 하나만 써야 하는가, 아니면 세 개를 써야 하는가?“라는 질문에 대한 답은 “도구를 하나만 써야 하는가, 세 개를 써야 하는가?“라는 질문과 같습니다. 즉, 여러분이 해결하려는 작업이 한 가지인가, 아니면 세 가지인가에 달려 있습니다.

벡터 검색은 질문과 비슷하게 들리는 것을 찾습니다. 그래프 순회는 묻고 있는 대상에 의존하고 있는 관계를 찾습니다. 관계형 쿼리는 무엇이 실제로 참(True)인지를 검증합니다. 프로덕션 RAG 시스템은 이 세 가지 모두를 필요로 하며, 공유된 표준 ID, 트랜잭셔널 아웃박스, 그리고 어떤 소스를 언제 신뢰해야 할지 알고 있는 융합 계층을 통해 이들을 하나로 엮어내야 합니다.

아키텍처 자체는 복잡하지 않습니다. 하지만 엔티티 해소, 출처 추적, 그리고 PostgreSQL을 단일 진실의 원천으로 유지하는 규율 — 대다수의 구현이 실패하는 지점은 바로 여기입니다.

식별자 계층을 가장 먼저 구축하십시오. 나머지는 그 뒤를 따를 것입니다.