Agentic AI/Memory

2. [Summary Memory] 대화 이력 슬라이딩 윈도우 및 자동 요약 압축 시스템

아톨 2026. 8. 31. 16:26
반응형

이전 코드에서 한 단계 더 발전하여 대화 개수 기준(슬라이딩 윈도우)을 적용해 대화 이력(History)을 자동으로 잘라내고 요약 및 압축하는 완성형 파이썬 스크립트입니다.

 

  • #History_Management (대화 이력 관리): 대화형 AI가 이전 맥락을 기억하도록 대화 목록을 저장하고 전달하는 메커니즘입니다.
  • #Sliding_Window (슬라이딩 윈도우): 전체 대화 중 최근 $N$개의 메시지만 최신 상태로 유지하고, 나머지 오래된 메시지는 분리해내는 기법입니다.
  • #Context_Compression (컨텍스트 압축): 오래된 대화들을 LLM에 전달해 3줄 요약문으로 만든 뒤 system 메시지로 변환하여 전체 토큰 수를 대폭 줄이는 기술입니다.
  • #List_Slicing (리스트 슬라이싱): 파이썬의 messages[-4:]나 messages[:-4]처럼 목록의 특정 부분만 잘라내는 언어 특성 기능입니다.

 

1. 환경 설정 및 상수 정의

load_dotenv()
API_KEY = os.getenv("OPENAI_API_KEY")
MODEL_NAME = os.environ.get("MODEL_NAME", "gpt-5.4-mini")

MAX_HISTORY_MESSAGES = 10
KEEP_RECENT_MESSAGES = 4
  • MAX_HISTORY_MESSAGES = 10: 전체 메시지 개수가 10개를 초과할 때만 압축을 시작하도록 설정하는 임계값(Threshold)입니다.
  • KEEP_RECENT_MESSAGES = 4: 압축을 진행하더라도 가장 최근에 주고받은 4개의 메시지는 요약하지 않고 원본 그대로 유지합니다.

2. OpenAI 클라이언트 생성

client = OpenAI(api_key = API_KEY)
  • 환경 변수에서 가져온 API 키를 전달해 OpenAI 통신용 클라이언트 객체를 생성합니다.

3. 오래된 대화 요약 함수 (summarize_old_messages)

def summarize_old_messages(old_messages):
    """오래된 대화 이력을 모델이 3줄 이내로 압축한다."""

    # 메시지 리스트를 하나의 문자열로 변환
    conversation_text = "\n".join(
        f"{m['role'].upper()}: {m['content']}" 
        for m in old_messages
    )
    print("\n[요약 전 대화]")
    print(conversation_text)

    # OpenAI에게 요약 요청
    response = client.responses.create(
        model = MODEL_NAME,
        input = [
            {
                "role":"system",
                "content":(
                    "아래 대화의 핵심 내용을 3줄 이내로 압축 요약하세요."
                    "사용자가 언급한 상황, 방향 또는 중요한 요청 사항을 중심으로 정리하세요."
                ),
            },
            {"role":"user", "content":conversation_text}
        ],
    )
    return response.output_text
  • 분리된 오래된 메시지 목록(old_messages)을 하나로 묶어 USER: ... \n ASSISTANT: ... 형태의 텍스트로 가공한 후, client.responses.create를 호출해 3줄 이내 요약 결과를 반환받습니다.

4. History 압축 핵심 로직 (compress_history) 코드에서 가장 중요한 대화 분질 및 재조합 단계입니다.

def compress_history(messages):
    # ① 검사: 대화가 아직 짧다면 압축하지 않음
    if len(messages) <= MAX_HISTORY_MESSAGES:
        print("\nHistory 압축 필요 없음")

    # ② 분리: 최근 4개와 오래된 이전 대화 나누기 (리스트 슬라이싱)
    recent_messages = messages[-KEEP_RECENT_MESSAGES:]
    old_messages = messages[:-KEEP_RECENT_MESSAGES]

    # ③ 요약: 오래된 대화만 요약 실행
    summary = summarize_old_messages(old_messages)

    # ④ 시스템 메시지화: 요약본을 system 역할 딕셔너리로 변환
    summary_message = {
        "role": "system", 
        "content": (
            "[이전 대화 요약]\n"
            f"{summary}"
        )
    }

    # ⑤ 재조합: [요약 시스템 메시지] + [최근 메시지 4개]
    new_messages = [summary_message] + recent_messages
    return new_messages
  • 슬라이싱 기법: messages[-4:]는 뒤에서부터 4개를 추출하고, messages[:-4]는 끝 4개를 제외한 나머지 전체를 추출합니다.
  • 최종 재구성: 총 12개의 긴 대화 목록이 [이전 대화 요약 1개] + [최근 대화 4개] = 총 5개의 효율적인 데이터 구조로 압축됩니다.

5 ~ 7. 테스트 데이터 실행 및 결과 출력

messages = [

    {
        "role": "user",
        "content": "나는 AI Agent를 공부하고 있어."
    },

    {
        "role": "assistant",
        "content": "Python 기반 Agent부터 시작하면 좋습니다."
    },

    {
        "role": "user",
        "content": "Tool Calling도 배우고 싶어."
    },

    {
        "role": "assistant",
        "content": "계산기와 파일 읽기 같은 Tool을 연결할 수 있습니다."
    },

    {
        "role": "user",
        "content": "대화 기록이 계속 길어지는 것이 걱정돼."
    },

    {
        "role": "assistant",
        "content": "오래된 대화를 요약하는 방법을 사용할 수 있습니다."
    },

    {
        "role": "user",
        "content": "나는 코드 설명을 자세하게 듣는 것을 좋아해."
    },

    {
        "role": "assistant",
        "content": "각 코드의 흐름과 구조를 친절하게 설명하겠습니다."
    },

    {
        "role": "user",
        "content": "이제 실제 OpenAI API와 연결하고 싶어."
    },

    {
        "role": "assistant",
        "content": "Responses API를 사용하면 됩니다."
    },

    {
        "role": "user",
        "content": "그리고 Retry 기능도 넣고 싶어."
    },

    {
        "role": "assistant",
        "content": "API 오류 발생 시 재시도하는 구조를 만들 수 있습니다."
    },
]

compressed_messages = compress_history(messages)

print("\n")
print("=" * 60)
print("압축 전 메시지 수: ", len(messages))
print("압축 후 메시지 수: ", len(compressed_messages))
print("=" * 60)

for i, message in enumerate(compressed_messages):
    print("\n" + "-"*60)

    print(f"[{i}] ROLE")
    print(message["role"])

    print("\nCONTENT")
    print(message["content"])
  • 전체 대화 메시지 수가 12개이므로 MAX_HISTORY_MESSAGES(10) 기준을 넘어서 압축 로직이 작동합니다.
  • 결과적으로 압축 전 12개에서 압축 후 5개로 줄어든 메시지 목록과 각 역할별 내용이 콘솔에 출력됩니다.

 

 

반응형