드디어 내 실제 노트를 믿고 맡길 수 있는 OpenClaw 메모리 설정을 찾았습니다
요약
OpenClaw 에이전트의 효율적인 메모리 설정을 위해 SQLite FTS5와 로컬 Ollama 임베딩을 결합한 검색 스택을 제안합니다. 이는 불필요한 토큰 낭비를 줄이고 개인정보를 보호하며 에이전트의 답변 품질을 높이는 최적의 방법입니다.
핵심 포인트
- SQLite FTS5와 로컬 Ollama 임베딩 조합이 최적의 메모리 스택임
- 효율적인 메모리 설계는 프롬프트 팽창과 토큰 비용을 방지함
- 키워드 검색과 의미론적 회상을 결합하여 컨텍스트 최적화 달성
- 잘못된 메모리 설정은 개인정보 유출 및 에이전트 오작동의 원인이 됨
저는 간단한 답을 찾으러 나섰습니다. 개인정보 보호, 신뢰성, 그리고 내 노트를 클라우드 배출물(cloud exhaust)로 만들지 않는 것을 중요하게 생각한다면, OpenClaw에서 실제로 무엇을 메모리로 사용해야 할까요?
저는 지루한 답변을 예상했습니다.
하지만 대신 저는 엄청난 함의를 지닌 작은 논쟁을 발견했습니다.
이 r/openclaw 스레드에서 누군가가 매우 평범한 질문을 던졌습니다: “저는 내장된 openclaw memory_wiki를 사용하고 있습니다. 요즘 다른 방식이 더 우수하다는 증거가 있나요?”
이것은 일상적인 질문처럼 들립니다. 하지만 그렇지 않습니다.
당신의 에이전트(agent)가 업무 문서, 신원 정보, 클라이언트의 특이사항, 운영 선호도, 그리고 미완성된 아이디어들을 기억하기 시작하면, 메모리는 더 이상 단순한 기능이 아닙니다. 그것은 인프라(infrastructure)가 됩니다.
그리고 만약 설정을 잘못하면, 단순히 답변의 질이 떨어지는 것에 그치지 않습니다. 개인정보 유출, 프롬프트 팽창(prompt bloat), 그리고 디버깅하기 어려운 기이한 에이전트 간 상호작용(cross-agent behavior)이 발생하게 됩니다.
OpenClaw 설정, Ollama 임베딩 (embeddings), SQLite FTS5, 그리고 몇 가지 "실제 메모리" 시스템을 파헤친 후 내린 저의 결론은 다음과 같습니다:
대부분의 OpenClaw 사용자에게 가장 좋은 메모리 스택은 SQLite FTS5 + 로컬 Ollama 임베딩 (embeddings)입니다.
순수 벡터(pure vectors)가 아닙니다.
내장된 위키(wiki)를 맹목적으로 신뢰하는 것도 아닙니다.
정말로 필요하지 않다면 거대한 자율 메모리 프레임워크(autonomous-memory framework)를 사용하는 것도 아닙니다.
그저 키워드 검색을 잘 수행하면서 필요할 때 의미론적 회상(semantic recall)을 더해주는, 로컬에서 검토 가능한 검색 스택(retrieval stack)일 뿐입니다.
이것이 사람들이 인정하는 것보다 더 중요한 이유
많은 사람들이 메모리를 마치 편의 기능처럼 이야기합니다.
그렇지 않습니다.
메모리는 당신의 에이전트가 관련 있는 사실 3가지를 필요로 할 때마다 GPT-5, Claude, 또는 Qwen에 3,000개의 쓰레기 토큰을 쏟아붓는 것을 방지하는 방법입니다.
이것이 컨텍스트 최적화 (context optimization)의 실질적인 측면입니다.
좋은 메모리란 다음과 같은 것을 의미합니다:
- 관련 없는 컨텍스트 감소
- 낭비되는 토큰 감소
- 더 나은 답변
- 낮은 지연 시간 (latency)
- "왜 갑자기 저 이야기를 꺼내지?" 하는 순간의 감소
만약 당신이 OpenClaw, n8n, Make, Zapier, OpenClaw와 연결된 Discord 봇, 또는 커스텀 자동화에서 에이전트를 실행한다면, 이것은 매우 중요합니다. 잘못된 메모리 설계는 조용히 잘못된 비용 설계로 이어집니다.
특히 상위 단계(upstream)에서 여전히 토큰당 과금(per-token billing) 방식을 사용하고 있다면 이는 더욱 사실입니다. 검색(Retrieval) 실수는 프롬프트 팽창(prompt inflation)으로 이어집니다. 프롬프트 팽창은 예상치 못한 지출로 이어집니다.
내장된 OpenClaw 옵션은 유혹적입니다
가장 마찰이 적은(lowest-friction) 설정을 원한다면, memory_wiki가 명백한 첫 번째 선택지입니다.
공정하게 말해서, 그것은 합리적인 기본값입니다. 이미 OpenClaw 내부를 사용하고 있다면, 내장된 메모리 레이어(memory layer)를 사용하는 것이 저항이 가장 적은 경로입니다.
제 문제는 memory_wiki가 나쁘다는 것이 아닙니다.
제 문제는, 추가적인 테스트 없이 제 실제 노트를 믿고 맡기기에는 그것이 내부적으로 어떻게 동작하는지에 대한 퍼스트 파티(first-party) 상세 정보가 충분하지 않다는 점입니다.
메모리가 다음과 같은 것들을 보유하고 있을 때:
- 개인적인 노트
- 업무 절차
- 클라이언트별 지침
- 신원 파일 (identity files)
- 장기 실행 자동화 상태 (long-running automation state)
…저는 다음과 같은 지루한 질문들에 대한 답을 원합니다:
- 정확히 무엇이 인덱싱(indexed)되는가?
- 검색(retrieval) 순위는 어떻게 매겨지는가?
- 여러 에이전트(agents) 간의 격리는 얼마나 잘 이루어지는가?
- 다중 사용자 설정(multi-user setups)에서는 어떤 일이 발생하는가?
- 검색 경로(retrieval path)를 검사하고 디버깅할 수 있는가?
이 질문들이 중요한 이유는 제가 다른 OpenClaw 토론에서 발견한 한 구절 때문입니다:
지식 유출 (knowledge bleed)
그것이 진짜 위험 요소입니다.
“클라우드 대 로컬(cloud vs local)”이 아닙니다.
“벡터 대 그래프(vectors vs graphs)”도 아닙니다.
“전통적인 검색 대 AI 네이티브 검색(old school search vs AI-native search)”도 아닙니다.
진짜 질문은 이것입니다: 당신의 OpenClaw 에이전트가 무언가를 기억할 때, 당신이 그것을 확인하고, 제어하며, 잘못된 대화로 유출되지 않도록 막을 수 있는가?
그 점이 저를 지루한 옵션으로 이끌었습니다.
지루한 승자: SQLite FTS5는 여전히 말도 안 되게 좋습니다
OpenClaw 메모리 토론에서 제가 찾은 가장 설득력 있는 답변은 다음과 같은 요소로 구축된 커스텀 스택을 가리켰습니다:
- SQLite FTS5
better-sqlite3- Porter stemming
MEMORY.md,IDENTITY.md,SOUL.md,AGENTS.md와 같은 마크다운(markdown) 파일들
그것은 즉시 블랙박스(black-box) 메모리 레이어보다 더 신뢰할 수 있다는 느낌을 주었습니다.
왜일까요?
제가 그것을 머릿속으로 그려볼 수 있기 때문입니다.
디스크 위의 파일들. 로컬 데이터베이스. 제가 검사할 수 있는 검색. 제가 테스트할 수 있는 쿼리(queries). 제가 설명할 수 있는 결과.
그것이 중요합니다.
SQLite FTS5가 제공하는 것
SQLite FTS5는 여전히 실제 메모리 사용 사례의 매우 높은 비중을 커버합니다:
- Porter tokenizer를 통한 어간 추출 (stemming)
- BM25 스타일의 랭킹 (ranking)
- 접두사 검색 (prefix search)
NEAR를 이용한 근접 검색 (proximity search)- 스니펫 생성 (snippet generation)
- 로컬 전용 저장 (local-only storage)
- 추가 인프라 불필요 (zero extra infrastructure)
많은 "에이전트 메모리 (agent memory)" 문제들에 대해, 처음부터 벡터 데이터베이스 (vector database)가 필요한 것은 아닙니다.
당신에게 필요한 것은 다음과 같습니다:
- 깨끗한 소스 파일 (clean source files)
- 검색 가능한 텍스트 (searchable text)
- 설명 가능한 랭킹 (explainable ranking)
- 네임스페이스 분리 (namespace separation)
이것만으로도 놀라울 정도로 멀리 갈 수 있습니다.
최소한의 스키마 (Minimal schema)
가장 작고 유용한 시작점은 다음과 같습니다:
CREATE VIRTUAL TABLE docs USING fts5(
path,
namespace,
...
매우 단순한 삽입 (insert) 흐름은 다음과 같을 수 있습니다:
INSERT INTO docs(path, namespace, content)
VALUES ('memory/client-a/IDENTITY.md', 'client-a', 'Client A prefers CSV exports on Fridays.');
그리고 기본적인 검색:
SELECT path, snippet(docs, 2, '[', ']', '...', 12) AS snippet
FROM docs
WHERE docs MATCH 'csv friday'
...
이것은 이미 많은 "AI 메모리 (AI memory)" 설정들보다 나은데, 왜냐하면 검사(inspectable)가 가능하기 때문입니다.
만약 검색 결과(retrieval)가 이상하다면, 디버깅할 수 있습니다.
그 사실 하나만으로도 엄청난 운영상의 이점이 됩니다.
하지만 순수 키워드 검색만으로는 충분하지 않습니다
SQLite FTS5는 기준점(baseline)이지, 정답 전체는 아닙니다.
노트가 지저분해지거나, 표현이 바뀌거나, 사용자가 동일한 용어를 사용하지 않고 의미론적으로 관련된 질문을 하기 시작하면, 키워드 검색은 놓치는 것들이 생기기 시작합니다.
그 지점에서 임베딩 (embeddings)이 도움이 됩니다.
제 의견은 이렇습니다: 네, 아마도 임베딩을 원하시겠지만 — 메모리 시스템 전체가 아니라, 두 번째 검색 경로 (second retrieval lane)로서만 사용해야 합니다.
Ollama는 로컬 임베딩을 명확한 선택지로 만듭니다
이 스택이 현재 실용적인 이유는 Ollama 때문입니다.
호스팅된 임베딩 API 없이도 HTTP를 통해 로컬에서 임베딩을 실행할 수 있습니다.
그것은 다음을 의미합니다:
- 개인적인 노트를 제3자에게 보내지 않음
- 임베딩 호출에 대한 별도의 비용 결제 없음
- 검색을 위한 추가적인 클라우드 의존성 없음
- 쉬운 로컬 개발
Ollama에서 사용할 수 있는 몇 가지 유용한 임베딩 모델:
mxbai-embed-large— 334Mnomic-embed-textall-minilm
예시:
ollama pull mxbai-embed-large
그 다음 로컬에서 임베딩 (embeddings)을 생성합니다:
curl http://localhost:11434/api/embed -d '{
"model": "mxbai-embed-large",
"input": "Client A prefers CSV exports on Fridays"
...
이것으로 끝입니다. 로컬 시맨틱 검색 (semantic retrieval)은 더 이상 생소한 기술이 아닙니다.
올바른 설계는 하이브리드 검색 (hybrid retrieval)입니다
이 부분은 사람들이 계속해서 실수하는 지점입니다.
임베딩 전용 메모리는 더 이상 최선의 기본 설정이 아닙니다.
순수 벡터 유사도 (vector similarity)는 퍼지 회상 (fuzzy recall)에는 뛰어나지만, 정확한 용어, 식별자, 이름, 파일 수준의 출처 (provenance), 그리고 설명 가능성 (explainability)에는 취약합니다.
그렇기 때문에 가장 실용적인 설정은 하이브리드 (hybrid) 방식입니다:
- FTS5: 정확한 용어, 파일명, 약어 및 강력한 키워드 관련성을 위해 사용
- 임베딩 (embeddings): 시맨틱 회상 (semantic recall)을 위해 사용
- 그 위에 선택적으로 리랭킹 (reranking) 또는 병합 로직 추가
간단한 검색 전략은 다음과 같을 수 있습니다:
- FTS5 검색 실행
- 벡터 유사도 (vector similarity) 검색 실행
- 상위 후보군 병합
- 파일/청크 (chunk) 단위로 중복 제거
- 가장 좋은 소수의 결과만을 모델에 전달
이렇게 하면 검색을 마법처럼 만들지 않고도 훨씬 더 나은 회상 (recall) 성능을 얻을 수 있습니다.
하이브리드 검색 패스를 위한 의사 코드 (Pseudocode)
const keywordHits = searchFts5(query, { namespace: 'client-a', limit: 10 })
const vectorHits = searchVectors(queryEmbedding, { namespace: 'client-a', limit: 10 })
...
이것이 제가 실제로 신뢰할 수 있는 스택입니다.
실용적인 로컬 아키텍처
만약 제가 오늘 OpenClaw를 위해 이 시스템을 구축한다면, 다음과 같이 할 것입니다:
markdown files
-> ingestion script
-> SQLite FTS5 index
...
권장되는 파일 레이아웃
memory/
personal/
IDENTITY.md
...
이 구조는 의도적으로 지루하게 설계되었습니다.
이런 경우에는 지루한 것이 좋습니다.
Node.js 인제스션 (ingestion) 스크립트 예시
다음은 better-sqlite3를 사용한 축약된 예시입니다.
import Database from 'better-sqlite3'
import fs from 'node:fs'
import path from 'node:path'
...
그리고 검색 함수입니다:
function search(namespace, query) {
return db.prepare(`
SELECT
...
이것만으로도 에이전트 워크플로 (agent workflow) 내에서 유용한 메모리 검색 (memory retrieval)을 수행하기에는 충분합니다.
Letta나 OpenMemory는 어떤가요?
여기서부터는 "메모리"라는 단어를 무엇으로 정의하느냐에 따라 답이 달라집니다.
Letta
Letta는 단순한 검색 (retrieval)이 아닙니다.
Letta는 메모리를 장기 실행 에이전트 (long-lived agent)를 위한 런타임 (runtime)의 일부로 취급합니다. 핵심 메모리 블록 (core memory blocks)은 컨텍스트 (context) 내에 유지됩니다. 오래된 메시지는 저장소 (storage)에 보존됩니다. 에이전트는 도구 (tools)를 통해 메모리를 업데이트할 수 있습니다.
이는 단순한 RAG (Retrieval-Augmented Generation)보다 훨씬 강력한 모델입니다.
만약 다음과 같은 기능이 필요하다면:
- 지속적인 에이전트 상태 (persistent agent state)
- 자기 편집 메모리 (self-editing memory)
- 멀티 에이전트 메모리 블록 (multi-agent memory blocks)
- 장기 실행 자율 워크플로 (long-running autonomous workflows)
...Letta는 진지하게 고려해 볼 가치가 있습니다.
하지만 이는 더 큰 아키텍처적 부담 (architectural commitment)을 의미하기도 합니다.
만약 당신의 실제 요구사항이 "OpenClaw가 노트를 로컬에서, 정확하고, 안전하게 기억하게 만드는 것"이라면, Letta는 당신에게 필요한 것보다 더 거대한 시스템일 수 있습니다.
OpenMemory
OpenMemory는 이식성 (portability)과 도구 간의 메모리 이동을 중요하게 생각한다면 흥미로운 선택지입니다.
그것은 확실한 장점입니다.
하지만 단순한 OpenClaw 배포를 원한다면, 이는 여전히 실행하고, 추론하고, 보안을 유지해야 할 또 다른 레이어 (layer)가 됩니다.
만약 당신의 우선순위가 운영의 평온함 (operational calm)이라면, 모든 추가 레이어는 그 자체로 정당성을 입증해야 합니다.
제 입장에서, 대부분의 설정은 첫날부터 그런 추가적인 복잡성을 필요로 하지 않습니다.
옵션들을 조사한 후 나의 순위
| 옵션 | 나의 견해 |
|---|---|
OpenClaw memory_wiki | 가장 쉬운 내장 경로를 원하고 요구사항이 단순하다면 최적입니다. 마찰(friction)이 가장 적지만, 민감하거나 멀티 테넌트 (multi-tenant) 메모리를 맡기기 전에는 한계치를 엄격하게 테스트해 봐야 합니다. |
| ... |
모두가 건너뛰는 부분: 지식 유출 (knowledge bleed) 방지
이 섹션이 가장 중요합니다.
가장 무서운 실패 모드 (failure mode)는 잘못된 랭킹 (ranking)이 아닙니다.
바로 에이전트 간 오염 (cross-agent contamination)입니다.
서로 다른 사용자, 디스코드 채팅, 클라이언트 또는 내부 팀을 위해 여러 에이전트를 실행한다면, 첫날부터 격리 규칙 (isolation rules)이 필요합니다.
나의 최소 규칙
- 테넌트(tenant) 또는 에이전트 역할(agent role)별로 별도의 인덱스(index) 사용
IDENTITY.md와 같은 별도의 신원 파일(identity files) 관리- 기본적으로 별도의 임베딩 컬렉션(embedding collections) 사용
- 검색 히트(retrieval hits) 기록
- 모든 메모리 결과에 대해 소스 경로(source paths) 기록
- 메모리 쓰기(memory writes) 범위를 좁게 유지
- 실제로 필요한 경우가 아니라면 광범위한 자기 수정(self-editing) 허용 금지
단순한 네임스페이스(namespace) 규칙 하나가 많은 고통을 줄여줍니다:
function getNamespace({ clientId, agentRole })) {
return `${clientId}:${agentRole}`
}
그리고 모든 검색(retrieval) 호출은 네임스페이스를 요구해야 합니다:
const namespace = getNamespace({ clientId: 'client-a', agentRole: 'support-bot' })
const hits = search(namespace, userQuery)
네임스페이스가 선택 사항이라면, 누군가는 이를 잊어버릴 것입니다.
누군가 이를 잊어버리면, 결국 메모리 누수(memory leaks)가 발생합니다.
이것이 토큰 비용(token costs)에도 중요한 이유
이것은 단지 개인정보 보호와 신뢰성의 문제만이 아닙니다.
비용 문제이기도 합니다.
나쁜 메모리는 나쁜 검색(retrieval)을 의미합니다.
나쁜 검색은 비대해진 프롬프트(bloated prompts)를 의미합니다.
비대해진 프롬프트는 더 높은 지출과 더 느린 에이전트를 의미합니다.
이것이 제가 애초에 이 주제에 관심을 갖는 이유 중 하나입니다.
하루 종일 에이전트를 실행한다면, 검색 품질은 상위 단계(upstream)로 보내는 컨텍스트(context)의 양에 직접적인 영향을 미칩니다. 그리고 토큰당 비용을 지불하고 있다면, 모든 부주의한 메모리 결정은 복리로 쌓여 손실을 키웁니다.
이것이 자동화 중심의 팀에게 정액제(flat-rate) AI 인프라가 매력적인 이유이기도 합니다. 에이전트가 끊임없이 검색하고, 요약하고, 다시 쓰고, 도구 호출(tool calls)을 반복한다면, 예측 가능한 컴퓨팅(predictable compute)은 사람들이 인정하는 것보다 훨씬 더 중요합니다.
Standard Compute는 이 지점에서 흥미로운데, 토큰당 과금 방식 대신 월정액 요금제로 OpenAI 호환 API를 제공하기 때문입니다. 만약 OpenClaw, n8n, Make, Zapier 또는 커스텀 에이전트를 무거운 워크플로우(workflows)에 연결하고 있다면, 이는 실험의 경제학을 변화시킵니다. 토큰 미터를 감시하는 대신 검색 품질을 개선하는 데 시간을 쓸 수 있습니다.
이것이 좋은 메모리 설계를 대체하는 것은 아닙니다.
단지 메모리 설계 실수가 즉각적인 결제 불안(billing anxiety)으로 이어지지 않음을 의미할 뿐입니다.
내가 실제로 실행할 구성
만약 오늘 당장 실제 노트, 업무 문서, 그리고 장기 실행 자동화(long-running automations)를 위해 단 하나의 OpenClaw 메모리 스택을 선택해야 한다면, 저는 다음과 같은 구성을 사용할 것입니다:
- 마크다운 (markdown) 소스 파일
- 키워드 검색 (keyword retrieval)을 위한 SQLite FTS5
- 의미론적 회상 (semantic recall)을 위한 Ollama 임베딩 (embeddings)
- 에이전트(agent) 또는 테넌트(tenant)별 엄격한 네임스페이스 분리 (namespace separation)
- 관계가 반복적으로 중요한 경우에만 선택적으로 사용하는 경량 팩트 그래프 (fact graph)
마지막 부분은 선택 사항이지만 유용합니다.
동일한 엔티티(entities)가 여러 작업에 걸쳐 계속 나타난다면, 아주 작은 팩트 그래프가 "단순히 더 열심히 검색하기"보다 더 나은 성능을 발휘할 수 있습니다.
예시:
- Sam은 결제 승인자이다
- Sam은 화요일 배포(deploys)를 싫어한다
- Project Atlas는 법률 검토(legal review)로 인해 차단되었다
- 클라이언트 A는 CSV를 원하고, 클라이언트 B는 JSON을 원한다
이러한 관계 중심적인 사실들은 벡터 유사성 (vector similarity)이 매번 이를 재발견하기를 기대하는 대신, 명시적으로 모델링될 수 있습니다.
하지만 저는 거기서부터 시작하지는 않을 것입니다.
저는 다음과 같은 가장 단순한 스택부터 시작할 것입니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기