🤖

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

⚠️

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

이미지 로딩 중...

Context & Memory 패턴 기초 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2026. 2. 1. · 7 Views

Context & Memory 패턴 기초 완벽 가이드

AI 에이전트의 핵심인 컨텍스트와 메모리 관리 패턴을 초급자도 이해할 수 있도록 설명합니다. 슬라이딩 윈도우부터 장기 메모리까지, 실무에서 바로 적용할 수 있는 6가지 핵심 패턴을 다룹니다.


목차

  1. 슬라이딩_윈도우_큐레이션_전략
  2. 벡터_캐시_활용법
  3. 에피소딕_메모리_구현
  4. 장기_메모리_관리_기법
  5. 컨텍스트_윈도우_최적화
  6. 메모리_압축_및_요약_패턴

1. 슬라이딩 윈도우 큐레이션 전략

김개발 씨는 AI 챗봇 프로젝트를 진행하던 중 이상한 현상을 발견했습니다. 대화가 길어질수록 챗봇이 앞서 나눈 대화를 까맣게 잊어버리는 것이었습니다.

"분명히 제 이름을 알려줬는데 왜 자꾸 물어보는 거죠?"

슬라이딩 윈도우는 가장 최근의 대화만 기억하고 오래된 대화는 버리는 전략입니다. 마치 카메라 뷰파인더처럼 보이는 영역만 선명하게 유지하는 것과 같습니다.

이 방식을 이해하면 제한된 컨텍스트 윈도우를 효율적으로 활용할 수 있습니다.

다음 코드를 살펴봅시다.

from collections import deque
from typing import List, Dict

class SlidingWindowMemory:
    def __init__(self, max_messages: int = 10):
        # 최대 메시지 수를 제한하는 큐 생성
        self.messages = deque(maxlen=max_messages)

    def add_message(self, role: str, content: str):
        # 새 메시지 추가 시 오래된 것은 자동 삭제
        self.messages.append({"role": role, "content": content})

    def get_context(self) -> List[Dict]:
        # 현재 윈도우 내 모든 메시지 반환
        return list(self.messages)

    def get_token_count(self) -> int:
        # 대략적인 토큰 수 계산
        return sum(len(m["content"].split()) for m in self.messages)

김개발 씨는 입사 6개월 차 주니어 개발자입니다. 회사에서 고객 상담용 AI 챗봇을 개발하라는 미션을 받았습니다.

처음에는 모든 대화 내용을 그대로 AI에게 전달하면 될 줄 알았습니다. 그런데 문제가 생겼습니다.

대화가 30분 이상 지속되자 갑자기 API 호출이 실패하기 시작했습니다. 에러 메시지를 확인해보니 "context length exceeded"라는 문구가 보였습니다.

선배 개발자 박시니어 씨가 다가와 상황을 살펴봅니다. "아, 이건 토큰 제한 때문이에요.

GPT-4도 한 번에 처리할 수 있는 텍스트 양에 한계가 있거든요." 그렇다면 슬라이딩 윈도우란 정확히 무엇일까요? 쉽게 비유하자면, 슬라이딩 윈도우는 마치 열차 창문과 같습니다.

열차가 움직이면 창밖 풍경도 계속 바뀝니다. 지나간 풍경은 더 이상 보이지 않고, 새로운 풍경이 창문 안으로 들어옵니다.

이처럼 슬라이딩 윈도우도 최신 대화만 "창문" 안에 유지합니다. 슬라이딩 윈도우가 없던 시절에는 어땠을까요?

개발자들은 매번 수동으로 대화 기록을 잘라내야 했습니다. 어디서부터 자를지 판단하기 어려웠고, 중요한 맥락이 잘려나가는 경우도 많았습니다.

더 큰 문제는 토큰 수를 일일이 계산해야 했다는 점입니다. 바로 이런 문제를 해결하기 위해 슬라이딩 윈도우 패턴이 등장했습니다.

파이썬의 deque를 사용하면 이 패턴을 아주 간단하게 구현할 수 있습니다. maxlen 파라미터를 설정하면 큐가 가득 찼을 때 자동으로 가장 오래된 항목이 제거됩니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 생성자에서 **deque(maxlen=max_messages)**를 생성합니다.

이 부분이 핵심입니다. maxlen을 10으로 설정하면 항상 최근 10개의 메시지만 유지됩니다.

add_message 메서드가 호출될 때마다 새 메시지가 추가되고, 11번째 메시지가 들어오면 가장 오래된 첫 번째 메시지가 자동으로 삭제됩니다. 실제 현업에서는 어떻게 활용할까요?

고객 상담 챗봇을 개발한다고 가정해봅시다. 대부분의 상담은 최근 몇 번의 대화만 기억하면 충분합니다.

"이전에 말씀하신 환불 건 말이에요"라고 할 때, 보통 직전 5-10개 대화 안에 그 내용이 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 윈도우 크기를 너무 작게 설정하는 것입니다. 3-4개의 메시지만 유지하면 중요한 맥락을 잃어버리기 쉽습니다.

반대로 너무 크게 설정하면 토큰 비용이 급증합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 조언대로 슬라이딩 윈도우를 적용하니 토큰 초과 에러가 사라졌습니다. "이렇게 간단한 방법이 있었군요!" 슬라이딩 윈도우는 가장 기본적이면서도 효과적인 메모리 관리 전략입니다.

하지만 이것만으로는 장기적인 맥락을 유지하기 어렵습니다. 다음 장에서는 이를 보완하는 방법을 알아보겠습니다.

실전 팁

💡 - 윈도우 크기는 보통 토큰 제한의 60-70% 정도로 설정하세요

  • 시스템 프롬프트는 윈도우 밖에 별도로 관리하는 것이 좋습니다
  • 중요한 메시지는 별도 플래그로 표시해 우선순위를 두세요

2. 벡터 캐시 활용법

김개발 씨의 챗봇은 이제 대화 길이 문제는 해결했습니다. 그런데 새로운 문제가 생겼습니다.

일주일 전에 나눈 대화 내용을 사용자가 물어보면 챗봇은 전혀 기억하지 못했습니다. "지난주에 추천해준 그 책 이름이 뭐였죠?"

벡터 캐시는 텍스트를 숫자 벡터로 변환해서 저장하고, 나중에 유사한 내용을 빠르게 검색하는 기법입니다. 마치 도서관의 색인 카드처럼 내용의 "의미"를 기준으로 정보를 찾아줍니다.

이를 활용하면 장기 기억을 효율적으로 구현할 수 있습니다.

다음 코드를 살펴봅시다.

import numpy as np
from typing import List, Tuple

class VectorCache:
    def __init__(self):
        self.vectors = []  # 임베딩 벡터 저장
        self.contents = []  # 원본 텍스트 저장

    def add(self, text: str, embedding: np.ndarray):
        # 텍스트와 벡터를 함께 저장
        self.vectors.append(embedding)
        self.contents.append(text)

    def search(self, query_embedding: np.ndarray, top_k: int = 3) -> List[Tuple[str, float]]:
        # 코사인 유사도로 가장 관련 있는 내용 검색
        similarities = []
        for i, vec in enumerate(self.vectors):
            sim = np.dot(query_embedding, vec) / (np.linalg.norm(query_embedding) * np.linalg.norm(vec))
            similarities.append((self.contents[i], sim))
        # 유사도 높은 순으로 정렬 후 반환
        return sorted(similarities, key=lambda x: x[1], reverse=True)[:top_k]

김개발 씨는 고민에 빠졌습니다. 슬라이딩 윈도우만으로는 오래된 대화를 기억할 수 없었습니다.

모든 대화를 데이터베이스에 저장해두긴 했는데, 그걸 어떻게 효율적으로 찾아올 수 있을까요? 박시니어 씨가 화이트보드에 그림을 그리며 설명을 시작합니다.

"벡터 임베딩이라는 개념을 알아야 해요. 텍스트를 숫자 배열로 바꾸는 거죠." 그렇다면 벡터 캐시란 정확히 무엇일까요?

쉽게 비유하자면, 벡터 캐시는 마치 도서관 사서의 머릿속과 같습니다. 사서는 책의 제목을 정확히 기억하지 못해도 "우주에 관한 책", "사랑 이야기"처럼 내용의 특징만 말해도 적절한 책을 찾아줍니다.

벡터 캐시도 마찬가지로 질문의 "의미"를 이해하고 관련된 기억을 찾아옵니다. 기존 키워드 검색 방식은 어떤 문제가 있었을까요?

"강아지 산책"이라고 검색하면 "반려견 외출"이라는 표현은 찾지 못했습니다. 같은 의미인데도 단어가 다르면 매칭되지 않았던 것입니다.

또한 오타가 있으면 검색 결과가 아예 나오지 않는 문제도 있었습니다. 바로 이런 문제를 해결하기 위해 벡터 임베딩과 유사도 검색이 등장했습니다.

임베딩 모델은 텍스트를 수백에서 수천 차원의 벡터로 변환합니다. 비슷한 의미의 텍스트는 벡터 공간에서 가까운 위치에 놓이게 됩니다.

이를 활용하면 "강아지 산책"과 "반려견 외출"이 가까운 벡터로 표현되어 함께 검색됩니다. 위의 코드를 한 줄씩 살펴보겠습니다.

VectorCache 클래스는 두 개의 리스트를 관리합니다. vectors는 임베딩 벡터를, contents는 원본 텍스트를 저장합니다.

search 메서드에서는 코사인 유사도를 계산합니다. 두 벡터의 방향이 얼마나 비슷한지를 측정하는 공식입니다.

실제 현업에서는 어떻게 활용할까요? RAG (Retrieval-Augmented Generation) 시스템을 구축할 때 벡터 캐시는 필수입니다.

사용자가 "지난주에 얘기한 프로젝트 마감일"을 물어보면, 벡터 검색으로 관련 대화를 찾아 AI에게 컨텍스트로 전달합니다. 이렇게 하면 AI가 과거 대화를 "기억"하는 것처럼 동작합니다.

하지만 주의할 점도 있습니다. 벡터 캐시의 크기가 커지면 검색 속도가 느려집니다.

실무에서는 FAISS, Pinecone, Chroma 같은 전문 벡터 데이터베이스를 사용합니다. 또한 임베딩 모델의 품질에 따라 검색 정확도가 크게 달라지므로 적절한 모델 선택이 중요합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 벡터 캐시를 적용하자 챗봇이 일주일 전 대화도 정확히 기억해냈습니다.

"아, 지난주에 '클린 코드' 책을 추천드렸었죠!" 벡터 캐시는 AI 에이전트의 장기 기억을 구현하는 핵심 기술입니다. 단순한 슬라이딩 윈도우를 넘어 진정한 "기억력"을 갖춘 AI를 만들 수 있게 해줍니다.

실전 팁

💡 - OpenAI의 text-embedding-3-small 모델은 비용 대비 성능이 우수합니다

  • 중요도에 따라 벡터에 가중치를 부여하면 검색 품질이 향상됩니다
  • 주기적으로 오래된 벡터를 정리하여 캐시 효율을 유지하세요

3. 에피소딕 메모리 구현

김개발 씨는 한 가지 더 고민이 생겼습니다. 벡터 검색으로 관련 정보는 찾을 수 있는데, 대화의 "흐름"이 끊어지는 느낌이었습니다.

"어제 회의에서 결정된 건 뭐였죠?"라는 질문에 단편적인 정보만 나열될 뿐, 회의 전체 맥락이 전달되지 않았습니다.

에피소딕 메모리는 개별 사건이나 경험을 하나의 에피소드로 묶어서 저장하는 방식입니다. 마치 일기장의 한 페이지처럼 특정 시점의 경험 전체를 하나의 단위로 기억합니다.

이를 통해 맥락이 풍부한 기억을 구현할 수 있습니다.

다음 코드를 살펴봅시다.

from datetime import datetime
from typing import List, Optional
from dataclasses import dataclass

@dataclass
class Episode:
    id: str
    timestamp: datetime
    context: str          # 상황 설명
    messages: List[dict]  # 대화 내용
    summary: str          # 에피소드 요약
    tags: List[str]       # 검색용 태그

class EpisodicMemory:
    def __init__(self):
        self.episodes: List[Episode] = []

    def create_episode(self, context: str, messages: List[dict], tags: List[str]) -> Episode:
        # 새로운 에피소드 생성 및 요약
        summary = self._generate_summary(messages)
        episode = Episode(
            id=f"ep_{len(self.episodes)}",
            timestamp=datetime.now(),
            context=context, messages=messages,
            summary=summary, tags=tags
        )
        self.episodes.append(episode)
        return episode

김개발 씨는 인간의 기억에 대해 생각해보았습니다. 우리는 "어제 점심"을 기억할 때 음식 이름만 떠올리지 않습니다.

누구와 함께 먹었는지, 어떤 대화를 나눴는지, 분위기는 어땠는지 함께 기억합니다. 박시니어 씨가 흥미로운 이야기를 들려줍니다.

"인지과학에서는 이걸 에피소딕 메모리라고 불러요. 개별 경험을 하나의 '에피소드'로 묶어서 저장하는 거죠." 그렇다면 에피소딕 메모리란 정확히 무엇일까요?

쉽게 비유하자면, 에피소딕 메모리는 마치 드라마의 한 회차와 같습니다. 드라마를 볼 때 우리는 개별 대사만 기억하지 않습니다.

"그 회차에서 주인공이 어떤 상황에 처했고, 어떤 결정을 내렸다"는 전체 맥락을 기억합니다. 에피소딕 메모리도 대화의 조각이 아닌 "하나의 완결된 상호작용"을 저장합니다.

에피소딕 메모리가 없으면 어떤 문제가 생길까요? 벡터 검색만 사용하면 관련된 문장들을 찾을 수는 있지만, 그 문장들이 어떤 맥락에서 나왔는지 알기 어렵습니다.

"프로젝트 마감일은 금요일입니다"라는 문장만으로는 어떤 프로젝트인지, 왜 그렇게 결정되었는지 알 수 없습니다. 바로 이런 문제를 해결하기 위해 에피소딕 메모리 패턴이 등장했습니다.

에피소딕 메모리는 대화가 끝날 때마다 그 대화 전체를 하나의 에피소드로 묶습니다. 여기에 상황 설명(context), 요약(summary), 태그(tags)를 추가하여 나중에 쉽게 검색하고 이해할 수 있게 합니다.

위의 코드를 한 줄씩 살펴보겠습니다. Episode 데이터 클래스는 하나의 에피소드가 가져야 할 정보를 정의합니다.

timestamp는 언제 일어난 일인지, context는 어떤 상황이었는지, messages는 실제 대화 내용을 담습니다. summary는 AI가 생성한 요약이고, tags는 빠른 검색을 위한 키워드입니다.

실제 현업에서는 어떻게 활용할까요? 업무용 AI 비서를 만든다고 가정해봅시다.

사용자가 "지난 분기 실적 회의 내용 알려줘"라고 하면, 에피소딕 메모리에서 해당 에피소드를 찾아 전체 맥락과 함께 제공합니다. 단순히 숫자만 나열하는 것이 아니라, 그 숫자가 어떤 배경에서 논의되었는지까지 전달할 수 있습니다.

하지만 주의할 점도 있습니다. 에피소드를 너무 잘게 나누면 맥락이 분산됩니다.

반대로 너무 크게 묶으면 검색 효율이 떨어집니다. 보통 하나의 "주제"나 "세션"을 기준으로 에피소드를 구분하는 것이 좋습니다.

또한 요약을 생성할 때 중요한 정보가 누락되지 않도록 프롬프트를 잘 설계해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

에피소딕 메모리를 도입하자 챗봇의 응답이 훨씬 풍부해졌습니다. "어제 회의에서 마감일을 금요일로 정했습니다.

당시 리소스 상황을 고려해서 결정된 거였어요." 에피소딕 메모리는 AI에게 "경험"을 부여하는 강력한 패턴입니다. 단편적인 정보가 아닌 맥락 있는 기억을 제공할 수 있습니다.

실전 팁

💡 - 에피소드 종료 시점을 명확히 정의하세요 (시간 기준, 주제 변경 등)

  • 요약 생성에는 GPT-4o-mini 같은 경량 모델을 사용해 비용을 절감하세요
  • 중요한 에피소드에는 별도 플래그를 달아 우선 검색되게 하세요

4. 장기 메모리 관리 기법

김개발 씨의 AI 비서는 이제 꽤 똑똑해졌습니다. 그런데 서비스를 6개월간 운영하다 보니 메모리 저장소가 폭발적으로 커졌습니다.

검색 속도도 느려지고, 저장 비용도 치솟았습니다. "모든 기억을 영원히 저장할 수는 없겠네요..."

장기 메모리 관리는 중요한 정보는 오래 보존하고, 덜 중요한 정보는 점진적으로 잊어가는 전략입니다. 마치 우리 뇌가 자주 떠올리는 기억은 강화하고, 안 쓰는 기억은 희미해지게 하는 것과 같습니다.

이를 통해 효율적이고 지속 가능한 메모리 시스템을 구축할 수 있습니다.

다음 코드를 살펴봅시다.

from datetime import datetime, timedelta
from typing import Dict
import math

class LongTermMemory:
    def __init__(self, decay_rate: float = 0.1):
        self.memories: Dict[str, dict] = {}
        self.decay_rate = decay_rate

    def store(self, key: str, content: str, importance: float = 1.0):
        # 중요도와 함께 메모리 저장
        self.memories[key] = {
            "content": content,
            "importance": importance,
            "last_accessed": datetime.now(),
            "access_count": 1
        }

    def recall(self, key: str) -> str:
        # 접근 시 기억 강화
        if key in self.memories:
            self.memories[key]["access_count"] += 1
            self.memories[key]["last_accessed"] = datetime.now()
            return self.memories[key]["content"]
        return None

    def calculate_retention(self, key: str) -> float:
        # 망각 곡선 기반 유지율 계산
        mem = self.memories[key]
        days = (datetime.now() - mem["last_accessed"]).days
        retention = mem["importance"] * math.exp(-self.decay_rate * days)
        return retention * (1 + math.log(mem["access_count"] + 1))

김개발 씨는 문득 학창 시절을 떠올렸습니다. 시험 전날 벼락치기로 외운 내용은 금방 잊어버렸지만, 반복해서 공부한 내용은 오래 기억에 남았습니다.

"AI 메모리도 이렇게 만들 수 없을까요?" 박시니어 씨가 고개를 끄덕입니다. "좋은 착안이에요.

실제로 에빙하우스의 망각 곡선이라는 개념이 있어요. 이걸 AI 메모리에 적용할 수 있습니다." 그렇다면 장기 메모리 관리란 정확히 무엇일까요?

쉽게 비유하자면, 장기 메모리 관리는 마치 정원 가꾸기와 같습니다. 정원사는 건강한 식물은 잘 돌보고, 시든 식물은 정리합니다.

자주 가지치기를 하지 않으면 정원은 무질서해지고, 건강한 식물까지 영향을 받습니다. 메모리도 마찬가지로 중요한 것은 강화하고 불필요한 것은 정리해야 합니다.

장기 메모리 관리가 없으면 어떤 문제가 생길까요? 메모리가 무한히 늘어나면서 저장 비용이 기하급수적으로 증가합니다.

검색 시간도 늘어나 응답 속도가 느려집니다. 더 심각한 문제는 오래된 불필요한 정보가 검색 결과를 오염시켜 AI의 응답 품질을 떨어뜨린다는 것입니다.

바로 이런 문제를 해결하기 위해 장기 메모리 관리 패턴이 등장했습니다. 핵심 아이디어는 각 메모리에 **유지율(retention)**을 부여하는 것입니다.

유지율은 시간이 지남에 따라 감소하지만, 자주 접근하면 다시 높아집니다. 유지율이 일정 수준 이하로 떨어진 메모리는 삭제하거나 아카이브합니다.

위의 코드를 한 줄씩 살펴보겠습니다. calculate_retention 메서드가 핵심입니다.

먼저 마지막 접근 이후 지난 일수를 계산합니다. 그 다음 지수 함수를 사용해 시간에 따른 감쇠를 적용합니다.

마지막으로 접근 횟수에 로그를 취해 자주 사용된 메모리에 보너스를 줍니다. 실제 현업에서는 어떻게 활용할까요?

장기 운영되는 AI 서비스에서는 필수적인 패턴입니다. 사용자가 자주 언급하는 정보(이름, 선호도 등)는 높은 유지율을 유지하고, 일회성 정보(날씨 문의 등)는 자연스럽게 잊혀집니다.

정기적으로 배치 작업을 실행해 유지율이 낮은 메모리를 정리합니다. 하지만 주의할 점도 있습니다.

중요도 점수를 어떻게 매길지 신중하게 결정해야 합니다. 단순히 빈도만 고려하면 "안녕하세요"같은 인사말이 가장 중요한 기억이 되어버립니다.

내용의 의미적 중요도를 AI로 평가하는 방법을 병행하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

장기 메모리 관리를 도입한 뒤, 저장소 크기가 안정적으로 유지되었습니다. 검색 속도도 빨라졌고, 무엇보다 AI가 정말 중요한 것만 기억하는 느낌이 들었습니다.

장기 메모리 관리는 AI 시스템의 지속 가능성을 위한 필수 패턴입니다. 무한히 늘어나는 메모리가 아닌, 살아 숨 쉬는 유기적인 기억 체계를 만들 수 있습니다.

실전 팁

💡 - decay_rate는 서비스 특성에 맞게 조정하세요 (빠른 잊힘 vs 느린 잊힘)

  • 삭제 전 아카이브 계층을 두어 필요시 복구할 수 있게 하세요
  • 주기적인 정리 작업은 사용량이 적은 시간대에 실행하세요

5. 컨텍스트 윈도우 최적화

김개발 씨는 이제 메모리 관리의 기초는 익혔습니다. 그런데 새로운 고민이 생겼습니다.

같은 8K 토큰 윈도우를 사용해도 어떤 정보를 넣느냐에 따라 AI 응답 품질이 크게 달랐습니다. "어떻게 하면 제한된 공간을 최대한 효율적으로 쓸 수 있을까요?"

컨텍스트 윈도우 최적화는 제한된 토큰 공간에 가장 가치 있는 정보를 배치하는 전략입니다. 마치 한정된 가방 공간에 여행 필수품을 효율적으로 패킹하는 것과 같습니다.

이를 통해 같은 비용으로 더 나은 AI 응답을 얻을 수 있습니다.

다음 코드를 살펴봅시다.

from typing import List, Dict
from dataclasses import dataclass

@dataclass
class ContextItem:
    content: str
    priority: int      # 우선순위 (1=최상위)
    token_count: int
    category: str      # system, user_info, recent, retrieved

class ContextWindowOptimizer:
    def __init__(self, max_tokens: int = 8000):
        self.max_tokens = max_tokens
        # 카테고리별 예약 토큰
        self.reserved = {"system": 1000, "user_info": 500}

    def optimize(self, items: List[ContextItem]) -> List[ContextItem]:
        # 우선순위별로 정렬
        sorted_items = sorted(items, key=lambda x: x.priority)
        selected = []
        used_tokens = 0

        for item in sorted_items:
            # 카테고리별 예약 공간 확인
            reserve = self.reserved.get(item.category, 0)
            if used_tokens + item.token_count <= self.max_tokens - reserve:
                selected.append(item)
                used_tokens += item.token_count

        return selected

김개발 씨는 흥미로운 실험을 해보았습니다. 같은 질문에 대해 컨텍스트에 무엇을 넣느냐만 바꿔봤더니 AI 응답 품질이 천차만별이었습니다.

어떤 조합은 핵심을 정확히 짚었고, 어떤 조합은 엉뚱한 답변을 내놓았습니다. 박시니어 씨가 설명을 덧붙입니다.

"컨텍스트 윈도우는 AI의 '작업 기억'이에요. 사람도 한 번에 7개 정도의 정보만 머릿속에 담을 수 있듯이, AI도 한 번에 처리할 수 있는 정보량에 한계가 있어요." 그렇다면 컨텍스트 윈도우 최적화란 정확히 무엇일까요?

쉽게 비유하자면, 컨텍스트 윈도우 최적화는 마치 배낭여행 짐 싸기와 같습니다. 배낭 용량은 한정되어 있으니 여권, 돈, 약처럼 필수품을 먼저 챙기고, 남는 공간에 있으면 좋은 것들을 넣습니다.

무거운데 쓸모없는 물건은 과감히 빼야 합니다. 컨텍스트를 신경 쓰지 않으면 어떤 문제가 생길까요?

불필요한 정보가 중요한 정보를 밀어냅니다. "오늘 날씨 어때요?"라는 질문에 6개월 치 대화 기록을 다 넣으면 정작 중요한 시스템 프롬프트나 사용자 선호도가 잘려나갑니다.

토큰 비용도 낭비되고, 응답 품질도 떨어지는 최악의 상황이 됩니다. 바로 이런 문제를 해결하기 위해 컨텍스트 윈도우 최적화 패턴이 등장했습니다.

핵심 아이디어는 정보를 카테고리와 우선순위로 분류하는 것입니다. 시스템 프롬프트, 사용자 기본 정보, 최근 대화, 검색된 기억 등으로 나누고, 각각에 적절한 공간을 할당합니다.

위의 코드를 한 줄씩 살펴보겠습니다. ContextWindowOptimizer는 먼저 특정 카테고리에 토큰을 예약합니다.

시스템 프롬프트에 1000토큰, 사용자 정보에 500토큰을 미리 확보해두는 식입니다. optimize 메서드는 우선순위가 높은 항목부터 차례로 선택하면서 예약된 공간을 침범하지 않도록 합니다.

실제 현업에서는 어떻게 활용할까요? 전문 상담 AI를 만든다고 가정해봅시다.

시스템 프롬프트(상담 가이드라인)는 반드시 포함되어야 합니다. 사용자의 상담 이력 요약도 필수입니다.

그 다음 현재 대화 내용, 마지막으로 관련 FAQ나 문서를 넣습니다. 이 순서를 지키면 일관성 있는 품질을 보장할 수 있습니다.

하지만 주의할 점도 있습니다. 우선순위를 잘못 설정하면 오히려 성능이 떨어집니다.

시스템 프롬프트를 너무 길게 작성하면 실제 대화 공간이 부족해집니다. 또한 토큰 계산을 대충 하면 예상치 못하게 잘리는 경우가 생깁니다.

tiktoken 같은 정확한 토크나이저를 사용하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.

컨텍스트 윈도우 최적화를 적용하자, 같은 토큰 비용으로 AI 응답 품질이 눈에 띄게 향상되었습니다. "공간 관리가 이렇게 중요했군요!" 컨텍스트 윈도우 최적화는 비용 효율과 성능을 모두 잡는 실용적인 패턴입니다.

제한된 자원을 현명하게 사용하는 능력은 시니어 개발자의 핵심 역량이기도 합니다.

실전 팁

💡 - tiktoken 라이브러리로 정확한 토큰 수를 계산하세요

  • 시스템 프롬프트는 전체의 15-20%를 넘지 않도록 하세요
  • A/B 테스트로 최적의 카테고리 비율을 찾으세요

6. 메모리 압축 및 요약 패턴

김개발 씨는 마지막 퍼즐 조각을 맞추려 합니다. 지금까지 배운 패턴들은 모두 정보를 선택하거나 버리는 방식이었습니다.

그런데 때로는 모든 정보가 중요한데 공간이 부족한 경우가 있었습니다. "정보를 잃지 않으면서 공간을 줄일 수는 없을까요?"

메모리 압축 및 요약은 긴 대화나 문서를 핵심만 남기고 줄이는 기법입니다. 마치 두꺼운 책의 핵심을 한 장짜리 요약본으로 만드는 것과 같습니다.

이를 통해 정보의 본질은 유지하면서 토큰 사용량을 대폭 줄일 수 있습니다.

다음 코드를 살펴봅시다.

from typing import List
from dataclasses import dataclass

@dataclass
class CompressedMemory:
    original_tokens: int
    compressed_tokens: int
    summary: str
    key_points: List[str]

class MemoryCompressor:
    def __init__(self, llm_client):
        self.llm = llm_client

    def compress(self, messages: List[dict], ratio: float = 0.3) -> CompressedMemory:
        # 원본 대화를 요약으로 압축
        original_text = "\n".join([m["content"] for m in messages])

        prompt = f"""다음 대화를 핵심만 유지하여 {int(ratio*100)}% 길이로 요약하세요.
        중요한 결정사항, 숫자, 이름은 반드시 포함하세요.

        대화 내용:
        {original_text}"""

        summary = self.llm.generate(prompt)
        key_points = self._extract_key_points(original_text)

        return CompressedMemory(
            original_tokens=len(original_text.split()),
            compressed_tokens=len(summary.split()),
            summary=summary, key_points=key_points
        )

김개발 씨는 한 가지 딜레마에 빠졌습니다. 고객과 2시간 동안 나눈 상담 대화가 있었습니다.

다음 상담 때 이 내용을 참고해야 하는데, 원본 그대로 넣으면 토큰 초과, 그렇다고 버리면 중요한 맥락을 잃게 됩니다. 박시니어 씨가 해결책을 제시합니다.

"메모리 압축을 사용해보세요. AI가 직접 요약을 만들어주니까, 핵심 정보는 유지하면서 크기를 대폭 줄일 수 있어요." 그렇다면 메모리 압축 및 요약이란 정확히 무엇일까요?

쉽게 비유하자면, 메모리 압축은 마치 책의 줄거리 요약과 같습니다. 500페이지 소설도 2-3페이지 요약본으로 핵심 스토리를 전달할 수 있습니다.

세부 묘사는 줄어들지만, 주요 인물과 사건은 그대로 남습니다. 메모리 압축도 대화의 "줄거리"를 보존합니다.

메모리 압축이 없으면 어떤 문제가 생길까요? 긴 대화를 그냥 자르면 중요한 결정사항이나 약속이 사라질 수 있습니다.

"다음 주 화요일에 다시 연락드릴게요"라는 약속이 잘려나가면 큰 문제가 됩니다. 반대로 모든 것을 저장하면 비용이 감당하기 어려워집니다.

바로 이런 문제를 해결하기 위해 메모리 압축 패턴이 등장했습니다. 핵심 아이디어는 AI를 사용해 AI를 위한 요약을 만드는 것입니다.

사람이 읽을 요약이 아니라, 나중에 AI가 컨텍스트로 활용할 요약을 생성합니다. 따라서 자연스러운 문장보다 핵심 정보의 밀도가 중요합니다.

위의 코드를 한 줄씩 살펴보겠습니다. compress 메서드는 원본 대화를 받아 LLM에게 요약을 요청합니다.

프롬프트에서 중요한 부분은 "중요한 결정사항, 숫자, 이름은 반드시 포함하세요"입니다. 이렇게 지시하지 않으면 AI가 추상적인 요약만 만들어 정작 필요한 세부 정보를 놓칠 수 있습니다.

실제 현업에서는 어떻게 활용할까요? 장기간 운영되는 고객 서비스 챗봇에서 자주 사용됩니다.

각 상담 세션이 끝나면 요약을 생성하고, 다음 상담 시 이 요약을 컨텍스트에 포함합니다. 원본 대화는 별도 저장소에 보관하되, 실시간 처리에는 압축된 버전을 사용합니다.

하지만 주의할 점도 있습니다. 압축 과정에서 정보 손실이 발생할 수 있습니다.

특히 미묘한 뉘앙스나 조건부 약속("만약 ~하면 ~할게요")이 누락되기 쉽습니다. 중요한 대화는 압축률을 낮추고, 원본도 함께 보관하는 것이 안전합니다.

또한 요약 생성 자체에도 비용이 들므로 적절한 시점에 배치로 처리하세요. 다시 김개발 씨의 이야기로 돌아가 봅시다.

메모리 압축을 도입한 뒤, 2시간짜리 상담 기록이 500자 요약으로 압축되었습니다. 핵심 정보는 모두 담겨 있었고, 다음 상담에서 AI가 자연스럽게 이전 맥락을 이어갔습니다.

메모리 압축 및 요약은 Context & Memory 패턴의 마지막 퍼즐 조각입니다. 앞서 배운 모든 패턴과 함께 사용하면 효율적이고 지능적인 AI 메모리 시스템을 구축할 수 있습니다.

실전 팁

💡 - 압축에는 GPT-4o-mini 같은 저렴한 모델을 사용하세요

  • key_points를 별도로 추출해 빠른 검색에 활용하세요
  • 압축 전후 토큰 수를 기록해 효율성을 모니터링하세요

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

#Python#AI-Agent#Memory-Pattern#Context-Window#Vector-Cache#LLM#AI,Agent,Memory

댓글 (0)

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

함께 보면 좋은 카드 뉴스