Anthropic이 고도화된 컨텍스트 검색 기법으로 AI 시스템을 어떻게 개선하는지 살펴봅니다. 대규모 언어 모델에서 정보 접근성과 관련성을 높이기 위한 Anthropic의 접근 방식을 소개합니다.
AI 모델이 특정 상황에서 제대로 작동하려면, 관련 배경 지식에 접근할 수 있어야 합니다. 예를 들어, 고객 지원 챗봇은 해당 기업의 제품·서비스 정보를 알아야 하고, 법률 분석 봇은 방대한 판례 데이터를 참조할 수 있어야 합니다.
이러한 지식을 보강하기 위해 개발자들이 일반적으로 사용하는 방법이 RAG(Retrieval-Augmented Generation, 검색 증강 생성)입니다. RAG는 지식 베이스에서 관련 정보를 검색해 사용자 프롬프트에 추가함으로써 모델의 응답 품질을 크게 높이는 기법입니다. 하지만 기존 RAG 방식에는 문제가 있습니다. 정보를 인코딩하는 과정에서 맥락이 소실되어, 지식 베이스에서 적절한 정보를 찾아오지 못하는 경우가 빈번합니다.
이 글에서는 RAG의 검색 단계를 획기적으로 개선하는 방법을 소개합니다. "Contextual Retrieval(맥락적 검색)"이라 부르는 이 방법은 Contextual Embeddings와 Contextual BM25라는 두 가지 하위 기법으로 구성됩니다. 이를 적용하면 검색 실패율을 49% 줄일 수 있으며, 리랭킹(reranking)까지 결합하면 67%까지 감소시킬 수 있습니다. 이는 검색 정확도의 의미 있는 향상이며, 후속 작업의 성능 개선으로 직결됩니다.
쿡북을 참고하면 Claude를 활용한 Contextual Retrieval 솔루션을 손쉽게 구축할 수 있습니다.
때로는 가장 단순한 방법이 최선일 수 있습니다. 지식 베이스가 200,000 토큰(약 500페이지 분량) 이하라면, RAG 같은 별도 기법 없이 전체 지식 베이스를 프롬프트에 그대로 포함시키면 됩니다.
몇 주 전 Claude에 프롬프트 캐싱 기능을 출시했는데, 이 기능 덕분에 위 방식의 속도와 비용 효율이 크게 개선됩니다. 이제 자주 사용하는 프롬프트를 API 호출 간에 캐싱할 수 있어, 지연 시간은 2배 이상 단축되고 비용은 최대 90%까지 절감됩니다(자세한 작동 방식은 프롬프트 캐싱 쿡북에서 확인하세요).
그러나 지식 베이스 규모가 커지면 더 확장 가능한 솔루션이 필요합니다. 바로 여기서 Contextual Retrieval이 등장합니다.
컨텍스트 윈도우에 담을 수 없을 만큼 큰 지식 베이스에는 보통 RAG를 적용합니다. RAG는 다음과 같은 전처리 단계를 통해 지식 베이스를 준비합니다:
실행 시점에 사용자가 쿼리를 입력하면, 벡터 데이터베이스에서 쿼리와 의미적으로 가장 유사한 청크를 찾습니다. 그런 다음 가장 관련성 높은 청크를 생성 모델에 보내는 프롬프트에 추가합니다.
임베딩 모델은 의미적 관계를 잘 포착하지만, 정확한 일치가 중요한 경우에는 놓치는 부분이 생길 수 있습니다. 다행히 이런 상황에 도움이 되는 오래된 기법이 있습니다. BM25(Best Matching 25)는 어휘 매칭(lexical matching)을 활용해 정확한 단어나 구문 일치를 찾는 랭킹 함수입니다. 고유 식별자나 전문 용어가 포함된 쿼리에 특히 효과적입니다.
BM25는 TF-IDF(Term Frequency-Inverse Document Frequency, 단어 빈도-역문서 빈도) 개념을 발전시킨 것입니다. TF-IDF는 특정 단어가 문서 컬렉션에서 얼마나 중요한지를 측정합니다. BM25는 여기에 문서 길이를 고려하고 단어 빈도에 포화 함수를 적용하여, 흔한 단어가 결과를 지배하는 현상을 방지합니다.
시맨틱 임베딩이 실패하는 상황에서 BM25가 어떻게 성공할 수 있는지 예를 들어 보겠습니다. 사용자가 기술 지원 데이터베이스에서 "Error code TS-999"를 검색한다고 가정해 봅시다. 임베딩 모델은 에러 코드에 관한 일반적인 내용을 찾아올 수는 있지만, "TS-999"라는 정확한 문자열 일치를 놓칠 수 있습니다. BM25는 이 특정 텍스트 문자열을 직접 찾아 관련 문서를 식별합니다.
임베딩과 BM25를 결합하면 RAG 솔루션이 가장 적절한 청크를 더 정확하게 검색할 수 있습니다. 과정은 다음과 같습니다:
BM25와 임베딩 모델을 함께 활용하면, 기존 RAG 시스템은 정확한 용어 매칭과 넓은 의미적 이해를 균형 있게 결합하여 더 포괄적이고 정확한 결과를 제공할 수 있습니다.

이 접근 방식을 사용하면 단일 프롬프트에 담을 수 없는 대규모 지식 베이스까지 비용 효율적으로 확장할 수 있습니다. 하지만 기존 RAG 시스템에는 심각한 한계가 있습니다. 바로 맥락이 소실된다는 점입니다.
기존 RAG에서는 효율적인 검색을 위해 문서를 작은 청크로 분할합니다. 대부분의 경우 잘 작동하지만, 개별 청크에 충분한 맥락이 담기지 않으면 문제가 발생합니다.
예를 들어, 미국 SEC 보고서 같은 재무 정보 모음이 지식 베이스에 포함되어 있고, 다음과 같은 질문이 들어왔다고 가정해 봅시다: "ACME Corp의 2023년 2분기 매출 성장률은 얼마인가요?"
관련 청크에 다음과 같은 텍스트가 포함되어 있을 수 있습니다: "해당 기업의 매출은 전분기 대비 3% 성장했습니다." 그러나 이 청크만으로는 어떤 기업을 가리키는지, 어떤 시점의 데이터인지 알 수 없어 적절한 정보를 검색하거나 활용하기 어렵습니다.
Contextual Retrieval은 각 청크를 임베딩("Contextual Embeddings")하고 BM25 인덱스를 생성("Contextual BM25")하기 전에, 해당 청크에 맞는 설명적 맥락을 앞에 추가하여 이 문제를 해결합니다.
앞서 든 SEC 보고서 예시로 돌아가 봅시다. 청크가 어떻게 변환되는지 보겠습니다:
original_chunk = "The company's revenue grew by 3% over the previous quarter."
contextualized_chunk = "This chunk is from an SEC filing on ACME corp's performance in Q2 2023; the previous quarter's revenue was $314 million. The company's revenue grew by 3% over the previous quarter."맥락 정보를 활용해 검색을 개선하는 시도는 이전에도 있었다는 점을 짚고 넘어갈 필요가 있습니다. 기존 제안으로는 청크에 범용 문서 요약을 추가하는 방법(실험해 본 결과 개선 효과가 매우 제한적이었습니다), 가상 문서 임베딩(hypothetical document embedding), 요약 기반 인덱싱(summary-based indexing) 등이 있습니다(평가 결과 성능이 낮았습니다). 이러한 방법들은 이 글에서 제안하는 방식과 다릅니다.
물론, 지식 베이스에 포함된 수천 또는 수백만 개의 청크에 수동으로 맥락을 부여하는 것은 현실적으로 불가능합니다. Contextual Retrieval을 구현하기 위해 Claude를 활용합니다. 전체 문서의 맥락을 바탕으로 각 청크를 설명하는 간결한 맥락 정보를 생성하도록 지시하는 프롬프트를 작성했습니다. 아래는 각 청크의 맥락을 생성하는 데 사용한 Claude 3 Haiku 프롬프트입니다:
<document>
{{WHOLE_DOCUMENT}}
</document>
Here is the chunk we want to situate within the whole document
<chunk>
{{CHUNK_CONTENT}}
</chunk>
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else. 이렇게 생성된 맥락 텍스트는 보통 50~100 토큰 정도이며, 임베딩과 BM25 인덱스 생성 전에 청크 앞에 추가됩니다.
실제 전처리 흐름은 다음과 같습니다:

Contextual Retrieval을 사용해 보고 싶다면, 쿡북에서 시작할 수 있습니다.
Contextual Retrieval은 앞서 언급한 프롬프트 캐싱 기능 덕분에 Claude에서 저비용으로 구현할 수 있습니다. 프롬프트 캐싱을 사용하면 매 청크마다 참조 문서를 전달할 필요가 없습니다. 문서를 캐시에 한 번만 로드한 뒤, 이후에는 캐싱된 내용을 참조하면 됩니다. 청크 크기 800토큰, 문서 크기 8,000토큰, 맥락 지시문 50토큰, 청크당 맥락 100토큰을 기준으로, 맥락화된 청크를 생성하는 일회성 비용은 문서 토큰 100만 개당 $1.02입니다.
코드베이스, 소설, ArXiv 논문, 과학 논문 등 다양한 지식 도메인에 걸쳐 임베딩 모델, 검색 전략, 평가 지표를 달리하며 실험을 진행했습니다. 각 도메인에서 사용한 질문과 답변의 예시는 부록 II에서 확인할 수 있습니다.
아래 그래프는 최고 성능을 보인 임베딩 구성(Gemini Text 004)으로 상위 20개 청크를 검색했을 때, 전체 지식 도메인에 대한 평균 성능을 나타냅니다. 평가 지표로는 1 - recall@20을 사용했으며, 이는 상위 20개 청크 안에 포함되지 못한 관련 문서의 비율을 측정합니다. 전체 결과는 부록에서 확인할 수 있으며, 문맥화는 우리가 평가한 모든 임베딩-소스 조합에서 성능을 개선했습니다.
실험 결과는 다음과 같습니다:

Contextual Retrieval을 구현할 때 염두에 둘 사항은 다음과 같습니다:
항상 평가를 수행하세요: 문맥화된 청크를 전달하되, 문맥과 원본 청크를 구분하여 제공하면 응답 생성 품질이 더 향상될 수 있습니다.
마지막으로, Contextual Retrieval에 또 다른 기법을 결합하면 성능을 한층 더 끌어올릴 수 있습니다. 기존 RAG에서는 AI 시스템이 지식 베이스를 검색해 잠재적으로 관련 있는 정보 청크를 가져옵니다. 지식 베이스 규모가 크면 초기 검색 단계에서 관련성과 중요도가 천차만별인 수백 개의 청크가 반환되기도 합니다.
리랭킹(Reranking)은 가장 관련성 높은 청크만 모델에 전달하기 위해 널리 사용되는 필터링 기법입니다. 리랭킹을 적용하면 모델이 처리하는 정보량이 줄어들어 응답 품질이 높아지고, 비용과 지연 시간도 절감됩니다. 핵심 단계는 다음과 같습니다:

현재 시장에는 다양한 리랭킹 모델이 있습니다. 본 실험에서는 Cohere 리랭커를 사용했습니다. Voyage에서도 리랭커를 제공하고 있으나, 이번에는 테스트하지 못했습니다. 실험 결과, 다양한 도메인에서 리랭킹 단계를 추가하면 검색 성능이 더욱 향상되는 것을 확인했습니다.
구체적으로, 리랭킹을 적용한 Contextual Embedding + Contextual BM25 조합은 상위 20개 청크 검색 실패율을 67% 줄였습니다 (5.7% → 1.9%).

리랭킹에서 중요하게 고려할 점은 지연 시간과 비용에 미치는 영향입니다. 특히 많은 수의 청크를 리랭킹할 때 이 문제가 두드러집니다. 리랭커가 모든 청크를 병렬로 처리하더라도 런타임에 추가 단계가 생기므로 약간의 지연은 불가피합니다. 더 많은 청크를 리랭킹하면 성능은 좋아지지만 지연과 비용이 늘어나는 트레이드오프가 존재합니다. 사용 사례에 맞는 최적의 균형점을 찾기 위해 다양한 설정으로 실험해 볼 것을 권장합니다.
위에서 설명한 기법들—임베딩 모델, BM25 사용 여부, 문맥화 검색 적용 여부, 리랭커 사용 여부, 상위 K개 결과 수—의 다양한 조합을 여러 데이터셋 유형에 걸쳐 광범위하게 테스트했습니다. 주요 결과를 요약하면 다음과 같습니다:
지식 베이스를 활용하는 모든 개발자가 쿡북을 참고하여 이러한 접근 방식을 직접 실험해 보고, 한 단계 높은 성능을 달성하길 바랍니다.
아래는 Retrievals @ 20 기준으로 데이터셋, 임베딩 제공사, BM25 병용 여부, 문맥화 검색 적용 여부, 리랭킹 적용 여부에 따른 세부 결과입니다.
Retrievals @ 10 및 @ 5 결과와 각 데이터셋의 질문·답변 예시는 부록 II를 참조하세요.

연구 및 집필: Daniel Ford. 핵심 피드백을 제공한 Orowa Sikder, Gautam Mittal, Kenneth Lien, 쿡북 구현을 담당한 Samuel Flamini, 프로젝트 조율을 맡은 Lauren Polansky, 그리고 블로그 포스트 완성에 기여한 Alex Albert, Susan Payne, Stuart Ritchie, Brad Abrams에게 감사드립니다.