본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 27. 10:14

나의 AI 어시스턴트가 아무것도 기억하지 못했던 이유와 해결 방법

요약

AI 어시스턴트 구축 시 발생하는 컨텍스트 유지 문제를 해결하기 위한 방법을 다룹니다. 단순 대화 기록 누적이나 요약 방식의 한계를 지적하며, RAG 패턴을 활용한 검색 증강 메모리(Retrieval-Augmented Memory) 도입을 해결책으로 제시합니다.

핵심 포인트

  • 단순 대화 기록 누적은 토큰 제한 및 컨텍스트 상실 문제를 야기함
  • 대화 요약 방식은 세부 정보 누락과 요약본의 길이 증가 문제가 있음
  • 검색 증강 메모리(Retrieval-Augmented Memory)를 통해 관련 대화만 검색하여 활용 가능
  • 대화 내용을 임베딩하여 벡터 데이터베이스에 저장하고 유사도 기반으로 검색하는 구조 권장

저에게 문제가 하나 있었습니다. 저는 우리 팀의 내부 문서—API, 배포 가이드, 그리고 모범 사례(best practices)에 관한 질문에 답할 수 있는 AI 어시스턴트를 구축했습니다. 처음 몇 번의 상호작용은 마법 같았습니다. 하지만 곧 어시스턴트가 무언가를 잊기 시작했습니다. 질문에 올바르게 답했다가도, 다섯 메시지 뒤에는 앞서 한 말과 모순되는 답변을 내놓곤 했습니다. 마치 아주 똑똑한 금붕어와 대화하는 것 같았습니다.

저는 주말 내내 머리를 쥐어뜯으며 다양한 해결책을 시도했습니다. 제가 배운 내용이 여러분의 골칫거리를 덜어줄 수도 있을 것입니다.

문제: 컨텍스트 (context)는 확장되지 않는다

저는 단순한(naive) 방식을 사용하고 있었습니다. 대화 기록 전체를 시스템 프롬프트 (system prompt)에 그냥 집어넣는 방식이었습니다. 새로운 사용자 메시지가 올 때마다, 이전의 어시스턴트 응답을 추가하여 전체를 다시 제출했습니다. 2~3회 정도의 대화(turn)까지는 잘 작동했습니다. 하지만 그 이후에는 토큰 제한 (token limit)에 걸리거나, 모델이 이전의 컨텍스트 (context)를 놓치기 시작했습니다.

제 코드는 대략 다음과 같았습니다:

import openai

messages = [{"role": "system", "content": "You are a helpful assistant for our docs."}]
...

단순하지만 실패할 수밖에 없는 구조였습니다. messages 리스트가 무제한으로 커졌기 때문입니다. 머지않아 gpt-3.5-turbo의 4K 토큰 윈도우 (token window)에 도달하게 되었습니다. 그리고 그 전이라도 모델은 환각 (hallucinating)을 일으키거나 이전의 지침을 잊기 시작했습니다.

시도했지만 효과가 없었던 방법들

1. 히스토리 (history) 자르기

마지막 N개의 메시지만 유지하는 방식을 시도했습니다. 하지만 그러면 어시스턴트가 초기 컨텍스트 (예: "우리 API는 RESTful입니다")를 놓치게 되었습니다. 만약 사용자가 10회 전의 대화 내용에 의존하는 질문을 던지면, 그 내용은 사라진 상태였습니다.

2. 주기적으로 대화 요약하기

GPT를 별도로 호출하여 대화를 짧은 단락으로 요약하고, 기존 히스토리를 그 요약본으로 교체하는 방식을 사용했습니다. 이 방법은 작동했습니다... 하지만 대화가 여러 차례 진행된 후에는 요약본 자체도 너무 길어지기 시작했습니다. 또한, 요약은 세부 사항을 놓칩니다. 제 어시스턴트는 이전에 언급된 구체적인 설정 옵션들을 기억해내지 못했습니다.

3. 더 큰 모델 (gpt-4) 사용하기

더 많은 토큰은 더 많은 비용을 의미합니다. 게다가 gpt-4는 8K 제한이 있으며, 이 역시 여전히 유한합니다. (복잡한 문제를 디버깅하는 것과 같은) 긴 대화의 경우, 결국 한계에 부딪히게 됩니다. 또한, 팀 도구로 사용하기에는 비용이 많이 들었습니다.

마침내 해결책이 된 것: 검색 증강 메모리 (retrieval-augmented memory)

저는 모든 것을 단일 프롬프트 (single prompt)에 담으려 했던 것이 문제였다는 것을 깨달았습니다. 대신, 대화 내용을 외부 메모리에 저장하고 각 턴 (turn)마다 관련 있는 부분만 검색하여 가져와야 했습니다.

이는 본질적으로 RAG (Retrieval-Augmented Generation, 검색 증강 생성)에서 사용되는 것과 동일한 패턴이지만, 정적인 문서 저장소 대신 대화 기록에 적용한 것입니다.

핵심 아이디어:

  • 어시스턴트가 응답할 때마다 사용자의 질문과 어시스턴트의 답변(또는 중요한 부분만)을 모두 임베딩 (embed) 합니다.
  • 해당 임베딩들을 벡터 데이터베이스 (vector database)에 저장합니다.
  • 새로운 쿼리 (query)가 들어올 때마다 코사인 유사도 (cosine similarity)를 통해 가장 관련성이 높은 과거 상호작용을 검색합니다.
  • 검색된 조각들을 추가적인 컨텍스트 (context)로서 프롬프트에 삽입합니다.

이 방식을 통해 모델은 전체 대화가 아닌, 가장 적절한 과거 기록의 작은 창 (window)만을 전달받게 됩니다. 이는 어시스턴트에게 전체 로그를 다시 재생해 주는 대신, 기억의 색인 (index)을 제공하는 것과 같습니다.

구현: 최소 기능의 메모리 매니저 (memory manager)

저는 벡터 저장소로 FAISS (설정이 빠름)를 사용했고, 임베딩을 생성하기 위해 OpenAI의 text-embedding-ada-002를 사용했습니다. 제가 구축한 기능의 단순화된 버전은 다음과 같습니다:

import openai
import faiss
import numpy as np
...

그리고 이를 어시스턴트 루프 (loop)에 통합한 방법은 다음과 같습니다:

memory = MemoryManager()

def ask_with_memory(question):
...

이 접근 방식은 망각 문제를 해결했습니다. 이제 어시스턴트는 30회 이상의 턴이 지난 후에도 대화 초반의 세부 사항을 일관되게 기억합니다. 토큰 소비량은 대략 일정하게 유지됩니다. 각 쿼리에는 상위 3개의 관련 메모리와 현재 질문만 포함되기 때문입니다.

교훈 및 트레이드오프 (trade-offs)

  • 검색(Retrieval)은 완벽하지 않습니다. 사용자가 과거의 메모리와 임베딩(embedding) 유사도가 낮은 질문을 던지면, 어시스턴트는 여전히 잊어버릴 수 있습니다. 저는 긴 대화의 경우 k 값을 5로 늘려 이를 완화했지만, 그만큼 프롬프트(prompt)가 길어집니다.
  • 저장 비용. 모든 상호작용을 임베딩하는 것은 지연 시간(latency)을 추가합니다 (OpenAI 사용 시 임베딩당 약 100ms). 실시간 채팅을 위해 임베딩을 배치(batch) 처리하거나 캐싱(cache)해야 했습니다. 로컬 임베딩 모델(예: sentence-transformers)을 사용하는 대안은 더 느리지만 비용은 저렴합니다.
  • 메모리 가지치기(Memory pruning). FAISS 인덱스는 시간이 지남에 따라 커집니다. 저는 최대 메모리 크기(500개 항목)를 설정하고, 가득 차면 가장 오래된 항목을 삭제하도록 했습니다. 투박한 LRU(Least Recently Used) 방식이지만 효과는 있습니다.
  • 사용자가 주제에서 벗어난 질문을 한다면? 검색 결과로 관련 없는 과거 채팅이 반환될 수 있습니다. 저는 점수가 낮은 결과는 버리도록 유사도 임계값(similarity threshold)을 설정했습니다.

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

토큰 제한(token limits)을 쫓아다니는 대신, 처음부터 이 패턴으로 시작했더라면 좋았을 것입니다. 또한:

  • Chroma나 Pinecone과 같은 전용 벡터 데이터베이스(vector database)를 사용하겠습니다 (더 나은 영속성 제공).
  • 타임스탬프(timestamp)를 저장하여 모델이 최신성을 신호(signal)로 활용하게 하겠습니다.
  • 다양한 텍스트 청킹(text chunking) 방식을 실험하겠습니다. 전체 Q/A 쌍을 저장하는 대신, 질문과 답변을 별도의 메모리로 분리한다면 검색 정밀도(retrieval precision)를 높일 수 있을지도 모릅니다.

호스팅된 솔루션은 어떤가요?

이러한 종류의 메모리 관리를 즉시 처리해 주는 서비스들이 있습니다. 예를 들어, Interwest AI의 어시스턴트 플랫폼은 내부적으로 유사한 검색 기반 메모리를 사용합니다. 하지만 솔직히 말해서, 직접 구축해 봄으로써 트레이드오프(trade-offs)를 깊이 있게 배울 수 있었습니다. 프로덕션 앱을 확장하는 중이라면 관리형 솔루션(managed solution)을 고려하십시오. 하지만 사이드 프로젝트나 내부 도구라면 FAISS로 직접 구현하는 것도 완전히 괜찮은 방법입니다.

요점 (The takeaway)

AI 어시스턴트가 모든 것을 기억할 필요는 없습니다. 그저 적절한 것을 기억하면 됩니다. 컨텍스트 검색(Context retrieval)은 언제나 컨텍스트 채워넣기(Context stuffing)보다 우월합니다.

LLM (Large Language Model)과의 긴 대화를 처리하기 위해 어떤 설정을 사용하시나요? 비슷한 메모리 접근 방식을 시도해 보셨나요, 아니면 완전히 다른 방식을 사용하시나요? 여러분의 경험을 댓글로 남겨주세요. 함께 의견을 나누고 싶습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0