🔥 고급2026-07-255~7분
RAG 재순위화(Reranking) 실전: 정확도·지연 트레이드오프와 비용 최적 설계
Cross-encoder 재순위화는 RAG 정확도를 크게 높이지만 지연과 비용이 따른다. 단계별 필터링과 캐시 전략으로 두 마리 토끼를 잡는 설계를 다룬다.
RAGrerankingproduction
왜 재순위화가 필요한가
Bi-encoder 벡터 검색은 속도는 빠르지만 의미 유사도를 코사인 거리로만 판단한다. 실험 결과 Top-10 recall은 85% 수준이지만 Top-3 precision은 55%에 그치는 경우가 많다. LLM에 전달하는 컨텍스트가 부정확하면 hallucination 비율이 직접 올라간다. Cross-encoder 재순위화를 추가하면 Top-3 precision이 78%까지 상승하는 것이 일반적이다.
3단계 파이프라인 설계
1단계 — 벡터 검색 (< 50ms): Top-50을 빠르게 소환한다. 이 단계에서 필터를 최대한 적용해 후보를 줄인다.
2단계 — Cross-encoder 재순위화 (50~200ms): Top-50을 쿼리와 쌍으로 묶어 점수를 계산한다. cohere.rerank 또는 로컬 ms-marco-MiniLM-L-6-v2를 사용한다. Cohere Rerank v3 기준 1,000 쌍 재순위화 비용은 약 $0.002, 지연은 평균 120ms.
3단계 — LLM 호출 (300~800ms): 재순위화된 Top-5만 컨텍스트로 넘긴다. Top-50 전체를 넘기는 것 대비 입력 토큰을 80% 절감한다.
import anthropic
import cohere
co = cohere.Client("COHERE_API_KEY")
client = anthropic.Anthropic()
def rag_with_rerank(query: str, candidates: list[dict]) -> str:
# 2단계: 재순위화
docs = [c["text"] for c in candidates]
rerank_result = co.rerank(
model="rerank-v3.5",
query=query,
documents=docs,
top_n=5,
)
top_docs = [
candidates[r.index]["text"]
for r in rerank_result.results
]
context = "\n\n".join(top_docs)
# 3단계: LLM 호출
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=512,
messages=[
{
"role": "user",
"content": f"Context:\n{context}\n\nQuestion: {query}",
}
],
)
return response.content[0].text
실패 모드와 운영 체크리스트
실패 모드
- 재순위화 API 타임아웃: 외부 reranker가 200ms SLA를 초과하면 전체 파이프라인 P99가 급등한다. 폴백으로 bi-encoder 점수를 그대로 사용하는 경로를 반드시 준비한다.
- 후보 수 과소설정: Top-20 이하로 벡터 검색을 제한하면 재순위화가 개선할 여지 자체가 없다. 최소 Top-30 이상을 권장한다.
- 재순위화 점수 threshold 미설정: 낮은 점수 문서가 Top-5 안에 포함되면 오히려 노이즈가 된다. 점수 0.4 미만은 필터링하는 규칙을 추가한다.
운영 체크리스트
- [ ] 재순위화 API 레이턴시 P50/P99 별도 추적
- [ ] 재순위화 폴백 경로 구현 및 경보 설정
- [ ] 월별 reranker 비용 vs. LLM 토큰 절감액 비교 리포트
- [ ] Top-N 변경 시 offline 정확도 평가 재실행
- [ ] 재순위화 점수 분포 모니터링 (분포 드리프트 감지)