8: Memory Management (ko)
패턴 요약
메모리 관리는 에이전트가 턴 간·세션 간으로 일관성을 유지하게 해줍니다. 장에서는 단기 맥락 메모리(현재 대화의 일관성 유지)와 장기 지속 메모리(지속적 사실/선호/학습 경험/지시사항을 저장)를 구분합니다.
에이전트가 대화 기록 추적, 작업 진행 관리, 개인화 응답, 이전 지식 재조회 또는 이전 상호작용 학습이 필요할 때 이 패턴을 사용합니다. 컨텍스트 창은 제한되고 휘발성이므로, 영속적 메모리는 전체 대화 텍스트에 의존하는 방식이 아니라 명시적 저장·조회 메커니즘이 있어야 합니다.
구현에서는 메모리 관리가 제어된 읽기/쓰기 루프처럼 동작해야 합니다. 응답 생성 전 관련 메모리를 조회하고, 프롬프트에는 유용한 단기 컨텍스트만 유지하고, 턴 후 내구성 있는 메모리 후보를 추출·검증해 안전하고 유용한 항목만 예측 가능한 이름공간과 키로 저장합니다.
패턴 설명
개념 개요
메모리 관리는 "무엇을 기억할지, 어디에 둘지, 언제 꺼낼지"를 결정하는 일입니다. 단기 메모리는 현재 스레드의 작업 컨텍스트를 담당하고, 장기 메모리는 개별 대화 밖에서 살아남는 외부 저장소입니다.
이 장에서는 메모리를 상태 없는 질문응답형 시스템을 넘어, 상태를 유지하는 에이전트의 실무적 요구사항으로 봅니다. 메모리 인식 에이전트는 스레드를 재개하고, 사용자 선호를 기억하며, 과거 성공 전략을 재사용하고, 현재 프롬프트가 모두 포함하지 못하는 관련 지식을 조회할 수 있습니다.
문제
LLM 호출은 애플리케이션이 컨텍스트를 제공하지 않으면 기본적으로 상태가 없습니다. 각 턴을 독립 처리하면 에이전트는 과거 메시지를 잊고, 진행 상태를 잃고, 개인화를 못 하며, 과거 지식을 재사용하지 못합니다. 모든 이전 텍스트를 프롬프트에 쑤셔넣으면 컨텍스트 창 초과, 비용 증가, 노이즈/오래된 정보 유입이 생깁니다.
메모리 관리는 즉시 상태와 영속 지식을 분리하고, 조회와 저장을 워크플로의 명시적 단계로 만듦으로써 이를 해결합니다.
사용 시점
- 동일 스레드 내 다턴 대화의 일관성이 필요할 때 사용합니다.
- 단계별 작업을 재개하거나 진행 상황을 추적해야 할 때 사용합니다.
- 선호, 프로필, 과거 이슈 기반의 개인화가 필요할 때 사용합니다.
- 이전 대화 또는 외부 지식을 의미론적으로 검색해야 할 때 사용합니다.
- 재사용 가능한 예시, 성공 워크플로, 절차 지침을 저장해야 할 때 사용합니다.
- 컨텍스트 창 제한으로 관련 메모리만 요약/필터링/조회해야 할 때 사용합니다.
사용하지 말아야 할 때
- 정보 영속화가 필요 없는 단일 요청 작업에는 이 패턴을 쓰지 않습니다.
- 보존/동의/삭제/개인정보 규칙이 없으면 장기 메모리를 피합니다.
- 모든 상호작용을 필터 없이 저장하면 조회 품질이 떨어지므로 사용하지 않습니다.
- 오래되었거나 상충된 사실이 반복될 수 있으면 영구 개인화를 피하거나 재확인 로직이 있어야 합니다.
- 결정론적 워크플로가 입력 상태를 모두 받는 경우 외부 메모리 저장소 사용은 불필요할 수 있습니다.
- 자기 업데이트 방식은 리뷰·롤백·평가 통제가 없다면 사용하지 않습니다.
작동 방식
- 그래프는 사용자 메시지와 스레드 ID를 받으며, 장기 메모리에는 사용자/앱 네임스페이스를 사용합니다.
- 단기 메모리는 그래프 상태 또는 체크포인터에서 최근 메시지, 요약, 작업 진행을 불러옵니다.
- 장기 메모리를 네임스페이스와 쿼리로 검색해 관련 사실/선호/예시/지시를 가져옵니다.
- 현재 입력, 최근 스레드 컨텍스트, 선택적 요약, 검색된 메모리를 결합해 제한된 프롬프트를 만듭니다.
- 모델이 현재 턴 및 호출된 메모리에 기반해 응답을 생성합니다.
- 메모리 추출 단계에서 새로운 턴에서 보존할 사실/업데이트 후보를 찾습니다(선호도, 수정된 프로필 데이터 등).
- 검증 단계에서 일시적/민감/근거 불충분/중복/충돌 후보를 필터링합니다.
- 승인된 메모리는 예측 가능한 네임스페이스와 키로 장기 저장소에 저장합니다.
- 최종화 단계에서 조회/저장/건너뜀/플래그 처리 메타데이터를 기록합니다.
트레이드오프
| 이점 | 비용 또는 위험 |
|---|---|
| 다턴 대화 일관성을 유지합니다. | 상태 및 체크포인터 관리가 정교해야 합니다. |
| 세션 간 개인화를 지원합니다. | 영속 메모리는 개인정보, 동의, 삭제 책임을 동반합니다. |
| 관련 메모리만 조회해 프롬프트 부풀림을 줄입니다. | 검색 결과가 오래되거나 관련없는 정보일 수 있습니다. |
| 과거 상호작용과 예시로부터 학습을 가능하게 합니다. | 잘못된 추출은 노이즈나 환각 사실을 저장할 수 있습니다. |
| 작업 진행 재개가 가능합니다. | 동시 쓰기 시 키 규칙이 없으면 덮어쓰기/중복이 발생합니다. |
| 절차 지침을 시간에 따라 진화시킬 수 있습니다. | 자기수정 지침은 평가·롤백 없는 경우 성능 저하를 일으킬 수 있습니다. |
최소 예시
1차 대화:
사용자: "아침 비행과 통로 좌석을 선호해요."
-> 내구성 선호도 추출
-> ("user-123", "travel_preferences")에 저장
이후 대화:
사용자: "덴버 가는 항공편 찾아줘."
-> 스레드 컨텍스트 로드
-> 여행 선호도 메모리 조회
-> 아침 비행/통로 좌석 선호 반영해 응답 생성
-> 턴에서 새로 생긴 내구성 선호도 저장
LangGraph 매핑
| 패턴 개념 | LangGraph 요소 |
|---|---|
| 현재 대화 스레드 | messages 같은 상태 필드, 체크포인터로 영속화 |
| 제한된 작업 컨텍스트 | short_term_summary, recent_messages, prompt_context 같은 상태 필드 |
| 장기 메모리 저장소 | LangGraph BaseStore / InMemoryStore 또는 운영형 저장소 |
| 사용자/앱 메모리 범위 | 네임스페이스 예: (user_id, "profile"), (app_id, "procedures") |
| 특정 기억 사실 | 네임스페이스 키 + JSON 값(예: "travel_preferences") |
| 의미론적 호출 | 현재 요청을 쿼리로 사용하는 store search() |
| 메모리 추출 | extract_memory_candidates 같은 노드 |
| 검증/필터 | validate_memory_updates 같은 노드 |
| 메모리 쓰기 | store_memory_updates 같은 노드 |
| 조회/쓰기 결정 | has_retrieval_error, has_memory_updates, needs_review 조건부 간선 |
LangGraph 구현 목표
여행 도우미용 메모리 인식 예제를 만듭니다. 사용자가 내구성 있는 선호도(예: 선호 출발 시각, 좌석, 호텔 제약, 식단 요구)를 전달하면, 같은 사용자/다른 스레드에서도 이를 검색해 다음 요청에 반영해야 합니다.
예제는 장의 두 메모리 유형을 모두 보여주어야 합니다.
- 단기 메모리: 스레드 단위 메시지·진행 상태를 LangGraph 체크포인터로 유지.
- 장기 메모리: 사용자 범위 사실을 JSON 문서로 LangGraph store에 네임스페이스/키로 저장·조회.
모든 사용자 문장을 저장해서는 안 됩니다. 내구성 있고 사용자 관련인 사실만 추출·검증하고, 건너뛴 항목과 그 이유를 기록해야 합니다. 최종 응답에는 어떤 메모리를 조회했고 어떤 메모리를 저장했는지 테스트가 단정할 수 있는 메타데이터가 포함돼야 합니다.
상태 형태
그래프가 필요한 상태 필드를 나열합니다.
| 필드 | 타입 | 목적 |
|---|---|---|
input | str | 원본 사용자 입력 또는 작업 설명. |
user_id | str | 장기 메모리 네임스페이스에 쓰일 사용자 식별자. |
thread_id | str | 체크포인터에서 사용하는 대화 스레드 ID. |
messages | list | 현재 스레드 메시지 (단기 메모리). |
short_term_summary | str | 메시지 이력 과다 시 사용되는 압축 요약. |
retrieval_query | str | 현재 입력을 바탕으로 생성한 장기 메모리 검색 쿼리. |
retrieved_memories | list[dict] | 조회된 메모리 목록(네임스페이스, 키, 값, 점수 포함). |
prompt_context | dict | 입력, 최근 메시지, 요약, 조회 메모리를 결합한 모델 입력 컨텍스트. |
response | str | 현재 턴의 응답. |
memory_candidates | list[dict] | 현재 턴에서 추출한 후보 사실/선호/예시/지시 업데이트. |
approved_memory_updates | list[dict] | 장기 저장으로 확정된 메모리 후보. |
skipped_memory_updates | list[dict] | 일시적·중복·민감·근거 부재 등 이유로 버린 후보. |
memory_write_results | list[dict] | 저장 성공/실패 메타데이터와 네임스페이스·키 결과. |
errors | list[str] | 조회·검증·모델·저장 처리 중 오류 목록. |
status | str | 워크플로 상태: ok, needs_review, failed. |
final_output | dict | 사용자 응답 + 조회/저장 메모리 메타데이터. |
노드
| 노드 | 책임 |
|---|---|
prepare_turn | input, user_id, thread_id 검증 및 기본 필드 초기화, 사용자 메시지 단기 상태 반영. |
build_retrieval_query | 현재 입력과 관련 단기 컨텍스트를 바운드된 검색 쿼리로 변환. |
retrieve_long_term_memory | 사용자 범위 스토어 네임스페이스에서 관련 메모리를 검색. |
compact_short_term_memory | 컨텍스트가 큰 경우 오래된 메시지를 요약/축약. |
build_prompt_context | 입력, 최근 메시지, 요약, 조회 메모리를 합쳐 제약된 모델 컨텍스트를 만듦. |
generate_response | 준비된 컨텍스트와 조회 메모리를 활용해 답변 생성. |
extract_memory_candidates | 입력/응답에서 영속 저장할 가치가 있는 사실·선호를 추출. |
validate_memory_updates | 근거 부족/일시적/민감/중복/충돌 후보를 제거하고 승인 항목을 정규화. |
store_memory_updates | 승인 항목을 결정론적 네임스페이스·키로 저장. |
finalize | 응답 본문과 상태(status, 조회/저장 목록, 건너뜀, 오류)를 반환. |
mark_needs_review | 메모리 충돌, 개인정보 이슈, 스토어 실패 등으로 사람이 개입해야 할 메타데이터와 함께 종료. |
엣지
조건부 분기를 포함해 그래프 흐름을 정의합니다.
START -> prepare_turn -> build_retrieval_query -> retrieve_long_term_memory
retrieve_long_term_memory -> compact_short_term_memory -> build_prompt_context -> generate_response
generate_response -> extract_memory_candidates -> validate_memory_updates
validate_memory_updates -> store_memory_updates -> finalize -> END
validate_memory_updates -> finalize -> END
validate_memory_updates -> mark_needs_review -> END
retrieve_long_term_memory -> mark_needs_review -> END
조건부 엣지 요구사항:
retrieve_long_term_memory에서 검색 성공/무결과 여부와 무관하게compact_short_term_memory로 이동합니다.- 조회가 정확성에 위험을 주거나 개인화가 오해될 수 있을 만큼 실패하면
mark_needs_review로 이동합니다. - 승인된 메모리 업데이트가 하나 이상 있으면
store_memory_updates로 이동합니다. - 승인 항목이 없고 리뷰 사유도 없으면
finalize로 바로 이동합니다. - 후보가 기존 메모리와 충돌하거나 민감 데이터로 정책 검토가 필요하거나 정규화 실패 시
mark_needs_review로 이동합니다. store_memory_updates는 저장 일부 실패가 나더라도 응답을 보존하고, 실패는errors와final_output에 노출해야 합니다.
입력과 출력
- 입력: 사용자 메시지,
user_id,thread_id. - 출력: 응답 텍스트와 상태 및 메모리 메타데이터.
- 중간 산출물: 검색 쿼리, 조회 메모리, 단기 요약, 프롬프트 컨텍스트, 메모리 후보, 승인/건너뜀 항목, 쓰기 결과.
예시 입력 형태:
{
"input": "Please find flight options for my Denver trip next month.",
"user_id": "user-123",
"thread_id": "thread-456"
}
성공 출력 예시:
{
"status": "ok",
"response": "I will prioritize morning flights and aisle seats for the Denver options.",
"retrieved_memories": [
{
"namespace": ["user-123", "travel"],
"key": "preferences",
"value": {
"flight_time": "morning",
"seat": "aisle"
}
}
],
"stored_updates": [],
"skipped_updates": [
{
"candidate": "Find me a flight to Denver.",
"reason": "transient request, not durable memory"
}
],
"errors": []
}
실패 사례
예상 실패, 재시도, 폴백 동작, 사람 검토 지점을 문서화합니다.
- 입력이 비어 있으면 모델 호출 전에 실패하고
status를failed로 설정합니다. user_id누락은 정책에 따라 장기 메모리 비활성화 또는 명확한 검증 실패 처리해야 합니다.thread_id가 비면 체크포인터 단기 메모리 사용 전에 실패해야 합니다.- 조회 스토어가 없거나 실패해도 메모리 없음으로 위장하면 안 되며, 오류를 기록하고 계속 진행할지/검토할지 결정해야 합니다.
- 관련 없는 조회 결과는 프롬프트 구성 전 필터링해야 모델 혼란을 줄입니다.
- 컨텍스트 초과는
compact_short_term_memory로 처리하고 무한 기록을 넣지 않습니다. - 현재 발화에 근거하지 않은 후보는 저장하지 않고 건너뜁니다.
- 민감/규제 대상 정보는 보존 정책이 명시된 경우만 저장해야 합니다.
- 선호 변경 같은 충돌 업데이트는 충돌 메타데이터와 함께 교체하거나
mark_needs_review를 사용합니다. - 중복 후보는 기존 키 갱신이나 생략으로 처리해 반복 메모리 생성을 방지합니다.
- 저장 실패 시 사용자 응답은 유지하되 저장 성공으로 오인하지 않도록 오류를 명시합니다.
- 절차 메모리 업데이트는 향후 동작에 큰 영향을 미치므로 더 엄격한 검증 또는 리뷰가 필요합니다.
테스트 아이디어
- 사용자 선호를 영속 저장하고 지정한 네임스페이스/키로 저장되는 정상 경로를 검증합니다.
- 동일
user_id의 다른 스레드에서 저장 선호가 조회되어 응답 메타데이터에 반영되는지 검증합니다. - 체크포인터를 통해 스레드 메시지가 유지되는지 검증해 단기 연속성 확인.
"Book a flight to Denver"같은 일시 요청이 장기 저장으로 건너뜀 처리되는지 검증.- 기존 사실의 중복만 추출된 경우 저장이 발생하지 않는지 검증.
- 상충되는 선호를 충돌 메타데이터 업데이트 또는
needs_review로 처리하는지 검증. - 조회 오류가
errors에 남고 빈 결과로 위장되지 않는지 검증. - 메시지 이력 초과 시 컨텍스트 압축이 실행되는지 검증.
final_output에 항상status,response,retrieved_memories,stored_updates,skipped_updates,errors가 포함되는지 검증.- 네트워크 없이 InMemory store와 모킹된 모델로 테스트 가능한지 검증.
열린 질문
docs/agentic-design-patterns-toc.md는 Chapter 8을121-141로 기록하지만, 보이는 추출은 1-based 페이지132-153, 0 기반131-152(로컬 chapter 카운터1-22)로 시작합니다. 향후 문서에서 TOC 논리 페이지와 보이는 PDF 구간 중 어떤 기준을 표준화할지 확인이 필요합니다.- 추출된 Chapter 8 페이지는 chapter-local 푸터 번호가
1-22이며 TOC 범위는 21페이지입니다. 동일한 모순이 있으므로 두 표기 모두 유지하고 강제 변환은 피할지 검토가 필요합니다. - 장에서 ADK, LangChain, LangGraph, Vertex Memory Bank를 언급합니다. 첫 구현은 네이티브 체크포인터+스토어 동작에 집중하고, 외부 관리형 메모리 서비스는 범위 밖으로 둬야 할 수 있습니다.
- 변경된 사용자 선호를 profile 단일 JSON 덮어쓰기, 개별 사실 문서 추가, 또는 둘 다 지원할지 판단이 필요합니다.
- 민감 메모리 필터링을 장 패턴에서 경량 결정론 검증기로 할지, 향후 Guardrails/Safety 패턴에서 처리할지 결정이 필요합니다.