임베딩 & 리랭커 모델
2-stage retrieval 파이프라인의 두 축 — 개념, 2026년 최신 모델 아키텍처, 그리고 poc-chat-search 코드에 어떻게 적용되어 있는지.
TL;DR
임베딩(bi-encoder)은 query·doc 을 독립적으로 벡터화해 수만 건에서 후보를 빠르게 추리고, 리랭커(cross-encoder)는 (query, doc) 쌍을 함께 처리해 정밀하게 재정렬한다.
코드에 쓰이는 모델은 Gemini Embedding 2 (3072d, MRL, 250+ 언어, multimodal MTEB 1위) + Cohere Rerank 4 Pro (32K context, 100+ 언어, ELO 1627).
둘은 경쟁이 아니라 보완 — recall 은 임베딩, precision 은 리랭커. 50~100 후보 → top-10 정렬이 표준.
1. 왜 두 단계인가 — Recall vs Precision
매물 DB 가 50,000건 있을 때, 사용자 쿼리에 대해 가장 적합한 매물 5건을 찾는다고 하자. 두 가지 극단이 있다.
모든 쌍 정밀 채점
모든 매물에 대해 (query, doc) 쌍을 cross-encoder 에 넣어 채점. 정확하지만 한 쿼리당 50,000 회 LLM 호출. 실시간 검색 불가능.
임베딩 유사도 top-5
모든 매물을 미리 벡터화해 두고 cosine top-5. 빠르지만 query·doc 토큰이 한 번도 만나지 않아 미세한 의미 차이를 놓침. 정확도 한계.
2-stage retrieval 은 두 극단의 절충 — 임베딩으로 50~100건 recall, 리랭커로 top-10 precision. 전수 비교 대비 500~1000× 빠르면서 벡터-only 대비 정확도 +20~40%p.
2. Bi-encoder vs Cross-encoder
둘의 본질적 차이는 query 와 document 가 모델 내부에서 만나는가이다.
독립 인코딩 → 벡터 비교
query 와 doc 을 각각 따로 인코더에 통과시켜 벡터로 변환. 비교는 외부에서 cosine·dot-product 로.
장점
- doc 벡터를 미리 계산해 벡터 DB(Qdrant) 에 저장 → query 1회 인코딩 + ANN 검색
- 수억 건 인덱스에서도 ms 단위 응답
한계
- 전체 의미를 단일 벡터로 압축 → 미세한 어휘·문맥 차이 손실
- query·doc 토큰이 서로 attention 으로 만나지 못함
쌍 통합 인코딩 → 점수
query 와 doc 을 하나로 concat 해서 같이 Transformer 에 입력. 모든 token 이 cross-attention 으로 상호작용. 최종 layer 의 [CLS] 토큰을 sigmoid 통과 → 0~1 관련도 점수.
장점
- token-level 상호작용 → 미묘한 부정·조건·수치까지 포착
- 최신 모델 기준 단일 reranker 적용만으로 RAG 정확도 +40% 보고도 있음
한계
- 사전 계산 불가 — 모든 후보에 대해 매번 inference
- 비용·레이턴시가 N(후보 수)에 선형 비례
그래서 둘은 경쟁이 아니라 직렬 조합이다. Bi-encoder 가 ANN 으로 50건 좁히고, Cross-encoder 가 그 50건에 대해서만 정밀 채점.
3. 임베딩 모델 — Google Gemini Embedding 2
모델 ID: google/gemini-embedding-2-preview (OpenRouter passthrough).
| 속성 | 값 | 의미 |
|---|---|---|
| 차원 | 3072 (default), MRL 가변 | 의미 표현의 해상도 |
| Context | 8,192 tokens | 한 번에 인코딩 가능한 길이 |
| 언어 | 250+ (Korean 우수) | 다국어 multilingual MTEB 1위 (+5pt vs 2위) |
| 입력 | text · image · doc · audio · video | natively multimodal — 한 벡터공간에 5 modality |
| 가격 | $0.20 / 1M input tokens | OpenRouter passthrough 기준 |
| 아키텍처 | Gemini decoder + pooling | last-token / mean-pool 로 벡터 추출 |
3.1 Matryoshka Representation Learning (MRL)
MRL 은 학습 기법이지 별도 아키텍처가 아니다. 2022 NeurIPS (Kusupati et al.) 논문이 원조. 핵심 아이디어:
MRL 의 학습 트릭
벡터 d=3072 의 정보가 차원 전체에 균등 분포하도록 두지 않고, 앞쪽 차원에 중요 정보를 집중시키도록 loss 를 설계. 학습 시 {3072, 1536, 768, 256, 128} 같은 다양한 prefix 에서 동시에 contrastive loss 를 걸어, 어디서 잘라도 의미가 보존되게 함.
결과: vec[:768] 만 잘라 써도 품질 90%+ 보존. 저장·검색 비용을 직선적으로 줄일 수 있는 nested embedding. 권장 차원은 3072 / 1536 / 768.
3.2 비대칭 임베딩 (Asymmetric Retrieval)
같은 텍스트라도 역할(query vs document)에 따라 다른 벡터로 표현하는 기법.
# Vertex AI / Google AI Studio 직접 호출 시
embed(text="강남 30평대 신축", task_type="RETRIEVAL_QUERY") # 짧은 질문
embed(text="강남구 역삼동 ...", task_type="RETRIEVAL_DOCUMENT") # 긴 매물 설명
동일 텍스트라도 RETRIEVAL_QUERY 와 RETRIEVAL_DOCUMENT 의 벡터는 다르다. Q&A 처럼 짧은 질문이 긴 문서를 찾는 비대칭 시나리오에 최적화됨. Symmetric similarity 모델 대비 retrieval 품질이 명확히 향상.
OpenRouter wrapper 제약
현재 코드는 OpenRouter /embeddings 엔드포인트를 거치는데, wrapper 가 task_type 옵션을 무시한다. 검증: 같은 텍스트의 query/doc 벡터 cosine = 1.0 (완전 동일). Vertex AI 직접 호출로 전환하면 비대칭 임베딩 활성화 가능. 어댑터에는 task_type 을 이미 넘기도록 작성해 두어, 직접 호출 전환 시 한 줄도 안 바꿔도 된다.
4. 리랭커 모델 — Cohere Rerank 4 Pro
모델 ID: cohere/rerank-4-pro (OpenRouter passthrough).
| 속성 | 값 | 의미 |
|---|---|---|
| 아키텍처 | Cross-encoder | (query, doc) 쌍 통합 인코딩 |
| Context | 32K tokens | Rerank 3.5 대비 4× — 긴 매물 description truncate 불필요 |
| 언어 | 100+ (Korean 공식) | finance·healthcare·manufacturing 도메인 검증 |
| 품질 | ELO 1627 | Qwen Reranker 8B · Jina v3 · Voyage 2.5 와 동급 이상 |
| 가격 | $2.50 / 1M tokens 또는 $2.40 / 1k req | 임베딩보다 12× 비쌈 — top-N 만 호출하는 이유 |
| 출력 | relevance_score 0~1 + 정렬된 index | 원본 후보 list 의 reorder 인덱스 |
4.1 왜 32K 가 중요한가
이전 세대 reranker (BGE-reranker-v2-m3) 는 max_length 512. 한국어 매물 description 은 한자 2~3천자 흔하다 → 토큰화하면 1500~3000 토큰. 512 안에 우겨넣으려면 심한 truncate 필요 → 가격·구조·옵션 같은 후반부 정보가 잘려 정확도 손실.
Rerank 4 Pro 는 32K → description 전체 + address + agent comment 까지 한 번에 reranker 에 던질 수 있다. 코드의 DEFAULT_TEXT_CHARS = 2000 안전 cap 은 레이턴시·비용 절약용이지 모델 한계가 아니다.
4.2 Cohere 공식 API vs OpenRouter wrapper
OpenRouter wrapper 제약 — Cohere 직접 호출과의 차이
Cohere 공식 /rerank 는 documents=[{address, description, ...}] 와 rank_fields=[...] 를 지원해, 필드별로 가중치를 줄 수 있다. OpenRouter wrapper 는 documents=string[] 만 허용 (검증: dict 형태로 보내면 ZodError 400).
그래서 코드에서는 address 와 description 을 단일 string 으로 concat 해서 보낸다. Cohere 직접 계약 시 rank_fields 활성화로 정확도 추가 향상 가능.
5. 파이프라인 — 코드 흐름
사용자 질의가 LLM 답변으로 변환되기까지의 retrieval stage:
-
1
~50msembed_query("강남 30평대 신축")Gemini Embedding 2 · 3072d 벡터 1개 생성
-
2
~20msQdrant ANN search (cosine top-50)사전 인덱싱된 매물 벡터와 근사 최근접 탐색 — recall 우선
-
3
~400msrerank(query, 50 candidates) → top-10Cohere Rerank 4 Pro cross-encoder · 정밀 재정렬
-
4
~1.5sGemini 2.5 Flash 호출top-10 매물을 context 로 주입해 자연어 응답 생성
레이턴시 합 ~2s. 임베딩이 가장 싸고 빠르므로 ANN 단계는 후보를 50~100 까지 키워도 무방. reranker 가 가장 비싸므로 top-10 ~ top-20 으로 제한.
6. 어댑터 코드 매핑
두 모델은 hexagonal 구조의 adapter 로 격리되어 있다 — 도메인은 port 만 보고, 구현은 언제든 교체 가능.
# poc-chat-search/src/adapters/embedding/openrouter_gemini_adapter.py
MODEL_NAME = "google/gemini-embedding-2-preview"
DEFAULT_DIMENSIONS = 3072
DEFAULT_BATCH_SIZE = 32 # 수백건 동시 시 OpenRouter 빈 응답 이슈
def embed_query(self, text: str) -> EmbeddingResult:
vec = self._post([text], task_type="RETRIEVAL_QUERY")[0]
return EmbeddingResult(vector=vec, dimensions=len(vec))
def embed_document(self, text: str) -> EmbeddingResult:
vec = self._post([text], task_type="RETRIEVAL_DOCUMENT")[0]
return EmbeddingResult(vector=vec, dimensions=len(vec))
# poc-chat-search/src/adapters/reranker/openrouter_cohere_adapter.py
MODEL_NAME = "cohere/rerank-4-pro"
def rerank(self, query, candidates, *, top_k=10, text_chars=None):
cap = text_chars or self.DEFAULT_TEXT_CHARS # 2000자
documents = [
f"{c.get('address','')} {str(c.get('description',''))[:cap]}".strip()
for c in candidates
]
r = self._client.post("/rerank", json={
"model": self._model_name,
"query": query,
"documents": documents,
"top_n": top_k,
})
# 응답: {"results": [{"index": int, "relevance_score": float}, ...]}
설계 포인트
(1) 두 어댑터 모두 환경변수 폴백 없음 — 키 누락 시 즉시 KeyError. 운영에서 무음 실패 방지.
(2) task_type 을 미리 넘겨 둠 → 직접 provider 전환 시 코드 변경 0.
(3) batch_size=32 는 OpenRouter Gemini wrapper 의 경험적 한계 (수백건 동시 → 빈 응답). 안전한 chunked POST.
7. 참고문헌
- Google AI Blog — Gemini Embedding 2: Our first natively multimodal embedding model
- Google Developers Blog — Building with Gemini Embedding 2: Agentic multimodal RAG and beyond
- Google AI for Developers — Embeddings — Gemini API 공식 문서
- Vertex AI Docs — Choose an embeddings task type (RETRIEVAL_QUERY/DOCUMENT)
- MindStudio — What Is Matryoshka Representation Learning in Gemini Embedding 2?
- VentureBeat — Google's Gemini Embedding 2 arrives with native multimodal support
- OpenRouter — google/gemini-embedding-2-preview (pricing & API)
- VentureBeat — Cohere's Rerank 4 quadruples the context window over 3.5
- Agentset — Cohere Rerank 4 Pro — Reranker Details & Performance
- OpenRouter — cohere/rerank-4-pro (pricing & API)
- Pinecone Learn — Rerankers and Two-Stage Retrieval
- Towards Data Science — Advanced RAG Retrieval: Cross-Encoders & Reranking
- Mudassar Hakim (Medium) — Inside the RAG Retrieval Pipeline: Bi-Encoders, Cross-Encoders, Re-Rankers
- Ailog Research — Cross-Encoder Reranking Improves RAG Accuracy by 40%
- Kusupati et al. NeurIPS 2022 — Matryoshka Representation Learning