
kioku-mesh: 왜 내 AI의 장기 기억(Long-term memory)에 Zenoh를 도입했는가
요약
AI 에이전트의 세션 간 장기 기억 문제를 해결하기 위해 분산 메시징 시스템인 Zenoh를 도입한 kioku-mesh 프로젝트를 소개합니다. SQLite의 한계를 극복하고 여러 머신과 에이전트 간의 데이터 일관성을 유지하는 방법을 다룹니다.
핵심 포인트
- 단일 머신 기반 메모리의 한계와 에이전트 간 컨텍스트 공유 문제 지적
- SQLite 동기화 방식의 충돌 및 일관성 문제 분석
- 분산 통신 미들웨어 Zenoh를 통한 데이터 복제 및 관리
- HLC(Hybrid Logical Clock)를 활용한 분산 시스템의 인과 관계 보장
이 글은 Claude (AI)의 도움을 받아 작성되었습니다. 저는 게시하기 전에 내용을 검토하고 편집했습니다.
Claude Code와 웹 앱 사이의 간극
Claude Code를 한동안 사용하다가 AI 어시스턴트의 웹 버전으로 돌아가면 그 간극이 매우 당혹스럽습니다. 웹 앱에서는 모든 것을 처음부터 다시 설명해야 하며, 심지어 내부적으로 정말 같은 모델이 맞는지 의구심이 들 정도입니다. 이미 많은 분이 알고 계시듯, 그 차이는 컨텍스트 (Context)에 있습니다.
Claude Code는 세션 간에 메모리를 유지합니다. 하지만 단일 머신(Single machine) 내에서만 가능합니다. 집에서 구축한 컨텍스트는 사무실 컴퓨터에서는 백지 상태이며, 여러 에이전트 (Agent)가 동시에 실행될 때 각 에이전트는 각자 따로 "학습"하게 됩니다.
가장 명백한 첫 번째 해결책은 에이전트가 SQLite에 메모를 적어두게 하는 것입니다. 수많은 MCP 메모리 앱들이 딱 거기까지만 구현합니다.
그렇다면 왜 kioku-mesh는 그 위에 분산 메시징 시스템 (Distributed messaging system)인 Zenoh를 올렸을까요?
왜 SQLite만으로는 충분하지 않은가
SQLite는 하나의 프로세스(Process)와 하나의 파일에 종속되어 있습니다. 동일한 메모리를 읽고 쓰는 여러 머신에 퍼져 있는 에이전트들에게는 적합한 형태가 아닙니다.
단순한 임시방편은 머신 간에 SQLite 파일을 rsync로 동기화하는 것입니다. 하지만 이는 금방 한계에 부딪힙니다:
- 충돌 (Conflicts): 집과 사무실에서 동시에 쓰기를 수행할 경우, 어느 쪽이 우선권을 가질지 결정할 방법이 없습니다.
- 일관성 (Consistency): 복사가 일어나는 시점에 따라 한쪽은 오래된 데이터 (Stale data)를 기반으로 계속 실행됩니다.
- 오프라인 (Offline): 네트워크가 끊기면 동기화를 전혀 할 수 없으며, 다시 연결되었을 때 충돌이 쌓이게 됩니다.
여러 머신과 여러 에이전트가 동시에 쓰기를 수행하는 순간, SQLite 파일을 복사한다는 것은 직접 충돌 해결 로직 (Conflict-resolution logic)을 작성해야 함을 의미합니다. 저는 그것을 직접 작성하고 싶지 않았습니다.
왜 Zenoh인가
kioku-mesh가 Zenoh에 의존하는 이유는 바로 제가 그 충돌 문제를 직접 해결하지 않아도 되기 때문입니다.
Zenoh란 무엇인가
Zenoh는 Eclipse Foundation에서 만든 오픈 소스 분산 통신 미들웨어 (middleware)입니다. IoT 및 로보틱스를 위해 구축되었으며, 제가 주목하는 부분은 클라우드 서비스의 개입 없이 사용자의 자체 장비에서 완전히 실행된다는 점입니다. Zenoh는 발행/구독 (pub/sub) 모델과 키-값 저장소 (key-value store)를 제공합니다. (키-값 저장소는 이름/값 쌍을 읽고 쓰는 단순한 형태의 데이터베이스입니다. Redis와 DynamoDB가 유명한 예시입니다.) Zenoh는 네트워크 기반의 발행/구독 및 복제 (replication) 기능을 이 저장소 위에 구현합니다.
HLC (Hybrid Logical Clock) 타임스탬프
HLC는 물리적 시계 (wall-clock time)와 논리적 시계 (causal order, 인과적 순서)를 결합한 타임스탬프입니다. 이는 분산 시스템 (distributed-systems) 연구에서 파생되었으며 CockroachDB와 같은 시스템에서 사용됩니다.
일반적인 물리적 시계 (wall-clock time)를 사용할 경우, 모든 장비의 시계는 조금씩 어긋나기 때문에 (NTP skew), 어떤 쓰기 작업이 나중에 발생했는지 신뢰성 있게 판단할 수 없습니다. HLC는 이러한 오차를 흡수하면서도 인과 관계, 즉 B가 A 이후에 발생했다는 사실을 정확하게 기록합니다.
여기서 Zenoh의 장점은 HLC가 내장되어 있다는 것입니다. 사용자가 별도로 구현할 필요가 없으며, 모든 쓰기 작업 시 타임스탬프가 자동으로 부착됩니다. 제 자체 테스트 결과, NTP 오차가 12초를 넘어서는 상황에서도 인과 관계가 유지되었으므로, 충돌 해결 (conflict resolution)을 위해 추가 코드가 필요하지 않았습니다.
복제 플러그인의 다이제스트 비교 (digest comparison)
두 노드가 연결되어 있는 동안 주기적인 동기화 비용을 저렴하게 유지하기 위해, Zenoh는 hot/warm/cold
에이전트가 save_observation을 호출하면 다음과 같은 일이 발생합니다:
store.put_observation()이 Zenoh로PUT mem/obs/...를 발행합니다.- zenohd의 storage_manager가 이를 RocksDB에 영구 저장합니다.
- 복제 플러그인 (replication plugin)이 다른 노드에 있는 zenohd로 이를 전파합니다.
- 성공하면, 호출 프로세스는 즉시 자신의 SQLite에 업서트 (upsert)합니다.
읽기 경로 (The read path)
검색은 로컬 SQLite에서 반환됩니다. Zenoh에 요청하지 않습니다.
# store.search_observations() (스케치)
def search_observations(query, ...):
idx = get_index()
...
SQLite를 최신 상태로 유지하기
그렇다면 SQLite는 어떻게 최신 상태를 유지할까요? 두 가지 메커니즘이 있습니다.
def on_obs(sample):
obs = Observation.from_json(sample.payload.to_string())
idx.upsert(obs) # SQLite에 직접 쓰기
...
2. 시작 시 재구축 (consistency)
장시간 실행되는 프로세스 (MCP 서버)는 시작 시 한 번 rebuild_from_zenoh()를 실행하여, Zenoh의 mem/obs/**를 전체 스캔함으로써 SQLite를 재구축합니다. 이를 통해 프로세스가 다운되어 있는 동안 변경된 사항(예: 다른 노드로부터의 복제)을 모두 반영합니다.
실제로 5만 개의 레코드에 대해 약 0.4초가 소요됩니다. 이는 MCP 서버 시작 시 단 한 번만 발생하므로 체감되지 않습니다.
성능 (Performance)
제가 SQLite를 선택하게 된 결정적인 이유는 측정값 때문이었습니다. 처음에는 Zenoh를 대상으로 전체 스캔 (full scan)을 수행하며 시작했지만, 데이터 가져오기 (fetching) 속도가 너무 느려 SQLite를 캐시 (cache)로 전환했습니다.
Zenoh 전체 스캔 (이전 경로)
모든 데이터를 네트워크를 통해 가져온 다음, Python에서 필터링합니다.
| 레코드 수 | 지연 시간 (Latency) |
|---|---|
| 16k | 2.2 s (스캔 횟수가 지배적이며 limit를 무시함) |
| 36MB 저장소 | 타임아웃 (10초 제한에 걸림) |
SQLite 로컬 인덱스 (현재 경로)
로컬 SQLite에 SQL 쿼리를 던지기만 하면 됩니다.
| 레코드 수 | 작업 | 지연 시간 (Latency) |
|---|---|---|
| 50k | 재구축 (rebuild) | ~0.4 s |
| ... |
Zenoh 전체 스캔과 SQLite 사이의 격차는 대략 50,000배에 달합니다. 36MB 저장소에서 Zenoh를 통한 검색은 타임아웃이 발생하지만, SQLite는 5만 개의 레코드에서도 1밀리초 미만 내에 결과를 반환합니다.
마무리 (Wrapping up)
"kioku-mesh가 왜 Zenoh를 사용하는가"에 대한 짧은 답변은 다음과 같습니다: 여러 대의 머신이 동시에 데이터를 쓸 때도 깨지지 않는 무언가를 만들기 위해서입니다.
SQLite는 빠르고 간편하지만, 단일 머신 내부에서만 동작합니다. Zenoh를 추가하면 머신 간에 데이터를 동기화(sync)할 수 있으며, HLC (Hybrid Logical Clock)를 통해 쓰기 충돌(write conflicts)을 자동으로 해결합니다. 읽기(Read) 작업은 SQLite에서 직접 가져오기 때문에 여전히 빠릅니다. Zenoh는 "쓰기 및 동기화" 작업을 수행하고, SQLite는 "빠른 읽기" 작업을 수행하며, 이러한 역할 분담이 분산형 장기 기억(distributed long-term memory)을 작동하게 만듭니다.
kioku-mesh는 오픈 소스입니다.
h-wata / kioku-mesh
도구와 머신을 가로지르는 AI 코딩 에이전트용 공유 메모리. 로컬 우선(Local-first) SQLite, 선택적 Zenoh+RocksDB 메시(mesh), MCP 네이티브.
도구와 머신을 가로지르는 AI 코딩 에이전트용 공유 메모리.
kioku (記憶)는 기억(memory)을 의미합니다.
kioku-mesh는 코딩 에이전트에게 공유 메모리 저장소를 제공합니다. Claude Code, Codex CLI, Gemini CLI 및 기타 MCP 클라이언트들은 신뢰할 수 있는 LAN/VPN 메시(mesh) 환경 내의 한 대 또는 여러 대의 머신으로부터 동일한 관찰(observations) 데이터를 저장하고 검색할 수 있습니다.
기본 설정은 로컬 방식이며 데몬(daemon)이 필요하지 않습니다. 메시(Mesh) 모드는 호스트 간에 동일한 메모리 풀을 복제하고 싶을 때 사용할 수 있습니다.
kioku-mesh를 사용하는 이유
코딩 에이전트의 컨텍스트(context)는 여러 머신에 걸쳐 파편화됩니다. 어떤 노트북에서 그 작업을 했는지, 다른 호스트의 에이전트가 무엇을 결정했는지, 그리고 왜 보조 에이전트가 간단한 의견을 내기 위해 모든 것을 처음부터 다시 읽어야 하는지 등의 문제가 발생합니다. kioku-mesh는 해당 기억을 하나의 공유 풀(shared pool)에 유지하여, 사용자의 어떤 머신에 있는 에이전트라도 이를 회상할 수 있게 합니다.
모든 것을 한 곳에 저장하는 장기 기억 도구들과 달리, 이 공유 풀은 피어 투 피어(peer-to-peer)...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
