Deep Agents 메모리(Memory) 다루기

Deep Agents 메모리(Memory) 다루기

메모리는 에이전트가 대화를 거듭하며 배우고 더 나아지게 만드는 기능이에요. Deep Agents는 파일시스템 기반 메모리로 메모리를 일급 시민으로 취급해요. 에이전트가 메모리를 파일로 읽고 쓰고, 그 파일이 어디에 저장될지는 backends로 내가 제어하죠. 이 글에서는 메모리가 동작하는 방식과 범위(scoping) 설정, 그리고 고급 사용법까지 차근차근 살펴볼게요.

출처: 공식문서

메모리 동작 방식 (How memory works)

  1. 에이전트를 메모리 파일로 연결합니다. 에이전트를 만들 때 memory=로 파일 경로를 넘겨줘요. 작업 수행 방법을 알려주는 재사용 가능한 지시사항인 절차적 메모리 skillsskills=로 넘길 수도 있어요. 파일이 어디 저장되고 누가 접근할 수 있는지는 backend가 제어합니다.
  2. 에이전트가 메모리를 읽습니다. 에이전트는 시작 시 메모리 파일을 시스템 프롬프트에 로드하거나, 대화 중 필요할 때 즉시 읽을 수 있어요. 예를 들어 skills은 온디맨드 로딩을 사용해요. 시작 시에는 스킬 설명만 읽고, 작업과 일치할 때만 전체 스킬 파일을 읽는 식이죠. 이렇게 하면 능력이 필요해지기 전까지 컨텍스트를 가볍게 유지해요.
  3. 에이전트가 메모리를 갱신합니다(선택). 새 정보를 배우면 에이전트가 내장 edit_file 도구로 메모리 파일을 갱신할 수 있어요. 갱신은 대화 중(기본값)에 일어나거나, background consolidation으로 대화 사이에 백그라운드에서 일어날 수 있어요. 변경 사항은 저장되어 다음 대화에서 사용할 수 있죠. 모든 메모리가 쓰기 가능한 건 아니에요. 개발자가 정의한 skills와 조직 정책은 보통 읽기 전용입니다.

가장 흔한 두 패턴은 모든 사용자가 공유하는 에이전트 범위 메모리(agent-scoped)와 사용자별로 격리된 사용자 범위 메모리(user-scoped)예요. 코딩 에이전트가 AGENTS.md로 발견하는 생성형 리포지토리 위키는 OpenWiki를 참고하세요.

범위 메모리 (Scoped memory)

에이전트 메모리는 같은 메모리 파일을 에이전트를 쓰는 모든 사람이 공유하도록, 또는 사용자마다 개별로 가지도록 범위를 설정할 수 있어요.

에이전트 범위 메모리 (Agent-scoped memory)

에이전트에게 시간이 지나며 진화하는 고유한 정체성을 주는 방식이에요. 에이전트 범위 메모리는 모든 사용자가 공유하므로, 에이전트가 매 대화를 통해 자신만의 페르소나·축적된 지식·학습된 선호도를 쌓아요. 사용자와 상호작용하며 전문성을 키우고 접근 방식을 다듬고 잘 되는 방법을 기억하죠. 쓰기 권한이 있으면 skills도 학습하고 갱신할 수 있어요. 핵심은 backend 네임스페이스예요. 네임스페이스를 (assistant_id,)로 설정하면 이 에이전트의 모든 대화가 같은 메모리 파일을 읽고 쓰게 돼요.

사용자 범위 메모리 (User-scoped memory)

각 사용자에게 고유한 메모리 파일을 주는 방식이에요. 핵심 에이전트 지시는 고정된 채, 에이전트가 사용자별 선호도·컨텍스트·이력을 기억하죠. 사용자 범위 backend에 저장하면 skills도 사용자별로 가질 수 있어요. 네임스페이스는 (user_id,)를 쓰므로 각 사용자가 격리된 메모리 파일 복사본을 얻어요. 사용자 A의 선호도가 사용자 B의 대화로 절대 새지 않아요.

고급 사용법 (Advanced usage)

메모리 경로·범위의 기본 설정 외에도 더 고급 파라미터를 구성할 수 있어요.

일화 메모리 (Episodic memory)

일화 메모리는 과거 경험의 기록을 저장해요. 무엇이, 어떤 순서로, 어떤 결과로 일어났는지를 담는 거죠. AGENTS.md 같은 파일에 저장되는 사실·선호도인 의미 메모리(semantic memory)와 달리, 일화 메모리는 전체 대화 컨텍스트를 보존해서 에이전트가 무엇을 배웠는지 뿐 아니라 어떻게 문제를 해결했는지도 기억할 수 있어요. 코딩 에이전트를 위한 리포지토리 수준 위키를 만들고 유지하려면 OpenWiki를 보세요. Deep Agents는 이미 일화 메모리를 뒷받침하는 메커니즘인 checkpointers를 쓰고 있어요. 모든 대화가 체크포인트 스레드로 영속화되거든요. 과거 대화를 검색 가능하게 만들려면 스레드 검색을 도구로 감싸면 돼요. 이때 user_id는 파라미터로 넘기는 게 아니라 런타임 컨텍스트에서 가져옵니다.

사용자나 조직 단위로 스레드 검색 범위를 제한하려면 메타데이터 필터를 조정하면 돼요.

이 방식은 복잡한 다단계 작업을 하는 에이전트에게 유용해요. 예를 들어 코딩 에이전트가 과거 디버깅 세션을 돌아보고 바로 근본 원인으로 건너뛸 수 있죠.

조직 수준 메모리 (Organization-level memory)

조직 수준 메모리는 사용자별 네임스페이스 대신 조직 전체 네임스페이스를 쓰는 것만 빼고 사용자 범위 메모리와 같은 패턴이에요. 조직의 모든 사용자·에이전트에 적용돼야 하는 정책이나 지식에 쓰면 좋죠. 조직 메모리는 보통 읽기 전용으로 두어 공유 상태를 통한 프롬프트 주입을 막아요.

애플리케이션 코드에서 조직 메모리를 채울 수 있어요. 조직 수준 메모리를 읽기 전용으로 강제하려면 permissions을 쓰고, 커스텀 검증 로직이 필요하면 policy hooks를 쓰세요.

백그라운드 통합 (Background consolidation)

기본적으로 에이전트는 대화 중(핫패스)에 메모리를 써요. 대안으로는 대화 사이에 백그라운드 작업으로 메모리를 처리하는 방식이 있는데, 가끔 sleep time compute라고도 불러요. 별도의 딥 에이전트가 최근 대화를 검토하고 핵심 사실을 뽑아 기존 메모리와 병합하지요.

대부분의 애플리케이션에서 핫패스로 충분해요. 많은 대화에서 지연을 줄이거나 메모리 품질을 높여야 한다면 백그라운드 통합을 추가하면 돼요. 권장 패턴은 메인 에이전트 옆에 **통합 에이전트(consolidation agent)**를 배포하는 거예요. 최근 대화 이력을 읽고 핵심 사실을 뽑아 메모리 저장소에 병합하는 딥 에이전트죠. 그리고 cron 스케줄로 트리거해요. 주기는 사용자가 실제로 에이전트와 대화하는 빈도를 반영해서 고르세요. 일일 트래픽이 꾸준한 채팅 프로덕트라면 몇 시간마다, 일주일에 몇 번 쓰는 도구라면 하루 또는 일주일에 한 번이면 충분해요. 사용자가 대화하는 것보다 훨씬 자주 통합하면 무의미한 실행에 토큰만 태우게 돼요.

통합 에이전트 (Consolidation agent)

통합 에이전트는 최근 대화 이력을 읽고 핵심 사실을 메모리 저장소에 병합해요. langgraph.json에 메인 에이전트 옆에 함께 등록합니다. (consolidation_agent.py, langgraph.json)

크론 (Cron)

cron job이 통합 에이전트를 고정 스케줄로 실행해요. 에이전트가 최근 대화를 검색해서 메모리로 합성하지요. 통합이 실제 활동을 대략 따라가도록 스케줄을 사용 패턴에 맞추세요. 통합 에이전트를 cron job으로 예약합니다.

백그라운드 프로세스로 에이전트를 배포하는 더 자세한 내용은 going to production 문서를 참고하세요.

읽기 전용 vs 쓰기 가능 메모리 (Read-only vs writable memory)

기본적으로 에이전트는 메모리 파일을 읽고 쓸 수 있어요. 조직 정책이나 컴플라이언스 규칙 같은 공유 상태는 메모리를 읽기 전용으로 만들고 싶을 수 있는데, 그러면 에이전트가 참조만 하고 수정은 못 해요. 이렇게 하면 공유 메모리를 통한 프롬프트 주입을 막고, 파일에 뭐가 들어 있는지를 애플리케이션 코드만이 제어하게 돼요.

보안 고려사항: 한 사용자가 다른 사용자가 읽는 메모리에 쓸 수 있다면, 악의적인 사용자가 공유 상태에 지시를 주입할 수 있어요. 이를 완화하려면:

  • 특별한 공유 이유가 없는 한 기본적으로 사용자 범위 (user_id)를 쓰세요
  • 공유 정책에는 읽기 전용 메모리를 쓰세요(에이전트가 아닌 애플리케이션 코드로 채우기)
  • 에이전트가 공유 메모리에 쓰기 전에 human-in-the-loop 검증을 넣으세요. 민감한 경로에 대한 쓰기에 사람 승인을 요구하려면 interrupt를 사용하세요.

읽기 전용 메모리를 강제하려면 permissions으로 특정 경로에 대한 쓰기를 선언적으로 거부해요. 커스텀 검증 로직(레이트 리밋, 감사 로깅, 콘텐츠 검사)에는 backend policy hooks를 쓰세요.

동시 쓰기 (Concurrent writes)

여러 스레드가 메모리에 병렬로 쓸 수 있지만, 같은 파일에 대한 동시 쓰기는 last-write-wins 충돌을 낼 수 있어요. 사용자 범위 메모리에서는 사용자가 보통 한 번에 하나의 활성 대화를 가지므로 드물어요. 에이전트 범위나 조직 범위 메모리에서는 백그라운드 통합으로 쓰기를 직렬화하거나, 주제별로 별도 파일로 메모리를 구성해 경합을 줄이세요. 실제로 충돌로 쓰기가 실패해도 LLM은 보통 재시도하거나 우아하게 복구하므로, 손실된 쓰기 하나가 치명적이진 않아요.

같은 배포의 여러 에이전트 (Multiple agents in the same deployment)

공유 배포에서 각 에이전트가 자기만의 메모리를 갖게 하려면 네임스페이스에 assistant_id를 추가해요. 사용자 범위 없이 에이전트 단위 격리만 필요하면 assistant_id만 쓰면 됩니다.

더 알아보기 (Learn more)

  • OpenWiki: 코딩 에이전트가 AGENTS.md로 찾는 리포지토리 위키 생성·유지
  • Backends: 메모리 파일 저장 위치 선택
  • Context engineering: 단기 메모리, 오프로딩, 요약
  • Skills: 온디맨드 절차적 메모리