Vector Retrieval for RAG (벡터 검색과 RAG 검색 계층)
"RAG에서 '관련 문서를 찾는다'는 건 실제로 어떻게 일어나고, 벡터 DB는 키워드 검색이나 RDBMS와 무엇이 다른가?" 이 페이지는 그 질문에 답한다. 벡터 검색은 비정형 데이터를 임베딩 벡터로 바꿔 저장하고, 질의 벡터와 가까운 벡터를 유사도로 찾아내는 검색 방식이다. RAG에서는 생성 전에 근거를 가져오는 retrieval 단계를 맡는다. 이 페이지는 임베딩·인덱스·유사도라는 검색 메커니즘과, 장애 관제 Agent가 과거 해결 사례를 검색 도구로 쓴 사례를 함께 다룬다. RAG가 지식을 축적하는 방식과 어떻게 다른지는 RAG vs Persistent Wiki에서 다룬다.
무엇을 저장하고 어떻게 찾나
- 벡터 DB는 비정형 데이터를 임베딩 벡터로 변환해 저장하고, 의미 기반 유사도 검색(k-NN)을 빠르게 수행하는 데이터베이스 시스템이다.[1]
- 임베딩은 텍스트·이미지 같은 비정형 데이터를 AI 모델로 고차원 숫자 벡터로 바꾸는 과정이다. 의미가 비슷한 데이터는 벡터 공간에서 가까운 위치에 놓이므로, 컴퓨터가 숫자 연산으로 의미의 유사성을 판단할 수 있게 된다.[1:1]
- 데이터는 스키마가 엄격한 정형(RDBMS, CSV), 스키마를 포함하지만 유연한 반정형(JSON, XML), 구조가 없는 비정형(텍스트, 이미지, 음성)으로 나뉜다. 비정형이 전체 데이터의 80% 이상이며, 벡터 DB는 이 비정형 데이터 처리에 특화돼 있다.[1:2]
- 두 벡터의 가까움은 주로 아래 두 척도로 잰다. 코사인 유사도는 크기와 무관하게 방향의 유사도를 재며, 검색·추천 시스템에서 가장 널리 쓰인다.[1:3]
전체 탐색 대신 근사 인덱스
- 모든 벡터와 거리를 계산하는 전체 탐색은 비효율적이라, 인덱스로 후보를 먼저 추린다.[1:4]
| 알고리즘 | 방식 | 특징 |
|---|---|---|
| LSH (Locality-Sensitive Hashing) | 유사한 벡터가 같은 버킷에 충돌하도록 설계한 해시 | 재현율을 높이려고 여러 해시 함수를 쓴다 |
| PQ (Product Quantization) | 고차원 벡터를 저차원 조각으로 나누고 코드북으로 압축 | 메모리를 줄이고 검색 속도를 높인다 |
| HNSW (Hierarchical Navigable Small World) | 벡터를 노드, 거리를 엣지로 하는 계층형 그래프 | 현재 가장 널리 쓰이는 고성능 알고리즘이다 |
- 같은 노트의 미니 벡터 DB 설계(SQLite + LSH)는 검색을 두 단계로 나눈다. 벡터 하나에 여러 LSH 함수를 적용해 hash 값 여러 개를 저장하고, 검색할 때는 hash가 맞는 후보를 먼저 뽑은 뒤 그 후보에 대해서만 정확한 거리를 계산한다.[1:5]
-- 원본 벡터 저장
CREATE TABLE vectors (
id TEXT PRIMARY KEY,
vector_data BLOB, -- 벡터 (바이너리)
metadata TEXT -- JSON
);
-- LSH 인덱스 (1:N 관계)
CREATE TABLE lsh_index (
hash_value TEXT,
vector_id TEXT REFERENCES vectors(id) ON DELETE CASCADE
);
CREATE INDEX idx_hash ON lsh_index(hash_value);
- 이 구조는 B+ Tree 인덱스처럼 정확한 키로 위치를 찾는 인덱스와 다르다. 후보를 근사적으로 좁히고 정확도 일부를 속도와 맞바꾸는 인덱스로 볼 수 있다.
검색 방식 비교
| 구분 | RDBMS | 전문 검색 (Full-Text) | 벡터 DB |
|---|---|---|---|
| 검색 방식 | 일치값, 조건 검색 | 역색인 기반 키워드 (TF-IDF, BM25) | k-NN 유사도 (ANN, HNSW) |
| 실제 쿼리 | SELECT * FROM ... |
MATCH(body) AGAINST('keyword') |
collection.search(query_vector, k=5) |
| 유사성 판단 | 없음 (정확 일치) | 어휘적 유사성 | 의미적·맥락적 유사성 |
| 주요 용도 | 정형 데이터, 트랜잭션 | 문서, 웹페이지 검색 | 이미지, 추천, 챗봇 |
- 위 표는 원문의 비교를 그대로 옮긴 것이다.[1:6] 전문 검색은 같은 단어가 있는지를, 벡터 검색은 뜻이 비슷한지를 본다는 점이 핵심 차이다.
저장소 선택
- 원문(2026-03 작성)은 주요 벡터 DB를 다음과 같이 비교한다.[1:7]
| DB | 유형 | 저장 방식 | 인덱싱 |
|---|---|---|---|
| Pinecone | 완전 관리형 클라우드 | 분산 Blob Storage | 자체 ANN, 자동 최적화 |
| Milvus | 오픈소스 분산형 | 세그먼트 단위 객체 스토리지 | HNSW/IVF_FLAT/IVF_PQ 수동 선택 |
| Weaviate | 오픈소스 분산형 | 로컬 디스크 (LSM Tree) | HNSW 기본, RAM 상주 |
| Chroma | 오픈소스 인-프로세스 | 로컬 SQLite | HNSW 기본, 자동 업데이트 |
| Faiss | 오픈소스 라이브러리 | 인메모리 (파일 저장 가능) | Flat/IVF/HNSW/PQ 수동 제어 |
- 장애 관제 Agent 프로젝트는 Chroma와 Pinecone을 검토한 뒤 PostgreSQL 확장인 PGVector를 골랐다. 근거는 세 가지다.[2]
- 이미 PostgreSQL을 쓰고 있어 확장만 추가하면 되고, 관리할 서비스가 늘지 않는다.
- 해결 기록(
ResolutionRecord)과 그 임베딩을 같은 DB 트랜잭션에 저장할 수 있다. 벡터 DB를 따로 두면 두 저장소 사이의 정합성 관리가 복잡해진다. - Spring AI가 PGVector용
VectorStore를 공식 지원한다.
- 이 선택은 전용 벡터 DB의 성능보다 기존 인프라 재사용과 트랜잭션 일관성을 우선한 결정으로 읽을 수 있다.
Agent 안의 RAG: 장애 관제 사례
- 운영자가 Discord에
resolved <alertId> <해결방법>또는false-positive <alertId>를 입력하면, 그 알람의 해결 과정이 PGVector에 임베딩되어 저장된다. 이후 비슷한 알람이 오면 Agent가 과거 사례를 먼저 참조한다.[3] - 임베딩 모델은 Ollama로 로컬에서 돌리는 all-minilm이다.[3:1]
- Agent의 도구 호출 순서는 과거 사례 검색(
search_rag)이 첫 번째다. 과거에 같은 알림을 어떻게 해결했는지가 가장 유용한 정보라서 먼저 보고, 그다음 알림이 아직 발생 중인지 확인한다.[4] - 시뮬레이션에서 RAG가 과거 사례를 찾은 시나리오에서는 Agent가 과거 사례를 결론에 반영하면서 현재 상황과 비교했다. 사례가 없는 시나리오에서는 metric 조회만으로 원인을 특정했다.[5]
- 다만 이 시뮬레이션은 LLM 추론을 스크립트로 대체했기 때문에, 실제 모델이 이 순서로 도구를 고르는지는 보장하지 못한다.[5:1]
- 같은 현상이라도 원인이 다를 수 있으므로, 검색된 과거 사례는 현재 metric·로그로 다시 확인해야 하는 가설로 다루는 편이 안전하다고 볼 수 있다. 시뮬레이션에서 Agent가 과거 사례와 현재 상황을 비교하는 구조로 결론을 낸 것도 이 방향과 맞는다.
- 이렇게 저장된 해결 기록을 찾을 때 유사도가 높다는 것은 그 기록이 지금 알람과 의미상 가깝다는 뜻이지, 원인이 같다는 보장은 아니라는 것이 이 페이지의 해석이다.
관련
- RAG vs Persistent Wiki: 이 페이지가 retrieval 메커니즘을 다룬다면, 그 페이지는 retrieval 결과가 다음 질문에 남는지를 비교한다.
- Agent Tool Use: 장애 관제 Agent에서 벡터 검색은
search_rag라는 도구 하나로 노출되고, 호출 순서는 prompt로 정한다. - Spring Transaction: 해결 기록과 임베딩을 같은 트랜잭션으로 묶는 이유가 PGVector 선택 근거와 연결된다.
- B+ Tree Indexing: 정확한 키 탐색 인덱스와 근사 유사도 인덱스의 차이를 비교할 때 기준점이 된다.
테스트 질문
- 전문 검색(BM25)과 벡터 검색은 각각 어떤 종류의 유사성을 판단하는가?
- HNSW 같은 인덱스가 있는데도 검색 결과가 정확히 최근접이 아닐 수 있는 이유는 무엇인가?
- 장애 관제 Agent가 별도 벡터 DB 대신 PGVector를 고른 이유 중 트랜잭션과 관련된 것은 무엇인가?
- 벡터 검색이 가장 비슷하다고 돌려준 과거 사례를 그대로 조치의 근거로 쓰면 안 되는 이유는 무엇이고, 무엇과 다시 비교해야 하는가?
출처
벡터 데이터베이스.md — "비정형 데이터를 임베딩 벡터로 변환하여 저장하고, 의미 기반 유사도 검색(k-NN)을 고속으로 수행하는 데이터베이스 시스템.", 데이터 유형 표("비정형 (Unstructured) | 구조 없음, 전체 데이터 80% 이상"), "의미적으로 유사한 데이터 → 벡터 공간에서 가까운 위치에 매핑", "전체 탐색(Exhaustive Search) 비효율 → 인덱스로 후보 추려냄.", LSH/PQ/HNSW 표("HNSW ... 현재 가장 널리 사용되는 고성능 알고리즘"), 유클리드 거리·코사인 유사도 수식, "코사인 유사도: 방향성 유사도 측정 (크기 무관) → 검색·추천 시스템에서 가장 널리 사용", RDBMS/전문 검색/벡터 DB 비교 표, 주요 벡터 DB 비교 표, 미니 벡터 DB 스키마, "하나의 벡터 → 여러 LSH 함수 → 여러 hash_value 생성 / 검색 시 hash 매칭 → 후보 추출 → 정확한 거리 계산", frontmatter
date: 2026-03-12↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎02-기술-스택-선택-근거.md — "PGVector vs Chroma vs Pinecone", "추가 인프라 없음: 이미 PostgreSQL을 사용하고 있어 PGVector 확장만 추가하면 된다.", "트랜잭션 일관성: 알람 해결 피드백을 받았을 때
ResolutionRecord를 PostgreSQL에 저장하고 PGVector에 임베딩을 저장하는 두 작업이 같은 DB 트랜잭션에 있다. 벡터 DB를 별도로 두면 두 저장소 간 정합성 관리가 복잡해진다.", "Spring AI 1.1.0이spring-ai-starter-vector-store-pgvector를 공식 지원한다." ↩︎01-프로젝트-개요.md — "운영자가
resolved <alertId> <해결방법>또는false-positive <alertId>를 Discord에 입력하면 해당 알람의 해결 과정이 PGVector에 임베딩되어 저장된다. 이후 유사한 알람 발생 시 에이전트가 과거 사례를 먼저 참조한다.", "all-minilm (임베딩 모델, PGVector 인덱싱용)", 기술 스택 표 "임베딩 모델 | Ollama all-minilm (로컬)" ↩︎ ↩︎01-왜-react-에이전트인가.md — "1. search_rag — 우리 서버 과거 사례 먼저 확인 / 2. verify_alert — 알람이 현재도 발생 중인지 확인", "RAG를 먼저 보는 건, 과거에 같은 알림이 왔을 때 어떻게 해결했는지가 가장 유용한 정보이기 때문이다." ↩︎
06-에이전트-시뮬레이션-결과.md — "RAG에서 과거 사례를 찾은 시나리오(OOMKilled, GPUHighTemperature, ContainerDown, ApolloFrameLatency)에서 에이전트는 과거 사례를 결론에 반영하고 현재 상황과 비교하는 구조로 분석을 마무리했다. RAG 결과가 없는 시나리오(HostHighCpuLoad)에서는 메트릭 조회만으로 원인을 특정했다.", "LLM 추론을 스크립트로 대체했기 때문에 Gemini가 실제로 이 도구들을 이 순서로 선택하는지는 이 테스트로 보장할 수 없다." ↩︎ ↩︎