
지속성 메모리는 지속성 실행 상태가 아니다
요약
AI 에이전트 설계 시 흔히 발생하는 '메모리 지속성'과 '실행 상태 지속성'의 혼동을 지적합니다. 에이전트가 과거의 기록을 기억하는 것과 실제 머신에서 프로세스가 살아있는 상태를 유지하는 것은 완전히 다른 차원의 문제임을 설명합니다.
핵심 포인트
- 메모리 지속성은 에이전트의 기록일 뿐, 실제 실행 상태를 보장하지 않음
- 실행 상태는 CPU 컨텍스트, 메모리 페이지 등 실제 머신의 라이브 튜플임
- 에이전트가 기록한 정보와 실제 시스템 상태 사이의 괴리(범주 오류) 주의 필요
- AI 네이티브 컴퓨팅을 위한 새로운 아키텍처 계층 정의의 필요성
AI 에이전트(AI-agent) 설계에서 가장 흔히 발생하는 범주 오류 — 그리고 대화를 기억하는 것이 런타임(runtime)을 유지하는 것과 왜 같지 않은지에 대하여.
docs.cmdop.com/blog/execution-state-continuity-02-memory-vs-execution-state에서 처음 게시됨 — The Command-Operator Execution Layer 시리즈의 일부입니다.
당신의 에이전트는 대화를 기억합니다. 개발 서버가 여전히 3000번 포트에서 실행 중이라고 확신합니다. 하지만 실제로는 그렇지 않습니다 — 클라이언트가 연결을 해제했을 때 프로세스(process)가 종료(reaped)되었습니다.
이 문장에는 현대 AI 에이전트(AI-agent) 설계에서 가장 비용이 많이 드는 단 하나의 오해가 담겨 있습니다. 에이전트는 기쁘게 서버를 시작했다고 당신에게 말할 것입니다. 명령을 로그(log)로 남겼을 것입니다. 그 단계를 자신의 작업 노트에 요약했을 것입니다. 심지어 몇 달 동안 충실히 살아남을 memory.md 파일에 "dev server running on :3000"이라고 작성했을지도 모릅니다. 그리고 그 기록의 모든 단어는 _에이전트가 무엇을 했는지_에 대해서는 사실입니다. 하지만 그 중 어느 것도 _현재 머신(machine)에서 무엇이 일어나고 있는지_에 대해서는 사실이 아닙니다. 런처(launcher)는 종료 코드(exit code) 0을 반환했고, 클라이언트가 연결을 해제했을 때 프로세스(process)는 종료(reaped)되었으며, 에이전트는 이제 더 이상 존재하지 않는 환경을 바탕으로 자신 있게 추론하고 있습니다.
업계는 에이전트가 _기억(remember)_하게 만들기 위해 지난 2년 동안 놀라운 기계 장치들을 구축해 왔습니다. 벡터 데이터베이스 (Vector databases), 계층적 요약 (hierarchical summarization), 에피소드 및 의미론적 메모리 계층 (episodic and semantic memory layers), 프로필 파일 (profile files), 검색 파이프라인 (retrieval pipelines) 등이 그것입니다. 이러한 작업은 실질적이고 가치 있는 일입니다. 하지만 이 과정에서 대부분의 팀이 인지하지 못할 정도로 만연한 범주 오류 (category error)가 조용히 발생했습니다. 바로 **메모리 지속성 (memory persistence)**과 **실행 상태 지속성 (execution-state persistence)**을 혼동하는 것입니다. 이 둘은 동일한 것의 두 가지 등급이 아닙니다. 이들은 서로 다른 객체이며, 서로 다른 프리미티브 (primitives), 서로 다른 수명, 그리고 — 가장 중요하게는 — 서로 다른 실패 모드 (failure modes)를 가집니다. 이 글은 그 둘 사이의 경계에 관한 것이며, AI 네이티브 컴퓨팅 (AI-native computing)을 위한 누락된 아키텍처 계층을 정의하는 첫 번째 구분을 제시합니다: 실행 상태 지속성은 메모리 지속성이 아닙니다.
두 가지 서로 다른 객체
정확한 정의부터 시작합시다. 모든 혼란은 모호한 언어에서 비롯되기 때문입니다.
**실행 상태 (Execution state)**는 실제 머신에서 지금 실행 중인 것들의 라이브 튜플 (live tuple)입니다:
S_e(t) = ( P_cpu , M_pages , F_fd , N_sock , T_pty )
P_cpu— 프로세서 실행 컨텍스트 (processor execution context): 프로그램 카운터 (program counter), 레지스터 (registers), 프로세스 트리 (process tree) 내의 모든 실행 중인 프로세스의 위치.M_pages— 가상 메모리 페이지 (virtual memory pages): 모든 라이브 프로세스의 힙 (heap), 스택 (stack), 매핑된 라이브러리 (mapped libraries).F_fd— 열린 파일 디스크립터 테이블 (open file-descriptor table): 활성 오프셋 (active offsets), 잠금 (locks), 파이프 (pipes), 반쯤 완료된 마이그레이션이 데이터베이스에 유지하고 있는 핸들 (handle).N_sock— 네트워크 소켓 (network sockets): Postgres에 대한 열린 연결, :3000에 바인딩된 리스너 (listener), 진행 중인 TCP 상태 (in-flight TCP state).T_pty— 의사 터미널 (pseudo-terminal) 구성: 대화형 셸 (interactive shell), 스크롤백 버퍼 (scrollback buffer), 비밀번호 프롬프트가 현재 stdin에서 차단 중임을 알고 있는 라인 디시플린 (line discipline).
실행 상태는 _무엇이 일어나고 있는가_에 대한 것입니다. 이는 프로세스가 존재하는 동안에만 존재합니다. 기본적으로 프로세스 트리가 소멸하거나 이를 생성한 클라이언트가 연결을 끊는 즉시 소멸됩니다.
**메모리 상태 (Memory state)**는 완전히 다른 것입니다. 즉, _무슨 일이 일어났는지_에 대한 영구적인 기록입니다:
- 메시지 배열 (message arrays) 및 대화 기록 (conversation transcripts),
- 임베딩 (embeddings) 및 벡터 인덱스 (vector indexes),
- 롤링 요약 (rolling summaries) 및 압축된 컨텍스트 (compacted context),
- 프로필 및 규칙 파일 (
user.md,memory.md, 페르소나 사양).
메모리 상태는 텍스트와 벡터입니다. 이는 디스크에 깔끔하게 직렬화 (serialize) 되고, 간단하게 복제 (replicate) 되며, 단일 세션보다 더 오래 지속되도록 설계되었습니다. 이것은 에이전트의 자서전입니다.
실수는 완벽한 자서전을 마치 맥박 (pulse)인 것처럼 취급하는 것입니다. 다음은 원시적인 수준에서 나열된 대조입니다:
시스템은 왼쪽 열에서는 완벽할 수 있지만, 오른쪽 열에는 아무것도 없을 수 있습니다. 이것이 오늘날 출시되는 거의 모든 에이전트 프레임워크의 기본 상태입니다.
실행 상태 지속성 (execution-state persistence)만이 해결할 수 있는 실패 모드 (Failure modes)
만약 메모리 지속성 (memory persistence)만으로 충분했다면, 이는 단순한 어휘상의 논쟁에 그쳤을 것입니다. 하지만 그렇지 않습니다. 왜냐하면 어떠한 양의 메모리로도 방지할 수 없는 실패 유형이 존재하기 때문입니다. 이는 에이전트의 세계 모델 (model of the world)과 실제 실행 중인 머신 사이의 정렬 (alignment) 실패입니다.
조용한 백그라운드 프로세스 사망 (Silent background-process death). 에이전트가 npm run dev &를 실행하고, 런처가 0을 반환하면, 에이전트는 성공을 기록합니다. 10분 후 클라이언트 세션이 종료되면, 고립된 (orphaned) 프로세스도 함께 회수됩니다. 에이전트의 다음 턴에서 curl localhost:3000을 실행하면 연결 거부 (connection-refused)를 받게 됩니다. 이제 에이전트는 코드의 버그가 아니라, 자체적인 런타임 연속성 (runtime continuity) 결여로 인한 결과인 이 실패를 진단하기 위해 LLM 호출을 소모해야 합니다. 메모리는 _서버를 실행하려는 의도_를 기억합니다. 오직 실행 상태 (execution state)만이 서버가 _지금 소켓에서 리스닝 중인지_를 알려줄 수 있습니다.
에이전트가 볼 수 없는 대화형 차단 상태 (Interactive blocked states). 명령어가 인증 요구(authentication challenge), apt 설정 화면, 또는 sudo 비밀번호 프롬프트에 부딪힙니다. 프로세스는 살아있지만, 에이전트의 상태가 없는(stateless) 명령 래퍼(command wrapper)가 소유하지 않은 PTY 라인 규율(line discipline)에서 표준 입력(stdin)을 기다리며 차단됩니다. 지속적인 터미널이 없다면, 에이전트는 프롬프트가 아닌 멈춤(hang) 상태로 인식합니다. 에이전트는 "열심히 작업 중"인 상태와 "절대 받지 못할 입력을 기다리는 중"인 상태를 구분할 수 없습니다. 지속적인 실행 상태(persistent execution-state) 프리미티브(primitive)가 있다면 PTY 차단 패턴을 관찰하고, 모델이나 인간으로부터 오는 입력을 라이브 표준 입력(stdin) 스트림으로 전달할 수 있습니다.
충돌 후의 비결정론적 재실행 (Non-deterministic re-execution after a crash). 런타임(runtime)이 일시적(ephemeral)이고 메모리만 살아남는 경우, 복구는 곧 "다시 하기"를 의미합니다. 에이전트는 절반만 적용된 마이그레이션을 다시 실행하고, 의존성(dependency)을 다시 설치하며, API 호출을 다시 보냅니다. 물리적으로 무엇이 완료되었는지에 대한 지속적인 실행 그래프(execution graph)가 없다면, "재개(resume)"는 "재시작(restart)"으로 무너집니다. 그리고 부분적으로 변이된(partially-mutated) 환경에 대한 재시작은 중복 쓰기와 상태 오염을 초래하는 원인이 됩니다.
환각된 환경 정렬 (Hallucinated environment alignment). 이것이 가장 심각한 문제입니다. 모델은 상태가 없는(stateless) 호출로부터 반환된 텍스트를 바탕으로 시스템에 대한 전체 그림을 구축합니다. 런처(launcher)가 깔끔하게 종료되었기 때문에 프로세스가 살아있다고 가정합니다. 모든 것을 시작했다는 완벽한 기억에 의해 강화된 모델의 내부 서사(internal narrative)는 실제 프로세스 트리인 P_cpu와 조용히 어긋납니다. 에이전트는 거짓말을 하는 것이 아닙니다. 에이전트는 더 이상 현실과 일치하지 않는 기계를 진심으로 묘사하고 있는 것입니다. 애초에 라이브 상태(live state)에 연결된 적이 없기 때문입니다.
공통된 맥락을 주목하십시오. 이 모든 것들은 구조적으로 메모리에게 보이지 않습니다. 메모리는 결정(decisions)의 기록입니다. 반면 이것들은 *결정이 실행된 기질(substrate)*의 실패입니다.
UI보다 오래 살아남은 힙 (The heap that outlived its UI)
라이브 세션(live session)의 긴 계보 중 한 가지 사례가 메모리 대 실행(memory-versus-execution)의 구분을 유난히 명확하게 만들어 주므로, 딱 그 하나만 살펴보겠습니다. (Part 1에서는 화면, tmux, tmate, Guacamole, 클라우드 워크스페이스, Live Share, 에이전트 런타임(agent runtimes)에 이르는 전체 흐름을 다루지만, 여기서는 단 하나의 핵심적인 사례만 다룹니다.)
**Jupyter 커널 (Jupyter kernel)**은 라이브 _힙 (heap)_을 이를 표시하는 UI로부터 분리했습니다. 독립적인 커널 프로세스가 변수 테이블, 임포트(imports), 열려 있는 연결들을 메모리에 유지하는 동안, 노트북 클라이언트들은 ZeroMQ를 통해 연결을 끊고 다시 연결해도 계산 내용을 잃지 않습니다. 이것이 바로 이 글에서 다루고자 하는 구분의 정확한 형태입니다. 노트북의 셀 (cells) — 디스크에 저장된 코드와 마크다운 — 은 메모리 상태(memory state)이며, 당신이 입력한 내용의 자서전입니다. 반면 커널의 상주 힙(resident heap)은 실행 상태(execution state)이며, 지금 바로 실행 중인 것입니다. 저장된 노트북을 어떤 머신에서든 다시 열어 모든 셀을 읽을 수 있지만(메모리는 완벽하게 지속됨), 해당 변수를 보유했던 커널이 종료되었기 때문에 df가 정의되지 않은 상태를 발견할 수 있습니다(실행 상태가 사라짐). Jupyter는 힙의 연속성 문제를 해결했지만, 이를 하나의 커널 프로세스에 종속시켰으며 오직 대화형 노트북에 대해서만 가능하게 했습니다. 이는 초기 단계에서 라이브 런타임(live runtime)을 클라이언트와 분리 가능한 것으로 취급하려 했던 분야의 가장 근접한 시도였습니다. 다만 _실행 상태 그 자체_를 일급 객체(first-class object)로 취급하는 단계에는 한 끝 차이로 미치지 못했습니다.
에이전트 시대는 그 간극을 물려받아 오히려 악화시켰습니다. 에이전트들이 그 위에 훌륭한 메모리 계층을 추가하면서도, 런타임은 여전히 이전처럼 일회용(disposable)으로 남겨두었기 때문입니다.
현재 시스템의 위치
두 열(column)을 기준으로 지형을 그려보면 그 패턴은 극명하게 드러납니다.
메모리는 풍부하지만 실행 상태(execution-state)는 빈약한 시스템들이 에이전트 프레임워크(agent-framework) 카테고리를 지배하고 있습니다. 대화형 메모리(conversational memory)를 중심으로 구성된 아키텍처들 — Hermes-class 설계나 대부분의 LangChain 또는 AutoGen 스타일의 루프(loop) — 는 왼쪽 열에 집중적으로 투자합니다. 즉, 과거 세션 전체에 대한 전체 텍스트 검색(full-text search)이 가능한 SQLite, 계층화된 SOUL/USER/MEMORY 컨텍스트 파일, 에피소드 및 시맨틱 저장소(episodic and semantic stores)에 대한 검색(retrieval) 등이 그것입니다. 반면, 이들의 실행 백엔드(execution backend)는 플러그인 방식이며 일회용입니다. 페이로드(payload)를 새로운 컨테이너나 서버리스 워커(serverless worker)로 보내고, 텍스트 출력을 수집한 뒤, 프로세스를 폐기합니다. 프로세스 계보(process lineage)는 분리되어 있고, 힙(heap)은 복구할 수 없으며, 코어(core) 내에 네이티브 PTY 표현이 존재하지 않습니다. 이들은 완벽한 자서전을 가지고 있지만, 턴(turn) 사이에는 맥박이 없습니다.
이와 대조되는 것은 무게 중심이 _살아있는 지속성 워크스페이스(live persistent workspace)_인 시스템들입니다. 모든 명령을 지속적인 셸(shell)을 통해 라우팅하고, 턴이 바뀌어도 인터프리터 커널(interpreter kernel)을 상주시키는 컨테이너 기반 개발 샌드박스(OpenHands 스타일의 액션 실행 서버가 명확한 공개 사례임)는 런타임(runtime)을 열어두어 cd, 환경 변수, 설치된 패키지 또는 로드된 변수가 하나의 액션에서 다음 액션으로 이어지게 합니다. 또한 인간이 작업 중간에 동일한 라이브 파일시스템(filesystem)과 터미널에 접속할 수 있습니다. microVM 스냅샷 엔진(E2B)은 파일시스템, 메모리, 프로세스를 게스트 전체 스냅샷(whole-guest snapshot)으로 캡처함으로써 라이브 환경 자체의 내구성(durability)을 더욱 밀어붙입니다. 워크스페이스 플랫폼(Daytona, Gitpod)은 중단 시 _디스크 볼륨(disk volume)_은 유지하지만 중단 시 휘발성 메모리는 삭제합니다. 즉, 내구성이 있는 환경(durable environment)이지, 내구성이 있는 라이브 프로세스(durable live process)는 아닙니다. 이러한 시스템들은 서로 다른 깊이로 실제 실행 상태(execution-state) 작업을 수행하고 있습니다. 이 시리즈의 나머지 주제가 될 최전선의 질문은, 해당 라이브 상태가 _일급 객체(first-class), 주소 지정 가능하며(addressable), 전송 독립적인 객체(transport-independent object)_로 취급되는지, 아니면 특정 클라이언트에 결합된 구현 세부 사항(implementation detail)으로 취급되는지 여부입니다.
여기서 핵심은 더 좁은 범위인 범주적 경계입니다. 즉, 메모리 지속성 (memory persistence)과 실행 상태 지속성 (execution-state persistence)은 서로 직교하는 축 (orthogonal axes)입니다. 한 시스템이 한쪽 측면에서는 세계 최고 수준일 수 있지만, 다른 쪽 측면에서는 전혀 갖추지 못할 수 있습니다. 대부분의 시스템이 그러합니다. 에이전트 아키텍처 (agent architecture)에서 발생하는 거의 모든 다른 혼란은 바로 이 점을 놓친 데서 비롯됩니다.
이것이 지금 중요한 이유
단일 턴 어시스턴트 (single-turn assistant)에게는 이 중 어느 것도 해당되지 않습니다. 질문을 하면, 명령을 하나 실행하고, 답변을 합니다. 그 이후에 런타임 (runtime)이 증발하더라도 상관없기 때문입니다. 에이전트의 수명이 짧았기 때문에 그 간극은 용인될 수 있는 수준이었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
