에이전트 메모리는 프롬프트 문제가 아니라 데이터베이스 문제다
에이전트의 전체 이력을 다음 프롬프트에 통째로 붙여넣는 것은 메모리가 아니라 늘어나는 청구서이자 토큰 상한선이다. 진짜 메모리는 검색 단계를 갖춘 데이터베이스다.

전체 이력을 다음 프롬프트에 붙여넣는 방식으로 기억하는 에이전트는 메모리를 가진 것이 아니다. 늘어나는 청구서, 단단한 토큰 상한선, 그리고 턴이 거듭될수록 나빠지는 지연 시간 곡선을 가진 것이다. 진짜 메모리는 저장 방식에 대한 결정이다. 무엇을 영구 보관할지, 어디에 둘지, 추론 시점에 관련된 조각만 어떻게 가져올지를 결정하는 것이다. 이것은 데이터베이스 문제이며, 이를 프롬프트 문제로 취급하는 것이 데모에서는 잘 돌아가던 에이전트가 2주차에 무너지는 이유다.
컨텍스트를 그냥 쑤셔 넣는 방식의 유혹적인 점은 처음에는 잘 작동한다는 것이다. 세션 초반에는 이력이 짧아서 모든 것이 들어맞고, 에이전트는 마치 기억하는 것처럼 보인다. 그러다 대화가 길어지면 프롬프트도 함께 커지고, 동시에 세 가지 일이 벌어진다. 매 턴마다 모든 것을 다시 보내야 하니 비용이 오르고, 모델이 매번 더 많이 읽어야 하니 지연 시간이 오르고, 결국 컨텍스트 윈도우에 부딪히면서 오래된 사실들이 조용히 앞에서 잘려 나간다. 이 실패는 패치로 고칠 수 있는 버그가 아니다. 아키텍처 그 자체다.
두 종류의 메모리, 두 종류의 저장소
"메모리"는 사실 서로 다른 두 가지 요구를 뭉뚱그린 표현이며, 이 둘을 혼동하는 지점에서 설계가 잘못된다.
세션 및 작업 메모리: 키-값 저장소
최근 턴들, 현재 작업 상태, 사용자의 단기 선호는 세션 ID, 사용자 ID, 스레드 ID 같은 알려진 키로 조회된다. 이 키에 대해 빠른 읽기와 쓰기, 그리고 오래된 세션이 스스로 만료되게 하는 TTL이 필요하다. 이것은 벡터 검색이 아니라 DynamoDB 테이블이다. 세션이나 사용자로 파티셔닝하고, 실행 중인 상태를 하나의 항목으로 유지하며, TTL을 설정하고, 매 턴 시작 시 이를 다시 읽는다. 임베딩도, 유사도 계산도 필요 없이 "우리가 어디까지 왔는지"를 빠르게 키로 조회하기만 하면 된다.
장기 시맨틱 메모리: 벡터 저장소
에이전트가 세션을 넘나들며 기억해야 할 사실, 과거의 결정, 학습된 선호, 관련 있는 과거 대화는 키로 조회되지 않는다. 의미로 조회된다. "이 새 메시지와 관련해서 내가 알고 있는 것은 무엇인가"라는 방식이다. 이것이 임베딩에 대한 시맨틱 검색이며, 벡터 저장소가 존재하는 이유다. AWS에서 현실적인 선택은 pgvector를 사용하는 Aurora Serverless v2다. 임베딩이 관계형 데이터 옆에 자리하고, SQL로 조회할 수 있으며, 이를 위해 별도의 전용 데이터베이스를 새로 구축할 필요가 없다.
패턴: 축적이 아니라 검색
메모리가 데이터베이스가 되고 나면, 매 턴은 더 이상 덧붙이는 대신 조회하기 시작한다. 루프는 다음과 같이 바뀐다.
on each turn:
1. read session state by key (DynamoDB: where were we)
2. embed the new user message
3. semantic search long-term store (pgvector: what is relevant)
4. assemble a bounded prompt:
system + tools
+ top-k retrieved memories
+ recent turns from session state
+ new message
5. call the model
6. write new facts back to the stores이제 사용자와의 관계가 아무리 오래 이어져도 프롬프트는 상한선을 유지한다. 천 번째 턴에 있는 대화도 열 번째 턴과 같은 크기의 프롬프트를 보낸다. 모든 메모리를 다 짊어지는 대신 관련 있는 소수의 메모리만 가져오기 때문이다. 비용과 지연 시간은 오르는 대신 평평해진다. 이것은 RAG를 작동하게 만드는 것과 동일한 검색 원칙을, 문서 코퍼스가 아니라 에이전트 자신의 이력에 적용한 것이다.
프롬프트가 숨겨 버리는, 여러분이 직접 결정해야 할 것들
메모리를 데이터베이스로 옮기면 컨텍스트 쑤셔 넣기 방식에서는 무시할 수 있었던 선택들을 강제로 마주하게 되고, 그 선택들이야말로 실제 엔지니어링이다.
- 무엇을 기억할 가치가 있는가. 모든 턴이 기억거리는 아니다. 잡담이 아니라 지속되는 사실과 결정을 기록해야 한다. 그렇지 않으면 저장소가 잡음으로 채워지고, 검색 단계가 그 잡음을 그대로 끌어올린다.
- 언제 잊을 것인가. 세션 상태에 대한 TTL, 그리고 오래된 선호가 수정된 선호보다 우선순위가 높아지지 않도록 장기 사실을 만료시키거나 대체하는 정책이 필요하다.
- 얼마나 가져올 것인가. top-k는 하나의 조절 나사다. 너무 적으면 에이전트가 잊어버리고, 너무 많으면 검색 단계가 아무런 가치를 더하지 못한 채 다시 비대해진 프롬프트로 돌아간다.
- 두 저장소 간의 일관성. 세션 메모리와 시맨틱 메모리는 서로 어긋날 수 있다. 어긋났을 때 어느 쪽이 우선하는지 결정해야 한다.
결론
컨텍스트 쑤셔 넣기는 메모리가 아니라, 토큰 청구서와 컨텍스트 상한선이 대신 결정을 내려줄 때까지 저장 방식에 대한 결정을 미루는 것이다. 문제를 나누어라. DynamoDB에 빠른 키 기반 세션 상태를, Aurora 위 pgvector에 시맨틱 장기 회상을 두고, 매 턴마다 두 저장소에서 가져와 상한선이 있는 프롬프트를 조립하는 검색 단계를 두어라. 메모리가 끝없이 커지는 프롬프트가 아니라 검색 단계를 갖춘 데이터베이스가 되는 순간, 비용과 지연 시간은 대화 길이에 따라 늘지 않게 되고, 무엇을 잊을지 컨텍스트 윈도우가 대신 정하게 두는 대신 에이전트가 무엇을 기억할지 스스로 결정할 수 있게 된다.
다음으로 읽을 글
- Cutting Amazon Bedrock Knowledge Base Costs by ~90% with pgvector, 장기 에이전트 메모리를 뒷받침하는 동일한 Aurora와 pgvector 저장소에 관한 글이다.
- Bedrock Agents vs Rolling Your Own Loop, 프레임워크가 메모리 계층을 대신 관리해 줄 때 그 소유권이 누구에게 있는지에 관한 글이다.
Aurora와 DynamoDB를 대규모로 운영하는 데이터베이스 및 인프라 측면은 ercan.cloud의 클라우드 필드 노트에서, 허브는 ercanermis.com에서 확인할 수 있다.
Ercan의 다른 글
같은 저자, 다른 영역의 사이트 두 개.