1 컨텍스트 관리가 중요한 이유
LLM 기반 Agent의 성능은 모델 자체의 능력보다 어떤 컨텍스트를 제공하느냐에 더 크게 좌우된다.
- 동일한 모델이라도 컨텍스트 제공 방식에 따라 통과율이 25% → 95%로 변할 수 있다
- 컨텍스트 윈도우에 모든 정보를 넣는 것은 오히려 성능을 저하시킨다
2 컨텍스트 과부하 문제
2.1 증상
- Agent가 관련 없는 정보에 주의를 빼앗겨 핵심 작업을 놓침
- 유사한 정보가 많을수록 잘못된 참조를 선택할 확률 증가
- 응답 지연 시간 증가, 비용 증가
2.2 원인
[전체 문서 투입] → 노이즈 증가 → 관련 정보 식별 어려움 → 성능 저하
3 컨텍스트 관리 전략
3.1 1. 프로그레시브 디스클로저 (Progressive Disclosure)
필요한 시점에 필요한 정보만 점진적으로 제공한다.
Step 1: 태스크 유형 파악 (최소 컨텍스트)
Step 2: 해당 태스크에 필요한 문서만 로드
Step 3: 세부 작업 시 추가 컨텍스트 제공
3.2 2. 계층적 컨텍스트 구조
Level 0: 시스템 프롬프트 (항상 존재)
Level 1: 도메인 스킬 문서 (태스크에 따라 선택)
Level 2: 구체적 참조 자료 (필요 시 동적 로드)
Level 3: 실행 결과/피드백 (실시간 생성)
3.3 3. 컨텍스트 윈도우 예산 배분
| 구성 요소 | 비율 | 설명 |
|---|---|---|
| 시스템 프롬프트 | ~10% | 역할, 규칙, 제약 조건 |
| 스킬/도구 설명 | ~20% | 현재 태스크에 필요한 도구 |
| 작업 컨텍스트 | ~40% | 코드, 문서, 데이터 |
| 대화 히스토리 | ~20% | 이전 턴의 핵심 내용 |
| 여유 공간 | ~10% | 응답 생성용 |
경고
비율은 태스크 특성에 따라 조정해야 한다. 코드 생성 태스크는 작업 컨텍스트 비율을 높이고, 대화형 태스크는 히스토리 비율을 높인다.
4 실전 설계 원칙
4.1 원칙 1: 최소 컨텍스트 원칙
태스크를 완수하기 위한 최소한의 정보만 제공한다.
4.2 원칙 2: 관련성 기반 선택
유사도(similarity)보다 태스크 관련성(relevance)을 기준으로 컨텍스트를 선택한다.
4.3 원칙 3: 동적 로드/언로드
장기 실행 Agent의 경우, 완료된 단계의 컨텍스트는 요약 후 제거하고 다음 단계의 컨텍스트를 로드한다.
# 개념적 예시
class ContextManager:
def __init__(self, max_tokens: int):
self.max_tokens = max_tokens
self.active_contexts = []
def load_skill(self, task_type: str) -> str:
"""태스크 유형에 맞는 스킬 문서를 로드한다."""
skill = self.skill_registry.get(task_type)
self.active_contexts.append(skill)
return skill.instructions
def summarize_and_release(self, context_id: str):
"""완료된 컨텍스트를 요약 후 해제한다."""
context = self.get_context(context_id)
summary = self.llm.summarize(context)
self.active_contexts.remove(context)
self.active_contexts.append(summary)4.4 원칙 4: 평가 기반 최적화
컨텍스트 구성을 변경할 때마다 정량적 평가를 수행한다.
- A/B 테스트: 컨텍스트 구성 A vs B의 태스크 통과율 비교
- Docker 기반 격리 환경에서 재현 가능한 평가 수행
5 RAG와 스킬의 하이브리드 컨텍스트 관리
[사용자 입력]
↓
[태스크 분류기]
├─ 정형 태스크 → [스킬 로드] → 구조화된 컨텍스트
└─ 비정형 질의 → [RAG 검색] → 검색 기반 컨텍스트
↓
[컨텍스트 병합 및 예산 조정]
↓
[Agent 실행]
힌트
스킬과 RAG는 경쟁 관계가 아니라 상호 보완 관계이다. 명확한 작업 절차가 있는 태스크는 스킬로, 탐색적 질의는 RAG로 처리하는 것이 효과적이다.