본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 12:49

LLM 이전의 컨텍스트 압축: 재현율(Recall)을 유지하며 토큰 줄이기

요약

RAG 시스템에서 긴 컨텍스트로 인한 비용 증가와 모델의 성능 저하(Lost in the Middle) 문제를 해결하기 위한 컨텍스트 압축 기술을 소개합니다. 검색된 청크를 추출적 또는 요약적 방식으로 압축하여 재현율을 유지하며 토큰을 효율적으로 줄이는 방법을 다룹니다.

핵심 포인트

  • 긴 컨텍스트는 비용을 높이고 모델의 중간 정보 인지 능력을 저하시킴
  • 컨텍스트 압축은 검색과 생성 사이에서 토큰 효율성을 높이는 계층임
  • 추출적 방식은 원문을 유지하여 환각을 방지하고 인용의 정확성을 보장함
  • 문장 단위 임베딩 유사도를 활용해 임계값을 넘는 문장만 선별 가능

당신은 상위 10개의 청크(chunk)를 검색하여 프롬프트에 붙여넣고 모델로 전송합니다. 각 청크는 400개의 토큰입니다. 이는 6번째 청크에 파묻힌 단 두 문장의 답변을 찾기 위해 4,000개의 컨텍스트 토큰을 사용하는 셈입니다. 당신은 입력값으로 이 4,000개 토큰 전체에 대한 비용을 지불합니다. 또한 당신은 눈에 보이지 않는 세금도 지불하고 있습니다. 모델은 정답과 유사하지만 정답은 아닌 텍스트의 벽 속에서 답을 찾아야 하며, 긴 컨텍스트는 올바른 사실이 존재하더라도 답변의 품질을 저하시킵니다.

Stanford의 "Lost in the Middle" 연구는 이를 명확히 보여주었습니다. 입력 컨텍스트가 커질수록, 모델은 시작 부분과 끝 부분의 정보는 안정적으로 사용하지만 중간에 끼어 있는 사실은 놓치는 경향이 있습니다 (Liu et al., 2023). 따라서 프롬프트 중간에 위치한 6번째 순위의 청크는 모델이 가장 취약한 지점이 됩니다.

컨텍스트 압축(Context compression)은 검색(retrieval)과 생성(generation) 사이에 위치하는 계층입니다. 당신은 넉넉하게 검색한 다음, 검색된 집합을 프롬프트에 들어갈 자격을 갖춘 부분으로 압축합니다. 두 가지 계열이 이를 수행합니다: 추출적(extractive) 방식과 요약적(abstractive) 방식입니다. 이들은 서로 다른 트레이드오프(trade-offs)를 가지며, 대부분의 팀은 자신의 데이터에 잘못된 방식을 선택하곤 합니다.

추출적(Extractive): 원문 문장은 유지하고 나머지는 버리기

추출적 압축은 검색된 텍스트의 각 단위를 쿼리(query)와 비교하여 점수를 매기고, 기준치를 통과한 단위만 유지합니다. 유지되는 텍스트는 소스에서 가져온 그대로(verbatim)입니다. 아무것도 다시 쓰지 않으므로, 컨텍스트에 환각(hallucination)이 발생하지 않습니다.

가장 단순한 버전은 문장 단위(sentence level)로 작동합니다. 각 청크(chunk)를 문장으로 나누고, 각 문장과 쿼리(query)를 임베딩(embedding)한 뒤, 유사도(similarity)가 임계값(threshold)을 넘는 문장들만 유지합니다.

import numpy as np
from sentence_transformers import SentenceTransformer

...

keep 비율은 조절 가능한 다이얼과 같습니다. keep=0.5로 설정하면 문장의 절반을 버리게 되며, 컨텍스트(context)의 토큰 비용(token cost)을 대략 절반으로 줄일 수 있습니다. 유지되는 문장들은 원본 그대로이므로, 소스를 가리키는 인용(citation)이 여전히 단어 하나하나 일치하게 유지됩니다.

문장 단위 필터링의 위험 요소는 참조 단절(reference breakage)입니다. 예를 들어 "30일 후에 만료됩니다"라는 문장은 실제 정답을 담고 있음에도 불구하고, "환불 정책이 무엇인가요"라는 쿼리와 공유하는 키워드가 없기 때문에 낮은 점수를 받을 수 있습니다. 이 문장을 잘라내면 모델은 수식어(qualifier)를 잃게 됩니다. 해결책은 유지된 모든 문장 주변에 작은 윈도우(window)를 유지하여, 공중에 뜬 대명사(dangling pronouns)나 이어지는 절(follow-on clauses)이 살아남도록 하는 것입니다.

def with_neighbors(
    sents: list[str],
    kept_idx: list[int],
...

학습된 추출기(trained extractor)는 코사인 유사도(cosine similarity)보다 더 나은 성능을 보입니다. bge-reranker와 같은 모델은 두 개의 별개 임베딩을 비교하는 대신, 두 요소를 함께 읽는 크로스 인코더(cross-encoder)로서 쿼리와 문장의 관련성(relevance)을 점수화합니다. 리랭커(reranker)는 단 한 번의 순전파(forward pass) 과정에서 쿼리와 문장을 동시에 보기 때문에, "30일 후에 만료됩니다"와 같은 사례를 더 자주 잡아낼 수 있습니다. 문장당 비용이 더 많이 들기 때문에, 코퍼스(corpus)의 모든 문장에 적용하지 말고 저렴한 임베딩 필터(embedding filter)를 거친 후보군(candidate set)에 대해서만 실행하십시오.

요약적(Abstractive): 청크를 더 압축된 요약본으로 재작성

요약적 압축(Abstractive compression)은 검색된 청크들을 작고 빠른 모델로 보내 쿼리에 집중된 요약문을 작성하도록 요청합니다. 출력물은 새로운 텍스트입니다. 이것이 매력이자 동시에 위험 요소입니다.

from openai import OpenAI

client = OpenAI()
...

여기서의 압축률(compression ratio)은 추출적 (extractive) 방식보다 훨씬 더 높을 수 있습니다. 모델은 동일한 사실을 맴도는 다섯 개의 문단을 하나의 문장으로 접을 수 있습니다. 장황한 코퍼스(corpus, 예: 고객 지원 상담 기록, 회의록, 법률 문구)의 경우, 추출적 방식은 유지하는 문장의 수에 따라 3050% 정도로 제한되는 반면, 요약적 (abstractive) 방식은 5075% 범위에 도달할 수 있습니다. 두 방식 모두 보장된 수치가 아닌, 코퍼스에 따라 달라지는 대략적인 경험칙 (rule of thumb)으로 취급하십시오.

비용은 두 가지 측면에서 발생합니다. 첫째, 답변 호출 전에 LLM 호출을 추가하게 되어 지연 시간 (latency)과 비용이 증가합니다. 둘째, 요약기가 수식어를 누락하거나 두 개의 사실을 하나의 잘못된 사실로 뭉뚱그릴 수 있습니다. 예를 들어, "$500 초과 주문 시 10% 할인"이라는 문구가 임계값이 요약 과정에서 사라지면서 "10% 할인이 있습니다"라고 돌아올 수 있습니다. 완화 방법으로는 Temperature 0 설정, 숫자와 날짜를 있는 그대로 복사하라는 명시적 지시, 그리고 모델이 관계를 지어내는 대신 아무것도 반환하지 않을 수 있도록 허용하는 NO_RELEVANT_CONTEXT 탈출구 (escape hatch)를 사용하는 것이 있습니다.

명확하게 기술한 트레이드오프 (trade-off)

세 가지 축을 기준으로 선택하십시오: 충실도 (faithfulness), 비율 (ratio), 그리고 비용 (cost).

추출적 (Extractive)요약적 (Abstractive)
원문에 대한 충실도높음 (verbatim)중간 (rewritten)
...

만약 당신의 도메인이 잘못된 사실에 대해 엄격하다면 (법률, 의료, 금융, 또는 숫자가 중요한 모든 분야), 추출적 방식으로 시작하십시오. 핵심은 원문 그대로 (verbatim) 보장하는 것입니다. 만약 코퍼스가 장황하고 반복적이며, 요약이 약간 빗나갔을 때의 비용이 낮다면, 요약적 방식이 더 많은 여유 공간 (headroom)을 확보해 줍니다.

두 방식의 장점을 모두 취하는 하이브리드 방식도 있습니다. 먼저 추출적 방식을 실행하여 명백히 무관한 문장들을 버린 다음, 남은 내용에 대해 요약적 방식을 실행하는 것입니다. 요약기는 더 깨끗하고 짧은 입력을 받게 되므로 환각 (hallucination)이 줄어들고 비용도 적게 들면서, 동시에 높은 압축률을 얻을 수 있습니다.

토큰 수뿐만 아니라 재현율 (recall)을 측정하라

토큰의 60%를 줄이면서 정답의 5%를 잃는 압축기는 나쁜 거래이며, 사용자가 불만을 제기하기 전까지는 그 문제를 알아차리지 못할 것입니다. 중요한 숫자는 압축된 컨텍스트(context)가 모델에 필요한 사실들을 여전히 포함하고 있는지 여부입니다. 이것이 바로 컨텍스트 재현율 (context recall)이며, 이를 직접 측정할 수 있습니다.

라벨링된 평가 세트(eval set)를 준비하세요: 질문, 검색된 청크(chunks), 그리고 정답(gold answer)입니다. 압축을 실행한 다음, 정답 사실(gold facts)이 살아남았는지 확인하십시오.

def context_recall(
    compressed: str,
    gold_facts: list[str],
...

정확한 부분 문자열 매칭 (Exact substring matching)은 조잡한 방식입니다. 의역을 허용하는 점수 산정 (paraphrase-tolerant scoring)을 위해서는, LLM 판사 (LLM judge) 또는 함의 모델 (entailment model)이 각 정답 사실에 대해 "이 컨텍스트가 이 사실을 뒷받침하는가?"를 읽도록 합니다. 어떤 방식이든 루프는 동일합니다: keep 비율을 탐색하거나 (또는 추출적 (extractive) 방식과 요약적 (abstractive) 방식을 비교하거나) 토큰 비용 대비 컨텍스트 재현율을 그래프로 그립니다. 여러분이 찾아야 할 것은 곡선의 무릎 (knee in the curve), 즉 토큰을 더 줄이는 것이 실제 정답을 희생시키기 시작하는 지점입니다.

새로 선별된 (cherry-picked) 세트가 아니라, 검색 (retrieval)에 사용하는 것과 동일한 평가 세트를 대상으로 실행하십시오. 압축을 수행할 가치가 있는 근본적인 이유는 압축이 리트리버 (retriever)와 비용 (bill) 사이의 임계 경로 (critical path)에 위치하기 때문입니다. 파이프라인의 나머지 부분처럼 취급하십시오: 도구를 갖추고 (instrument it), 다이얼을 돌리며 탐색하고, 블로그 포스트가 좋아했던 설정 대신 여러분의 데이터에서 승리하는 설정을 유지하십시오.

파이프라인에서 압축의 위치

순서는 고정되어 있습니다. 더 많이 검색하고, 재순위화 (rerank)하고, 압축하고, 생성 (generate)하십시오.

넓게 검색하십시오 (top 5 대신 top 20). 압축 덕분에 그럴 여유가 생기기 때문입니다. 가장 좋은 청크들이 상단에 밀집되도록 재순위화하십시오. 이는 두 가지 압축 방식 모두를 더 효과적으로 만듭니다. 프롬프트를 토큰 값을 충분히 하는 부분으로 줄이기 위해 압축하십시오. 그런 다음, 더 짧고 날카로운 컨텍스트로 생성하십시오. 정답이 더 이상 중간에 파묻혀 있지 않기 때문에 모델이 더 안정적으로 읽을 수 있습니다.

검색된 컨텍스트(retrieved context)가 이미 작고 목표에 부합하거나, 롱 컨텍스트 모델(long-context model)을 사용 중이며 토큰 비용이 제약 사항이 아니라면 압축을 건너뛰십시오. 이 계층(layer)은 검색을 넉넉하게 수행하고, 입력 토큰당 비용을 지불하며, 컨텍스트가 커짐에 따라 답변 품질이 떨어지는 것을 목격할 때 그 가치를 발휘합니다. 이것이 대부분의 프로덕션 RAG(Retrieval-Augmented Generation) 상황입니다.

RAG Pocket Guide는 검색(retrieval), 청킹(chunking), 재순위화(reranking)와 함께 이 계층을 다루며, 추측하는 대신 여러분의 자체 코퍼스(corpus)에서 압축 전략을 선택할 수 있는 평가 방법론(eval methodology)을 제공합니다. 만약 프롬프트(prompt)가 비대해지고 답변 품질이 떨어지고 있다면, 압축은 여러분이 아직 사용하지 않고 있을 가장 저렴한 레버(lever)일 것입니다.

RAG Pocket Guide

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0