CHANGHYUNAN

Developer, maker, thinker.

RAG Hybrid Search 튜닝 가이드: 감 대신 Decision Tree로 판단하기

AIDev

Hybrid Search란

RAG에서 검색은 보통 두 갈래입니다.

Dense Search — 임베딩 벡터 기반. 쿼리와 문서의 의미적 유사도로 찾습니다. "고객 불만 처리"를 검색하면 "클레임 대응 프로세스"도 잡아냅니다. 대신 정확한 키워드나 고유명사에는 약합니다.

Sparse Search — BM25 같은 키워드 매칭 기반. "ERR-5012"를 검색하면 정확히 그 문자열이 있는 문서를 찾습니다. 대신 동의어나 문맥은 이해 못 합니다.

BM25 (Best Matching 25): 문서에서 특정 단어가 얼마나 자주, 얼마나 독점적으로 등장하는지를 수식으로 계산하는 고전적 랭킹 알고리즘. TF-IDF의 개량판.

TF-IDF (Term Frequency-Inverse Document Frequency): 문서 내 단어의 중요도를 수치화하는 기법. 특정 문서에서 자주 등장하되, 전체 문서에서는 드문 단어일수록 높은 값을 가집니다.

Hybrid Search는 이 둘을 조합하는 겁니다. Dense가 놓치는 걸 Sparse가 잡고, Sparse가 놓치는 걸 Dense가 잡습니다. 대부분의 프로덕션 RAG가 hybrid를 쓰는 이유입니다.

근데 문제는 — 조합하는 순간 건드릴 수 있는 파라미터가 확 늘어난다는 겁니다. 그래서 감으로 튜닝하게 됩니다. 이 글은 감 대신 decision tree로 판단하는 방법을 정리합니다.


평가 인프라부터 세팅하기

튜닝의 전제는 측정입니다. 이거 없으면 튜닝이 아니라 '찍기'일 뿐입니다.

Golden Test Set

쿼리 30~50개 + 각 쿼리에 대한 정답 문서(gold passage) 매핑. 이건 사람이 직접 만들어야 합니다. LLM한테 초안을 뽑되, 반드시 사람이 검수해야 합니다. 쿼리는 두 그룹으로 나눠야 합니다:

  • 키워드 쿼리: 고유명사, ID, 코드명 검색 (예: "ERR-5012", "calculateTax 함수")
  • 시맨틱 쿼리: 의미 기반 검색 (예: "고객 환불 절차가 어떻게 되나요")

이 분리가 나중에 alpha 튜닝할 때 핵심이 됩니다.

핵심 지표

지표 측정 대상 LLM 호출 용도
Recall@k 정답 문서가 top-k 안에 있는 비율 X 검색 커버리지
Precision@k top-k 중 관련 문서 비율 X 검색 정밀도
MRR (Mean Reciprocal Rank/평균 역순위) 정답의 평균 순위 역수 X 랭킹 품질
NDCG@k (정규화 감쇠 누적 효용) 관련 문서가 상위에 몰려있는 정도 X reranker 평가
SemScore 생성 답변과 정답의 임베딩 유사도 X 답변 품질
RAGAS Faithfulness 답변이 검색 문서에 근거하는지 O hallucination 탐지
RAGAS Context Precision 관련 청크의 상위 랭킹 여부 O 정밀 랭킹 평가

NDCG: Normalized Discounted Cumulative Gain. 상위 순위에 관련 문서가 몰려있을수록 높은 점수를 부여하는 랭킹 품질 지표입니다. "관련 문서가 있긴 한데 10번째에 있다"와 "1번째에 있다"를 구분합니다.

SemScore: 생성된 답변과 정답을 각각 임베딩한 뒤 코사인 유사도로 품질을 측정하는 지표. 12개 LLM의 답변을 8개 지표로 평가한 벤치마크에서, GPT-4 기반 평가(G-Eval)만큼 인간 판단과 높은 상관관계를 보였습니다. LLM 호출 없이 임베딩만으로 계산하므로 대량 하이퍼파라미터 스윕(sweep)에 적합합니다.

RAGAS: RAG 전용 평가 프레임워크. Faithfulness(근거 기반 답변인지), Answer Relevancy(질문에 맞는 답인지), Context Precision/Recall(검색 품질)을 LLM 호출로 판정합니다. 정확하지만 건당 30~45초, 비용도 높습니다.

평가 전략 2단계

단계 목적 지표 비용
Phase 1: 넓은 스윕 수십 개 설정을 빠르게 거르기 Recall@k + Precision@k + SemScore 낮음
Phase 2: 정밀 비교 상위 3~5개 설정 최종 선택 RAGAS Faithfulness + Context Precision 높음

Phase 1에서 80%를 걸러내고, Phase 2에서 남은 후보만 비싼 지표로 비교하면 됩니다.


튜닝 가능한 Knob 카탈로그

청킹 & 인덱싱

Chunk Size

문서를 몇 토큰 단위로 자를지. 작으면 세밀하지만 맥락이 끊기고, 크면 맥락은 유지되지만 노이즈가 섞입니다.

평가: chunk size만 바꿔가며 Recall@10과 Context Relevance(검색된 청크 중 답변에 실제로 쓸모 있는 토큰 비율)를 측정.

Chunk Size:   200   |  400   |  600   |  800   |  1000
Recall@10:    0.82  |  0.88  |  0.85  |  0.79  |  0.72
Ctx Relev:    0.41  |  0.55  |  0.62  |  0.58  |  0.45
결과 조치
Recall 높은데 답변 품질 낮음 → 맥락 부족 chunk size 키우기
Recall 자체가 낮음 → 정보가 묻혀 있음 chunk size 줄이기
Context Relevance < 0.4 → 노이즈 과다 chunk size 줄이기 + strategy 재검토
Ctx Relev ≥ 0.7 + Recall도 높음 sweet spot — 고정

3~4개 size(200/400/800)로 경향만 보면 됩니다. 50 단위 미세조정은 시간 낭비입니다.

Chunk Overlap

청크 간 겹치는 구간. 경계에서 정보가 잘리는 걸 방지합니다. chunk size의 10~25%.

평가: chunk size 고정, overlap만 바꿔가며 Recall@k 측정. 특히 경계 쿼리(정답이 두 청크에 걸쳐 있는 케이스)를 따로 확인.

결과 조치
경계 쿼리 Recall이 일반 쿼리보다 현저히 낮음 overlap 늘리기 (20~25%)
overlap 0%↔20% Recall 차이 < 2%p 10%로 낮춰서 인덱스 절약
overlap 늘려도 경계 쿼리 Recall 변화 없음 semantic chunking 전환 고려

Chunking Strategy

문서 유형 권장 strategy
구조적 (매뉴얼, FAQ, API docs) recursive — 문단/문장 경계 존중
비구조적 (회의록, 이메일) semantic 시도 → 안 되면 recursive
짧고 균일 (채팅, 댓글) fixed size
길고 구조적 (기술 문서, 논문) hierarchical — 작은 청크로 검색, 큰 청크 반환

Hierarchical (Parent-Child) Chunking: child chunk(100~500 토큰)를 검색 단위로 쓰되, 매칭 시 parent chunk(500~2000 토큰)를 LLM에 반환하는 2단 구조. "검색은 바늘로, 전달은 실타래로." 구조적 문서에서 relevance +20~35%. 자기 완결적 문서(FAQ)에서는 효과 미미.

Contextual Retrieval

청킹 후, 임베딩 전에 각 청크에 문서 맥락 요약을 prepend하는 전처리. Anthropic이 제안한 방식입니다.

"매출이 전분기 대비 3% 증가했습니다"

→ "이 청크는 Acme Corp 2024년 2분기 실적 보고서에서 발췌. 매출 추이 섹션. 매출이 전분기 대비 3% 증가했습니다"

검색 실패율 49% 감소. Reranker 결합 시 67% 감소.

파라미터 범위
On/Off 적용 여부
Context 길이 50~100 토큰
적용 대상 embedding / BM25 / 양쪽 다(가장 효과적)

평가: on/off의 Recall@k 비교.

결과 조치
대명사/지시어 많은 문서에서 검색 실패 잦음 적용
청크가 자기 완결적 (FAQ, API docs) 생략 — 인덱싱 비용 절약
Recall 향상 < 5%p 비용 대비 효과 부족 → 생략

임베딩

Embedding Model

상황 권장
도메인 특화 (의료, 법률, 코드) 도메인 파인튜닝 또는 multilingual 모델
범용 OpenAI text-embedding-3-large / Cohere embed v3
비용 민감 text-embedding-3-small / 오픈소스 (BGE, E5)

Embedding Dimensionality

차원 수를 줄여 속도/비용 절약. MRL 지원 모델이면 품질 손실이 적습니다.

Matryoshka Representation Learning (MRL): 임베딩 벡터의 앞쪽 N차원만 잘라도 성능이 유지되도록 학습하는 기법. 러시아 마트료시카 인형처럼 큰 벡터 안에 작은 벡터가 이미 유효하게 들어있는 구조. OpenAI text-embedding-3, Jina v3 등이 지원.

파라미터 범위
Target 차원 모델별 상이 (OpenAI: 256/512/1536/3072)
방식 MRL (모델 내장) / PCA (후처리)

PCA (Principal Component Analysis, 주성분 분석): 고차원 벡터에서 분산이 큰 축을 찾아 저차원으로 압축하는 통계 기법. 임베딩 맥락에서는 생성된 고차원 임베딩을 후처리로 차원 축소할 때 사용합니다.

판단: 차원 50% 축소 시 Recall@10 하락 < 2%p이면 줄이는 게 이득. 5%p 이상이면 유지.

Embedding Quantization

벡터 정밀도를 낮춰 저장/메모리/연산 비용 절감. 1M+ 벡터부터 의미 있습니다.

Quantization: float32(32비트) 벡터를 더 적은 비트로 표현하는 압축 기법. 정밀도를 일부 희생하고 저장 공간과 연산 속도를 얻습니다. 이미지/오디오 압축의 벡터 버전.

방식 압축률 품질 손실 비고
bfloat16 2x ~0% 무조건 적용 가능
float8 4x <0.3% 정확도/압축 최적 밸런스
int8 (scalar) 4x 소폭 calibration 필요
binary (1-bit) 32x 상당 full-precision rescoring 필수

판단: Recall@10 하락 < 1%p이면 채택. binary는 반드시 rescoring과 조합.


검색 (Retrieval)

Fusion Method: RRF vs Weighted Sum

Dense와 Sparse 결과를 합치는 방법. alpha보다 근본적인 선택입니다.

RRF (Reciprocal Rank Fusion): 각 retriever가 매긴 스코어를 무시하고, 순위만으로 결합하는 방식. score = Σ 1/(k + rank). BM25 점수와 cosine similarity처럼 스케일이 다른 값을 정규화할 필요가 없어서 out-of-the-box 안정성이 높습니다.

Weighted Score Fusion: alpha × dense_score + (1-alpha) × sparse_score. 스코어 정규화가 필요하지만 세밀한 제어가 가능합니다.

파라미터 설명 기본값
Fusion method RRF vs Weighted Sum
RRF k 상위 순위 우대 강도. 작으면 top 편중, 크면 평등 60
Score 정규화 (Weighted Sum) min-max / sigmoid / z-score min-max
결과 조치
Weighted Sum에서 alpha 튜닝해도 결과 불안정 score 정규화 문제 → RRF로 전환
RRF에서 한쪽 retriever 결과가 무시됨 retriever별 가중치 추가 또는 Weighted Sum 전환
RRF k=60에서 상위 결과 다양성 부족 k 낮추기 (20~40)

Alpha (Dense/Sparse 비율)

Weighted Score Fusion의 핵심 knob. alpha=1이면 pure dense, alpha=0이면 pure sparse.

평가: 키워드 쿼리와 시맨틱 쿼리를 분리해서 alpha 0.0~1.0 구간의 Recall@10을 측정.

Alpha:           0.0   0.2   0.4   0.5   0.6   0.8   1.0
키워드 Recall:   0.92  0.88  0.80  0.75  0.68  0.52  0.41
시맨틱 Recall:   0.35  0.52  0.70  0.76  0.82  0.88  0.90
전체   Recall:   0.63  0.70  0.75  0.76  0.75  0.70  0.66
결과 조치
전체 Recall에 명확한 피크 → 그게 최적 해당 값으로 고정
키워드 Recall < 0.7 → sparse 무시되는 중 alpha 낮추기
시맨틱 Recall < 0.7 → dense 무시되는 중 alpha 올리기
두 곡선의 교차 지점 → 균형점 교차점 ±0.1에서 미세조정
어떤 alpha에서도 양쪽 0.8 이상 안 나옴 alpha 문제 아님 → 청킹/임베딩 재검토

Top-k

검색 결과를 몇 개 가져올지.

평가: k를 5/10/20/30/50으로 바꿔가며 Recall@k와 Precision@k 동시 측정.

k:            5     10    20    30    50
Recall@k:    0.60  0.78  0.88  0.91  0.93
Precision@k: 0.72  0.58  0.38  0.28  0.18
결과 조치
k=10→20에서 Recall +10%p 이상 → 아직 놓치는 문서 많음 top-k 늘리기 (reranker 필수)
k=10→20에서 Recall +3%p 미만 → 추가분은 노이즈 top-k=10이면 충분
Precision@k < 0.3 → LLM에 노이즈 과다 top-k 줄이기 또는 reranker 추가
Reranker 있음 → top-k는 reranker 입력 풀 top-k 넉넉히(20~30) + top-n으로 필터

BM25 k1 / b

Sparse 검색의 세부 파라미터. alpha를 낮췄는데도 키워드 검색이 안 될 때만 건드립니다. 대부분 기본값으로 충분합니다.

k1 (term frequency saturation): 같은 단어가 문서에 반복 등장할 때 점수를 얼마나 더 줄지 조절. 기본 1.2. 높이면 반복에 민감, 낮추면 둔감.

b (length normalization): 문서 길이에 따른 점수 보정. 기본 0.75. 높이면 짧은 문서 유리, 낮추면 긴 문서도 공평.

k1 판단:

결과 조치
키워드 반복 문서가 상위에 와야 하는데 안 옴 k1 올리기 (1.5→2.0)
키워드 1회 문서가 반복 문서에 밀림 k1 낮추기 (1.2→0.8)

b 판단:

결과 조치
짧은 문서(FAQ)가 과도하게 상위 b 낮추기 (0.75→0.5)
긴 문서(매뉴얼)의 정보를 못 찾음 b 낮추기 (0.75→0.3)
문서 길이가 대체로 균일 기본값 유지

HNSW Index 파라미터

벡터 DB의 ANN 검색 파라미터. 거의 모든 벡터 DB가 HNSW를 쓰는데, 기본값 그대로 두는 경우가 대부분입니다. 데이터가 늘면 조용히 검색 품질이 떨어집니다.

HNSW (Hierarchical Navigable Small World): 벡터 검색을 위한 그래프 기반 인덱스 구조. 모든 벡터를 비교하는 대신, 그래프를 타고 이동하며 근접 이웃을 찾습니다. 정확도와 속도의 트레이드오프를 파라미터로 조절할 수 있습니다.

파라미터 설명 기본값 재인덱싱
ef_search 쿼리 시 탐색 깊이 100 불필요 (실시간 조정)
M 노드당 연결 수 16 필요
ef_construction 빌드 시 탐색 깊이 200 필요

평가: ef_search를 올려가며 Recall@10과 latency 동시 측정.

결과 조치
데이터 1M+ 이후 Recall 하락 (latency 정상) ef_search 올리기 (200~400)
ef_search 올려도 Recall 변화 없음 M이 낮음 → M 올리고 재인덱싱
인덱싱 시간 과다 ef_construction 낮추기 (recall 트레이드오프 확인)

Similarity Score Threshold

Top-k와 별개로 최소 유사도 설정. 순위 안에 있어도 점수가 낮으면 버립니다.

파라미터 범위
Score threshold 예: cosine similarity ≥ 0.7
적용 시점 검색 후 / reranking 후 / 양쪽
Fallback 전부 탈락 시 최소 1개 반환 vs "관련 정보 없음"
결과 조치
코퍼스에 없는 주제 쿼리 시 엉뚱한 답변 threshold 추가 (hallucination 방지)
정상 쿼리에서도 결과 부족 threshold 낮추기 또는 fallback 활성화
Reranker 점수가 bimodal(관련/무관 명확 분리) reranker 점수 기준 threshold가 효과적

후처리 (Post-retrieval)

Reranker + Top-n

검색 결과를 cross-encoder로 다시 정렬. 있고 없고의 차이가 가장 큰 knob 중 하나입니다.

Cross-encoder: 쿼리와 문서를 하나의 입력으로 묶어서 BERT 계열 모델에 통째로 넣는 방식. Bi-encoder(각각 따로 임베딩)보다 훨씬 정확하지만 모든 후보에 대해 개별 추론해야 해서 느립니다. 그래서 검색이 아니라 reranking에 쓰입니다.

평가: reranker 전후 NDCG@kSemScore 비교. Recall은 reranker로 바뀌지 않으므로(순서만 바뀜) NDCG가 적합합니다.

                  Reranker 없음    Reranker 있음
NDCG@10:          0.62            0.81
SemScore:         0.71            0.84
Latency (p50):    120ms           340ms
결과 조치
NDCG +0.15 이상 → 효과 확실 유지
NDCG +0.05 미만 → 효과 미미 제거 (latency만 증가)
latency 2배 이상 증가 경량 모델로 교체 (FlashRank, TinyBERT)
top-n=3과 top-n=5의 SemScore 차이 < 2%p top-n=3으로 절약
top-n=3이 top-n=10보다 SemScore 높음 top-n 줄이기 — less is more

Context Window Packing

검색 결과를 LLM 프롬프트에 어떻게 담는지. 검색은 잘 했는데 답변이 이상하면 여기부터 의심하세요.

Lost in the Middle: LLM이 긴 context의 앞부분과 뒷부분은 잘 활용하지만, 중간에 있는 정보는 무시하는 경향. 관련 청크를 중간에만 배치하면 있어도 못 쓰는 상황이 발생합니다.

파라미터 설명
청크 순서 관련도 높은 것을 앞/뒤에 배치 (Lost in the Middle 대응)
Context budget 모델 context window의 80% 이하 권장
메타데이터 포함 출처, 날짜, 관련도 점수를 같이 넣을지
중복 제거 overlap으로 인한 중복 텍스트 제거

평가: 동일 검색 결과로 packing 방식만 바꿔가며 SemScore + RAGAS Faithfulness 비교.

결과 조치
마지막 청크 정보를 답변이 무시 관련 청크를 앞과 뒤 양쪽에 배치
context 길어질수록 SemScore 하락 budget 줄이기 + top-n 줄이기
비슷한 내용의 청크가 중복 중복 제거 활성화
메타데이터 포함 시 Faithfulness 향상 출처 정보 포함 (grounding 강화)

쿼리 (Query Processing)

Query Rewriting

원래 쿼리를 LLM이 검색에 적합한 형태로 변환합니다. "아까 그거 다시 알려줘"를 "결제 취소 프로세스 안내"로 바꾸는 식.

평가: 원본 쿼리와 rewritten 쿼리 각각의 Recall@k 비교. 의도 보존율도 체크 — 원본과 rewritten을 LLM에 보여주고 "같은 정보를 요청하는가?" 판단시킵니다.

                    원본 쿼리    Rewritten    변화
대화형 쿼리 Recall:  0.61        0.79        +0.18 ✓
명확한 쿼리 Recall:  0.85        0.83        -0.02 (무해)
의도 보존율:         —           0.94
결과 조치
대화형 쿼리 Recall +10%p 이상 유지
명확한 쿼리 Recall -5%p 이상 하락 대화형 쿼리에만 조건부 적용
의도 보존율 < 0.9 프롬프트 수정 또는 제거

HyDE (Hypothetical Document Embeddings)

쿼리로 가상의 답변을 먼저 생성하고, 그 답변의 임베딩으로 검색합니다.

HyDE: "심부전 치료법은?"이라는 쿼리 대신, LLM이 "심부전 치료에는 ACE 억제제, 베타차단제, 이뇨제 등이 사용되며..."라는 가상 답변을 생성하고, 이 답변과 유사한 실제 문서를 검색하는 방식. 짧거나 추상적인 쿼리의 임베딩 품질을 높여줍니다.

평가: 원본 쿼리 vs HyDE 검색의 Recall@k. 짧은 쿼리(3단어 이하)와 긴 쿼리를 분리.

결과 조치
짧은 쿼리 Recall +15%p 이상 유지
긴 쿼리에서 Recall 하락 짧은 쿼리에만 조건부 적용
Latency +1.5초 이상 캐싱 적용 또는 제거
가상 답변 hallucination > 20% 프롬프트 강화 또는 제거

Decision Tree #1: 처음 셋업할 때

처음부터 잘 깔면 나중에 튜닝할 게 줄어듭니다. 이 순서대로 접근하면 됩니다.

[1] Chunking Strategy 결정
    │
    ├─ 구조적 문서 (매뉴얼, FAQ, API docs)
    │   → recursive + chunk size 300~500
    │
    ├─ 비구조적 문서 (회의록, 이메일)
    │   → semantic 시도 → 안 되면 recursive + 500~800
    │
    ├─ 짧고 균일 (채팅, 댓글)
    │   → fixed size + 200~400
    │
    └─ 길고 구조적 (기술 문서, 논문)
        → hierarchical (child 200~400, parent 800~1500)
    │
    overlap: chunk size의 15~20%
    │
[2] Contextual Retrieval
    │
    ├─ 대명사/지시어/상대 참조 많음 → 적용 (양쪽 다)
    └─ 청크가 자기 완결적 → 생략
    │
[3] Embedding Model
    │
    ├─ 도메인 특화 → 파인튜닝 or multilingual 모델
    └─ 범용 → text-embedding-3-large / Cohere embed v3
    │
[4] Fusion Method + Alpha
    │
    ├─ 처음이면 → RRF (k=60)로 시작
    └─ 세밀한 제어 → Weighted Sum
        ├─ 고유명사/코드/ID 중심 → alpha 0.3~0.5
        ├─ 의미 기반 중심 → alpha 0.7~0.8
        └─ 불확실 → alpha 0.5 → 평가 후 조정
    │
[5] Top-k
    │
    ├─ reranker 쓸 예정 → 20~30
    └─ reranker 안 쓸 예정 → 5~10
    │
[6] Reranker + Score Threshold
    │
    ├─ 정확도 최우선 → reranker + top-n 3~5
    ├─ 속도 최우선 → reranker 생략
    └─ 둘 다 → reranker + top-n 낮춰서 속도 보상
    │
    + score threshold (코퍼스에 없는 쿼리 대비)
    │
[7] Context Packing
    │
    ├─ 관련 청크를 앞/뒤 배치 (Lost in the Middle 대응)
    ├─ context budget ≤ 80%
    └─ 중복 제거 활성화
    │
[8] Query Processing (필요시)
    │
    ├─ 대화형 쿼리 (챗봇) → query rewriting
    ├─ 짧고 추상적 쿼리 → HyDE 시도
    └─ 명확한 키워드 중심 → 생략

Decision Tree #2: 이미 돌리고 있는데 문제있을 때

증상부터 찾아야 합니다.

관련 문서를 못 찾음 (Recall 낮음)

chunk size 줄이기 → 정보가 큰 덩어리에 묻혀 있을 수 있음
    ↓ 안 되면
top-k 늘리기
    ↓ 안 되면
alpha 조정 → sparse 비중 올려보기
    ↓ 안 되면
contextual retrieval 적용
    ↓ 안 되면
query rewriting 또는 HyDE 추가

노이즈가 많음 (Precision 낮음)

reranker 추가 → 가장 효과 큼
    ↓ 이미 있거나 안 되면
top-k / top-n 줄이기
    ↓ 안 되면
score threshold 추가
    ↓ 안 되면
chunk size 키우기 → 잘게 쪼갠 무의미한 조각이 상위에 올라오는 경우
    ↓ 안 되면
context packing 중복 제거 활성화

특정 키워드/ID 검색이 안 됨

alpha에서 sparse 비중 올리기 (0.3~0.4)
    ↓ 안 되면
BM25 k1 올리기 → 키워드 반복 민감도 높이기
    ↓ 안 되면
해당 키워드가 청크에 포함되는지 확인 → 청킹에서 잘렸을 수 있음
    ↓ 구조적 한계면
SPLADE 전환 고려 → 학습 기반 sparse, 동의어 자동 확장

SPLADE (SParse Lexical AnD Expansion): BM25를 대체하는 학습 기반 sparse retriever. BERT의 MLM head를 활용해 (1) 불필요한 단어의 가중치를 낮추고 (2) 원문에 없는 관련 단어를 자동 확장합니다. "심근경색" 검색 시 "heart attack"도 활성화.

답변의 맥락이 끊김

chunk size 키우기
    ↓ 안 되면
overlap 늘리기 (20~25%)
    ↓ 안 되면
hierarchical chunking → 작은 청크 검색, 큰 청크 반환
    ↓ 안 되면
contextual retrieval 적용

검색은 괜찮은데 답변이 이상함

context packing 순서 변경 → Lost in the Middle 대응
    ↓ 안 되면
top-n 줄이기 → 노이즈 context가 LLM을 혼란시키는 경우
    ↓ 안 되면
메타데이터 포함 → 출처 정보로 grounding 강화
    ↓ 여전하면
RAGAS Faithfulness로 hallucination 여부 정밀 진단

응답이 느림

top-k 줄이기
    ↓ 안 되면
reranker 제거 또는 경량 모델로 교체
    ↓ 안 되면
embedding 차원 축소 / quantization 적용
    ↓ 안 되면
HNSW ef_search 낮추기 (recall 트레이드오프 확인)

데이터가 늘어나니 성능 저하

HNSW ef_search 올리기 → 조용한 degradation 의심
    ↓ 안 되면
embedding quantization → 메모리 절약
    ↓ 안 되면
embedding 차원 축소
    ↓ 안 되면
metadata filtering → 검색 범위 사전 축소

마무리: 튜닝 우선순위

한 번에 하나씩 바꾸는 것이 좋습니다. 한꺼번에 바꾸면 뭐가 효과인지 모릅니다.

영향도 ★★★ — 먼저 잡을 것
  1. Chunk size + strategy
  2. Contextual Retrieval (on/off)
  3. Fusion method + Alpha

영향도 ★★☆ — 다음에 잡을 것
  4. Top-k + Reranker + Top-n (세트)
  5. Score Threshold
  6. Context Packing (순서, budget, 중복제거)
  7. Query Processing (rewriting, HyDE)

영향도 ★☆☆ — 필요할 때만
  8. HNSW params (데이터 1M+ 이후)
  9. BM25 k1/b
  10. Embedding quantization/dimensionality

각 단계에서 golden test set으로 before/after를 측정합니다. Phase 1(Recall@k + SemScore)로 빠르게 스윕하고, 최종 후보만 Phase 2(RAGAS)로 정밀 비교합니다.

파라미터 튜닝은 감이 아니라 계획입니다. Decision tree를 두고 순서대로 따라가 보는 접근이 필요합니다.

← Back to posts