AI 챗봇을 만들어 보면 짧은 대화는 잘 되다가, 대화가 길어질수록 앞에서 한 말을 잊거나 엉뚱한 답을 내놓는 현상을 마주합니다. 원인은 대부분 하나, 컨텍스트 윈도우 한계입니다. LLM은 한 번에 받아들일 수 있는 토큰 양이 정해져 있어, 대화가 그 한도를 넘으면 어떤 정보를 남기고 무엇을 버릴지 결정해야 합니다. 이 결정을 아무 전략 없이 “오래된 것부터 자르기”로 두면 정작 중요한 정보가 먼저 사라집니다. 이 글에서는 컨텍스트 윈도우의 작동 원리, 대화 이력을 요약·압축·슬라이딩 윈도우로 다루는 방법, 중요한 정보를 지키는 법, 토큰 예산을 배분하는 기준, 멀티턴에서 일관성을 유지하는 법, 그리고 장기 메모리를 저장하는 패턴까지 실무 관점에서 정리합니다.
컨텍스트 윈도우란 무엇인가
컨텍스트 윈도우는 LLM이 한 번의 호출에서 입력으로 받을 수 있는 토큰의 최대 개수입니다. 여기에는 시스템 프롬프트, 지금까지의 대화 이력, 사용자의 새 질문, 그리고 모델이 생성할 답변을 위한 여유 공간까지 모두 포함됩니다. 한도를 넘으면 호출이 거부되거나, 프레임워크가 임의로 앞부분을 잘라내 의도치 않게 정보가 사라집니다.
흔한 오해는 “컨텍스트가 큰 모델을 쓰면 관리는 필요 없다”는 생각입니다. 한도가 크면 여유는 늘지만, 문제가 사라지진 않습니다. 첫째, 컨텍스트가 길수록 토큰 비용과 응답 지연이 함께 커집니다. 둘째, 입력이 길어질수록 모델이 중간에 묻힌 정보를 놓치는 경향이 보고됩니다. 즉 가능한 한 많이 넣는 것이 아니라, 필요한 것을 골라 넣는 것이 핵심입니다. 모델별 정확한 컨텍스트 한도는 Anthropic 공식 문서(컨텍스트 윈도우) 같은 공식 문서에서 확인해야 합니다.
가장 단순한 출발 — 슬라이딩 윈도우
슬라이딩 윈도우는 최근 N개의 대화 턴만 유지하고 오래된 것은 버리는 방식입니다. 구현이 간단하고 토큰 사용을 일정하게 통제할 수 있어 기준선으로 삼기 좋습니다. 사용자가 직전 맥락을 이어 가는 단기 대화에서는 이것만으로도 충분히 자연스럽게 동작합니다.
def build_messages(system, history, user_input, keep_turns=8):
# 최근 keep_turns개 턴만 유지
recent = history[-keep_turns:]
return [system, *recent, user_input]한계는 분명합니다. 대화 초반에 사용자가 알려 준 이름·목표·제약 같은 정보가 윈도우 밖으로 밀려나면 챗봇이 그대로 잊습니다. “아까 말한 그거”가 통하지 않는 순간이 옵니다. 그래서 슬라이딩 윈도우는 단독으로 쓰기보다, 뒤에서 다룰 요약이나 메모리와 결합해 “최근 맥락은 윈도우로, 오래된 핵심은 다른 방식으로” 보관하는 조합이 일반적입니다.
대화 이력 요약과 압축
요약(summarization)은 오래된 대화 턴을 그대로 버리는 대신, 핵심만 압축한 요약문으로 바꿔 보관하는 방식입니다. 예를 들어 앞선 열 번의 주고받음을 “사용자는 서울 거주, 예산 200만 원 이내, 노트북을 찾는 중이며 게임은 안 함”처럼 몇 문장으로 줄여 시스템 영역에 넣어 둡니다. 토큰은 크게 절약되면서 핵심 맥락은 살아남습니다.
요약은 보통 두 가지 방식으로 운용합니다. 하나는 일정 길이를 넘을 때마다 오래된 부분을 한꺼번에 요약하는 방식이고, 다른 하나는 매 턴마다 기존 요약에 새 내용을 누적해 갱신하는 점진적 요약입니다. 점진적 방식은 매끄럽지만 요약을 위한 추가 LLM 호출이 들어 비용과 지연이 늘어납니다. 또 요약은 본질적으로 정보를 버리는 작업이라, 무엇을 남길지 지시하는 요약 프롬프트의 품질이 대화 일관성을 좌우합니다.
| 전략 | 토큰 효율 | 정보 보존 | 추가 비용 | 적합한 경우 |
|---|---|---|---|---|
| 슬라이딩 윈도우 | 높음 | 최근만 | 없음 | 단기 대화, 기준선 |
| 전체 이력 유지 | 낮음 | 완전 | 없음(토큰비 큼) | 짧은 세션, 한도 여유 |
| 요약·압축 | 중~높음 | 핵심 위주 | 요약 호출 | 긴 대화, 맥락 유지 필요 |
| 메모리+검색 | 높음 | 선택적 | 저장·검색 | 장기·다세션, 개인화 |
중요한 정보는 따로 지킨다
요약이든 슬라이딩이든, 절대 잃으면 안 되는 정보는 압축이나 삭제 대상에서 빼고 별도로 고정해야 합니다. 사용자의 이름, 진행 중인 작업의 목표, 명시한 제약(예산·기한·금지 사항), 확정된 결정 같은 항목이 여기 해당합니다. 이런 핵심 사실은 구조화된 형태로 추출해 시스템 프롬프트 영역에 항상 포함시키는 편이 안전합니다.
{
"user_profile": { "name": "지훈", "region": "서울" },
"task": { "goal": "노트북 추천", "budget": "200만원 이하" },
"constraints": ["게임 용도 아님", "무게 1.5kg 이하"],
"decisions": ["A모델 후보 제외"]
}이렇게 핵심 사실을 키-값 구조로 관리하면, 대화가 아무리 길어져도 이 블록만은 매 호출에 그대로 들어가 일관성이 유지됩니다. 대화 이력에서 새로운 확정 사실이 나올 때마다 이 구조를 갱신하는 작은 추출 단계를 두면, “기억해야 할 것”과 “흘려보내도 될 것”이 깔끔히 분리됩니다.
토큰 예산 배분하기
컨텍스트 관리는 결국 한정된 토큰을 어디에 쓸지 나누는 예산 문제입니다. 전체 한도에서 답변 생성용 여유를 먼저 떼어 두고, 나머지를 용도별로 배분하는 식으로 접근하면 한도 초과를 예방할 수 있습니다. 대략 다음과 같은 구획으로 나눕니다.
- 시스템 프롬프트 — 역할·규칙·말투 지시. 고정 비용이므로 군더더기를 줄여 둡니다.
- 핵심 사실 블록 — 위에서 만든 사용자 프로필·목표·제약. 항상 포함합니다.
- 대화 요약 — 오래된 이력의 압축본. 길이 상한을 정해 둡니다.
- 최근 대화 턴 — 슬라이딩 윈도우로 유지하는 직전 맥락.
- 검색된 참고 자료 — RAG로 가져온 문서 청크가 있다면 여기에 배정합니다.
- 답변 여유 공간 — 모델이 생성할 토큰. 미리 충분히 확보합니다.
각 구획에 상한을 정해 두고, 합이 한도를 넘으면 우선순위가 낮은 구획(보통 오래된 요약이나 참고 자료)부터 줄이는 규칙을 두면 안정적으로 운용됩니다. 토큰 수는 호출 전에 토크나이저로 미리 세어 한도를 넘지 않도록 점검하는 것이 좋습니다.
“무조건 다 넣기”는 비용과 정확도 양쪽에서 손해입니다.
컨텍스트 한도가 크다고 대화 전체와 모든 참고 문서를 매번 밀어 넣으면, 토큰 비용과 지연이 선형으로 늘고 모델이 핵심을 놓칠 위험도 커집니다. 긴 입력의 중간에 묻힌 정보가 답에 잘 반영되지 않는 경향이 보고됩니다. 필요한 것을 추려 넣는 편이 비용·속도·정확도 모두에 유리합니다.
멀티턴에서 일관성 유지하기
여러 턴에 걸쳐 대화가 이어질 때 일관성이 깨지는 흔한 원인은, 앞서 확정한 사실이 컨텍스트에서 사라졌거나 요약 과정에서 왜곡됐기 때문입니다. 이를 막으려면 몇 가지 원칙이 도움이 됩니다.
- 확정 사실은 구조화 보관 — 자유 텍스트로 흘려보내지 말고 키-값으로 추출해 매 호출에 포함합니다.
- 요약은 검증 가능하게 — 요약문에 사용자 원문의 핵심 표현을 보존해, 압축 과정의 의미 변형을 줄입니다.
- 모순 감지 — 새 입력이 기존 확정 사실과 충돌하면 덮어쓰지 말고, 사용자에게 확인하는 흐름을 둡니다.
- 참조 해소 — “그거”, “아까 그 모델” 같은 지시어가 무엇을 가리키는지 핵심 사실 블록으로 연결되게 합니다.
특히 작업형 챗봇(예약·주문·상담)에서는 일관성이 곧 신뢰입니다. 사용자가 앞서 말한 제약을 챗봇이 어기면 대화 전체가 무너지므로, 제약과 결정은 요약 대상에서 빼고 항상 원형 그대로 유지하는 편이 안전합니다.
장기 메모리 저장 패턴
한 세션을 넘어 사용자를 기억하려면, 컨텍스트 윈도우가 아니라 외부 저장소에 메모리를 두어야 합니다. 대표적인 패턴은 대화에서 추출한 사실을 데이터베이스나 벡터 DB에 저장해 두고, 다음 대화에서 관련 있는 것만 검색해 불러오는 방식입니다. 이는 RAG와 같은 원리로, 모든 과거 대화를 매번 넣는 대신 지금 대화에 필요한 기억만 골라 컨텍스트에 주입합니다.
| 메모리 유형 | 저장 위치 | 불러오는 방식 | 용도 |
|---|---|---|---|
| 단기(세션 내) | 대화 이력·요약 | 슬라이딩·요약 | 현재 대화 맥락 |
| 핵심 사실 | 구조화 저장(키-값) | 항상 포함 | 이름·목표·제약 |
| 장기(세션 간) | DB·벡터 DB | 관련성 검색 | 개인화·과거 이력 |
장기 메모리를 설계할 때는 무엇을 저장하고 무엇을 버릴지, 오래된 기억을 어떻게 만료시킬지, 개인정보를 어떻게 보호할지를 함께 정해야 합니다. 모든 발화를 무한정 쌓으면 검색 품질이 떨어지고 저장 비용이 늘며, 민감 정보 보관에 대한 책임도 커집니다. “기억할 가치가 있는 사실만 선별해 저장하고, 필요할 때만 불러온다”는 원칙이 장기 메모리의 핵심입니다.
적용 체크리스트
- 슬라이딩 윈도우를 기준선으로 두고, 긴 대화엔 요약을 결합했는가
- 이름·목표·제약 같은 핵심 사실을 구조화해 매 호출에 포함했는가
- 토큰 예산을 구획별로 나누고 상한·우선순위를 정했는가
- 호출 전 토크나이저로 토큰 수를 세어 한도 초과를 막았는가
- 요약이 핵심 표현을 보존하도록 요약 프롬프트를 점검했는가
- 새 입력과 기존 확정 사실의 모순을 감지·확인하는 흐름이 있는가
- 세션을 넘는 기억은 외부 저장 후 관련성 검색으로 불러오는가
- 장기 메모리의 만료·개인정보 보호 정책을 정의했는가
자주 묻는 질문
- 컨텍스트가 큰 모델을 쓰면 관리가 필요 없나요?
- 여유는 늘지만 필요는 사라지지 않습니다. 입력이 길수록 토큰 비용·지연이 커지고, 중간에 묻힌 정보를 모델이 놓치는 경향도 있습니다. 한도가 커도 “필요한 것을 골라 넣는” 관리는 여전히 효과적입니다.
- 요약과 슬라이딩 윈도우 중 무엇이 낫나요?
- 둘은 경쟁이 아니라 보완 관계입니다. 최근 맥락은 슬라이딩 윈도우로 정밀하게 유지하고, 오래된 이력은 요약으로 압축해 핵심만 남기는 조합이 긴 대화에서 안정적입니다.
- 중요한 정보가 자꾸 잊히는데 어떻게 막나요?
- 이름·목표·제약·결정 같은 핵심 사실을 자유 텍스트로 두지 말고 키-값으로 추출해, 요약·삭제 대상에서 빼고 매 호출에 항상 포함하세요. 이 블록은 대화가 길어져도 보존됩니다.
- 토큰 수는 어떻게 관리하나요?
- 전체 한도에서 답변용 여유를 먼저 떼고, 시스템·핵심 사실·요약·최근 턴·참고 자료에 상한을 배분합니다. 호출 전 토크나이저로 합계를 세어 한도를 넘으면 우선순위가 낮은 구획부터 줄입니다.
- 세션이 끝나도 사용자를 기억하게 하려면요?
- 컨텍스트 윈도우가 아니라 외부 저장소(DB·벡터 DB)에 핵심 사실을 저장하고, 다음 대화에서 관련 있는 기억만 검색해 불러오세요. 모든 과거 대화를 매번 넣지 말고 필요한 것만 주입하는 것이 비용·품질 모두에 유리합니다.
이 글은 AI 챗봇의 대화 컨텍스트를 관리하는 일반적인 전략과 패턴을 정리한 기술 참고 자료입니다. 모델별 컨텍스트 윈도우 한도, 토큰 단가, 긴 입력에서의 동작 특성은 모델과 시점에 따라 달라지므로, 구현 전에 사용하는 LLM 제공사의 공식 문서를 확인하시기 바랍니다. 본문의 코드와 데이터 구조는 개념 설명용 예시입니다.