긴 상담을 버티는 음성 에이전트: 동적 컨텍스트 윈도우 관리
짧은 문의는 매끄럽게 처리하던 음성 에이전트가 통화가 10분, 20분 길어지면 눈에 띄게 흔들립니다. 답이 느려지고, 앞에서 분명히 말한 내용을 다시 묻고, 어떤 구간에서는 처리 비용이 부쩍 뜁니다. 모델을 바꿔도 잘 낫지 않는데, 원인은 모델의 머리가 나빠서가 아니라 통화 내내 쌓인 대화를 '어떻게 모델에게 들려줄 것인가'를 설계하지 않았기 때문입니다. 긴 상담을 끝까지 또렷하게 끌고 가려면, 대화 이력을 매 턴 통째로 밀어 넣는 대신 동적으로 관리해야 합니다.
[시리즈] S2S·LLM 음성 에이전트 엔지니어링 63편 · 현재 4-1편
← 이전: 3-5 실시간 세션 관리 — 통화 맥락·고객 데이터 / 다음 → 4-2 WebRTC·WebSocket 초저지연 음성
- 3-4 통화 중 외부 API 도구 호출
- 3-5 실시간 세션 관리 — 통화 맥락·고객 데이터
- 4-1 동적 컨텍스트 윈도우 관리 (현재 글)
- 4-2 WebRTC·WebSocket 초저지연 음성
- 4-3 비동기 이벤트 루프와 동시 콜 처리
전체 목차 펼치기 (63편)
Part 1 — 음성 AI가 느린 이유: 레이턴시 진단 (4편)
- 1-1 AI 보이스봇이 답답한 이유 — Cascaded 레이턴시
- 1-2 1초의 벽 — 지연이 UX를 결정하는 이유
- 1-3 지연 줄이기 — 스트리밍·청크로 TTFT 단축
- 1-4 RAG 병목과 오디오 버퍼링 제어
Part 2 — 텍스트를 넘어: End-to-End S2S의 원리 (4편)
- 2-1 End-to-End S2S 작동 원리
- 2-2 OpenAI Realtime API vs 오픈소스 S2S
- 2-3 음성 토큰(Audio Token)이란
- 2-4 감정·톤 조절이 CS를 바꾸는 이유
Part 3 — 실시간 대화와 에이전트화: 인터럽트·MCP·도구 (5편)
- 3-1 실시간 인터럽트(끼어들기) 감지
- 3-2 실시간 음성에 MCP가 필요한 이유
- 3-3 스트리밍 중 MCP 초고속 데이터 룩업
- 3-4 통화 중 외부 API 도구 호출
- 3-5 실시간 세션 관리 — 통화 맥락·고객 데이터
Part 4 — 인프라·세션·보안: 프로덕션 백엔드 (5편)
- 4-1 동적 컨텍스트 윈도우 관리 (현재 글)
- 4-2 WebRTC·WebSocket 초저지연 음성
- 4-3 비동기 이벤트 루프와 동시 콜 처리
- 4-4 트래픽 스파이크를 견디는 확장성 설계
- 4-5 실시간 음성 보안 — 암호화·PII 마스킹
Part 5 — 운영·품질·평가: 에이전틱 워크플로우와 신뢰성 (7편)
- 5-1 에이전틱 워크플로우 — 응대를 넘어 완결로
- 5-2 상용화의 한계와 트러블슈팅
- 5-3 보이스 에이전트 평가(Eval) 설계
- 5-4 관측성(Observability) — 어디서 끊기나
- 5-5 가드레일과 환각 제어
- 5-6 상담사 전환(Handoff) 설계
- 5-7 프롬프트 A/B 테스트와 튜닝
Part 6 — 음성 경험 고도화·실전 통합: 한국어·UX·전화망·엣지 (5편)
- 6-1 한국어 음성 AI의 난관 — 존댓말·코드스위칭
- 6-2 턴테이킹과 시맨틱 VAD
- 6-3 보이스 페르소나 디자인
- 6-4 SIP·PSTN 전화망 연동
- 6-5 온디바이스·엣지 S2S와 프라이버시
Part 7 — S2S 도입 전략·구매 의사결정 (4편)
Part 8 — LLM 콜봇 PoC 현장 (6편)
- 8-1 LLM 콜봇 PoC — 검증의 갭
- 8-2 STT 정확도 93% 목표가 부족한 이유
- 8-3 금융 콜봇에서 소수점이 중요한 이유
- 8-4 TTFT 1.5초, 콜봇 스트리밍 파이프라인 설계
- 8-5 콜봇 SLM: 30B vs 70B 트레이드오프
- 8-6 PoC와 본사업 사이의 성능 괴리
Part 9 — 상담어시스트: 상담사 화면을 바꾸는 실시간 AI (7편)
- 9-1 상담어시스트란 무엇인가 — 실시간 STT가 상담사 화면을 바꾸는 구조
- 9-2 상담어시스트의 뼈대 — STT·인텐트·KMS 파이프라인
- 9-3 추천의 근거 — KMS·RAG 지식 그라운딩
- 9-4 통화 중 놓친 고지를 잡는 실시간 필수 안내 체크
- 9-5 AI가 상담 품질을 자동 채점하는 법 — AutoQA
- 9-6 신입 상담사에게 어시스트가 더 필요한 이유
- 9-7 온프레미스 상담어시스트 — GPU 제약과 모델 선택
Part 10 — 직접 만든 롤플레이 챗봇: RAG 에이전트 구축기 (8편)
- 10-1 왜 롤플레이 챗봇을 직접 만들었나
- 10-2 전체 아키텍처 한 장
- 10-3 RAG 파이프라인과 로컬 임베딩
- 10-4 멀티 LLM 백엔드 스위칭
- 10-5 AI 고객 페르소나 프롬프트 엔지니어링
- 10-6 퀴즈 풀 사전생성
- 10-7 대화가 끝나면 채점한다
- 10-8 혼자 쓰는 챗봇의 현실
Part 11 — KMS 실무 — 지식관리시스템의 설계와 운영 현실 (8편)
통화가 길어지면 무슨 일이 벌어지는가
대화형 모델은 매 턴, 지금까지의 대화를 다시 입력으로 받아 다음 발화를 만듭니다. 음성 에이전트도 다르지 않습니다. 사용자 발화와 에이전트 응답이 번갈아 쌓이고, 그 누적분이 매 턴 프롬프트에 다시 실립니다. 통화가 길어진다는 것은 곧 입력 길이가 단조롭게 늘어난다는 뜻입니다.
가장 순진한 방식은 "전체 대화를 매번 다 넣는 것"입니다. 짧은 통화에서는 멀쩡히 작동하기 때문에 초기에는 문제처럼 보이지 않습니다. 하지만 대화가 길어지면 이 방식은 세 방향에서 동시에 무너집니다. 입력 토큰이 늘어 비용이 가파르게 커지고, 입력을 처리하는 데 걸리는 시간이 늘어 응답이 느려지며, 정작 중요한 정보가 긴 맥락 속에 묻혀 정확도까지 떨어집니다. 길어질수록 느려지고, 비싸지고, 흐려지는 삼중고입니다.
윈도우를 다 채우면 안 되는 세 가지 이유
첫째는 비용입니다. 대부분의 모델은 매 턴 입력 전체를 다시 처리합니다. 즉 대화가 누적될수록 같은 앞부분을 반복해서 다시 보내는 셈이고, 누적된 이력이 길어질수록 통화 한 건에 드는 처리량은 단순 합산보다 빠르게 불어납니다. 구체 단가는 모델·시점마다 다르지만, 메커니즘 자체가 "길어질수록 가팔라지는" 구조라는 점이 핵심입니다.
둘째는 지연입니다. 모델이 답을 만들기 전에 입력을 먼저 읽어 들이는 단계(prefill)가 있는데, 이 시간은 입력 길이에 비례해 늘어납니다. 텍스트 챗이라면 조금 느린 정도로 넘어가지만, 실시간 음성은 통상 1초 안팎의 응답을 자연스럽다고 느끼는 매체라(체감지연 보정은 03편 참조), 입력이 길어져 생기는 지연은 곧장 어색한 침묵으로 드러납니다.
셋째는 품질입니다. 맥락이 아주 길어지면 모델이 중간에 놓인 정보를 흘려버리는 경향이 보고됩니다. 흔히 'lost in the middle'이라 부르는 현상으로, 윈도우를 꽉 채운다고 더 똑똑해지는 게 아니라 오히려 핵심을 놓칠 수 있다는 뜻입니다. 윈도우는 채울 공간이 아니라 아껴 써야 할 예산에 가깝습니다.
핵심 슬롯: 절대 잊으면 안 되는 것부터 분리
해법의 출발점은 분리입니다. 통화 내내 흐르는 자연어 대화와, 끝까지 정확히 유지돼야 하는 사실을 같은 그릇에 담지 않는 것입니다. 후자를 구조화된 '핵심 슬롯'으로 따로 빼서 프롬프트 상단에 항상 고정합니다.
핵심 슬롯에는 통화의 정체성과 진행 상태가 들어갑니다. 예컨대 누구와 이야기하고 있는지(식별·검증 상태), 지금 처리 중인 작업이 무엇인지, 이미 확정된 사실은 무엇인지(선택한 옵션, 합의된 날짜, 동의 여부) 같은 항목입니다. 이것들은 대화가 아무리 길어져도 요약으로 뭉개지면 안 되는 값들입니다. 슬롯은 자연어 서술이 아니라 키-값 형태로 또렷하게 유지하는 편이 안전합니다.
[핵심 슬롯 — 항상 프롬프트 상단 고정] - 식별/검증: 확인 완료 - 진행 중 작업: 예약 일정 변경 - 확정 사실: 변경 희망일=다음 주 화요일 오후, 본인 동의=받음 - 미해결: 변경 후 안내 문자 발송 여부
이렇게 빼 두면 두 가지 이득이 있습니다. 중요한 값이 긴 맥락에 묻히지 않고, 뒤에서 대화 본문을 압축하더라도 잃어선 안 될 정보가 보호됩니다.
이전 대화 요약(Summarization): 오래된 턴을 압축
핵심 슬롯을 분리했다면, 남은 자연어 대화는 통째로 들고 갈 필요가 없습니다. 최근 몇 턴은 원문 그대로가 중요하지만, 오래된 앞부분은 요점만 남기면 됩니다. 여기서 쓰는 기법이 슬라이딩 윈도우와 롤링 요약의 결합입니다.
작동은 단순합니다. 최근 N턴은 원문으로 유지하고(슬라이딩 윈도우), 그보다 오래된 대화는 짧은 요약본으로 접습니다. 대화가 더 진행돼 임계치를 넘으면, 윈도우 밖으로 밀려난 턴을 기존 요약본에 흡수시켜 요약을 갱신합니다(롤링). 결과적으로 프롬프트는 "오래된 것은 요약, 최근 것은 원문"이라는 일정한 형태를 유지하며, 통화가 길어져도 입력 길이가 무한정 늘지 않습니다.
다만 요약에는 대가가 따릅니다. 요약을 만드는 호출 자체가 비용과 시간을 쓰고, 압축 과정에서 정보가 일부 손실됩니다. 너무 자주 요약하면 배보다 배꼽이 커지고, 너무 늦게 하면 윈도우가 먼저 터집니다. 임계치(예: 원문 턴 수나 누적 토큰 기준)를 정해 그 선을 넘을 때만 요약을 돌리는 균형점 설계가 실무의 핵심입니다. 외부 지식을 검색해 끼워 넣는 RAG와는 다른 축이라는 점도 분명히 해 둘 필요가 있습니다(검색 결과 주입은 04편 참조). 여기서 다루는 것은 외부 지식이 아니라 대화 자체의 누적 이력입니다.
동적 프롬프트 조립: 매 턴 무엇을 넣을지 결정
지금까지의 조각을 매 턴 하나의 프롬프트로 합치는 단계가 동적 조립입니다. 매번 같은 자리에 같은 종류를 끼워 넣는 고정된 레시피를 두면, 모델 입장에서도 일관되고 운영 입장에서도 측정·캐싱이 쉬워집니다.
프롬프트 = [ 시스템 규칙 (역할·말투·제약) # 고정 + 핵심 슬롯 (구조화 상태) # 매 턴 갱신, 항상 상단 + 대화 요약본 (오래된 턴 압축) # 임계치 초과 시 갱신 + 최근 N턴 원문 (슬라이딩 윈도우) # 매 턴 이동 + (필요 시) 검색·외부 조회 결과 # 해당 턴에만 ]
조립 순서에도 의도가 있습니다. 시스템 규칙과 핵심 슬롯처럼 잘 바뀌지 않는 부분을 앞쪽 고정 자리에 두면, 뒤에서 다룰 프롬프트 캐싱과 맞물려 매 턴 같은 앞부분을 다시 계산하는 낭비를 줄일 수 있습니다. 반대로 매 턴 바뀌는 최근 원문은 뒤쪽에 배치합니다.
그렇다면 컨텍스트를 줄이는 싸움은 결국 어디로 이어질까요? 따라가 보면 "한 에이전트가 떠안는 지식의 양"을 줄이는 싸움과 만납니다. 단일 거대 에이전트 하나에 모든 상담 지식(요금·해지·기술지원·결제…)을 다 담으면, 시스템 규칙과 슬롯·도구 설명만으로도 고정부가 비대해져 매 턴 부담이 커지고 운영도 어려워집니다. 그래서 도메인별 서브 에이전트로 쪼갠 멀티 에이전트 구조로 가는 것이 자연스러운 귀결입니다. 앞단의 오케스트레이터가 의도 분류 결과를 받아 어느 서브 에이전트로 보낼지 라우팅하고, 선택된 서브 에이전트는 자기 도메인에 필요한 규칙·도구·지식만 좁게 들고 들어갑니다. 같은 통화라도 호출되는 컨텍스트가 도메인 단위로 작게 유지되는 셈입니다.
이렇게 쪼개 두면 모델 선택에서도 여지가 생깁니다. 서브마다 다른 크기·종류의 모델을 섞어 쓸 수 있어, 단순 분류·확인성 응대는 작고 빠른 모델로, 복잡한 추론이 필요한 도메인만 큰 모델로 배정하는 식의 절충이 가능합니다. 다만 라우팅이 한 겹 끼는 만큼 의도 분류가 틀리면 엉뚱한 서브로 새고, 도메인 사이를 오가는 통화에서는 슬롯 같은 공유 상태를 어떻게 넘겨줄지가 새로운 설계 포인트가 됩니다. 컨텍스트 윈도우를 아끼는 문제가 어느 선을 넘으면 "프롬프트를 어떻게 접느냐"에서 "에이전트를 어떻게 나누느냐"로 옮겨간다는 점은 미리 염두에 둘 만합니다.
| 구분 | 전체 이력 통째로 | 동적 컨텍스트 관리 |
|---|---|---|
| 입력 길이 | 통화 길이에 비례해 무한 증가 | 요약+최근 N턴으로 상한 유지 |
| 비용 추이 | 길어질수록 가파르게 상승 | 완만하게 안정화 |
| 핵심 정보 | 긴 맥락에 묻힐 위험 | 슬롯으로 상단 고정·보호 |
| 응답 지연 | prefill 증가로 악화 | 입력 상한으로 통제 |
| 구현 부담 | 거의 없음(초기엔 편함) | 요약·임계치 설계 필요 |
실시간 음성에서의 추가 제약
텍스트 챗이라면 요약을 응답 직전에 잠깐 돌려도 됩니다. 하지만 실시간 음성에서는 사용자가 침묵을 못 견디므로, 무거운 요약 호출이 응답 경로 한가운데에 끼면 그 시간이 그대로 어색한 공백이 됩니다. 그래서 요약은 응답을 막지 않는 비동기·백그라운드 작업으로 돌리는 것이 정석입니다. 사용자에게 답하는 흐름은 기존 요약본과 최근 원문으로 즉시 진행하고, 요약 갱신은 그 뒤(또는 사용자가 말하는 동안)에 조용히 처리해 다음 턴에 반영하는 식입니다.
또 하나의 지렛대는 프롬프트 캐싱입니다. 시스템 규칙처럼 매 턴 동일한 앞부분은 모델이 다시 계산하지 않도록 캐시할 수 있어, 반복 처리 비용과 prefill 지연을 함께 줄일 수 있습니다. 동적 조립에서 고정부를 앞에 모아 둔 설계가 여기서 빛을 발합니다. 정리하면, 긴 통화의 컨텍스트 관리는 "무엇을 남기고 무엇을 접느냐"라는 메모리 설계와, "그 작업을 응답 경로 밖으로 빼느냐"라는 실시간 설계가 함께 가야 완성됩니다.
세 전략을 한눈에
지금까지의 세 축을 한 표로 정리하면, 각 전략이 무엇을 얻고 무엇을 감수하는지가 또렷해집니다.
| 전략 | 장점 | 한계 |
|---|---|---|
| 핵심 슬롯 고정 | 확정 사실을 상단에 보호해 긴 맥락에도 안 묻힘, 정정·추적이 쉬움 | 무엇을 슬롯으로 뺄지 사전 설계가 필요하고, 잘못 넣은 값은 통화 내내 따라옴 |
| 슬라이딩 윈도우 + 롤링 요약 | 오래된 턴을 압축해 입력 길이에 상한을 두고, 통화가 길어져도 비용·지연이 완만해짐 | 요약 호출 자체가 비용·시간을 쓰고 손실 압축이라 디테일이 빠질 수 있음, 임계치 튜닝 필요 |
| 고정부 프롬프트 캐싱 | 매 턴 같은 앞부분을 다시 계산하지 않아 반복 처리 비용과 prefill 지연을 함께 줄임 | 고정된 앞부분에만 효과가 크고 매 턴 바뀌는 최근 대화에는 이득이 제한적 |
세 전략은 서로를 대체하지 않고 겹쳐 써야 제값을 하며, 긴 통화의 품질은 결국 무엇을 남기고 무엇을 접을지 매 턴 설계하는 메모리 관리에서 갈립니다. 여기 정리한 접근은 2026년 6월 기준의 이야기이고 세부 구현은 환경마다 다를 수 있으니, 각자의 시스템에 맞는 출발점으로 삼아 주시면 좋겠습니다.
댓글
댓글 쓰기