프로덕션 RAG 시스템의 컨텍스트 윈도우 최적화

문제 상황

운영 중인 문서 검색 서비스에서 긴 문서를 질의할 때 응답이 부정확하거나 컨텍스트를 벗어나는 답변이 자주 발생했다. Claude 3.5 Sonnet의 200K 토큰 윈도우를 사용하고 있었지만, 실제로는 top-k를 10으로 설정해 매번 과도한 컨텍스트를 전송하고 있었다.

분석

로그를 분석한 결과 평균 쿼리당 150K 토큰을 소비하고 있었다. 비용도 문제였지만, 더 큰 이슈는 'lost in the middle' 현상이었다. 관련성 높은 청크가 중간에 묻혀 모델이 제대로 참조하지 못하는 경우가 많았다.

# 기존 설정
retriever = vectorstore.as_retriever(
    search_kwargs={"k": 10}
)

해결 과정

  1. 청크 크기 조정: 512 토큰에서 256 토큰으로 축소. 더 세밀한 검색이 가능해졌다.
  2. top-k 감소: 10개에서 5개로 줄였다. MMR(Maximal Marginal Relevance)을 적용해 다양성도 확보했다.
  3. 재순위화: Cross-encoder로 검색된 청크를 재정렬해 가장 관련성 높은 내용을 상단에 배치했다.
retriever = vectorstore.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 5,
        "fetch_k": 20,
        "lambda_mult": 0.7
    }
)

# Cohere rerank 적용
reranker = CohereRerank(top_n=3)

결과

평균 토큰 사용량이 150K에서 60K로 감소했고, 응답 정확도는 오히려 12% 향상됐다. 비용은 약 60% 절감됐다. 컨텍스트가 적을수록 모델이 더 집중한다는 점을 다시 확인했다.

교훈

큰 컨텍스트 윈도우가 있다고 해서 무조건 많이 넣는 것이 답은 아니다. 품질 좋은 소수의 청크가 대량의 노이즈보다 낫다. 재순위화 단계를 추가하는 것만으로도 큰 개선을 볼 수 있었다.