본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 14:09

AI 어시스턴트를 포기할 뻔했습니다 — 컨텍스트 처리(Context Handling) 문제를 해결한 방법

요약

개인용 AI 어시스턴트 개발 중 발생하는 토큰 제한 및 컨텍스트 소실 문제를 해결하기 위한 계층적 컨텍스트 관리 방법을 소개합니다. 최근 메시지는 원문 그대로 유지하고, 이전 대화는 주기적으로 요약하여 전달하는 효율적인 구조를 제안합니다.

핵심 포인트

  • 단순 메시지 누적 방식은 토큰 제한과 비용 문제 발생
  • 슬라이딩 윈도우 방식은 장기 기억 상실 문제 유발
  • 계층적 컨텍스트 관리: 최근 메시지 원문 + 이전 히스토리 요약 조합
  • 임계값 기반의 요약 트리거를 통해 지연 시간과 비용 최적화

지난 몇 달 동안 개인용 AI 어시스턴트를 만들어 왔습니다. 여러분도 아는 그런 종류 말이죠. 대화를 나누면 사용자가 말한 내용을 기억하고, 이메일 요약, 노트에 대한 질문 답변, 혹은 단순히 의견을 나누는 도구(sounding board)로서 작업을 도와주는 그런 것 말입니다.

처음에는 주말 프로젝트로 시작했습니다. 몇 개의 Python 스크립트, OpenAI 호환 API 엔드포인트, 그리고 터미널에서의 간단한 루프가 전부였습니다. 저는 자만했습니다. "봐, 내가 AI를 만들었어!"라고 말이죠. 하지만 곧 상황이 나빠졌습니다.

대화가 길어지기 시작하는 순간, 봇은 쓸모없어졌습니다. 세 메시지 전에 했던 말을 잊어버리거나, 스스로 모순된 말을 하거나, 똑같은 조언을 반복하기 시작했습니다. API에 점점 더 많은 토큰(tokens)을 쏟아붓고 있었고, 제 지갑은 울고 있었습니다. 무언가 바뀌어야 했습니다.

순진한 접근 방식 (그리고 그것이 실패한 이유)

저의 첫 번째 시도는 아주 단순했습니다. 단순히 모든 새로운 메시지를 리스트에 추가하고, 전체 이력을 messages 배열로 API에 보내는 것이었습니다. 약 10번의 대화까지는 작동했습니다... 그러다 토큰 제한(token limits)이 걸렸습니다. API가 가장 오래된 메시지를 잘라내기 시작하면서 대화의 흐름이 깨졌습니다.

슬라이딩 윈도우(sliding window) 방식도 시도해 보았습니다. 마지막 N개의 메시지만 유지하는 방식이죠. 이전보다 나았지만, 어시스턴트가 장기적인 컨텍스트(long-term context)를 잃어버렸습니다. 만약 제가 "어제 언급했던 그 책을 다시 알려줘"라고 물으면, 봇은 전혀 알지 못했습니다. 저는 본질적으로 몇 번의 대화마다 제 봇의 뇌를 절제(lobotomizing)하고 있었던 셈입니다.

또 다른 막다른 길은 매 턴마다 대화의 이전 부분을 요약하는 것이었습니다. 기술적으로는 작동했지만, 지연 시간(latency)과 비용이 추가되었습니다. 매 턴마다 전체 이력을 다시 요약해야 했습니다. 지속 가능하지 않았습니다.

필요했던 것 (하지만 이름은 몰랐던 것)

저에게는 다음과 같은 기능을 수행할 수 있는 시스템이 필요했습니다:

  • 가장 최근의 N개 메시지를 온전하게 유지 (정확한 응답을 위해)
  • 대화의 이전 부분에 대한 압축된 요약(compressed summary) 유지
  • 언제 요약을 할지, 언제 원문 메시지(raw messages)를 전달할지를 자동으로 결정
  • 값비싼 벡터 데이터베이스(vector database)나 파인튜닝(fine-tuning) 없이 작동

이것은 대화형 AI(conversational AI)에서 잘 알려진 패턴인 **계층적 컨텍스트 관리(hierarchical context management)**로 밝혀졌습니다. 당시에는 그 이름을 몰랐을 뿐입니다.

마침내 해답을 찾은 접근 방식

하이레벨 디자인(high-level design)은 다음과 같습니다:

[Messages]
  ├─ 최근 메시지 (최근 5-10개 메시지) → API에 원문 그대로 전달
  └─ 이전 히스토리 → 주기적으로 정적 요약 문자열(static summary string)로 요약

핵심적인 통찰은 모든 메시지마다 요약을 수행할 필요가 없다는 점입니다. 대화 내용이 충분히 커져서 중요한 콘텐츠를 밀어낼 정도가 되었을 때만 요약을 교체(rotate)하면 됩니다. 저의 사용 사례에서는 임계값(threshold)을 설정했습니다. 최근 윈도우(recent window)가 6개를 초과하고, 해당 윈도우 내의 가장 오래된 메시지가 X분보다 더 오래되었을 때 요약을 트리거합니다.

이를 구현한 Python 클래스는 다음과 같습니다:

import time
from typing import List, Dict, Optional

...

이 클래스는 API로 전송할 컨텍스트 배열(context array)을 구축합니다. 이제 시스템 프롬프트(system prompt)에는 압축된 요약이 포함되며, 최근 메시지들은 원문 그대로 유지됩니다. 트레이드오프(trade-off)가 있냐고요? 요약 과정에서 뉘앙스(nuance)를 놓칠 수 있습니다. 하지만 90%의 사용 사례에서는 충분히 효과적입니다.

API 호출을 포함한 실제 코드

이를 실제 OpenAI 호환 API에 연결하는 방법은 다음과 같습니다 (제 설정에서는 ai.interwestinfo.com의 엔드포인트를 사용했습니다):

import openai

context = ContextManager(max_recent=6)
...

이 패턴은 저에게 효과적이었습니다. 이제 봇은 10분 전의 핵심 사항들을 기억하며, 저는 토큰(tokens) 비용으로 파산하지도 않습니다.

배운 점과 트레이드오프(trade-offs)

  • 요약 품질이 중요합니다 (Summarization quality matters). 만약 요약기(summarizer)가 형편없다면(제 단순한 절단 방식처럼), 어시스턴트는 컨텍스트(context)를 잘못 해석할 것입니다. 나중에 저는 요약을 위해 전용으로 파인튜닝(fine-tuned)된 모델로 교체했으며, 이를 통해 재현율(recall)이 크게 향상되었습니다.
  • 시간 기반 트리거링은 취약합니다 (Time-based triggering is fragile). 활동이 한 시간 동안 없으면 요약 내용이 오래된 정보(stale)가 될 수 있습니다. 그래서 저는 체크 로직을 추가했습니다: 마지막 요약이 5분보다 오래되었고 최근 버퍼(buffer)가 가득 찼다면, 다시 요약합니다.
  • 실시간 질의응답(Q&A)에는 적합하지 않습니다. 모든 이전 메시지를 정확하게 재현해야 하는 경우(예: 법률 또는 의료 분야), 이 방식은 정보를 손실합니다. 이럴 때는 전체 벡터 데이터베이스(vector database)가 필요할 것입니다.
  • 조절 가능한 트레이드오프(trade-off)입니다. max_recent, 요약 임계값(threshold), 그리고 요약 길이는 모두 사용자가 조절할 수 있는 노브(knobs)입니다. 처음에는 작게 시작하여 품질과 비용의 균형이 맞을 때까지 점진적으로 늘려가세요.

다음에 다시 한다면 다르게 할 점

만약 처음부터 다시 시작한다면, 저는 요약 단계를 비동기 백그라운드 작업(async background job)으로 구축할 것입니다. 현재는 _maybe_summarize 호출이 트리거될 때 메인 스레드(main thread)를 차단(block)합니다. CLI 어시스턴트에게는 큰 문제가 아니지만, 많은 동시 사용자가 있는 웹 앱에서는 문제가 됩니다.

또한, 모델의 토큰 제한(token limit)에 맞춰 요약 길이를 미리 검증(pre-validate)할 것입니다. 현재 버전에서는 요약이 시스템 프롬프트(system prompt) 슬롯을 초과하여 커질 수 있으며, 이로 인해 API가 최근 메시지들을 잘라버리는(truncate) 현상이 발생합니다. 토큰 예산(token budget)을 강제할 필요가 있습니다.

마지막으로, 데이터베이스와의 동기화를 명시적으로 처리할 것입니다. 현재 컨텍스트는 인메모리(in-memory) 방식입니다. 서버가 재시작되면 어시스턴트는 모든 것을 잊어버립니다. 간단한 Redis 저장소를 사용하면 이 문제를 해결할 수 있습니다.

여러분은 컨텍스트를 어떻게 처리하시나요?

다른 개발자분들은 이 문제를 어떻게 해결하는지 궁금합니다. 고정된 토큰 윈도우(token window)를 사용하시나요? 벡터 스토어(vector store)를 사용하시나요? 아니면 모델의 내부 메모리에 의존하시나요(그에 따른 비용을 지불하면서)? 댓글로 알려주세요. 의견을 나누고 싶습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0