🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Context Optimization 기법 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 28. · 4 Views

Context Optimization 기법 완벽 가이드

AI 에이전트의 컨텍스트 윈도우를 효율적으로 관리하는 핵심 최적화 기법들을 다룹니다. 토큰 사용량을 대폭 줄이면서도 성능을 유지하는 실무 전략을 배웁니다.


목차

  1. 프로덕션_최적화_사례_박시니어의_70-80%_규칙
  2. Compaction_기법_50-70%_토큰_감소
  3. Observation_Masking_기법_60-80%_감소
  4. KV-Cache_Optimization_캐시_적중률_극대화
  5. Context_Partitioning_서브_에이전트_격리
  6. 최적화_기법_종합_실전_적용_가이드

1. 프로덕션 최적화 사례 박시니어의 70-80% 규칙

어느 날 김개발 씨가 AI 에이전트를 프로덕션에 배포했습니다. 그런데 이상합니다.

개발 환경에서는 멀쩡하던 에이전트가 실제 서비스에서는 응답이 느려지고, 때로는 중요한 맥락을 잊어버리는 현상이 발생했습니다. 비용도 예상보다 훨씬 많이 나왔습니다.

70-80% 규칙은 컨텍스트 윈도우를 최대 용량의 70-80%까지만 사용하라는 프로덕션 최적화 원칙입니다. 마치 하드디스크를 꽉 채우면 컴퓨터가 느려지는 것처럼, 컨텍스트 윈도우도 여유 공간이 필요합니다.

이 규칙을 지키면 안정적인 성능과 예측 가능한 비용을 유지할 수 있습니다.

다음 코드를 살펴봅시다.

class ContextManager:
    def __init__(self, max_tokens=128000):
        self.max_tokens = max_tokens
        # 70-80% 규칙 적용: 안전 마진 확보
        self.safe_threshold = int(max_tokens * 0.75)
        self.warning_threshold = int(max_tokens * 0.70)

    def check_context_health(self, current_tokens):
        # 현재 사용량 대비 건강 상태 체크
        usage_ratio = current_tokens / self.max_tokens
        if usage_ratio > 0.80:
            return "CRITICAL", "즉시 컨텍스트 압축 필요"
        elif usage_ratio > 0.70:
            return "WARNING", "압축 준비 권장"
        return "HEALTHY", "정상 운영 중"

김개발 씨는 입사 6개월 차 AI 엔지니어입니다. 첫 번째 AI 에이전트 프로젝트를 성공적으로 런칭했다고 생각했습니다.

그런데 일주일 후, 운영팀에서 연락이 왔습니다. "API 비용이 예산의 3배를 넘었어요!" 선배 개발자 박시니어 씨가 모니터링 대시보드를 함께 살펴봅니다.

"컨텍스트 사용량 그래프를 보세요. 거의 매번 95% 이상을 사용하고 있네요.

이게 문제입니다." 그렇다면 왜 컨텍스트를 꽉 채우면 안 되는 걸까요? 쉽게 비유하자면, 컨텍스트 윈도우는 마치 책상 위의 작업 공간과 같습니다.

책상이 서류로 가득 차면 새로운 문서를 놓을 자리가 없어집니다. 급하게 공간을 만들려고 중요한 서류를 치우다가 필요한 것까지 잃어버리게 됩니다.

컨텍스트 윈도우도 마찬가지입니다. 70-80% 규칙이 없던 시절에는 어땠을까요?

개발자들은 컨텍스트가 가득 찰 때마다 **강제 절단(truncation)**이 발생했습니다. 중요한 시스템 프롬프트나 최근 대화 내용이 갑자기 사라졌습니다.

더 큰 문제는 이런 상황을 예측할 수 없다는 것이었습니다. 사용자마다 대화 패턴이 달랐고, 언제 문제가 터질지 아무도 몰랐습니다.

박시니어 씨가 화이트보드에 그래프를 그립니다. "프로덕션에서는 항상 버퍼 영역을 확보해야 합니다.

70%에서 경고를 띄우고, 80%를 넘으면 자동으로 압축을 시작하는 거죠." 위의 코드를 살펴보겠습니다. safe_threshold는 전체 용량의 75%로 설정됩니다.

이 지점을 넘으면 시스템이 압축 준비를 시작합니다. warning_threshold는 70%로, 이때부터 모니터링 알림이 발생합니다.

check_context_health 메서드는 현재 토큰 사용량을 받아서 상태를 반환합니다. 80%를 넘으면 CRITICAL, 70%를 넘으면 WARNING, 그 이하면 HEALTHY입니다.

이렇게 단계별로 관리하면 갑작스러운 장애를 예방할 수 있습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 고객 상담 챗봇을 운영한다고 가정해봅시다. 어떤 고객은 간단한 질문만 하고, 어떤 고객은 복잡한 문제로 30분 넘게 대화합니다.

70-80% 규칙을 적용하면 긴 대화에서도 안정적으로 서비스를 유지할 수 있습니다. 대화가 70%를 넘으면 오래된 내용을 요약하고, 80%를 넘으면 핵심만 남기는 방식입니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 고정된 임계값을 사용하는 것입니다.

모델마다 컨텍스트 윈도우 크기가 다릅니다. GPT-4는 128K, Claude는 200K입니다.

하드코딩된 숫자가 아니라 비율 기반으로 설정해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 조언대로 70-80% 규칙을 적용한 후, API 비용이 40% 줄었습니다. 무엇보다 "에이전트가 갑자기 맥락을 잊어버렸어요"라는 민원이 사라졌습니다.

실전 팁

💡 - 컨텍스트 사용량을 실시간으로 모니터링하는 대시보드를 구축하세요

  • 70%, 80%, 90% 단계별 알림을 설정하여 선제적으로 대응하세요
  • 모델별로 다른 컨텍스트 크기를 고려하여 비율 기반 임계값을 사용하세요

2. Compaction 기법 50-70% 토큰 감소

김개발 씨가 컨텍스트 모니터링을 시작했습니다. 그런데 문제가 생겼습니다.

사용량이 70%를 넘을 때마다 알림이 울리는데, 대체 어떻게 줄여야 할지 모르겠습니다. "오래된 메시지를 그냥 삭제하면 되나요?" 박시니어 씨가 고개를 저었습니다.

"그러면 중요한 맥락을 잃어버립니다. Compaction을 사용해야 해요."

Compaction은 긴 대화 내용을 핵심만 남기고 압축하는 기법입니다. 마치 두꺼운 책을 요약본으로 만드는 것과 같습니다.

원본 내용의 50-70%를 줄이면서도 중요한 정보는 보존할 수 있습니다. 이 기법을 마스터하면 긴 세션에서도 효율적으로 컨텍스트를 관리할 수 있습니다.

다음 코드를 살펴봅시다.

async def compact_conversation(messages, target_ratio=0.4):
    # 보존해야 할 메시지 분류
    system_msgs = [m for m in messages if m['role'] == 'system']
    recent_msgs = messages[-3:]  # 최근 3개는 무조건 보존

    # 압축 대상 메시지 선별
    to_compact = messages[len(system_msgs):-3]

    # LLM을 사용해 핵심 내용 요약
    summary = await llm.summarize(
        content=to_compact,
        instruction="핵심 결정사항, 사용자 요구사항, 중요 컨텍스트만 추출"
    )

    # 압축된 컨텍스트 재구성
    compacted = system_msgs + [{"role": "system",
        "content": f"[이전 대화 요약]\n{summary}"}] + recent_msgs
    return compacted

김개발 씨는 Compaction이라는 단어를 처음 들었습니다. "압축이요?

그냥 zip 파일처럼 줄이는 건가요?" 박시니어 씨가 웃으며 설명합니다. "비슷하지만 조금 달라요.

zip은 원본 그대로 복원되잖아요. Compaction은 손실 압축에 가깝습니다.

중요한 건 남기고, 덜 중요한 건 과감히 버리는 거죠." 쉽게 비유하자면, Compaction은 마치 회의록 작성과 같습니다. 2시간짜리 회의를 녹음하면 엄청난 분량이 됩니다.

하지만 회의록은 핵심 결정사항, 담당자, 일정만 담습니다. 중간에 나눈 잡담이나 반복된 논의는 빠집니다.

그래도 회의의 본질은 유지됩니다. Compaction이 없던 시절에는 어땠을까요?

개발자들은 두 가지 선택지밖에 없었습니다. 첫째, 오래된 메시지를 단순 삭제하는 것.

하지만 이러면 "아까 말씀하신 건 어떻게 됐나요?"라는 질문에 에이전트가 멍하게 됩니다. 둘째, 모든 메시지를 그대로 유지하는 것.

하지만 이러면 비용이 폭증하고, 결국 강제 절단이 발생합니다. 박시니어 씨가 화이트보드에 구조를 그립니다.

"Compaction의 핵심은 3단계 분류입니다. 절대 건드리면 안 되는 것, 요약해도 되는 것, 그리고 버려도 되는 것." 위의 코드를 자세히 살펴보겠습니다.

먼저 system_msgs를 분리합니다. 시스템 프롬프트는 에이전트의 정체성과 규칙을 담고 있어서 절대 압축하면 안 됩니다.

다음으로 recent_msgs로 최근 3개 메시지를 보존합니다. 방금 나눈 대화는 현재 맥락을 이해하는 데 필수적이기 때문입니다.

to_compact는 그 사이에 있는 메시지들입니다. 이것들을 LLM에게 보내서 요약을 요청합니다.

중요한 건 요약 지시문입니다. "핵심 결정사항, 사용자 요구사항, 중요 컨텍스트만 추출"이라고 명확하게 지정합니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 코드 리뷰 에이전트를 운영한다고 가정해봅시다.

개발자가 여러 파일에 대해 질문을 합니다. 처음에는 파일 A에 대해 논의하고, 그 다음 파일 B, 파일 C로 넘어갑니다.

파일 D를 논의할 때쯤이면 컨텍스트가 가득 찹니다. 이때 Compaction을 적용하면 "파일 A는 함수 분리 완료, 파일 B는 타입 오류 3개 수정, 파일 C는 테스트 추가 예정"처럼 핵심만 남길 수 있습니다.

하지만 주의할 점도 있습니다. 흔히 하는 실수는 너무 공격적인 압축입니다.

target_ratio를 0.2로 설정하면 토큰은 많이 줄지만, 중요한 맥락까지 사라질 수 있습니다. 또한 압축 시점도 중요합니다.

너무 자주 압축하면 LLM 호출 비용이 추가됩니다. 70-80%에 도달했을 때 한 번에 압축하는 것이 효율적입니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. Compaction을 적용한 후, 평균 세션 길이가 3배로 늘어났습니다.

사용자들은 더 긴 대화를 나눌 수 있게 되었고, 에이전트는 여전히 맥락을 잘 기억했습니다.

실전 팁

💡 - 시스템 프롬프트와 최근 메시지는 절대 압축하지 마세요

  • 압축 비율은 0.3-0.5 사이가 적절합니다 (원본의 30-50% 유지)
  • 압축된 요약은 별도의 시스템 메시지로 추가하여 역할을 명확히 하세요

3. Observation Masking 기법 60-80% 감소

김개발 씨가 파일 분석 에이전트를 만들었습니다. 사용자가 파일을 업로드하면 내용을 분석해주는 기능입니다.

그런데 이상합니다. 작은 파일은 잘 처리하는데, 큰 파일을 올리면 컨텍스트가 순식간에 가득 찹니다.

"파일 내용이 대화 기록에 계속 남아있네요." 박시니어 씨가 지적했습니다. "Observation Masking이 필요합니다."

Observation Masking은 도구 실행 결과물을 선택적으로 숨기거나 요약하는 기법입니다. 마치 영화에서 중요한 장면만 보여주고 나머지는 편집하는 것과 같습니다.

파일 읽기, API 호출, 검색 결과 등 대용량 출력물을 효율적으로 관리하여 60-80%의 토큰을 절약할 수 있습니다.

다음 코드를 살펴봅시다.

class ObservationMasker:
    def __init__(self, max_observation_tokens=2000):
        self.max_tokens = max_observation_tokens

    def mask_observation(self, tool_name, raw_output):
        token_count = count_tokens(raw_output)

        # 임계값 이하면 그대로 보존
        if token_count <= self.max_tokens:
            return raw_output

        # 도구별 마스킹 전략 적용
        if tool_name == "read_file":
            return self._mask_file_content(raw_output)
        elif tool_name == "search":
            return self._mask_search_results(raw_output)

        # 기본: 앞뒤만 보존하고 중간 생략
        return f"{raw_output[:500]}...[{token_count-1000} tokens masked]...{raw_output[-500:]}"

김개발 씨는 로그 분석 에이전트를 테스트하고 있었습니다. 사용자가 10MB짜리 로그 파일을 업로드했습니다.

에이전트가 파일을 읽는 순간, 컨텍스트 사용량이 90%를 돌파했습니다. "잠깐, 파일 내용 전체가 대화 기록에 들어간 건가요?" 박시니어 씨가 고개를 끄덕입니다.

"네, 그게 함정이에요. LLM 에이전트에서 도구를 호출하면, 그 **결과물(Observation)**이 다음 턴에 컨텍스트로 들어갑니다.

큰 파일을 읽으면 그 내용이 고스란히 남는 거죠." 쉽게 비유하자면, Observation Masking은 마치 서류 정리함과 같습니다. 회사에서 모든 서류를 책상 위에 펼쳐놓으면 일을 할 수 없습니다.

중요한 건 앞에 두고, 나머지는 서랍에 넣어둡니다. 필요할 때 다시 꺼내보면 됩니다.

Observation Masking이 없던 시절에는 어땠을까요? 개발자들은 도구 사용을 최소화해야 했습니다.

파일을 읽는 것도, 검색을 하는 것도, 모든 결과가 컨텍스트를 잡아먹었기 때문입니다. 어떤 팀은 아예 도구 사용 횟수를 3회로 제한하기도 했습니다.

에이전트의 능력을 스스로 제한한 셈이었죠. 박시니어 씨가 코드를 가리킵니다.

"핵심은 도구별 전략입니다. 모든 결과를 같은 방식으로 처리하면 안 됩니다." 위의 코드를 살펴보겠습니다.

먼저 max_observation_tokens로 임계값을 설정합니다. 2000 토큰 이하의 결과는 그대로 보존합니다.

작은 출력은 마스킹의 오버헤드가 더 클 수 있기 때문입니다. mask_observation 메서드는 도구 이름에 따라 다른 전략을 적용합니다.

파일 읽기라면 파일 특성에 맞는 마스킹을, 검색이라면 검색 결과에 맞는 마스킹을 합니다. 기본 전략은 앞뒤 보존입니다.

처음 500자와 마지막 500자를 남기고, 중간은 "N tokens masked"로 대체합니다. 파일의 시작(헤더, import문)과 끝(결론, return문)이 가장 중요한 경우가 많기 때문입니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 코드 리팩토링 에이전트를 만든다고 가정해봅시다.

사용자가 1000줄짜리 파일의 리팩토링을 요청합니다. 에이전트가 파일을 읽을 때 전체를 컨텍스트에 넣으면 그것만으로 토큰의 절반을 사용합니다.

하지만 Observation Masking을 적용하면 파일 구조(클래스 정의, 함수 시그니처)만 남기고 세부 구현은 마스킹할 수 있습니다. 실제로 수정할 때는 해당 부분만 다시 읽으면 됩니다.

하지만 주의할 점도 있습니다. 흔히 하는 실수는 중요한 정보까지 마스킹하는 것입니다.

에러 로그를 분석할 때 에러 메시지가 중간에 있다면, 앞뒤만 남기면 핵심을 놓칩니다. 따라서 도구별로 커스텀 마스킹 전략을 만드는 것이 중요합니다.

에러 로그라면 "ERROR", "Exception" 키워드가 있는 줄을 우선 보존해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

Observation Masking을 적용한 후, 같은 로그 파일을 분석하는 데 필요한 토큰이 75% 줄었습니다. 에이전트는 여전히 핵심 에러를 정확히 찾아냈습니다.

실전 팁

💡 - 도구별로 다른 마스킹 전략을 설계하세요 (파일, 검색, API 응답 각각 다르게)

  • 임계값 이하의 작은 출력은 마스킹하지 말고 그대로 두세요
  • 마스킹된 내용이 필요하면 다시 도구를 호출할 수 있도록 설계하세요

4. KV-Cache Optimization 캐시 적중률 극대화

김개발 씨가 비용 리포트를 보다가 이상한 점을 발견했습니다. 똑같은 시스템 프롬프트를 매번 보내는데, 왜 매번 전체 비용이 청구되는 걸까요?

"혹시 이걸 재사용할 수 있는 방법이 없나요?" 박시니어 씨가 미소를 지었습니다. "KV-Cache를 아직 모르셨군요."

KV-Cache Optimization은 이전에 처리한 토큰의 연산 결과를 재사용하는 기법입니다. 마치 수학 문제를 풀 때 중간 계산 결과를 메모해두는 것과 같습니다.

시스템 프롬프트처럼 반복되는 부분의 캐시 적중률을 높이면 응답 속도가 빨라지고 비용도 절감됩니다.

다음 코드를 살펴봅시다.

class CacheOptimizedAgent:
    def __init__(self):
        # 캐시 친화적인 프롬프트 구조 설계
        self.static_prefix = self._build_static_prefix()

    def _build_static_prefix(self):
        # 변하지 않는 부분을 앞에 배치 (캐시 적중률 극대화)
        return [
            {"role": "system", "content": SYSTEM_PROMPT},  # 고정
            {"role": "system", "content": TOOL_DEFINITIONS},  # 고정
            {"role": "system", "content": FEW_SHOT_EXAMPLES},  # 고정
        ]

    def build_messages(self, conversation):
        # 고정 프리픽스 + 동적 대화 내용
        return self.static_prefix + conversation

    # 프리픽스가 동일하면 KV-Cache 히트!
    # 대화 부분만 새로 연산하면 됨

김개발 씨는 LLM의 내부 동작에 대해 궁금해졌습니다. "토큰을 처리할 때마다 뭔가 계산을 하는 거잖아요.

그 계산 결과를 저장해둘 수는 없나요?" 박시니어 씨가 고개를 끄덕입니다. "바로 그게 KV-Cache입니다.

K는 Key, V는 Value예요. Transformer 모델에서 Attention을 계산할 때 사용하는 중간 결과물이죠." 쉽게 비유하자면, KV-Cache는 마치 요리의 밑준비와 같습니다.

식당에서 매번 양파를 처음부터 까고 다지면 시간이 오래 걸립니다. 그래서 미리 손질해둔 양파를 냉장고에 넣어둡니다.

주문이 들어오면 밑준비된 재료에 마지막 조리만 하면 됩니다. KV-Cache도 이전에 처리한 토큰의 연산 결과를 저장해두고 재사용합니다.

하지만 여기서 중요한 점이 있습니다. 캐시가 유효하려면 프리픽스가 정확히 일치해야 합니다.

박시니어 씨가 예시를 듭니다. "시스템 프롬프트가 'You are a helpful assistant.'라고 합시다.

첫 번째 요청과 두 번째 요청에서 이 부분이 동일하면, KV-Cache를 재사용할 수 있어요. 하지만 'You are a helpful AI assistant.'로 한 글자라도 바뀌면?

캐시 미스입니다." 위의 코드를 살펴보겠습니다. static_prefix는 절대 변하지 않는 부분을 모아둡니다.

시스템 프롬프트, 도구 정의, Few-shot 예시 등이 여기에 해당합니다. 이것들을 항상 메시지 배열의 앞에 배치합니다.

build_messages 메서드는 고정 프리픽스 뒤에 동적인 대화 내용을 붙입니다. 이렇게 하면 프리픽스 부분의 KV-Cache가 재사용되고, 대화 부분만 새로 연산하면 됩니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 고객 상담 챗봇을 운영한다고 가정해봅시다.

시스템 프롬프트는 "당신은 A사의 고객 상담 챗봇입니다..."로 시작하고 5000 토큰 정도 됩니다. 하루에 10,000건의 대화가 발생합니다.

KV-Cache 없이는 매번 5000 토큰을 처음부터 처리합니다. 하지만 캐시 적중률이 100%라면?

이 5000 토큰의 연산을 완전히 건너뛸 수 있습니다. Claude API의 경우 캐시된 토큰은 90% 할인된 가격으로 청구됩니다.

하지만 주의할 점도 있습니다. 흔히 하는 실수는 동적 정보를 프리픽스에 넣는 것입니다.

현재 시간이나 사용자 ID를 시스템 프롬프트에 넣으면 매번 달라지므로 캐시 미스가 발생합니다. 이런 정보는 프리픽스 뒤, 대화 시작 부분에 넣어야 합니다.

또 다른 실수는 프롬프트 순서를 바꾸는 것입니다. 같은 내용이라도 순서가 바뀌면 캐시 미스입니다.

시스템 프롬프트, 도구 정의, Few-shot의 순서를 고정하고 절대 바꾸지 마세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.

KV-Cache 최적화를 적용한 후, API 비용이 35% 줄었습니다. 응답 속도도 평균 200ms 빨라졌습니다.

같은 성능에 더 적은 비용, 이것이 캐시의 힘입니다.

실전 팁

💡 - 시스템 프롬프트, 도구 정의, Few-shot 예시는 반드시 메시지 배열 앞에 고정 배치하세요

  • 현재 시간, 사용자 ID 등 동적 정보는 프리픽스 뒤에 넣으세요
  • 프롬프트 버전 관리를 철저히 하여 불필요한 캐시 미스를 방지하세요

5. Context Partitioning 서브 에이전트 격리

김개발 씨가 복잡한 작업을 처리하는 에이전트를 만들었습니다. 코드 분석, 테스트 작성, 문서화까지 한 번에 해주는 에이전트입니다.

그런데 작업이 길어질수록 에이전트가 "혼란"스러워 보입니다. 코드 분석 중에 갑자기 문서화 내용을 언급하기도 합니다.

"이건 컨텍스트 오염이에요." 박시니어 씨가 진단했습니다.

Context Partitioning은 복잡한 작업을 독립된 서브 에이전트로 분리하여 컨텍스트를 격리하는 기법입니다. 마치 회사에서 부서를 나누는 것과 같습니다.

각 부서는 자신의 업무에 집중하고, 필요한 정보만 다른 부서와 공유합니다. 이렇게 하면 컨텍스트 오염을 방지하고 각 작업의 품질을 높일 수 있습니다.

다음 코드를 살펴봅시다.

class OrchestratorAgent:
    def __init__(self):
        self.sub_agents = {
            "analyzer": AnalyzerAgent(max_context=32000),
            "tester": TesterAgent(max_context=32000),
            "documenter": DocumenterAgent(max_context=32000)
        }

    async def execute_complex_task(self, task):
        # 1단계: 분석 에이전트에게 코드 분석 위임
        analysis = await self.sub_agents["analyzer"].run(
            context={"code": task.code}  # 필요한 컨텍스트만 전달
        )

        # 2단계: 테스트 에이전트에게 결과만 전달
        tests = await self.sub_agents["tester"].run(
            context={"analysis_summary": analysis.summary}  # 요약만 전달
        )

        # 각 서브 에이전트는 독립된 컨텍스트에서 실행

김개발 씨는 "만능 에이전트"를 꿈꿨습니다. 하나의 에이전트가 모든 것을 처리하면 얼마나 편할까요?

하지만 현실은 달랐습니다. "에이전트가 코드를 분석하다가 갑자기 '테스트 케이스를 작성해야 합니다'라고 말해요.

아직 분석도 안 끝났는데..." 박시니어 씨가 설명합니다. "그게 **컨텍스트 오염(Context Pollution)**입니다.

모든 정보가 한 컨텍스트에 섞여있으면, 모델이 관련 없는 정보에 영향을 받아요." 쉽게 비유하자면, Context Partitioning은 마치 사무실 칸막이와 같습니다. 오픈 오피스에서 모든 직원이 한 공간에 있으면 집중하기 어렵습니다.

옆자리 대화가 들리고, 다른 팀의 회의 내용이 섞입니다. 하지만 부서별로 공간을 분리하면 각자 업무에 집중할 수 있습니다.

Context Partitioning이 없던 시절에는 어땠을까요? 개발자들은 에이전트의 프롬프트에 "이전 내용을 잊고 새로운 작업에 집중하세요"라는 지시를 넣었습니다.

하지만 이건 효과가 제한적이었습니다. LLM은 컨텍스트에 있는 모든 정보에 접근할 수 있기 때문입니다.

진짜 해결책은 물리적으로 컨텍스트를 분리하는 것이었습니다. 박시니어 씨가 아키텍처를 그립니다.

"Orchestrator 패턴을 사용하세요. 메인 에이전트는 작업을 분배하고 결과를 취합하는 역할만 합니다.

실제 작업은 서브 에이전트들이 독립적으로 수행합니다." 위의 코드를 살펴보겠습니다. OrchestratorAgent는 세 개의 서브 에이전트를 관리합니다.

분석(analyzer), 테스트(tester), 문서화(documenter) 에이전트입니다. 각각 32000 토큰의 독립된 컨텍스트를 가집니다.

execute_complex_task 메서드에서 핵심은 필요한 컨텍스트만 전달하는 것입니다. 분석 에이전트에게는 코드만, 테스트 에이전트에게는 분석 요약만 전달합니다.

원본 코드 전체가 아니라 분석 결과의 요약만 넘깁니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 대규모 코드 리팩토링 에이전트를 만든다고 가정해봅시다. 프로젝트에 100개의 파일이 있습니다.

하나의 에이전트가 모든 파일을 처리하면 컨텍스트가 금방 가득 찹니다. 하지만 파일별로 서브 에이전트를 생성하면, 각 에이전트는 해당 파일에만 집중합니다.

결과물만 Orchestrator에게 보고하고, Orchestrator가 전체를 통합합니다. 하지만 주의할 점도 있습니다.

흔히 하는 실수는 너무 많은 정보를 서브 에이전트에 전달하는 것입니다. 격리의 의미가 없어집니다.

또한 서브 에이전트 간 의존성을 잘 관리해야 합니다. 테스트 에이전트가 분석 결과를 필요로 한다면, 분석이 먼저 완료되어야 합니다.

병렬 실행이 가능한 작업과 순차 실행이 필요한 작업을 구분하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.

Context Partitioning을 적용한 후, 각 서브 작업의 품질이 눈에 띄게 좋아졌습니다. 분석 에이전트는 분석에만, 테스트 에이전트는 테스트에만 집중했기 때문입니다.

전체 작업 시간도 병렬 실행 덕분에 40% 단축되었습니다.

실전 팁

💡 - 서브 에이전트에게는 필요한 최소한의 컨텍스트만 전달하세요

  • 독립적인 작업은 병렬로 실행하여 속도를 높이세요
  • Orchestrator는 가볍게 유지하고, 실제 작업은 서브 에이전트에게 위임하세요

6. 최적화 기법 종합 실전 적용 가이드

김개발 씨가 지금까지 배운 기법들을 정리했습니다. 70-80% 규칙, Compaction, Observation Masking, KV-Cache, Context Partitioning.

다섯 가지나 되니 머리가 복잡합니다. "이것들을 언제, 어떤 순서로 적용해야 하나요?" 박시니어 씨가 화이트보드 앞에 섰습니다.

"좋은 질문이에요. 실전 적용 전략을 알려드릴게요."

최적화 기법 종합 가이드는 다섯 가지 기법을 상황에 맞게 조합하는 실전 전략입니다. 마치 요리에서 재료마다 다른 조리법을 적용하는 것처럼, 각 기법은 특정 상황에서 빛을 발합니다.

올바른 조합으로 최대 90%의 토큰을 절약하면서도 성능을 유지할 수 있습니다.

다음 코드를 살펴봅시다.

class OptimizedAgentPipeline:
    def __init__(self):
        # 1. KV-Cache 최적화: 고정 프리픽스 설정
        self.static_prefix = build_cached_prefix()

        # 2. Context Partitioning: 서브 에이전트 준비
        self.sub_agents = initialize_sub_agents()

        # 3. Observation Masking: 도구 결과 필터
        self.masker = ObservationMasker(max_tokens=2000)

        # 4. Compaction: 압축 관리자
        self.compactor = ContextCompactor(threshold=0.70)

        # 5. 70-80% 규칙: 상태 모니터
        self.monitor = ContextMonitor(warning=0.70, critical=0.80)

    async def process(self, user_input):
        # 파이프라인 순서대로 적용
        context = await self._build_optimized_context()
        return await self._execute_with_monitoring(context)

김개발 씨는 노트를 펼쳤습니다. 다섯 가지 기법을 각각 이해했지만, 실전에서 어떻게 조합해야 할지 막막했습니다.

박시니어 씨가 화이트보드에 표를 그립니다. "기법마다 적용 시점이 다릅니다.

이걸 이해하면 자연스럽게 조합할 수 있어요." 먼저 KV-Cache Optimization입니다. 이건 항상 적용해야 합니다.

에이전트를 설계할 때부터 고정 프리픽스 구조를 만들어두세요. 비용이 들지 않고, 부작용도 없습니다.

안 할 이유가 없습니다. 다음은 Observation Masking입니다.

이건 도구를 사용할 때마다 적용됩니다. 파일을 읽든, 검색을 하든, API를 호출하든, 결과물이 나올 때마다 마스킹 여부를 판단합니다.

도구 래퍼에 마스킹 로직을 넣어두면 자동으로 적용됩니다. 70-80% 규칙지속적인 모니터링입니다.

매 턴마다 현재 토큰 사용량을 체크합니다. 아직 여유가 있으면 아무것도 하지 않습니다.

70%를 넘으면 경고를 띄우고, 80%를 넘으면 다음 기법을 트리거합니다. Compaction70-80% 도달 시 적용됩니다.

평소에는 작동하지 않다가, 컨텍스트가 임계값을 넘으면 자동으로 압축을 시작합니다. 마치 휴대폰의 저장공간이 부족해지면 자동으로 정리를 권유하는 것과 같습니다.

Context Partitioning작업 복잡도에 따라 결정됩니다. 단순한 Q&A라면 필요 없습니다.

하지만 여러 단계의 복잡한 작업이라면 서브 에이전트로 분리하는 것이 좋습니다. 위의 코드에서 OptimizedAgentPipeline 클래스는 이 모든 것을 통합합니다.

생성자에서 다섯 가지 기법을 순서대로 초기화합니다. KV-Cache를 위한 고정 프리픽스, Partitioning을 위한 서브 에이전트, Masking을 위한 마스커, Compaction을 위한 압축기, 그리고 모니터링을 위한 모니터.

process 메서드는 사용자 입력을 받아서 최적화된 파이프라인을 통과시킵니다. 각 기법이 적절한 시점에 자동으로 적용됩니다.

실제 현업에서는 어떻게 조합할까요? 예를 들어 코드 리뷰 에이전트를 만든다고 가정해봅시다.

기본 설정에서 KV-Cache를 적용합니다(시스템 프롬프트 고정). 사용자가 파일을 업로드하면 Observation Masking이 작동합니다(대용량 파일 필터링).

대화가 길어져 70%를 넘으면 Compaction이 시작됩니다(오래된 리뷰 내용 요약). 복잡한 리팩토링 요청이 들어오면 Partitioning을 적용합니다(분석, 수정, 테스트 서브 에이전트 분리).

이렇게 조합하면 상황에 따라 유연하게 대응할 수 있습니다. 간단한 작업에는 가벼운 최적화만, 복잡한 작업에는 모든 기법을 동원합니다.

하지만 주의할 점도 있습니다. 흔히 하는 실수는 과도한 최적화입니다.

모든 기법을 항상 최대 강도로 적용하면 오히려 역효과가 납니다. Compaction을 너무 자주 하면 LLM 호출 비용이 추가됩니다.

Partitioning을 불필요하게 적용하면 오버헤드가 생깁니다. 적절한 균형을 찾는 것이 핵심입니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 종합 가이드를 따라 에이전트를 재설계한 결과, 토큰 사용량이 60% 줄고 비용이 절반으로 떨어졌습니다.

무엇보다 에이전트의 응답 품질은 그대로 유지되었습니다. 박시니어 씨가 마지막으로 덧붙입니다.

"최적화는 끝이 없어요. 하지만 이 다섯 가지 기법만 제대로 익히면, 프로덕션 레벨의 에이전트를 운영할 수 있습니다."

실전 팁

💡 - KV-Cache는 설계 단계부터 적용하고, 나머지는 런타임에 조건부로 적용하세요

  • 모니터링 대시보드를 구축하여 각 기법의 효과를 측정하세요
  • 과도한 최적화보다 적절한 균형을 찾는 것이 중요합니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#AI Engineering#Context Optimization#Token Management#LLM#Agent Architecture

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

인터넷 게이트웨이와 퍼블릭 라우팅 설정 완벽 가이드

AWS VPC에서 인터넷과 통신하기 위한 핵심 구성요소인 인터넷 게이트웨이와 퍼블릭 라우팅 설정을 단계별로 학습합니다. 초급 개발자도 쉽게 따라할 수 있도록 실무 예제와 함께 설명합니다.

LLM-as-Judge TypeScript 실전 구현 가이드

프로덕션급 LLM 평가 시스템을 TypeScript로 구현하는 방법을 다룹니다. 19개의 테스트로 검증된 Direct Scoring, Pairwise Comparison, Rubric Generation 패턴을 실습하며, Eugene Yan과 Vercel AI SDK 6의 연구를 실제 코드에 적용합니다.

실전 예제 X-to-Book System 분석 완벽 가이드

Claude Agent SDK의 핵심 스킬들이 실제 프로젝트에서 어떻게 조합되는지 X-to-Book System을 통해 분석합니다. 멀티 에이전트 패턴부터 메모리 시스템까지, 실전 아키텍처의 비밀을 파헤쳐 봅니다.

LLM-as-a-Judge 고급 평가 기법 완벽 가이드

LLM을 활용한 자동 평가 시스템의 최신 기법을 다룹니다. Direct Scoring, Pairwise Comparison, 편향 완화 전략, 그리고 Panel of LLMs까지 실무에서 바로 적용할 수 있는 고급 평가 패턴을 소개합니다.

Evaluation 프레임워크 완벽 가이드

AI 에이전트의 성능을 어떻게 측정하고 평가할 수 있을까요? 비결정성이라는 본질적 특성부터 다차원 루브릭, 성능 변동 모델, 그리고 프로덕션 모니터링까지 체계적으로 살펴봅니다.