기억하는 에이전트: AgentCore에서 단기 및 장기 메모리 구현하기
요약
본 글은 AgentCore를 사용하여 에이전트에 단기 및 장기 메모리를 구현하는 방법을 설명합니다. 세션별 전용 이벤트 로그(단기 메모리)와 네임스페이스 기반 레코드(장기 메모리)를 활용하여, 이전 대화 내용이나 특정 비즈니스 정보를 기억하게 하는 방식을 다룹니다.
핵심 포인트
- AgentCore는 단기/장기 메모리를 분리하여 관리합니다.
- 단기 메모리는 액터 및 세션별 전용 이벤트 로그로 구현됩니다.
- 장기 메모리는 네임스페이스 기반의 일련의 레코드로 추출됩니다.
- AWS 환경에서 microVM과 IAM 권한 설정이 필요합니다.
응답이 끝나는 순간 모든 것을 잊어버리는 에이전트는 올바른 기본값이며, 재주문 어시스턴트에게는 잘못된 동작입니다. 이 글에서는 해당 에이전트에 메모리를 추가하고 세 가지 방식으로 이를 증명합니다. 첫째, 선호도가 새로운 세션까지 유지됩니다. 둘째, 두 번째 매니저는 첫 번째 매니저의 메모리를 볼 수 없습니다. 셋째, 실패한 재고 보충 건은 그것이 의미했던 바에 의해 기억됩니다.
아래 제시된 모든 숫자는 이 글을 작성하는 동안 하나의 AWS 계정, us-east-1에서 측정되었습니다.
사용 사례
Northwind Gear는 40개의 매장을 운영하며 매주 월요일에 재고를 보충합니다. 한 매장 관리자가 재주문 어시스턴트에게 북중국 지역만 중요하다고 말한 적이 있습니다. 다음 주 월요일, 새로운 세션에서 어시스턴트는 다시 어떤 지역인지 물었습니다. 모델에는 아무 문제가 없었습니다. 그 문장은 발화된 세션을 벗어나지 않았기 때문에 나중에 세션에서 찾을 만한 것이 없었던 것입니다.
AgentCore가 여기서 하는 일
아홉 가지 기능 중 세 가지가 이 사용 사례를 구현합니다.
- Memory: 하나의 메모리 리소스가 양쪽 절반을 모두 보유합니다. 단기 메모리는 액터(actor)와 세션(session)별로 키가 지정된 추가 전용 이벤트 로그이며, 장기 메모리는 서비스가 해당 이벤트에서 네임스페이스(namespace)로 추출하는 일련의 레코드입니다. 이 글에서는 그 분할에 대해 깊이 다룹니다.
- Runtime:
app/aia_uc02/를 관리형 엔드포인트(managed endpoint)로 호스팅합니다. 각 세션은 자체 마이크로VM(microVM)을 할당받으며, 이것이 세션 ID가 중요한 이유입니다. - Identity, 가볍게: 액터 ID는 invoke 페이로드(payload)나 런타임 헤더(runtime header)에 도착하며, 메모리 네임스페이스의 파티션 키(partition key)가 됩니다. 여기에는 Cognito 풀은 없습니다.
나머지 여섯 가지 기능은 사용하지 않습니다: 게이트웨이(Gateway), 정책(Policy), 평가(Evaluations), 코드 인터프리터(Code Interpreter), 브라우저(Browser), 그리고 런타임에서 방출하는 것 이상의 관측 가능성(Observability) 작업은 없습니다.
요구 사항
- Node.js 20 이상 (여기서는 22.22.2 사용), AgentCore CLI 0.31.1, AWS CLI 2.x, uv 0.11.9
- 호스트에서 Python 3.10 ~ 3.14 사용; 런타임 대상은 계약에 설정된 PYTHON_3_13입니다.
- Bedrock 모델 액세스가 가능한 AWS 계정, 리전
us-east-1필요 - 환경 변수
MODEL_ID를 통해 전달되는 모델deepseek.v3.2 - 런타임 생성, 해당 런타임의 역할(role), 그리고 실행 역할을 가진 메모리 리소스에 대한 IAM 권한; CLI가 두 개의 역할을 모두 작성해 줍니다.
agentcore --version # 0.31.1
aws configure get region # us-east-1
aws sts get-caller-identity --query Account --output text
...
코드
https://github.com/pinxiong/bedrock-agentcore-in-action/tree/main/usecases/02-memory
02-memory/
├── run.py # 모든 작업 수행; tests/와 agentcore로 포워딩
├── Makefile # run.py로 포워딩하며, 로직은 포함하지 않음
...
memory.py가 기사의 핵심 주제이며, agentcore.json에서 네임스페이스 레이아웃이 결정됩니다.
작동 방식
메모리 리소스는 한 번 선언되며, 전략(strategies)들이 어떤 장기 기록이 존재할지 결정합니다:
"memories": [{
"name": "aia_uc02_mem",
"eventExpiryDuration": 30,
...
액터 ID는 네임스페이스 내부에 있으므로, 격리(isolation)는 코드가 기억해야 할 필터가 아니라 키 공간(key space)의 속성입니다. 리플렉션 네임스페이스는 세션별 네임스페이스보다 한 단계 위에 위치하므로, /episodes/{actorId}/에 대한 접두사 읽기는 단일 에피소드와 그 모든 에피소드를 통해 작성된 교훈(lessons) 모두에 도달합니다.
에이전트 자체는 선호도(preference)를 저장하지 않습니다. 대신 턴을 추가하고 질문을 합니다:
def remember(self, actor, session_id, turns):
# ... 실제 코드는 이를 try/except로 감싸고 실패 시 None을 반환합니다.
event = self.client.create_event(
...
이벤트를 작성하는 것이 추출(extraction)을 시작하는 것이기도 합니다. 이것이 바로 두 번째 세션이 첫 번째 세션에서 언급한 문장을 찾을 수 있는 이유입니다. 추출은 invoke의 일부가 아닙니다. CLI는 19초 만에 반환되었고 (그중 모델 호출에 12,117ms 소요), 기록(records)은 나중에 도착했습니다.
검색(Retrieval)은 모델이 호출되기 전에 발생합니다. 왜냐하면 기록들이 시스템 프롬프트의 일부가 되기 때문입니다:
records = store.recall(actor, prompt) if store else []
agent = Agent(
model=load_model(),
...
세션당이 아닌 invoke당 새로운 Agent를 사용합니다. 캐시된 agent는 첫 번째 턴의 메모리만 계속 제공할 것이고, 검색 실패는 기록되어 무시됩니다. 기억할 수 없는 에이전트라도 여전히 유용합니다.
배포하기 (Deploy it)
use case 폴더에서:
make doctor
toolchain
... 네 가지 검사: python 3.14.0, node v22.22.2, aws cli 2.15.6, uv 0.11.9 ...
...
메모리 계약(memory contract) 검사가 존재하는 이유는 EPISODIC 전략이 누락된 배포가 성공한 후 훨씬 나중에 어설션(assertion)에 실패하기 때문입니다.
make deploy
배포 대상 로드 ............ 1 ms
프로젝트 유효성 검사 .................. 682 ms
CDK 종속성 동기화 ............. 2 ms
...

단계별 시간은 첫 번째 배포의 것이고, Outputs는 같은 스택에 대한 몇 분 후 재배포입니다.
두 세션, 두 개의 다른 세션 ID가 있습니다. 첫 번째 것은 선호도만 명시합니다:
python3 run.py invoke --session b0b998ab-89ca-4387-99cb-fc01130e9108-ff3568e8 \
--prompt "지금부터는 북중국 지역만 재배치합니다. 다른 모든 지역은 무시하세요."
명확한 지침에 따라, 저는 북중국 지역만 재배치할 것입니다.
북중국 지역에서는 세 곳의 매장에서 재배치가 필요합니다: 베이징 하이뎬(42개의 보온 텀블러), 톈진 훠핑(17개의 단열 머그컵), 그리고 스자좡 치아오시(8개의 도시락).
...```
두 번째 것은 새로운 세션이며 지역에 대해서는 아무것도 언급하지 않습니다:
python3 run.py invoke --session 03fd411e-293b-4aa2-a219-bd996c7e3481-6b0df16e
--prompt "Draft this week's reorder list."
I'll create the reorder list for the North China region as specified in your preferences. ...
Here's this week's reorder list for the North China region:
Store: Beijing Haidian, SKU: SKU-1001, ... Reorder Quantity: 42 units
...
두 기록 모두 잘려 있으며, `...`은 모든 잘린 부분을 표시합니다. 또한 스토어 라인에서 모델이 방출한 볼드 마커가 사라졌는데, 이 페이지에서는 볼드로 렌더링됩니다. 두 번째 세션에서는 북중국(North China)에 대한 문장을 본 적이 없었습니다. 출처일 수 있는 유일한 곳은 메모리 리소스였습니다.
[](/images/agentcore-memory-that-remembers/S2-cross-session-recall.png)
배포(deployment)에 대해 실행되는 검증(assertions):
make test
memory
session 1 e9f9d728131040b1ad532866e69b4981-cd9243d4
extraction visible after 1.5s · 1 record(s)
...
1.5초가 필요한 이유는 설명이 필요합니다. 해당 선호도(preference)는 이미 같은 리소스에 대해 이전 실행에서 추출되었기 때문입니다. 에피소드는 새것이었고, 959[ms]가 소요되었습니다.
[](/images/agentcore-memory-that-remembers/S3-verify.png)
테어다운(Teardown)은 두 단계이며, 두 번째 단계는 삭제합니다:
make down
aia.py status 02
agentcore remove all -y
All schemas reset to empty state
agentcore deploy -y --target default
...
총 소요 시간(Total Duration)은 이 단계에서 기록되는 배포 로그(`evidence/18-down-deploy.txt`)에서 가져왔으며, 나머지는 콘솔 출력(`evidence/12-down-raw.txt`)입니다.
[](/images/agentcore-memory-that-remembers/S4-teardown.png)
삭제(Removal)는 로컬 계약(local contract)만 비울 뿐이며, 조정하는 배포(reconciling deploy)가 스택을 파괴합니다. 의도적으로 살아남는 한 가지 것이 있습니다: CDK 부트스트랩 스택과 그 에셋 버킷입니다. 메모리 리소스는 살아남지 않으므로, 재배포(re-deploy)는 비어 있는 상태에서 시작하여 모든 검증을 다시 획득합니다.
## 생성된 결과물
[](/images/agentcore-memory-that-remembers/F1-memory-architecture.png)
측정된 루프, 액터 하나, 세션 두 개:
두 액터가 각각 `/users/store_manager_north/preferences`와 `/users/store_manager_south/preferences`에 기록했습니다. '이 매장 관리자가 어느 지역을 재주문하는지'라는 의미론적 쿼리는 하나에게는 북중국(North China)을, 다른 하나에게는 남중국(South China)을 반환했고, 모델에게 '이번 주 재주문 목록 초안 작성.'이라고 요청하자 남부 매니저에게는 남중국이 나왔습니다. 액터 ID가 키의 일부이기 때문에 잘못된 필터링은 없습니다.
세 번째 단언은 저에게 무언가를 가르쳐 주었습니다. 에피소드 추출(Episodic extraction)은 느립니다. 에피소드는 발생한 사건으로부터 약 15분 후에 작성되었습니다. 이것이 고장 난 전략은 아니며, 단지 더 느린 대기열일 뿐이며, 300초의 마감 시간은 그것에 의해 실패할 수밖에 없습니다.
`/episodes/store_manager_north/`에 대한 접두사 읽기는 두 가지 종류의 기록을 반환합니다: 해당 세션 아래에 파일된 `BASE` 에피소드와, 그 모든 것을 아우르는 한 단계 위에서 작성된 `REFLECTION`입니다. 이것이 하나의 템플릿에는 `{sessionId}`가 있고 다른 하나에는 없는 이유입니다:
BASE /episodes/store_manager_north/195f957a...-78cd451f/
{"situation":"사용자가 재고 보충 실패를 보고했습니다... 9일 리드 타임"}
REFLECTION /episodes/store_manager_north/
...
한 번 추적된 호출에서 측정된 비용은, 두 모델 호출에 걸쳐 3,433개의 입력 토큰과 210개의 출력 토큰이었으며, 이는 각각 백만 토큰당 0.62달러와 1.85달러를 기준으로 0.0025달러입니다. 메모리 저장 및 추출 호출은 별도로 청구되므로, 저는 그 부분을 측정하지 않았습니다.
## 문제 해결 (Troubleshooting)
- `Invalid length for parameter runtimeSessionId, value: 22, valid min length: 33`. 짧고 읽기 쉬운 세션 ID는 괜찮아 보이며 전송되기 전에 실패합니다. 이제 스모크 테스트는 두 개의 uuid4 절반으로 ID를 생성하는데, 이는 16진수(hex) uuid가 32자이므로 최소 길이보다 한 글자가 부족하기 때문입니다.
- `no episodic record ... within 1500s`. 고장 난 전략이 아니라 단지 느린 대기열일 뿐입니다. 마감 시간을 관찰된 15분 이상으로 늘리고, 실제로 기다리는 시간이 얼마나 걸렸는지 출력하여 기사에 있는 숫자가 측정값임을 보여주세요.
## 주요 시사점 (Takeaways)
메모리(Memory)는 하나가 아니라 두 가지 메커니즘으로 구성됩니다. 직접 작성하는 이벤트 로그와 서비스 추출 기록입니다. 새 세션에서는 후자만 살아남습니다.
액터 ID를 네임스페이스 템플릿 안에 넣어주세요. 이렇게 하면 격리가 비용이 들지 않으며, 코드 경로에서 잊어버릴 염려가 없습니다.
추출(Extraction) 과정은 비동기적이고 느립니다. 에피소드당 15분 정도 걸립니다. 모든 단언(assertion)에는 마감 기한(deadline)이 필요하며, 이 마감 기한은 생각하는 것보다 길어야 합니다.
검색(Retrieval)은 모델 호출 전에 시스템 프롬프트에 포함되어야 하며, 모델이 선택할 수도 있고 아닐 수도 있는 도구(tool) 안에 넣으면 안 됩니다.
해체(Teardown)는 실제 과정입니다. `remove all`은 계약을 비우고, 그 뒤에 배포되는 것이 삭제를 수행합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기