에이전트의 컨텍스트 윈도우(Context Window)에 무엇을 포함하고 무엇을 제거할 것인가
요약
에이전트의 컨텍스트 윈도우를 무한한 메모리가 아닌 제한된 '워킹 셋(working set)'으로 관리해야 함을 강조합니다. 효율적인 에이전트 구축을 위해 검색(retrieval)보다 불필요한 정보를 제거(eviction)하는 설계가 핵심임을 설명합니다.
핵심 포인트
- 컨텍스트 윈도우는 RAM처럼 작고 효율적으로 관리해야 하는 워킹 셋이다.
- 에이전트 메모리의 핵심 역할은 정보를 가져오는 검색이 아닌, 적절히 내보내는 페이징이다.
- 정보 제거 결정은 충실도(Fidelity)와 최신성(Recency) vs 중요도(Salience)라는 두 축을 기준으로 이루어져야 한다.
- 정확성이 필수적인 정보는 유지하고, 요약 가능한 정보는 공격적으로 압축해야 한다.
대부분의 "에이전트 메모리 (agent memory)" 관련 글들은 정보를 어떻게 넣을 것인가에 집중합니다. 즉, 어떤 벡터 스토어 (vector store)를 사용할지, 어떻게 청킹 (chunking)할지, 어떻게 임베딩 (embedding)할지에 대한 내용입니다. 하지만 실무에서 더 어려운 질문은 그 반대입니다. 당신의 컨텍스트 윈도우 (context window)는 고정된 예산입니다. 오래된 도구 출력값 (tool output)에 소비하는 모든 토큰은, 실제로 다음 단계를 결정하는 데 사용될 수 있었던 토큰을 낭비하는 것입니다. 따라서 진짜 엔지니어링 문제는 검색 (retrieval)이 아니라 제거 (eviction)입니다. 즉, 무엇이 컨텍스트에 들어갈 자격을 얻고, 무엇이 삭제되거나 압축될 것인가의 문제입니다.
다음은 제가 여러 에이전트를 구축하며 유지해 온 모델입니다.
컨텍스트는 메모리가 아니라 워킹 셋 (working set)이다
운영 체제 (operating systems)에서 사용하는 용어를 빌려오겠습니다. 워킹 셋 (working set)은 프로세스가 현재 실제로 사용하는 메모리의 작은 조각입니다. 그 외의 모든 것은 디스크 (disk)에 저장되어 있다가 필요할 때 페이지 인 (paged in) 됩니다. 에이전트의 컨텍스트 윈도우 (context window)는 워킹 셋이며, 이를 장기 저장소처럼 취급하는 것이 에이전트를 느리고, 비싸고, 혼란스럽게 만드는 실수입니다.
지속 가능한 메모리 (Durable memory, 벡터 스토어, 노트, 로그 등)는 당신의 디스크입니다. 이는 매우 거대할 수 있습니다. 컨텍스트 윈도우 (context window)는 RAM입니다. 작고, 뜨겁고(hot), 경쟁이 치열합니다. "에이전트 메모리 (agent memory)"의 역할은 주로 페이저 (pager)입니다. 즉, 이번 단계에서 무엇을 워킹 셋으로 가져올지, 그리고 무엇을 디스크로 돌려보낼지를 결정하는 것입니다.
이런 관점으로 바라보면, 대부분의 컨텍스트 팽창 (context bloat)은 페이저 버그 (pager bug)라는 것을 알 수 있습니다. 검색 결과가 일치한다는 이유로 40개의 청크 (chunks)를 가져왔고, 세션 내내 그 40개를 모두 컨텍스트에 유지했다면, 이후의 모든 단계는 10단계 전에 이미 관련성을 잃은 40개의 요소에 주의를 기울이며 비용을 지불하게 됩니다.
하나의 축이 아닌 두 개의 축
저의 제거 (eviction) 결정을 더 명확하게 만들어 준 것은, 이 결정이 두 개의 독립적인 축을 따라 이루어진다는 사실을 깨달은 것이었습니다. 사람들은 흔히 이 두 축을 하나로 합쳐버리곤 합니다.
충실도 (Fidelity): 반드시 정확해야 하는가 vs 근사화 (Approximated)할 수 있는가. 어떤 콘텐츠는 압축할 경우 내용이 깨집니다. 에이전트가 내린 약속 ("운영 환경(prod)은 건드리지 않겠습니다"), 도구의 정확한 에러 문자열 (error string), ID, 사용자의 문자 그대로의 지시 사항 등이 이에 해당합니다. 반면, 다른 콘텐츠들은 점진적으로 품질이 저하되어도 괜찮습니다. 요점만 파악하면 되는 긴 문서, 오래된 추론 흔적 (reasoning trace), 해결된 하위 작업 (subtask)의 전사 기록 등이 그렇습니다. 두 번째 종류는 공격적으로 요약하십시오. 첫 번째 종류는 절대 요약하지 마십시오.
최신성 (Recency) vs 중요도 (Salience). 최신성은 구현하기 쉽지만, 그 자체만으로는 대부분 틀린 방식입니다. 중요한 토큰은 가장 최근의 토큰이 아니라, 현재 단계에서 가장 중요한 (salient) 토큰입니다. 즉, 이 결정의 근거가 되는 검색된 사실 (retrieved fact), 위반해서는 안 될 제약 조건 (constraint), 그리고 목표 (goal)입니다. 최근의 컨텍스트는 중요한 콘텐츠의 한 범주일 뿐, 중요도의 정의가 아닙니다.
이 두 축을 교차하면 실제로 구현 가능한 우선순위가 도출됩니다. '높은 중요도-반드시 정확해야 함 (High-salience-must-stay-exact)'은 신성한 영역입니다. 이를 있는 그대로 (verbatim) 유지하고, 가장 마지막에 제거하십시오. '낮은 중요도-근사화 가능 (Low-salience-can-approximate)'은 가장 먼저 압축하거나 삭제해야 할 대상입니다. 위험한 사분면은 '높은 충실도-낮은 중요도 (High-fidelity-but-low-salience)'입니다. 이는 현재 단계에 도움이 되지 않음에도 불구하고, 유지하기 위해 비용을 온전히 지불하고 있는 정확한 콘텐츠를 의미합니다. 대부분의 낭비는 바로 이곳에서 발생합니다.
구체적인 제거 정책 (Eviction Policy)
시작하기 위해 거창한 것이 필요하지는 않습니다. "윈도우가 넘칠 때까지 모든 것을 유지한다"는 방식보다 나은 정책은 다음과 같습니다:
- 불변 요소(Invariants)를 고정(Pin)하세요. 목표, 엄격한 제약 조건, 사용자의 실제 요청 등이 이에 해당합니다. 이들은 절대 제거(Evict)되거나 요약되지 않습니다. 크기는 작지만 항상 존재해야 합니다.
- 최근 N개의 턴(Turn)은 원문 그대로 유지하세요. 최신성은 대화의 일관성을 위한 중요도(Salience)의 저렴한 대리 지표입니다. N은 3~6 정도로 작게 설정합니다.
- 검색된 증거(Retrieved evidence)는 사용 후 등급을 낮추세요. 특정 단계의 근거가 된 청크(Chunk)는 한 줄짜리 주장과 영구 메모리(Durable memory)로 연결되는 포인터(Pointer)로 요약됩니다. 이후 단계에서 세부 정보가 필요하면 다시 페이지 인(Page in)하여 불러옵니다.
- 해결된 하위 작업(Subtasks)은 결과로 압축하세요. "설정을 파악함"이라는 전체 기록은 "설정은 X이며, 확인됨"으로 변환됩니다. 추론 과정이 당신을 그곳으로 인도했지만, 더 이상 구조를 지탱하는 핵심 요소(Load-bearing)는 아닙니다.
- 경과 시간(Age)이 아니라 중요도(Salience)에 따라 제거하세요. 공간이 필요할 때는 가장 오래된 것이 아니라, 중요도가 가장 낮은 항목을 버리세요. 오래된 불변 요소가 최신이지만 무관한 도구 출력값(Tool dump)보다 우선순위가 높습니다.
포인터로 되돌아가는 부분이 이 방식을 안전하게 만드는 핵심입니다. 당신은 정보를 삭제하는 것이 아니라, 결함 발생 시 다시 불러올 수 있는 방법과 함께 정보를 RAM에서 디스크로 옮기는 것입니다. 포인터 없는 요약은 그저 손실이 발생하는 삭제(Lossy deletion)일 뿐이며, 이것이 바로 에이전트가 분명히 알고 있어야 할 것들을 "망각"하게 만드는 방식입니다.
주의해야 할 실패 모드
미묘한 버그는 자신의 요약을 신뢰하는 데서 발생합니다. 검색된 증거를 한 줄의 주장으로 압축할 때, 그 주장은 이제 에이전트가 행동의 근거로 삼을 메모리가 됩니다. 그리고 그 주장은 중요한 주의 사항을 뭉뚱그려 버렸을지도 모르는 LLM에 의해 작성되었습니다. 소스(Source)에 대한 포인터를 유지하고, 단계의 위험도(Stakes)가 높을 때는 요약을 신뢰하는 대신 원본 증거를 다시 페이지 인(Page in)하여 불러오세요. 충실도(Fidelity)와 중요도(Salience)가 무엇을 유지할지 결정하며, 위험도(Stakes)가 자신의 압축을 언제 불신할지를 결정합니다.
이 중 어떤 것도 새로운 프레임워크를 필요로 하지 않습니다. 필요한 것은 컨텍스트 윈도우(Context window)를 저장소(Storage)로 취급하는 것을 멈추고, 스케줄러(Scheduler)로 취급하기 시작하는 것입니다. 즉, 무엇을 페이지 아웃(Page out)할지 결정하는 것이 진정한 기술인, 작고 경합이 치열한 자원으로 바라보는 것입니다.
AI의 도움을 받아 작성되었으며 사람이 편집하였습니다. 제가 이 주제를 고민하는 이유는 로컬 우선(local-first) 방식의 개인 메모리 계층(personal memory layer)을 구축하고 있기 때문입니다. 여기서 "디스크(disk)"는 당신이 캡처한 작업물이며, 페이저(pager)는 당신의 기기에서 실행됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기