본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 2장: 단기 메모리와 컨텍스트 윈도우 관리
2026년 2월 28일·AI / ML·

2장: 단기 메모리와 컨텍스트 윈도우 관리

슬라이딩 윈도우, 메시지 요약, 토큰 예산 관리, 중요도 기반 정리 등 에이전트 단기 메모리의 핵심 전략을 코드 예제와 함께 다룹니다.

16분354자10개 섹션
aillmai-agent
공유
agent-memory2 / 10
12345678910
이전1장: AI 에이전트 메모리의 필요성과 핵심 개념다음3장: 장기 메모리 — 벡터 기반 의미 검색

학습 목표

  • 단기 메모리의 역할과 컨텍스트 윈도우의 관계를 이해한다
  • 슬라이딩 윈도우와 메시지 요약 전략을 구현할 수 있다
  • 토큰 예산 관리와 중요도 기반 메시지 정리 기법을 적용할 수 있다
  • 컨텍스트 오버플로우 상황에서의 대응 전략을 설계할 수 있다

단기 메모리란 무엇인가

단기 메모리(Short-term Memory)는 현재 진행 중인 대화 세션의 맥락을 유지하는 메모리입니다. LLM의 컨텍스트 윈도우 안에서 관리되며, 세션이 종료되면 자연스럽게 소멸합니다.

인간의 작업 기억(Working Memory)에 비유할 수 있습니다. 전화번호를 잠시 외워서 입력하듯이, 에이전트도 현재 대화의 흐름을 파악하기 위해 최근 메시지들을 컨텍스트에 유지합니다.

문제는 대화가 길어질수록 컨텍스트가 누적되어 토큰 한계에 도달한다는 것입니다. 이 한계를 효과적으로 관리하는 것이 단기 메모리 전략의 핵심입니다.


슬라이딩 윈도우

가장 단순하면서도 효과적인 전략은 슬라이딩 윈도우(Sliding Window)입니다. 최근 N개의 메시지만 컨텍스트에 포함하고, 오래된 메시지는 제거합니다.

sliding-window.ts
typescript
interface Message {
  role: "user" | "assistant" | "system";
  content: string;
  tokenCount: number;
}
 
function applySlidingWindow(
  messages: Message[],
  maxMessages: number
): Message[] {
  const systemMessages = messages.filter((m) => m.role === "system");
  const conversationMessages = messages.filter((m) => m.role !== "system");
 
  // 시스템 메시지는 항상 유지, 대화 메시지만 자르기
  const recentMessages = conversationMessages.slice(-maxMessages);
 
  return [...systemMessages, ...recentMessages];
}

메시지 수 기반 vs 토큰 수 기반

단순히 메시지 개수로 자르는 것보다 토큰 수를 기준으로 관리하는 것이 더 정밀합니다. 메시지의 길이가 균일하지 않기 때문입니다.

token-based-window.ts
typescript
function applyTokenWindow(
  messages: Message[],
  maxTokens: number
): Message[] {
  const systemMessages = messages.filter((m) => m.role === "system");
  const systemTokens = systemMessages.reduce(
    (sum, m) => sum + m.tokenCount, 0
  );
 
  const budget = maxTokens - systemTokens;
  const conversationMessages = messages.filter((m) => m.role !== "system");
 
  // 최신 메시지부터 역순으로 예산 내에서 채우기
  const selected: Message[] = [];
  let usedTokens = 0;
 
  for (let i = conversationMessages.length - 1; i >= 0; i--) {
    const msg = conversationMessages[i];
    if (usedTokens + msg.tokenCount > budget) break;
    selected.unshift(msg);
    usedTokens += msg.tokenCount;
  }
 
  return [...systemMessages, ...selected];
}
Tip

토큰 수 계산에는 tiktoken(OpenAI) 또는 각 모델 제공사의 토크나이저를 사용합니다. 정확한 토큰 수 계산은 비용 예측과 컨텍스트 관리의 기초입니다.


메시지 요약

슬라이딩 윈도우의 단점은 오래된 메시지의 정보가 완전히 사라진다는 것입니다. 이를 보완하는 전략이 메시지 요약(Message Summarization)입니다.

동작 원리

  1. 컨텍스트가 임계값에 도달하면 오래된 메시지들을 요약합니다
  2. 요약문을 시스템 메시지 또는 별도의 요약 블록으로 컨텍스트에 삽입합니다
  3. 원본 메시지는 제거하고, 요약 + 최근 메시지로 컨텍스트를 재구성합니다

요약 프롬프트 설계

요약의 품질은 프롬프트에 크게 좌우됩니다. 핵심은 대화의 맥락적 연속성을 유지하면서 토큰을 줄이는 것입니다.

summarization-prompt.ts
typescript
const SUMMARIZATION_PROMPT = `
다음 대화 내용을 간결하게 요약하세요.
 
규칙:
1. 사용자의 핵심 요청과 에이전트의 주요 응답을 포함할 것
2. 결정된 사항, 합의된 내용을 명시할 것
3. 진행 중인 작업이나 미해결 질문을 표시할 것
4. 구체적인 수치, 이름, 코드 스니펫은 보존할 것
5. 200 토큰 이내로 요약할 것
 
대화 내용:
`;
 
async function summarizeMessages(
  messages: Message[],
  llm: LLMClient
): Promise<string> {
  const conversationText = messages
    .map((m) => `[${m.role}]: ${m.content}`)
    .join("\n");
 
  const response = await llm.complete({
    messages: [
      {
        role: "system",
        content: SUMMARIZATION_PROMPT,
      },
      {
        role: "user",
        content: conversationText,
      },
    ],
    maxTokens: 300,
  });
 
  return response.content;
}

점진적 요약 vs 일괄 요약

점진적 요약(Progressive Summarization)은 새로운 메시지가 추가될 때마다 기존 요약을 업데이트하는 방식입니다. 일괄 요약(Batch Summarization)은 임계값에 도달했을 때 한 번에 요약합니다.

방식장점단점
점진적 요약정보 손실 최소화, 부드러운 전환매 턴 추가 LLM 호출 비용
일괄 요약비용 효율적, 구현 단순요약 시점에 정보 손실 발생 가능

프로덕션에서는 일괄 요약을 기본으로 하되, 중요 대화에 대해서만 점진적 요약을 적용하는 하이브리드 방식이 일반적입니다.


토큰 예산 관리

컨텍스트 윈도우를 여러 구성 요소가 공유하므로, 각 요소에 토큰 예산을 배분하는 것이 중요합니다.

token-budget.ts
typescript
interface TokenBudget {
  systemPrompt: number;
  memorySummary: number;
  retrievedContext: number; // RAG나 장기 메모리에서 가져온 정보
  recentMessages: number;
  outputReserve: number;   // 응답 생성을 위한 여유분
}
 
function allocateBudget(totalTokens: number): TokenBudget {
  return {
    systemPrompt: Math.floor(totalTokens * 0.10),   // 10%
    memorySummary: Math.floor(totalTokens * 0.10),   // 10%
    retrievedContext: Math.floor(totalTokens * 0.20), // 20%
    recentMessages: Math.floor(totalTokens * 0.35),  // 35%
    outputReserve: Math.floor(totalTokens * 0.25),   // 25%
  };
}
 
// 128K 토큰 모델 기준
const budget = allocateBudget(128_000);
// systemPrompt: 12,800
// memorySummary: 12,800
// retrievedContext: 25,600
// recentMessages: 44,800
// outputReserve: 32,000
Info

출력 예약(Output Reserve)은 자주 간과되는 부분입니다. 모델이 긴 응답을 생성해야 하는 경우, 충분한 출력 토큰 공간을 확보하지 않으면 응답이 중간에 잘리거나 품질이 저하됩니다.


중요도 기반 메시지 정리

모든 메시지가 동일한 가치를 가지지는 않습니다. 중요도 기반 정리(Importance-based Pruning)는 각 메시지에 중요도 점수를 부여하고, 점수가 낮은 메시지부터 제거합니다.

중요도 산정 기준

importance-scoring.ts
typescript
interface ScoredMessage extends Message {
  importanceScore: number;
}
 
function scoreMessage(message: Message, index: number, total: number): number {
  let score = 0;
 
  // 1. 시간적 근접성: 최신 메시지일수록 높은 점수
  const recency = index / total;
  score += recency * 0.3;
 
  // 2. 역할 가중치: 시스템 메시지 > 사용자 메시지 > 에이전트 응답
  const roleWeight: Record<string, number> = {
    system: 1.0,
    user: 0.8,
    assistant: 0.5,
  };
  score += (roleWeight[message.role] ?? 0.5) * 0.2;
 
  // 3. 내용 기반 신호
  const content = message.content.toLowerCase();
  if (content.includes("결정") || content.includes("확정")) score += 0.15;
  if (content.includes("중요") || content.includes("반드시")) score += 0.10;
  if (content.includes("코드") || content.includes("```")) score += 0.10;
 
  // 4. 길이 가중치: 너무 짧은 메시지(인사 등)는 낮은 점수
  if (message.tokenCount < 10) score -= 0.15;
  if (message.tokenCount > 100) score += 0.05;
 
  return Math.max(0, Math.min(1, score));
}

선택적 제거 전략

중요도 점수를 기반으로 메시지를 정리할 때, 대화의 흐름이 끊기지 않도록 주의해야 합니다.

  1. 짝 보존: 사용자 질문과 에이전트 응답은 반드시 쌍으로 유지하거나 쌍으로 제거합니다
  2. 앵커 메시지 보호: 주제 전환이나 핵심 결정이 담긴 메시지는 제거하지 않습니다
  3. 최소 맥락 유지: 아무리 정리하더라도 최소 3-5개의 최근 교환은 보존합니다

자기 요약 기법

자기 요약(Self-Summarization)은 에이전트가 스스로의 응답을 생성할 때, 동시에 현재까지의 대화를 압축하는 기법입니다.

self-summarization.ts
typescript
const SELF_SUMMARY_INSTRUCTION = `
응답을 생성한 후, 현재까지의 대화 상태를 다음 형식으로 요약하세요:
 
[CONVERSATION_STATE]
- 사용자 목표:
- 진행 상황:
- 미해결 사항:
- 핵심 결정:
[/CONVERSATION_STATE]
 
이 요약은 사용자에게 보이지 않으며, 내부 메모리로만 사용됩니다.
`;

이 기법의 장점은 별도의 요약 호출 없이 매 턴마다 최신 상태를 유지할 수 있다는 것입니다. 다음 턴에서는 이전 대화 전체 대신 이 요약만 참조하면 됩니다.


컨텍스트 오버플로우 전략

예상치 못하게 컨텍스트가 한계에 도달하는 경우를 대비한 오버플로우(Overflow) 전략도 필요합니다.

단계적 대응

  1. 1단계 -- 요약: 오래된 메시지를 요약하여 공간 확보
  2. 2단계 -- 정리: 중요도가 낮은 메시지 제거
  3. 3단계 -- 절단: 최후의 수단으로 슬라이딩 윈도우 강제 적용
  4. 4단계 -- 오프로드: 장기 메모리 저장소로 이동 (3장에서 상세 설명)
Warning

컨텍스트 오버플로우 시 무조건 오래된 메시지를 자르는 것은 위험합니다. 초기 시스템 프롬프트나 핵심 지시사항이 유실될 수 있기 때문입니다. 항상 시스템 메시지는 보호 대상으로 지정하세요.


정리

이번 장에서 다룬 단기 메모리 관리 전략을 요약합니다.

  • 슬라이딩 윈도우는 가장 단순하지만 효과적인 기본 전략입니다
  • 메시지 요약은 오래된 정보를 압축하여 맥락을 보존합니다
  • 토큰 예산 관리는 컨텍스트 윈도우를 체계적으로 분배합니다
  • 중요도 기반 정리는 가치가 낮은 메시지를 선별적으로 제거합니다
  • 자기 요약은 매 턴마다 대화 상태를 자동 압축합니다
  • 오버플로우 전략은 예기치 못한 상황에 대한 안전장치입니다

다음 장 미리보기

3장에서는 장기 메모리와 벡터 기반 의미 검색을 다룹니다. 단기 메모리의 한계를 넘어, 세션을 넘어 지속되는 메모리를 벡터 데이터베이스에 저장하고 검색하는 방법을 살펴봅니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#ai#llm#ai-agent

관련 글

AI / ML

3장: 장기 메모리 — 벡터 기반 의미 검색

벡터 데이터베이스에 메모리를 저장하고 임베딩 기반으로 검색하는 장기 메모리 시스템의 설계와 구현 전략을 다룹니다.

2026년 3월 2일·16분
AI / ML

1장: AI 에이전트 메모리의 필요성과 핵심 개념

AI 에이전트가 왜 외부 메모리를 필요로 하는지, 컨텍스트 윈도우의 한계와 3가지 메모리 유형(단기/장기/에피소딕), 2026년 메모리 프레임워크 생태계를 개괄합니다.

2026년 2월 26일·14분
AI / ML

4장: 에피소딕 메모리 — 경험에서 학습하기

에이전트가 과거 상호작용을 에피소드로 기록하고, 경험 기반 의사결정과 패턴 학습에 활용하는 에피소딕 메모리 시스템을 다룹니다.

2026년 3월 4일·17분
이전 글1장: AI 에이전트 메모리의 필요성과 핵심 개념
다음 글3장: 장기 메모리 — 벡터 기반 의미 검색

댓글

목차

약 16분 남음
  • 학습 목표
  • 단기 메모리란 무엇인가
  • 슬라이딩 윈도우
    • 메시지 수 기반 vs 토큰 수 기반
  • 메시지 요약
    • 동작 원리
    • 요약 프롬프트 설계
    • 점진적 요약 vs 일괄 요약
  • 토큰 예산 관리
  • 중요도 기반 메시지 정리
    • 중요도 산정 기준
    • 선택적 제거 전략
  • 자기 요약 기법
  • 컨텍스트 오버플로우 전략
    • 단계적 대응
  • 정리
  • 다음 장 미리보기