콘텐츠로 이동

적응형 컨텍스트 관리

고정된 컨텍스트 관리 전략은 실패합니다. 짧은 태스크에는 과도하고, 긴 태스크에는 부족합니다. 적응형(Adaptive) 접근법은 현재 컨텍스트 사용률을 지속적으로 모니터링하고, 임계값에 따라 서로 다른 압축 전략을 자동으로 적용합니다.

핵심 아이디어는 **점진적 손실(Progressive Degradation)**입니다. 한 번에 정보를 많이 버리는 대신, 덜 중요한 것부터 단계적으로 압축합니다.

컨텍스트 사용률에 따라 다음 순서로 압축을 적용합니다.

단계컨텍스트 사용률적용 전략정보 손실
1단계60% 미만현상 유지없음
2단계60~70%도구 출력 후처리최소
3단계70~80%오래된 메시지 트리밍낮음
4단계80~90%대화 히스토리 요약중간
5단계90% 초과핵심 요약만 유지높음
class AdaptiveContextManager {
private readonly thresholds = [0.6, 0.7, 0.8, 0.9];
async compact(context: Context, maxTokens: number): Promise<Context> {
const usage = context.tokenCount / maxTokens;
if (usage < this.thresholds[0]) return context;
if (usage < this.thresholds[1]) return this.postProcessToolOutputs(context);
if (usage < this.thresholds[2]) return this.trimOldMessages(context);
if (usage < this.thresholds[3]) return this.summarizeHistory(context);
return this.keepEssentialSummaryOnly(context);
}
}

컨텍스트가 70% 이상 차면 요약이 필요합니다. 두 가지 주요 방식이 있습니다.

재귀적 요약 (Recursive Summarization)

섹션 제목: “재귀적 요약 (Recursive Summarization)”

오래된 메시지부터 점진적으로 요약을 누적합니다. 이전 요약 위에 새 내용을 요약하는 방식입니다.

원본 대화: [M1, M2, M3, M4, M5, M6, M7, M8]
1차 압축: [요약(M1~M4), M5, M6, M7, M8]
2차 압축: [요약(요약(M1~M4)+M5~M6), M7, M8]

장점: 오래된 정보는 더 많이 압축되어 자연스러운 망각 곡선을 구현합니다. 단점: 반복된 요약으로 초기 세부 정보가 왜곡될 수 있습니다.

계층적 요약 (Hierarchical Summarization)

섹션 제목: “계층적 요약 (Hierarchical Summarization)”

중요도에 따라 세부 수준을 다르게 유지합니다. 핵심 결정은 완전히 보존하고, 세부 실행 과정은 압축합니다.

계층 구조:
├── 핵심 결정 [완전 보존]
│ ├── "PostgreSQL 사용 결정"
│ └── "인증에 JWT 사용"
├── 중요 이벤트 [요약 보존]
│ └── "3번의 DB 연결 실패 → 재시도 로직 추가"
└── 일반 실행 과정 [최소 보존]
└── "파일 읽기, 함수 작성 등 루틴 작업"

장점: 의사 결정 맥락을 잃지 않습니다. 단점: 중요도를 판단하는 추가 로직이 필요합니다.

요약보다 단순하지만 빠른 방법입니다.

가장 오래된 메시지부터 순서대로 제거합니다. 구현이 단순하지만 초기 시스템 프롬프트나 중요한 초반 지시까지 삭제될 수 있습니다.

메시지에 중요도 태그를 부여하고, 낮은 중요도 메시지만 제거합니다.

interface TaggedMessage {
role: 'user' | 'assistant' | 'system';
content: string;
importance: 'critical' | 'normal' | 'ephemeral';
}
function selectiveTrim(
messages: TaggedMessage[],
targetTokens: number
): TaggedMessage[] {
// ephemeral → normal 순으로 제거
const priorities: TaggedMessage['importance'][] = ['ephemeral', 'normal'];
let result = [...messages];
for (const priority of priorities) {
if (estimateTokens(result) <= targetTokens) break;
result = result.filter(
(m) => m.importance !== priority || m === result[result.length - 1]
);
}
return result;
}

에이전트가 도구를 호출하면 종종 수천 토큰의 출력이 반환됩니다. 예를 들어 웹 검색 결과, 긴 파일 내용, API 응답 등입니다. 후처리는 이 출력에서 필요한 부분만 추출합니다.

async function postProcessToolOutput(
toolName: string,
rawOutput: string,
query: string
): Promise<string> {
// 웹 검색: 상위 3개 결과 요약만 유지
if (toolName === 'web_search') {
return extractTopResults(rawOutput, 3);
}
// 파일 읽기: 쿼리와 관련된 라인만 추출
if (toolName === 'read_file') {
return extractRelevantLines(rawOutput, query, contextLines: 5);
}
// 코드 실행: stdout만 유지, 중간 로그 제거
if (toolName === 'execute_code') {
return extractFinalOutput(rawOutput);
}
return rawOutput;
}
상황권장 전략
도구 출력이 매우 길다후처리로 관련 정보만 추출
초반 대화가 더 이상 필요 없다FIFO 트리밍
의사 결정 맥락을 보존해야 한다계층적 요약
대화가 매우 길고 전반적으로 관련 있다재귀적 요약
컨텍스트가 거의 꽉 찼다핵심 요약만 남기고 전부 교체

적응형 컨텍스트 관리는 컨텍스트 사용률을 실시간으로 추적하고, 5단계 임계값에 따라 도구 출력 후처리 → 트리밍 → 요약 → 핵심만 유지의 순서로 점진적 압축을 적용합니다. 정보 손실을 최소화하면서 컨텍스트 한계 내에서 에이전트를 안정적으로 운영하는 것이 목표입니다.