
AI 에이전트 메모리: 대화(Conversation) vs 컨텍스트(Context)
요약
AI 에이전트의 성능 저하를 막기 위해 대화 메모리와 컨텍스트 메모리를 분리하여 관리하는 전략을 제시합니다. 대규모 도구 출력값이 컨텍스트 윈도우를 초과하는 문제를 해결하기 위해 포인터 참조 방식을 제안합니다.
핵심 포인트
- 대화 메모리는 의미 기반 검색, 컨텍스트 메모리는 식별자 기반 검색이 필요함
- 대규모 도구 출력값을 컨텍스트에 직접 포함하면 토큰 비용과 오버플로 발생
- 데이터를 컨텍스트 외부에 저장하고 포인터로 참조하여 효율성 극대화
- 메모리 분리 시 토큰 사용량을 획기적으로 절감 가능
당신의 에이전트가 단 한 번의 도구 호출(tool call)로 토큰 예산을 다 써버리거나, 사용자가 세 번 전의 대화에서 말한 내용을 잊어버립니까? 근본 원인은 동일합니다. 에이전트에게는 두 가지 종류의 메모리가 있는데, 이들이 서로 뒤섞였기 때문입니다. 하나는 대화(conversation)를 담고 있고, 다른 하나는 로그와 같은 대규모 도구 출력값(tool outputs)을 담고 있습니다. 이들은 서로 다른 저장 방식과 검색(retrieval) 방식이 필요하며, 이들을 하나의 저장소로 취급하는 것이 에이전트를 느리고, 비싸고, 부정확하게 만드는 원인입니다.
이 포스트에서는 이 둘을 분리하여 유지하는 방법을 보여줍니다. 이제 프레임워크가 대규모 데이터를 대신 처리해 주며(더 이상 수동으로 포인터 코드를 작성할 필요가 없습니다), 프로덕션 환경에서는 이 두 메모리가 두 개의 AWS 서비스로 매핑됩니다. 저는 이를 직접 배포하고 그 차이를 측정했습니다.
AI Context Window Overflow: Memory Pointer Fix를 기반으로 작성되었습니다. 코드는 Strands Agents를 사용하며, 이 패턴은 다른 프레임워크에도 적용 가능합니다. 리포지토리: sample-why-agents-fail.
에이전트 메모리의 두 가지 종류란 무엇인가?
AI 에이전트에는 두 가지 종류의 메모리가 있습니다. **대화 메모리 (conversation memory)**는 말해진 내용(대화 턴, 선호도, 사실 관계)을 보유하며 의미(meaning)에 의해 회상되는 반면, **컨텍스트 메모리 (context memory)**는 대규모 도구 출력값(로그, 데이터셋, 문서)을 보유하며 정확한 식별자(identifier)에 의해 회상됩니다. 이들은 서로 다른 검색 방식을 가진 서로 다른 저장소이며, 한 곳에 다른 곳의 용도를 사용하는 것이 "내 에이전트가 무언가를 잊어버린다"와 "내 에이전트가 토큰 예산을 초과했다"라는 문제의 근본 원인입니다.
코드를 보기 전에, 이 차이점을 명확히 이해하십시오:
| 대화 메모리 (Conversation memory) | 컨텍스트 메모리 (Context memory) | |
|---|---|---|
| 보유 내용 | 대화 턴, 선호도, 추출된 사실 | 대규모 도구 출력값 (로그, 데이터셋) |
| ... |
저 표가 이 글의 핵심입니다. 아래의 모든 내용은 각 행이 코드의 어디에 위치하는지에 대한 설명일 뿐입니다.
왜 컨텍스트 메모리가 먼저 넘치는가 (overflows)
대규모 도구 출력(tool outputs)은 나눌 수 없고 모델 호출 시마다 매번 다시 전송되기 때문에 컨텍스트 윈도우(context window)를 초과(overflow)합니다. 200KB의 로그를 반환하는 도구는 단 한 번 200KB의 비용만 발생시키는 것이 아닙니다. 해당 페이로드(payload)는 원래의 질문이 윈도우 밖으로 밀려날 때까지 이후의 모든 턴(turn)의 입력값에 포함되어 따라다닙니다.
첫 번째 포스트는 IBM Research(Solving Context Window Overflow in AI Agents, 2025)를 통해 이를 수치화했습니다. 20,822,181개의 토큰을 소비하며 실패했던 재료 과학 워크플로우(materials-science workflow)는, 대규모 데이터를 컨텍스트 외부(outside context)에 저장하고 포인터(pointer)로 참조하게 하자 1,234개의 토큰만 사용하며 성공했습니다.
과거와 현재의 해결책: 대화(conversation)에 데이터를 넣지 마세요
원래의 포스트에서는 대규모 데이터를 수동으로 저장했습니다. 도구가 데이터를 agent.state에 쓰고 짧은 포인터 문자열을 반환하면, 다음 도구가 해당 키(key)를 통해 이를 다시 읽어오는 방식이었습니다. 작동은 하지만, 오프로딩(offloading) 로직이 모든 도구 내부에 존재해야 했습니다.
이제 Strands는 정확히 그 패턴을 일급 객체 플러그인(first-class plugin)인 ContextOffloader로 제공하므로, 여러분의 도구는 다시 일반적인 함수로 돌아갈 수 있습니다:
from strands import Agent
from strands.vended_plugins.context_offloader import ContextOffloader, FileStorage
...
도구 결과가 max_result_tokens보다 크면, 플러그인이 이를 가로채어 각 블록을 백엔드(backend)에 저장하고, 컨텍스트에는 작은 미리보기와 참조(reference)만을 남겨둡니다. 에이전트는 실제로 데이터가 필요할 때 **정확한 참조(exact reference)**를 통해 전체 데이터를 다시 가져올 수 있도록 retrieve_offloaded_content(reference) 도구를 갖게 됩니다.
Strands의 네이티브 메모리 포인터 패턴(Memory Pointer Pattern)이란 무엇인가?
네이티브 메모리 포인터 패턴 (Memory Pointer Pattern)은 ContextOffloader입니다. 이는 실행 시점에 크기가 너무 큰 도구 결과물 (tool results)을 가로채어, 각 블록을 스토리지 백엔드 (storage backend)에 저장하고, 컨텍스트 내의 결과물을 미리보기와 참조 (reference)로 교체하는 플러그인입니다. 대규모 데이터가 컨텍스트 윈도우 (context window)를 압도하지 않으며, 사용자의 도구는 포인터 로직을 직접 다룰 필요가 없습니다.
측정 결과
동일한 쿼리를 세 가지 전략으로 실행했습니다. 동일한 쿼리, gpt-4o-mini 모델, 2시간 분량의 로그:
| 전략 | 컨텍스트 내 토큰 수 |
|---|---|
| 관리 없음 | ~18,000 ~ 20,000 |
| ... |
이는 동일한 답변을 얻는 데 약 97% 더 적은 토큰을 사용함을 의미합니다. 로그 데이터가 무작위로 생성되므로 실행마다 수치는 달라질 수 있으며, test_native_pointer.py를 통해 이를 재현할 수 있습니다.
한 가지 솔직한 주의 사항을 말씀드리자면, 오프로더 (offloader)는 **안전망 (safety net)**이지 그 자체로 모든 승리를 보장하는 것은 아닙니다. 큰 절감 효과는 이를 **선택적 도구 (selective tool)**와 결합할 때 나타납니다. 제가 작성한 count_errors_by_service는 서버 측에서 정답을 계산하고 작은 요약본을 반환하므로, 에이전트는 요약본을 바탕으로 답변하고 로그는 오프로드된 상태로 유지됩니다. 선택적 도구가 없다면, 전체 데이터셋이 필요한 에이전트는 단순히 retrieve_offloaded_content를 호출하여 모든 데이터를 다시 가져오게 됩니다. 오프로더는 컨텍스트가 넘치지 않음을 보장하며, 선택적 도구는 토큰 수를 낮게 유지하는 역할을 합니다.
대부분의 에이전트를 위한 한 줄 코드
오프로딩 (offloading)과 요약 (summarization)을 별도로 연결할 필요가 없는 일반적인 멀티 턴 (multi-turn) 에이전트의 경우 다음과 같이 작성합니다:
agent = Agent(model=MODEL, tools=[...], context_manager="auto")
이 방식은 벤치마크로 검증된 기본값을 사용하여 SummarizingConversationManager (선제적 압축을 통해 이전 기록을 요약)와 ContextOffloader (인메모리 방식)를 결합합니다. 명시적으로 전달하는 모든 설정은 우선순위를 갖습니다.
동일한 아이디어를 실제 Amazon S3 스토리지에 적용하기
FileStorage는 로컬 디스크에 기록합니다. 코드 한 줄만 바꾸면 대규모 도구 출력물이 실제 S3 버킷 (S3 bucket)에 저장되며, 정확한 참조를 통해 호출될 뿐 컨텍스트 윈도우에는 절대 들어가지 않습니다:
from strands.vended_plugins.context_offloader import ContextOffloader, S3Storage
agent = Agent(
...
83KB의 로그 데이터셋이 S3에 저장되었고, 약 486개의 토큰이 컨텍스트 (Context)에 남았으며, 데이터는 정확한 참조를 통해 바이트 단위로(byte-for-byte) 그대로 돌아왔습니다:
📊 LLM 컨텍스트에 남은 토큰: 486
📦 S3로 오프로드(offloaded)된 객체: 1
컨텍스트 내 포인터: s3://…/log-artifacts/1781569100199_1_call_…_0
...
이것이 표의 두 번째 행, 즉 프로덕션 형태인 **정확한 식별자 회상 (exact-identifier recall)**입니다. 여러분은 "내 쿼리와 가장 유사한 로그"를 원하는 것이 아닙니다. 바로 그 로그를 정확하게 원합니다. 이것은 시맨틱 검색 (semantic search)이 아니라 객체 스토리지 (object storage)의 영역입니다.
프로덕션: 의도된 두 개의 메모리
프로덕션 환경에서는 이 분리가 아키텍처가 됩니다. Amazon Bedrock AgentCore 상의 에이전트는 각 메모리를 적절한 위치에 보관합니다:
- 대화 (Conversation) → AgentCore 메모리. 턴 (turns), 선호도, 추출된 사실들을 저장하며, 시맨틱 유사도 (
RetrieveMemoryRecords: 임베딩 (embeddings),top_k, 관련성 점수 (relevance score))를 통해 회상됩니다.actor_id를 통해 사용자별로 범위가 지정됩니다. Strands의AgentCoreMemorySessionManager를 통해 연결됩니다. - 컨텍스트 (Context) 메모리 → Amazon S3.
FileStorage대신S3Storage를 사용하는 동일한ContextOffloader를 사용합니다. 정확한 참조를 통해 회상됩니다.
왜 로그를 AgentCore 메모리에도 넣지 않을까요? AgentCore 메모리는 시맨틱적으로 가장 유사한 메모리를 회상하기 때문입니다. 이는 "ID를 통해 이 데이터셋을 있는 그대로 반환하라"는 요구에는 정확히 맞지 않습니다. 대화는 의미 (meaning)를 원하지만, 데이터는 정확한 키 (exact key)를 원합니다. 하나의 에이전트, 두 개의 메모리, 각각이 잘하는 일을 수행합니다.
agent = Agent(
model=BedrockModel(region_name=REGION),
tools=[fetch_application_logs, count_errors_by_service],
...
관측 가능성 (Observability)과 평가 (evaluation)의 자동 확보
AgentCore에서는 완전한 관측 가능성 (Observability)이 내장되어 있습니다. 계측 (Instrumentation) 라이브러리를 추가하기만 하면, 별도의 모니터링 코드를 작성하지 않고도 모든 호출에 대한 트레이스 (Traces), 메트릭 (Metrics), 로그 (Logs)를 얻을 수 있습니다. 배포 단계에서 이미 이 기능이 활성화되었습니다. 에이전트는 bedrock-agentcore 네임스페이스 아래에서 OpenTelemetry (OTEL) 트레이스와 메트릭을 방출하며, CloudWatch GenAI Observability dashboard를 통해 에이전트, 세션, 트레이스 뷰 (지연 시간 (Latency), 에러율 (Error rate), 토큰 사용량 (Token usage), 도구 호출 (Tool calls))를 즉시 확인할 수 있습니다.
이 덕분에 저는 앞서 발생한 ListEvents 권한 에러를 단 몇 초 만에 진단할 수 있었습니다. 추가 설정 없이도 실패한 트레이스가 CloudWatch에 바로 나타났기 때문입니다. AgentCore 에이전트에 대한 관측 가능성 데이터 보기를 참조하세요.
동일한 계측 데이터는 AgentCore Evaluations로 이어집니다. 동일한 트레이스를 기반으로 작업 완료 및 도구 호출 정확도에 대해 LLM-as-a-Judge 방식의 자동화된 점수 산출을 수행하므로, 출시 시점에만 측정하는 것이 아니라 에이전트의 품질을 지속적으로 측정할 수 있습니다.
어떤 메모리를, 언제 사용할 것인가
- 단순히 로컬 데이터 문제인가요?
ContextOffloader(FileStorage(...))를 사용하세요. 일반적인 도구(tools)를 사용하며, 포인터 코드가 필요 없습니다. - 전형적인 멀티턴(multi-turn) 에이전트인가요?
context_manager="auto"를 사용하세요. 한 줄로 요약(Summarization)과 오프로딩(offloading)을 모두 수행합니다. - 프로덕션(Production) 환경인가요? 대화(conversation)에는 AgentCore Memory를, 데이터(data)에는
ContextOffloader(S3Storage(...))를 사용하세요. 이 둘을 분리하여 유지해야 합니다. - 어떤 방식이든: 오프로더(offloader)를 원본 데이터 덩어리(raw blobs)가 아닌 요약본을 반환하는 선택적 도구(selective tools)와 함께 사용하세요. 오프로더는 오버플로(overflow)를 방지하고, 선택적 도구는 토큰 수(token count)를 낮게 유지합니다.
직접 시도해보기
Python 3.11+ 버전, uv, 그리고 OPENAI_API_KEY가 필요합니다 (BedrockModel로 모델을 교체할 수도 있습니다). S3 및 AgentCore 단계에는 AWS 자격 증명(credentials)도 필요합니다.
git clone https://github.com/aws-samples/sample-why-agents-fail
cd sample-why-agents-fail/stop-ai-agents-wasting-tokens/01-context-overflow-demo
uv venv && uv pip install -r requirements.txt
...
노트북(Notebooks): test_native_pointer.ipynb (로컬) 및 setup_agentcore_s3.ipynb (AWS에서 프로비저닝 + 배포 + 호출).
핵심 요약 (Key takeaways)
- 에이전트는 두 가지 메모리를 가집니다. 대화(Conversation, 의미론적/semantic)와 데이터(Data, 정확한 참조/exact reference)입니다. 대부분의 컨텍스트(context) 문제는 한쪽 메모리를 다른 쪽이 있어야 할 곳에 두었을 때 발생합니다.
- 더 이상 데이터 측면을 수동으로 구축할 필요가 없습니다.
ContextOffloader는 플러그인 형태의 메모리 포인터 패턴(Memory Pointer Pattern)이며, 도구(tools)는 일반적인 함수로 유지됩니다. - 이 데모에서 토큰 사용량을 약 97% 줄였습니다. 또한 83KB 데이터셋이 실제 S3로 오프로딩되었으며, 참조를 통해 바이트 단위로 정확하게(byte-for-byte) 복구됨을 확인했습니다.
- 프로덕션 환경에서는 두 메모리를 분리하여 유지하세요. 대화에는 AgentCore Memory를, 데이터에는 S3를 사용하세요. 의미(meaning)에 의해 호출되는 로그는 잘못된 설계입니다.
- 오프로더는 안전망이며, 선택적 도구가 핵심입니다. 데이터 덩어리(blobs)가 아닌 요약본(summaries)을 반환하세요.
- AgentCore를 사용하면 관찰 가능성(observability)과 평가(evaluation)가 무료로 제공됩니다. 라이브러리를 추가하기만 하면 별도의 모니터링 코드 없이도 트레이스(traces), 메트릭(metrics), 그리고 LLM-as-a-Judge 스코어링을 얻을 수 있습니다.
FAQ
ContextOffloader에 AWS가 필요한가요? 아니요. FileStorage 또는 InMemoryStorage를 사용하면 완전히 로컬에서 실행됩니다. S3Storage를 선택하거나 AgentCore에 배포할 때만 AWS가 필요합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

