문단 클릭 = 시작 위치 선택 · 재생은 우하단 버튼/P (재생 중 클릭 = 점프)

4주차 — LLM RAG 고급 검색 기법 (Multi-Vector · BM25 · Dense · Ensemble)

고급 검색 기법 전부를 "멀티벡터 스토어" 패턴 하나로 꿰고, 책의 인메모리 장난감 예제를 pgvector·Chroma 기반 실무형으로 재구축해 — 질의 확장(Multi-Query·HyDE), 문서 확장(Parent Document), 수동 BM25(SQL), HNSW 밀집 검색, RRF 앙상블까지 — 하이브리드 검색 파이프라인을 완성하라.


0. 사전 필수 용어 (선행지식)

  1. 임베딩(Embedding) — 텍스트를 고정 길이 숫자 벡터로 바꾼 것. 비유하면 문장에 "의미 좌표"를 찍는 일이다. 같은 손실(loss)로 학습된 모델이 만든 벡터끼리만 거리 비교가 가능하다. §3-E와 §6에서 본질을 다시 정의한다.
  2. RAG(Retrieval-Augmented Generation, 검색 증강 생성) — LLM이 답하기 전에 관련 문서를 검색해 프롬프트에 넣어 주는 구조. 비유하면 "오픈북 시험"이다. 본 강의 전체의 전제다.
  3. 청킹(Chunking) — 긴 문서를 검색 단위로 자르는 것. 비유하면 책을 카드 뭉치로 재단하는 일. 자를 크기가 검색 정밀도와 컨텍스트 풍부함을 좌우한다. §3-B와 §4 Step 2의 핵심 변수다.
  4. 리트리버(Retriever) — "질문 → 관련 문서 리스트"를 돌려주는 검색 컴포넌트 인터페이스. 비유하면 사서(司書)다. 이번 주의 모든 기법은 결국 리트리버 형태로 조립된다(§3~§5 전체).
  5. TF-IDF — 단어가 문서에 자주 나올수록(TF), 그리고 그 단어가 전체 문서에서 희귀할수록(IDF) 중요하다고 보는 고전 검색 지표. §3-F에서 구축하는 BM25의 뼈대다.

선행지식이 부족하면 AI 엔지니어링 EP17 (컨텍스트 증강과 RAG) 학습자료를 먼저 보고, PostgreSQL 기초는 공식 문서(https://www.postgresql.org/docs/)를 참조하라.


1. 주제 정의

이 강의(뉴런데브 RAG 실전 4주차)는 RAG 검색 고도화 기법을 "멀티벡터 스토어(Multi-Vector Store)" 패턴 하나로 통일해 가르친다. 하나의 원본 정보에 대해 벡터를 여러 개 잡아 검색 품질을 끌어올리는 접근이며, 두 갈래로 나뉜다.

  • 질의 확장(Query Expansion) — 질문 쪽을 여러 개로: Multi-Query, HyDE
  • 문서 확장(Document Expansion) — 답변이 될 문서 쪽을 여러 개로: Parent Document(이중 청킹)

실습은 노트북 6개로 진행한다.

노트북 주제
practice4-1 Parent Document Retriever (부모 1000 · 자식 200 이중 청킹)
practice4-2 Multi-Query Retriever (LLM 질의 확장)
practice4-3 HyDE (가상 문서 임베딩)
practice4-4 BM25 — PostgreSQL 수동 구축 (mecab-ko · tsvector · 트리거)
practice4-5 밀집 검색 (Dense Retrieval) — pgvector HNSW 인덱싱
practice4-6 EnsembleRetriever — BM25 + Dense 를 RRF 로 결합

⚠️ 주의: 강의의 출발 선언 — "책에 있는 내용을 그냥 따라 하면 장난감이라서 실무에 전혀 쓸 수가 없다." 책의 FAISS·BM25Retriever·InMemoryStore 는 모두 인메모리다. 실무 데이터는 RAM 에 올릴 수 있는 크기가 아니므로, 이번 주 전체가 인메모리 스토어를 배제하고 pgvector(PostgreSQL) 기반으로 재구축하는 과정이다.


2. 풀려는 문제

  • 문제 1 — 질문 품질 의존: 사용자의 질문은 대체로 형편없다. 단일 질문 벡터 하나로 검색하면 그 품질에 검색이 통째로 종속된다. → 질의 확장.
  • 문제 2 — 청크 크기 딜레마: 작게 자르면 검색은 정확하지만 LLM 에 줄 컨텍스트가 빈약하고, 크게 자르면 그 반대다. → 부모-자식 이중 청킹.
  • 문제 3 — 의미 검색의 키워드 취약성: 임베딩 유사도는 고유명사·수치("발행주식의 총수") 같은 정확 일치에 약하다. → 희소(BM25) + 밀집(Dense) 하이브리드.
  • 문제 4 — 인메모리의 실무 부적합: 프로세스 재시작이면 증발하고, 다른 프로세스에서 공유할 수 없고, 억 단위 레코드면 애초에 메모리에 안 올라간다. → PostgreSQL 영속화.
  • 문제 5 — 점수 체계 이질성: BM25 점수(13점대)와 코사인 유사도(0~1)는 스케일이 달라 직접 합산할 수 없다. → RRF 순위 융합.

3. 핵심 개념·구조

3-A. 멀티벡터 스토어 패턴 (큰 그림)

오늘 배우는 기법 대부분이 이 패턴의 변주다. 벡터를 여러 개 더 잡아 검색 기능을 향상시킨다 — 질문을 확장하든, 문서를 확장하든, 양쪽 다든.

3-B. Parent Document Retriever (문서 확장)

원본 문서를 자식 청크(200자) 로 잘게 잘라 임베딩·검색하고, 검색된 자식과 연결된 부모 청크(1000자) 를 최종 컨텍스트로 제공한다. 정확한 검색(자식)과 풍부한 컨텍스트(부모)를 동시에 얻는다.

💡 실무 노하우: 책은 부모 문서를 InMemoryStore(RAM)에 넣지만, 강의는 부모도 Chroma 컬렉션에 key-value 로 저장한다. 실무 데이터 크기와 서버 안정성 때문이다. 부모-자식 연결은 메타데이터로 유지한다. 또한 청크 분할은 고정 길이보다 시멘틱 분할기로 부모 안의 서브 청크를 나누면 부모-자식 관계가 더 유의미해진다(비용은 커진다).

3-C. Multi-Query Retriever (질의 확장)

사용자의 단일 질문을 LLM 이 여러 개(시연에서는 6개)의 다양한 질문으로 확장해 각각 검색하고 결과를 합친다. "사용자의 형편없는 질문을 LLM 이 좋은 질문들로 바꿔 검색하겠다"는 발상. LangChain 에 질의 확장 프롬프트까지 내장돼 있어 MultiQueryRetriever.from_llm(retriever=…, llm=…) 한 줄이면 끝난다.

3-D. HyDE (Hypothetical Document Embeddings)

질문으로 검색하는 대신, 질문에 대한 가상의 답변 문서를 LLM 으로 먼저 생성한 뒤 그 가상 문서로 유사도 검색을 수행하는 질의 변형 기법. 질문 임베딩과 답변(문서) 임베딩은 공간이 달라 유사도가 안 잡히던 구식 임베딩 시대의 처방이다.

⚠️ 주의: HyDE 의 전제는 "LLM 이 그럴듯한 답을 지어낼 수 있다"이다. LLM 이 답을 만들어낼 수 없는 도메인(사내 고유 데이터 등)이면 가상 문서 자체가 무의미해져 기법이 통하지 않는다. 또 질의마다 LLM 호출이 한 번 더 붙는다.

3-E. 희소 벡터 vs 밀집 벡터 — 정확한 정의 (강의 최중요 개념)

"0이 많으면 희소, 꽉 차 있으면 밀집"은 부정확하다.

  • 희소 벡터(Sparse Vector) = 각 차원이 완전히 분리된 독립 의미를 갖는 벡터. 사람이 차원을 설계(엔지니어링)할 수 있다. 값이 0으로 비는 것은 현실 데이터를 전부 확보할 수 없어서 유도되는 현상이지 정의가 아니다.
  • 밀집 벡터(Dense Vector) = 역전파(Backpropagation) 학습의 결과로만 만들어지는 벡터. 여러 차원이 뒤엉켜 복합 의미를 나타내며, 사람이 공학적으로 제작하는 것은 불가능하다. 희소 벡터의 "반대 개념"이 아니다.
  • 임베딩(Embedding) = 벡터의 부분집합. 같은 손실로 학습된 벡터들끼리만 유사도 비교가 가능하다. 서로 다른 임베딩 모델이 만든 벡터는 비교할 수 없다.

3-F. BM25 를 PostgreSQL 에 수동 구축

PostgreSQL 에는 BM25 가 내장돼 있지 않다. 핵심은 TF·IDF 이므로 이를 테이블로 직접 관리한다.

  • 형태소 분석: 기본 pgvector 이미지는 한국어 형태소 분석이 안 된다. mecab-ko(엔진) + 사전 + textsearch_ko 확장을 설치한 이미지를 써서 to_tsvector('korean', content) 로 토큰화한다.
  • tsvector: 콘텐츠의 텍스트를 토큰 분해해 토큰별 등장 횟수를 담은 타입. 문서의 길이는 텍스트 길이가 아니라 토큰 수다(BM25 의 doc_len·avgdl 도 토큰 수 기준).
  • 순색인(forward index): (문서, 토큰) 조합별 등장 횟수(TF) 테이블 — "이 문서에 어떤 토큰이 몇 번" 즉시 조회.
  • 역색인(inverted index): 토큰별 등장 문서 수(DF) 테이블 — "이 토큰이 몇 개 문서에 등장" 즉시 조회(IDF 용).
  • 통계 테이블: doc_count 와 total_len 을 유지 — 평균 문서 길이(avgdl)를 매번 전체 재계산하지 않고 합과 개수로 증분 관리.
  • 트리거: INSERT/UPDATE/DELETE 때마다 세 테이블을 증분 갱신. 삭제면 카운트 하나 깎고 그 문서 길이만큼 빼고, 삽입이면 반대로. 검색 시점이 아니라 미리 만들어 둔다.

✅ 확인: 이 구조의 장점 — BM25 전체가 애플리케이션 서버 없이 온리 쿼리(SQL 만)로 동작한다. 적재 후 CTE(WITH) 쿼리 하나로 BM25 점수를 계산해 랭킹한다.

3-G. 밀집 검색 — pgvector HNSW

책의 OpenAIEmbeddings(text-embedding-3-large) + FAISS.from_documents() 인메모리 방식 대신, 로컬 임베딩 모델로 벡터를 만들어 pgvector 에 진짜 ANN(근사 최근접 이웃) 인덱스인 HNSW 를 구축한다. 인덱스와 벡터가 PostgreSQL 서버에 영구 저장되므로 어떤 프로세스가 접속하든 동일한 인덱스를 그대로 쓴다.

⚠️ 주의 — 차원 제한: pgvector 의 ANN 인덱스(HNSW/IVFFlat)는 vector 타입 기준 2000차원 상한(절반 정밀도 halfvec 타입은 4000차원)이다. 로컬 임베딩 모델(embedding-8b, Qwen3 계열)은 4096차원을 그대로 내놓으므로 인덱스 생성이 실패한다. → MRL(Matryoshka Representation Learning) 학습을 한 임베딩 모델은 벡터 앞부분만 잘라 차원 축소해도 의미가 보존된다. MRL 미지원 모델은 다른 차원 축소 방법이 필요하다.

  • 인덱스 전략: 코사인 거리(cosine distance) 기반 HNSW, m=16(노드당 연결 수), ef_construction=64(구축 시 탐색 폭).
  • 코사인 유사도는 크기를 배제하고 각도로 의미 유사를 재는 척도다. 1에 가까울수록 유사. 음수 값은 "반대 의미"가 아니므로 그냥 버린다 — 임베딩 공간에서 부정(negation)은 그렇게 표현되지 않는다.

3-H. Ensemble Retriever — RRF 순위 융합

BM25 점수(13점대)와 코사인 유사도(0~1)는 아예 다른 차원의 값이라 직접 합칠 수 없다. 그래서 점수를 버리고 등수만 남긴다 — RRF(Reciprocal Rank Fusion). 각 리트리버의 결과를 순위 역수로 환산해 가중 합산하므로 상위권 가중이 세고 하위권은 자연 감쇠한다. LangChain EnsembleRetriever 의 기본 융합 방식이 RRF 다.


4. 실습·구현 가이드 (Step 1·2·3·4)

Step 1 — 인프라: Docker 로 pgvector + 한국어 형태소 분석 + pgAdmin

pgvector 확장이 설치된 공식 배포 이미지(PostgreSQL 17 기반)에서 시작해 mecab-ko(형태소 분석 엔진) + 사전 + textsearch_ko 확장을 추가로 설치한 이미지를 컨테이너로 띄운다. pgAdmin(dpage/pgadmin4 이미지)도 같이 잡아 두면 테이블·데이터를 GUI 로 확인할 수 있다.

# 접속 설정 (노트북 공통)
PG_HOST = "pgvector"; PG_PORT = 5432
PG_DSN = f"host={PG_HOST} port={PG_PORT} dbname={PG_DB} user={PG_USER} password={PG_PASSWORD}"

임베딩·LLM 은 LM Studio 로컬 서버(OpenAI 호환 API)로 서빙한다. 임베딩 모델은 Qwen3 계열 8B(4096차원)를 사용한다.

embeddings = OpenAIEmbeddings(
    model=EMBED_MODEL, base_url=LMSTUDIO_BASE_URL, api_key=LMSTUDIO_API_KEY,
    check_embedding_ctx_length=False,  # tiktoken 대신 원문 문자열 전송 — LM Studio 호환
    chunk_size=8,  # 배치 크기: 로컬 서버가 한 번에 받는 텍스트가 많으면 연결이 끊기므로 작게
)

💡 실무 노하우: 임베딩 서버는 요청 병렬도를 낮게(예: 4 이하) 잡아야 로컬 서버가 버틴다. 노트북을 여러 번 재실행할 것이므로 기존 컬렉션·테이블을 제거하고 초기화하는 코드를 각 노트북 앞에 넣어 멱등하게 만든다.

Step 2 — 문서 확장: Parent Document Retriever (practice4-1)

from langchain_text_splitters import RecursiveCharacterTextSplitter
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1000)  # 부모: 컨텍스트 제공
child_splitter  = RecursiveCharacterTextSplitter(chunk_size=200)   # 자식: 검색 단위

자식 벡터스토어(Chroma)와 부모 문서 저장소(역시 Chroma 컬렉션, key-value)를 만들고, 두 스플리터와 함께 ParentDocumentRetriever 를 생성해 문서를 추가한다. 통합 리트리버에 질의하면 부모 문서가, 벡터스토어에 직접 질의하면 자식 청크가 나오는 차이를 확인한다.

Step 3 — 질의 확장: Multi-Query · HyDE (practice4-2 · 4-3)

retriever = MultiQueryRetriever.from_llm(
    retriever=vectorstore.as_retriever(), llm=llm)  # 프롬프트 내장 — 지정만 하면 끝

로깅을 설정하면 LLM 이 생성한 하위 질의들을 콘솔에서 확인할 수 있다(시연 데이터: 분할 문서 219개). HyDE 는 가상 문서를 생성하는 프롬프트로 먼저 한 바퀴 돌린 뒤 그 출력을 임베딩해 검색한다 — LangChain 에 HyDE 체인이 내장돼 있다.

📚 참고: langchain 1.x 에서 ParentDocumentRetriever·MultiQueryRetriever·RetrievalQA 의 위치가 langchain_classic 으로 이동했다. 구버전과 신버전 임포트를 폴백으로 함께 두면 버전이 달라도 동작한다. 내부 메서드는 동일하다.

Step 4 — 하이브리드: 수동 BM25 + HNSW + 앙상블 (practice4-4 · 4-5 · 4-6)

문서 적재투자설명서.pdf 를 로드해 분할한다(분할 문서 수: 522).

from langchain_community.document_loaders import PyPDFLoader
docs = PyPDFLoader("투자설명서.pdf").load()
splits = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=200).split_documents(docs)

BM25(희소) — 순색인 테이블과 트리거를 DDL 로 구성한다.

-- 순색인: (문서, 토큰) 조합별 등장 횟수(TF)
CREATE TABLE IF NOT EXISTS practice4_bm25_term_freq (
  doc_id INTEGER NOT NULL REFERENCES practice4_bm25_docs(id) ON DELETE CASCADE,
  token  TEXT NOT NULL,
  tf     INTEGER NOT NULL,
  PRIMARY KEY (doc_id, token)
);
CREATE INDEX IF NOT EXISTS idx_practice4_bm25_term_freq_token
  ON practice4_bm25_term_freq (token);
-- 문서 삽입 시 형태소 분석과 토큰 수(doc_len) 계산을 자동화하는 트리거
CREATE OR REPLACE FUNCTION practice4_bm25_docs_before() RETURNS TRIGGER AS $$
BEGIN
  NEW.tsv := to_tsvector('korean', NEW.content);           -- mecab-ko 형태소 분석
  SELECT COALESCE(SUM(cardinality(positions)), 0) INTO NEW.doc_len
  FROM unnest(NEW.tsv) AS u(lexeme, positions, weights);   -- 문서 길이 = 토큰 수
  RETURN NEW;
END $$;

tsv 필드에 GIN 인덱스를 걸고, 역색인(DF)·통계(doc_count·total_len) 테이블에도 증분 트리거를 건다. 검색은 CTE 쿼리로 BM25 공식을 그대로 계산한다 — SUM( idf * (tf*(k1+1)) / (tf + k1*(1 - b + b*doc_len/avgdl)) ). 시연 데이터에서 상위 5건이 랭킹되고 1등 문서(id 48)가 13점대로 나온다.

밀집(Dense) — langchain_postgres v2 API 로 벡터 테이블과 HNSW 인덱스를 만든다.

from langchain_postgres import PGEngine, PGVectorStore
from langchain_postgres.v2.indexes import HNSWIndex, DistanceStrategy
hnsw_index = HNSWIndex(name="practice4_dense_docs_hnsw",
    distance_strategy=DistanceStrategy.COSINE_DISTANCE, m=16, ef_construction=64)
vectorstore.apply_vector_index(hnsw_index)

overwrite_existing=True 로 테이블을 매번 새로 만들면 몇 번을 재실행해도 안전하다. 4096차원 그대로 HNSW 를 걸면 실패하는 것을 먼저 확인하고(예상된 결과), MRL 절단 후 인덱스를 생성한다.

앙상블 — 두 리트리버를 LangChain 리트리버로 래핑해 결합한다. 수동 BM25 는 BaseRetriever 를 상속해 "쿼리 → SQL 검색 → Document 리스트" 로 동작을 바꿔 주기만 하면 된다.

dense_retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, dense_retriever], weights=[0.5, 0.5])

마지막으로 RetrievalQA 체인에 앙상블 리트리버를 넣어 최종 답변 생성까지 확인한다.


5. 사례·적용

  • 정확 일치 질의: "발행주식의 총수"를 묻자 밀집 검색 쪽이 정답 문단("발행주식의 총수 13,602,977")을 정확히 찾았고, RRF 순위 융합 덕에 이 문단이 앙상블 결과에도 살아남아 최종 답변에 그대로 반영됐다.
  • BM25 단독의 빈틈: 같은 코퍼스에서 BM25 단독으로는 키워드가 문서에 안 들어 있으면 못 찾는 사례를 확인 — 하이브리드가 필요한 이유의 실증.
  • 서비스 이식: 파이썬으로 개념·구조를 한 번 이해해 두면, 고성능이 필요할 때 이 코드를 베이스로 다른 언어(Rust·TypeScript)로 옮기는 작업은 AI 코딩 도구에 시키면 된다. 라이브러리가 파이썬에만 있으면 그 기능만 파이썬으로 감싸 Docker 에 올려 API 처럼 쓴다.
  • 리랭커 예고: 임베딩 0.6B 모델은 리랭커 0.6B 와 세트로 배포된다. 양자화(Q4 등)하면 두 모델 합쳐 수 GB 수준이라 로컬 GPU 에 동시 적재해 크로스 인코더 앞 단계로 쓸 수 있다.

💡 실무 노하우: 검색 품질 문제를 만나면 "에이전틱 루프로 풀 것인가, 멀티벡터 스토어로 풀 것인가"를 먼저 결정하라. LLM 호출 비용에 민감한 서비스라면 DB 레코드를 늘리는 멀티벡터 쪽이 대체로 싸다(§6·§7).


6. 핵심 원리

  1. 연산은 언제나 메모리와 교환된다. 사인값을 CPU 로 계산하는 대신 모든 라디안의 사인값을 미리 메모리에 잡아 두면 조회만으로 끝나듯이 — 검색 품질도 같다. 에이전틱 루프는 LLM 이 만족할 때까지 질의를 바꿔 재검색하는 "연산 지불" 노선이고, 멀티벡터 스토어는 레코드 100개면 될 DB 에 2,000개를 넣는 "메모리 지불" 노선이다. 임베딩 유사도 비교는 LLM 연산보다 훨씬 싼 연산이므로 DB 를 불리는 쪽이 대체로 경제적이다.
  2. 임베딩은 같은 손실로 학습된 벡터끼리만 비교 가능하다. 임베딩이 유사도 검색이 되는 이유는 신경망이 같은 손실로 학습됐기 때문이다. 다른 모델의 임베딩과는 비교 자체가 성립하지 않는다.
  3. 모든 신경망 학습은 긍정 학습이다 — 오답 공간은 우리가 넣어야 한다. 모델은 정답만 배웠지 오답 공간(무한히 크다)을 배운 적이 없다. 프롬프트가 뜻대로 안 되는 주원인이므로 부정 조건·실패 조건·종료 조건을 반드시 명시해야 모델이 바르게 동작한다.
  4. 미리 계산해 두는 색인 설계. BM25 의 TF·DF·avgdl 을 질의 시점에 계산하지 않고 트리거로 증분 유지한다 — "합과 개수"만 들고 있으면 평균은 언제든 싸게 나온다.

7. 한계·트레이드오프

💡 트레이드오프 요약: 이번 주 기법 전부가 "품질 ↔ 비용(연산·메모리·복잡도)" 교환이다. 무엇을 지불하는지 명확히 알고 선택하라.

  • LangChain 은 프로토타이핑용. 생태계·내장 구현이 압도적이라 입문·검증에는 최선이지만, 동시성이 약해 실서비스에 그대로 올리기 어렵다. 강의의 권장 — LangChain 으로 쉽게 짜서 개념을 확정하고, 서비스 전환 시 고성능 스택으로 이식하라.
  • HyDE 는 조건부. instruction 을 이해하는 LLM 기반 임베딩 모델(강의 사용: Qwen3 계열)이 주류가 되면서 질문↔답변 공간 문제가 완화됐고, 질의당 LLM 1회 추가 비용이 붙으며, LLM 이 답을 못 지어내는 도메인이면 실패한다.
  • HNSW 는 근사(approximate)다. 정확도 손실이 있으나 "합의 볼 수 있는" 수준의 휴리스틱 인덱스다. IVF·PQ(벡터 양자화) 등 대안 인덱스도 pgvector 가 지원한다.
  • RRF 는 점수 크기 정보를 버린다. 등수만 남기므로 한쪽 리트리버가 압도적으로 확신한 결과도 순위로만 반영된다. 정규화 후 가중합하는 Convex Combination(CC) 계열이 대안으로 존재한다.
  • ts_rank_cd 의 함정: 내장 전문검색 랭킹은 사실상 AND 결합이라, 토큰이 많은 문장형 질의(RAG 의 기본형)에서는 전 토큰 포함 문서가 드물어 랭킹이 무너진다. 짧은 키워드 질의(2~3 단어)에서만 쓸 만하다.
  • 코사인 음수 값은 버려라. 임베딩에서 음수 유사도는 "부정"이 아니므로 의미 해석하지 말 것.

8. 다른 접근과 비교

책(교재) 방식 강의 방식 차이의 이유
벡터 저장 FAISS (인메모리) pgvector + HNSW (영속) 실무 데이터 크기·프로세스 공유·재시작 안정성
부모 문서 저장 InMemoryStore (RAM) Chroma 컬렉션 (key-value) 동일
BM25 BM25Retriever (인메모리) PostgreSQL 수동 구축 (SQL 온리) 인메모리 배제 + 온리 쿼리로 앙상블
임베딩 OpenAIEmbeddings(text-embedding-3-large) LM Studio 로컬 서빙 (Qwen3 계열 8B) API 비용·로컬 통제
  • ts_rank_cd vs 수동 BM25: §7 참조 — AND 결합 vs 시그마 가중합.
  • 에이전틱 루프 vs 멀티벡터 스토어: 연산 지불 vs 메모리 지불(§6). 둘 다 유효하며 비용 구조로 선택.
  • 전용 검색엔진(예: Elasticsearch) vs PostgreSQL 단일화: 강의는 기존 업무 DB 와의 통합·운영 단순성을 들어 PostgreSQL(pgvector) 단일 스택을 택했다. mecab-ko 확장으로 한국어 형태소까지 커버되므로 별도 검색엔진 없이 하이브리드 검색이 성립한다.
  • 다음 주 예고 — GraphRAG: 그래프 형태로 구축된 데이터베이스의 그래프 구조 자체를 임베딩해, 질문과 가까운 그래프를 찾아 그 내용을 컨텍스트로 쓰는 방식. 순수 임베딩 검색과의 차이를 다룬다.

📚 참고 (Tier 1 오픈소스): pgvector https://github.com/pgvector/pgvector · LangChain https://github.com/langchain-ai/langchain · ChromaDB https://github.com/chroma-core/chroma · mecab-ko 기반 한국어 전문검색 확장 textsearch_ko


9. 메타인지 자기평가

  1. Multi-Query·HyDE·Parent Document 를 "질의 확장 / 문서 확장" 두 갈래에 정확히 배치할 수 있는가?
  2. 희소 벡터를 "0이 많은 벡터"가 아니라 "차원이 분리된 벡터"로, 밀집 벡터를 "역전파 학습의 결과"로 설명할 수 있는가? 값이 비는 현상이 왜 정의가 아니라 결과인지 말할 수 있는가?
  3. PostgreSQL 에서 BM25 를 구현하려면 어떤 테이블 3종(순색인·역색인·통계)이 필요하고, 트리거가 각각 무엇을 증분 유지하는지 그릴 수 있는가?
  4. ts_rank_cd 가 RAG 문장형 질의에 부적합한 이유를 "AND vs 시그마"로 설명할 수 있는가?
  5. pgvector HNSW 의 2000차원 제한에 4096차원 임베딩을 맞추는 방법(MRL 절단)과 그 전제 조건을 아는가?
  6. BM25 점수와 코사인 유사도를 왜 직접 합산할 수 없고, RRF 가 무엇을 버리고 무엇을 얻는지 평가할 수 있는가?
  7. 검색 품질 개선을 "연산 ↔ 메모리 교환"으로 환원해, 에이전틱 루프와 멀티벡터 스토어 중 하나를 비용 근거로 고를 수 있는가?
  8. 프롬프트에 부정·실패·종료 조건을 넣어야 하는 이유를 "긍정 학습과 오답 공간"으로 설명할 수 있는가?
난이도
에피소드
질문
카드를 로딩 중...
답변

클릭하거나 Space를 눌러 뒤집기

0 / 0
학습 진도 0%
이동   Space 뒤집기   R 셔플   P 음성 버전 2026-07-29