AI Engineering Academy · 강의

컨텍스트 한도 안에서 작업하는 전략

긴 대화를 토큰 한도를 초과하지 않고 처리하도록 슬라이딩 윈도우 메모리, 메시지 요약, 선택적 컨텍스트 정리를 구현합니다.

레슨 4/413개 단계

컨텍스트 한도 안에서 작업하는 전략은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

대화가 길어지는 문제

채팅 대화에서 메시지를 주고받을 때마다 다음 API 호출의 토큰 수가 증가합니다. 긴 고객 지원 세션이나 여러 시간 동안 진행되는 코딩 세션에서는 누적된 대화 기록이 쉽게 20,000~50,000토큰을 초과할 수 있습니다. 이 토큰을 모두 매 차례 API에 보내야 하므로 이미 처리한 토큰에 대해서도 반복해서 비용을 지불하게 됩니다.

문맥 관리 전략이 없으면 애플리케이션이 문맥 한도에 도달해 중단되거나, 메시지를 조용히 잘라내 모델이 이전의 중요한 문맥을 파악하지 못하게 됩니다. 모든 실제 운영 LLM 애플리케이션에는 문맥 증가를 관리하기 위한 명시적인 전략이 필요합니다.

전략 1: 슬라이딩 윈도우(마지막 N개 메시지)

가장 간단한 전략은 문맥에 마지막 N개 메시지만 남기고 이전 메시지는 버리는 것입니다. 이를 슬라이딩 윈도우라고 합니다. 구현하기 쉽고 비용을 예측할 수 있으며, 오래된 대화보다 최근 문맥이 중요한 많은 사용 사례에 충분합니다.

import openai

client = openai.OpenAI()

class SlidingWindowConversation:
    def __init__(self, system_prompt, window_size=10):
        self.system = system_prompt
        self.window_size = window_size
        self.history = []

    def chat(self, user_message):
        self.history.append({'role': 'user', 'content': user_message})

        # Keep only the last N messages
        windowed = self.history[-self.window_size:]
        messages = [{'role': 'system', 'content': self.system}] + windowed

        response = client.chat.completions.create(
            model='gpt-4o-mini',
            messages=messages
        )
        reply = response.choices[0].message.content
        self.history.append({'role': 'assistant', 'content': reply})
        print(f'Context: {len(windowed)} messages ({len(self.history)} total)')
        return reply

conv = SlidingWindowConversation('You are a helpful assistant.', window_size=6)
print(conv.chat('Hello! My name is Alice.'))
print(conv.chat('What is the capital of France?'))
print(conv.chat('What is my name?'))  # Might forget if window is small

전략 2: 토큰을 고려한 잘라내기

메시지 개수로 자르는 대신, 토큰을 고려한 잘라내기는 전체 토큰 수가 임계값 아래로 내려갈 때까지 메시지를 제거합니다. 메시지마다 길이가 크게 다르므로 이 방법이 더 정확합니다. 메시지 개수 제한을 사용하면 짧은 메시지 10개가 포함될 수도 있고 매우 긴 메시지 3개가 포함될 수도 있어 토큰 비용이 크게 달라집니다.

import tiktoken

def trim_to_token_budget(
    messages, system_prompt, model='gpt-4o-mini', max_input_tokens=8000
):
    enc = tiktoken.encoding_for_model(model)

    def count(msgs):
        return sum(len(enc.encode(m.get('content',''))) + 4 for m in msgs) + 3

    # System prompt token count
    system_tokens = len(enc.encode(system_prompt)) + 4
    budget = max_input_tokens - system_tokens

    # Trim from the oldest messages until we fit
    trimmed = list(messages)
    while trimmed and count(trimmed) > budget:
        trimmed.pop(0)  # Remove oldest message

    removed = len(messages) - len(trimmed)
    if removed:
        print(f'Trimmed {removed} old messages to stay within {max_input_tokens} tokens')
    return trimmed

전략 3: 대화 요약

요약은 오래된 대화 차례를 핵심 사실, 결정 사항, 사용자가 언급한 문맥을 보존하면서 간결한 요약으로 압축합니다. 단순히 과거 대화를 버리는 것이 아니라 이전 대화의 핵심을 유지하므로 슬라이딩 윈도우보다 정교한 방법입니다.

방식은 다음과 같습니다. 대화가 임계값을 초과하면 가장 오래된 N개 대화 차례를 '지금까지의 대화를 요약하세요'라는 프롬프트와 함께 모델에 보내고, 해당 대화 차례를 요약이 포함된 하나의 시스템 메시지로 바꾼 다음, 새롭게 압축된 기록으로 대화를 계속합니다.

import openai

client = openai.OpenAI()

def summarize_conversation(messages_to_summarize, model='gpt-4o-mini'):
    summary_prompt = [
        {'role': 'system', 'content': 'You summarize conversations. Be concise but preserve key facts, decisions, and any specific information the user shared (names, numbers, preferences).'},
        {'role': 'user', 'content': 'Summarize this conversation:\n' + '\n'.join(
            f"{m['role'].upper()}: {m['content']}" for m in messages_to_summarize
        )}
    ]
    resp = client.chat.completions.create(model=model, messages=summary_prompt, max_tokens=500)
    return resp.choices[0].message.content

def compress_history(history, keep_recent=4):
    if len(history) <= keep_recent:
        return history
    to_summarize = history[:-keep_recent]
    summary = summarize_conversation(to_summarize)
    summary_msg = {'role': 'system', 'content': f'Earlier conversation summary: {summary}'}
    return [summary_msg] + history[-keep_recent:]

print('Summarization strategy compresses old turns into a single summary message')

전략 4: 엔터티 메모리

엔터티 메모리는 원시 메시지 기록을 저장하는 대신 대화에서 언급된 핵심 사실(사용자 선호, 이름, 프로젝트 세부 정보, 내려진 결정 등)을 구조화된 형태로 추출하고 유지합니다. 각 대화 차례 전에 저장된 사실을 시스템 프롬프트에 삽입합니다.

모든 대화 차례의 모든 단어가 아니라 의미상 중요한 내용만 포함하므로 전체 기록보다 토큰 효율이 높습니다. 엔터티 메모리는 사용자가 대화 초반에 설정한 선호와 사실을 나중에 다시 언급하는 장기 실행 보조 도구에 특히 효과적입니다.

import openai
import json

client = openai.OpenAI()

def extract_entities(messages, model='gpt-4o-mini'):
    prompt = [
        {'role': 'system', 'content': 'Extract key facts from this conversation as JSON: {"user_name": null, "preferences": [], "key_facts": []}'},
        {'role': 'user', 'content': '\n'.join(f"{m['role']}: {m['content']}" for m in messages)}
    ]
    resp = client.chat.completions.create(
        model=model,
        messages=prompt,
        response_format={'type': 'json_object'}
    )
    return json.loads(resp.choices[0].message.content)

conversation = [
    {'role': 'user', 'content': 'Hi, I am Alice and I prefer Python over JavaScript.'},
    {'role': 'assistant', 'content': 'Great to meet you, Alice! Python is an excellent choice.'},
    {'role': 'user', 'content': 'I am building a REST API for my e-commerce startup.'}
]
entities = extract_entities(conversation)
print(json.dumps(entities, indent=2))

각 전략을 적용할 시점

애플리케이션의 특성에 따라 전략을 선택하세요.

  • 슬라이딩 윈도우: 최근 문맥만 중요하고 사용자가 오래된 대화를 다시 언급하지 않는 단순한 채팅 앱에 적합합니다. 구현 비용이 가장 저렴합니다.
  • 토큰을 고려한 잘라내기: 메시지 길이의 차이가 큰 모든 실제 운영 앱에 적합합니다. 메시지 개수에 따른 잘라내기보다 항상 낫습니다.
  • 요약: 장기 실행 보조 도구, 고객 지원 세션, 프로젝트 관리 봇에 적합합니다. 사용자는 몇 시간 전의 대화에서 나온 핵심 사실을 보조 도구가 기억하기를 기대합니다.
  • 엔터티 메모리: 개인 보조 도구, 사용자 선호를 반영하는 챗봇, 세션 내내 사용자 프로필이 중요한 튜터링 시스템에 적합합니다.

이러한 전략은 함께 사용할 수도 있습니다. 핵심 사실에는 엔터티 메모리를 사용하고 최근 대화에는 슬라이딩 윈도우를 사용하세요.

문맥 채우기의 대안으로서의 RAG

지식 중심 애플리케이션에서 가장 좋은 문맥 관리 전략은 문서를 문맥에 아예 넣지 않는 것입니다. 대신 RAG (검색 증강 생성)을 사용하여 현재 질의와 관련된 특정 문장 조각만 검색하세요. 이렇게 하면 문맥을 집중된 상태로 유지하고 비용을 줄이며, 전체 문서를 삽입하는 것보다 더 나은 답변을 제공하는 경우가 많습니다.

핵심은 긴 문맥 창이 넣을 수 있는가?라는 문제는 해결하지만 모델이 이를 잘 활용할 것인가?라는 문제는 해결하지 못한다는 점입니다. 정확한 검색은 현재 질문에 실제로 필요한 정보만 모델에 제공하여 두 문제를 모두 해결합니다.

최대 문맥 오류를 우아하게 처리하기

최선을 다하더라도 실제 운영 환경에서는 문맥 한도 오류가 발생할 수 있으며, 특히 사용자가 생성한 콘텐츠가 예상보다 길 때 그렇습니다. context_length_exceeded 오류는 처리되지 않은 예외로 사용자에게 전달되도록 두지 말고 항상 명시적으로 처리하세요.

import openai
import tiktoken

client = openai.OpenAI()

def chat_with_context_guard(messages, model='gpt-4o-mini', max_tokens=100000):
    enc = tiktoken.encoding_for_model(model)
    total = sum(len(enc.encode(m.get('content',''))) + 4 for m in messages) + 3

    while total > max_tokens and len(messages) > 2:
        # Remove the second message (keep system prompt)
        removed = messages.pop(1)
        total -= len(enc.encode(removed.get('content',''))) + 4
        print(f'Trimmed a message. New total: {total} tokens')

    try:
        return client.chat.completions.create(model=model, messages=messages)
    except openai.BadRequestError as e:
        if 'context_length' in str(e):
            return {'error': 'Message history too long. Please start a new conversation.'}
        raise

세션 간 문맥 유지

실제 애플리케이션에서 사용자는 앱을 닫았다가 다시 엽니다. 서버가 재시작되기도 합니다. 문맥 관리는 단일 세션 내에서뿐 아니라 세션 간 지속성도 고려해야 합니다. 이를 위해 대화 기록을 데이터베이스에 저장하고 각 세션 시작 시 불러와야 합니다.

합리적인 방식은 다음과 같습니다. 감사 및 미세 조정을 위해 전체 원시 기록을 PostgreSQL에 저장하되, 새 API 호출을 위한 문맥을 불러올 때는 요약 또는 잘라내기 전략을 적용하여 토큰 예산에 맞추세요. 이렇게 하면 데이터를 잃지 않으면서도 모든 요청마다 전체 기록에 대한 비용을 지불하지 않을 수 있습니다.

실제 운영 환경에서 문맥 증가 모니터링

모든 API 호출의 토큰 수를 기록하고 대화 ID별로 시간에 따라 추적하세요. 정상적인 대화는 요약이 시작되면서 점진적으로 증가한 뒤 정체되는 양상을 보여야 합니다. 문맥 한도까지 제한 없이 증가하는 대화는 잘라내기 로직이 올바르게 작동하지 않는다는 뜻입니다.

또한 모든 대화의 평균 문맥 길이를 모니터링하세요. 시간이 지나면서 이 값이 증가한다면 기본 시스템 프롬프트가 기능 추가로 길어지고 있거나, 사용자가 더 긴 입력을 붙여 넣고 있거나, RAG가 점점 더 큰 문맥 조각을 반환하고 있다는 의미일 수 있습니다. 각각의 원인에는 서로 다른 해결 방법이 필요합니다.

전략 결합: 실제 운영 패턴

견고한 실제 운영용 문맥 관리 시스템은 여러 전략을 계층적으로 결합합니다.

  1. 문맥 구성 전: 토큰 예산을 확인하고 기록합니다
  2. 엔터티 추출 적용: 핵심 사실이 담긴 구조화된 메모리 블록을 삽입합니다
  3. 최근 기록 추가: 마지막 6~10개 메시지를 있는 그대로 포함합니다
  4. 오래된 기록 요약: 오래된 기록이 있으면 지속적으로 갱신되는 요약을 삽입합니다
  5. 보호 확인: 그래도 예산을 초과하면 있는 그대로 포함된 가장 오래된 메시지를 잘라냅니다

이 계층적 접근 방식은 가장 중요한 정보(엔터티 메모리와 최근 문맥)를 보존하면서 오래된 기록을 요약으로 점진적으로 축소하고, 최후의 수단으로만 잘라냅니다.

빠른 확인

이 단원에서 배운 AI 엔지니어링 개념을 이해했는지 확인해 보세요.

단원 요약

이 단원에서는 다음을 배웠습니다. 슬라이딩 윈도우와 토큰을 고려한 잘라내기는 한도 내에 머물기 위해 오래된 문맥을 버리는 간단한 전략입니다. 요약은 핵심 사실을 보존하면서 오래된 대화 차례를 간결한 형태로 압축합니다. 또한 엔터티 메모리는 대화 전체에서 토큰 효율적인 장기 기억을 위해 구조화된 사실을 추출합니다. 다음 단원에서는 JSON 모드와 구조화된 출력을 사용하여 LLM이 신뢰할 수 있는 JSON을 반환하도록 하는 방법을 살펴봅니다.

무료로 시작

AI 튜터와 함께 Python을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
30
레슨
120

자주 묻는 질문

“컨텍스트 한도 안에서 작업하는 전략” 강의는 무료인가요?

네 — “컨텍스트 한도 안에서 작업하는 전략” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“컨텍스트 한도 안에서 작업하는 전략”에서 뭘 배우나요?

긴 대화를 토큰 한도를 초과하지 않고 처리하도록 슬라이딩 윈도우 메모리, 메시지 요약, 선택적 컨텍스트 정리를 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“컨텍스트 한도 안에서 작업하는 전략” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 토큰이란 무엇인가
  2. 컨텍스트 윈도우: 크기와 의미
  3. API 비용 계산 및 예측
  4. 컨텍스트 한도 안에서 작업하는 전략
← AI Engineering Academy(으)로 돌아가기