Semantic memory search for markdown knowledge bases
Project description
memsearch
OpenClaw's memory, everywhere.
๐ก memsearch extracts OpenClaw's memory system into a standalone library โ same markdown-first architecture, same chunking, same chunk ID format. Pluggable into any agent framework, backed by Milvus (local Milvus Lite โ Milvus Server โ Zilliz Cloud). See it in action with the included Claude Code plugin.
โจ Why memsearch?
- ๐ฆ OpenClaw's memory, everywhere โ OpenClaw has one of the best memory designs in open-source AI: markdown as the single source of truth โ simple, human-readable,
git-friendly, zero vendor lock-in - โก Smart dedup โ SHA-256 content hashing means unchanged content is never re-embedded
- ๐ Live sync โ File watcher auto-indexes on changes, deletes stale chunks when files are removed
- ๐งน Memory flush โ LLM-powered summarization compresses old memories, just like OpenClaw's flush cycle
- ๐งฉ Claude Code plugin included โ A real-world example: ccplugin/ gives Claude persistent memory across sessions with zero config
๐ How It Works
Markdown is the source of truth โ the vector store is just a derived index, rebuildable anytime.
โโโโ Search โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ "how to configure Redis?" โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ Embed โโโโโโถโ Cosine similarityโโโโโโถโ Top-K results โ โ
โ โ query โ โ (Milvus) โ โ with source info โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโ Ingest โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ MEMORY.md โ
โ memory/2026-02-09.md โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ โ
โ memory/2026-02-08.md โโโโถโ Chunker โโโโโโถโ Dedup โ โ
โ โ(heading, โ โ(chunk_hash PK) โ โ
โ โparagraph)โ โโโโโโโโโฌโโโโโโโโโ โ
โ โโโโโโโโโโโโ โ โ
โ new chunks only โ
โ โผ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ Embed & โ โ
โ โ Milvus upsertโ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโ Watch โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ File watcher (1500ms debounce) โโโถ auto re-index / delete stale โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโ Flush โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Retrieve chunks โโโถ LLM summarize โโโถ write memory/YYYY-MM-DD.md โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ The entire pipeline runs locally by default โ your data never leaves your machine unless you choose a remote Milvus backend or a cloud embedding provider.
๐งฉ Claude Code Plugin
memsearch ships with a Claude Code plugin โ a real-world example of OpenClaw's memory running outside OpenClaw. It gives Claude automatic persistent memory across sessions: every session is summarized to markdown, every prompt triggers a semantic search, and a background watcher keeps the index in sync. No commands to learn, no manual saving โ just install and go.
# Install memsearch, then launch Claude with the plugin
pip install memsearch
claude --plugin-dir ./ccplugin
Session start โโโถ start memsearch watch (singleton) โโโถ inject recent memories
โ
User prompt โโโถ memsearch search โโโถ inject relevant memories
โ
Claude stops โโโถ haiku summary โโโถ write .memsearch/memory/YYYY-MM-DD.md
โ โ
Session end โโโถ stop watch watch auto-indexes โโ
Under the hood: 4 shell hooks + 1 watch process, all calling the memsearch CLI. Memories are transparent .md files โ human-readable, git-friendly, rebuildable. See ccplugin/README.md for the full architecture, hook details, progressive disclosure model, and comparison with claude-mem.
๐ฆ Installation
pip install memsearch
Additional embedding providers
pip install "memsearch[google]" # Google Gemini
pip install "memsearch[voyage]" # Voyage AI
pip install "memsearch[ollama]" # Ollama (local)
pip install "memsearch[local]" # sentence-transformers (local, no API key)
pip install "memsearch[all]" # Everything
๐ Python API โ Build an Agent with Memory
The example below shows a complete agent loop with memory: save knowledge to markdown, index it, and recall it later via semantic search.
import asyncio
from datetime import date
from pathlib import Path
from openai import OpenAI
from memsearch import MemSearch
MEMORY_DIR = "./memory"
llm = OpenAI() # your LLM client
ms = MemSearch(paths=[MEMORY_DIR]) # memsearch handles the rest
def save_memory(content: str):
"""Append a note to today's memory log (OpenClaw-style daily markdown)."""
p = Path(MEMORY_DIR) / f"{date.today()}.md"
p.parent.mkdir(parents=True, exist_ok=True)
with open(p, "a") as f:
f.write(f"\n{content}\n")
async def agent_chat(user_input: str) -> str:
# 1. Recall โ search past memories for relevant context
memories = await ms.search(user_input, top_k=3)
context = "\n".join(f"- {m['content'][:200]}" for m in memories)
# 2. Think โ call LLM with memory context
resp = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"You have these memories:\n{context}"},
{"role": "user", "content": user_input},
],
)
answer = resp.choices[0].message.content
# 3. Remember โ save this exchange and index it
save_memory(f"## {user_input}\n{answer}")
await ms.index()
return answer
async def main():
# Seed some knowledge
save_memory("## Team\n- Alice: frontend lead\n- Bob: backend lead")
save_memory("## Decision\nWe chose Redis for caching over Memcached.")
await ms.index()
# Agent can now recall those memories
print(await agent_chat("Who is our frontend lead?"))
print(await agent_chat("What caching solution did we pick?"))
asyncio.run(main())
๐ Anthropic Claude example โ click to expand
pip install memsearch anthropic
import asyncio
from datetime import date
from pathlib import Path
from anthropic import Anthropic
from memsearch import MemSearch
MEMORY_DIR = "./memory"
llm = Anthropic()
ms = MemSearch(paths=[MEMORY_DIR])
def save_memory(content: str):
p = Path(MEMORY_DIR) / f"{date.today()}.md"
p.parent.mkdir(parents=True, exist_ok=True)
with open(p, "a") as f:
f.write(f"\n{content}\n")
async def agent_chat(user_input: str) -> str:
# 1. Recall
memories = await ms.search(user_input, top_k=3)
context = "\n".join(f"- {m['content'][:200]}" for m in memories)
# 2. Think โ call Claude with memory context
resp = llm.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1024,
system=f"You have these memories:\n{context}",
messages=[{"role": "user", "content": user_input}],
)
answer = resp.content[0].text
# 3. Remember
save_memory(f"## {user_input}\n{answer}")
await ms.index()
return answer
async def main():
save_memory("## Team\n- Alice: frontend lead\n- Bob: backend lead")
await ms.index()
print(await agent_chat("Who is our frontend lead?"))
asyncio.run(main())
๐ฆ Ollama (fully local, no API key) โ click to expand
pip install "memsearch[ollama]"
ollama pull nomic-embed-text # embedding model
ollama pull llama3.2 # chat model
import asyncio
from datetime import date
from pathlib import Path
from ollama import chat
from memsearch import MemSearch
MEMORY_DIR = "./memory"
ms = MemSearch(paths=[MEMORY_DIR], embedding_provider="ollama")
def save_memory(content: str):
p = Path(MEMORY_DIR) / f"{date.today()}.md"
p.parent.mkdir(parents=True, exist_ok=True)
with open(p, "a") as f:
f.write(f"\n{content}\n")
async def agent_chat(user_input: str) -> str:
# 1. Recall
memories = await ms.search(user_input, top_k=3)
context = "\n".join(f"- {m['content'][:200]}" for m in memories)
# 2. Think โ call Ollama locally
resp = chat(
model="llama3.2",
messages=[
{"role": "system", "content": f"You have these memories:\n{context}"},
{"role": "user", "content": user_input},
],
)
answer = resp.message.content
# 3. Remember
save_memory(f"## {user_input}\n{answer}")
await ms.index()
return answer
async def main():
save_memory("## Team\n- Alice: frontend lead\n- Bob: backend lead")
await ms.index()
print(await agent_chat("Who is our frontend lead?"))
asyncio.run(main())
๐๏ธ Milvus Backend Configuration
memsearch supports three Milvus deployment modes โ just change milvus_uri and milvus_token:
1. Milvus Lite (default โ zero config, local file)
ms = MemSearch(
paths=["./docs/"],
milvus_uri="~/.memsearch/milvus.db", # local file, no server needed
)
No server to install. Data is stored in a single .db file. Perfect for personal use, single-agent setups, and development.
2. Milvus Server (self-hosted)
ms = MemSearch(
paths=["./docs/"],
milvus_uri="http://localhost:19530", # your Milvus server
milvus_token="root:Milvus", # default credentials, change in production
)
Deploy via Docker (docker compose) or Kubernetes. Ideal for multi-agent workloads and team environments where you need a shared, always-on vector store.
3. Zilliz Cloud (fully managed)
ms = MemSearch(
paths=["./docs/"],
milvus_uri="https://in03-xxx.api.gcp-us-west1.zillizcloud.com",
milvus_token="your-api-key",
)
Zero-ops, auto-scaling managed service. Get your free cluster at cloud.zilliz.com. Great for production deployments and when you don't want to manage infrastructure.
๐ฅ๏ธ CLI Usage
Index markdown files
# Index one or more directories / files
memsearch index ./docs/ ./notes/
# Use a different embedding provider
memsearch index ./docs/ --provider google
# Force re-index everything
memsearch index ./docs/ --force
# Use a remote Milvus server
memsearch index ./docs/ --milvus-uri http://localhost:19530 --milvus-token root:Milvus
Search
memsearch search "how to configure Redis caching"
# Return more results
memsearch search "authentication flow" --top-k 10
# JSON output (for piping to other tools)
memsearch search "error handling" --json-output
Watch for changes
# Auto-index on file changes (Ctrl+C to stop)
memsearch watch ./docs/ ./notes/
# Custom debounce interval
memsearch watch ./docs/ --debounce-ms 3000
Flush (compress memories)
Summarize indexed chunks into a condensed memory using an LLM:
memsearch flush
# Use a specific LLM
memsearch flush --llm-provider anthropic
memsearch flush --llm-provider gemini
# Only flush chunks from a specific source
memsearch flush --source ./docs/old-notes.md
Configuration management
memsearch config init # Interactive wizard
memsearch config set milvus.uri http://localhost:19530
memsearch config get milvus.uri
memsearch config list --resolved # Show merged config from all sources
memsearch config list --global # Show ~/.memsearch/config.toml only
memsearch config list --project # Show .memsearch.toml only
Manage
memsearch stats # Show index statistics
memsearch reset # Drop all indexed data (with confirmation)
โ๏ธ Configuration
memsearch uses a layered configuration system. Settings are resolved in priority order (lowest โ highest):
- Built-in defaults
- Global config โ
~/.memsearch/config.toml - Project config โ
.memsearch.toml(in your working directory) - Environment variables โ
MEMSEARCH_SECTION_FIELD(e.g.MEMSEARCH_MILVUS_URI) - CLI flags โ
--milvus-uri,--provider, etc.
API keys
API keys for embedding and LLM providers are read from standard environment variables:
# Embedding providers (set the one you use)
export OPENAI_API_KEY="sk-..."
export OPENAI_BASE_URL="https://..." # optional, for proxies / Azure
export GOOGLE_API_KEY="..."
export VOYAGE_API_KEY="..."
# LLM for flush/summarization (set the one you use)
export ANTHROPIC_API_KEY="..." # for flush with Anthropic
๐ Embedding Providers
| Provider | Install | Env Var | Default Model |
|---|---|---|---|
| OpenAI | memsearch (included) |
OPENAI_API_KEY |
text-embedding-3-small |
memsearch[google] |
GOOGLE_API_KEY |
text-embedding-004 |
|
| Voyage | memsearch[voyage] |
VOYAGE_API_KEY |
voyage-3-lite |
| Ollama | memsearch[ollama] |
OLLAMA_HOST (optional) |
nomic-embed-text |
| Local | memsearch[local] |
โ | all-MiniLM-L6-v2 |
๐พ OpenClaw Compatibility
memsearch is designed to be a drop-in memory backend for projects following OpenClaw's memory architecture:
| Feature | OpenClaw | memsearch |
|---|---|---|
| Memory layout | MEMORY.md + memory/YYYY-MM-DD.md |
โ Same |
| Chunk ID format | hash(source:startLine:endLine:contentHash:model) |
โ Same |
| Dedup strategy | Content-hash primary key | โ Same |
| Flush target | Append to daily markdown log | โ Same |
| Source of truth | Markdown files (vector DB is derived) | โ Same |
| File watch debounce | 1500ms | โ Same default |
| Vector backend | Built-in | Milvus (Lite / Server / Zilliz Cloud) |
| Embedding providers | Built-in | Pluggable (OpenAI, Google, Voyage, Ollama, local) |
| Packaging | Part of OpenClaw monorepo | Standalone pip install |
If you're already using OpenClaw's memory directory layout, just point memsearch at it โ no migration needed.
๐ License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file memsearch-0.1.0.tar.gz.
File metadata
- Download URL: memsearch-0.1.0.tar.gz
- Upload date:
- Size: 399.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e3b7d2a69dab6e9ae68e3175b04fb22ad413adb6b42d50e7129e26b0bb9de0d
|
|
| MD5 |
2f73d7ec05704a8c3df148d80669ba69
|
|
| BLAKE2b-256 |
651f24a6f2a35f5aecb0e0b56b057c63a630d79316cf6fe55782567c69701d50
|
Provenance
The following attestation bundles were made for memsearch-0.1.0.tar.gz:
Publisher:
release.yml on zilliztech/memsearch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
memsearch-0.1.0.tar.gz -
Subject digest:
8e3b7d2a69dab6e9ae68e3175b04fb22ad413adb6b42d50e7129e26b0bb9de0d - Sigstore transparency entry: 939936269
- Sigstore integration time:
-
Permalink:
zilliztech/memsearch@b6edaa3683b3c56a43a12fd022d3732960084af4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zilliztech
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b6edaa3683b3c56a43a12fd022d3732960084af4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file memsearch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: memsearch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 34.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9601a80089eddfa218c7f779b2e57555586817cfde83b37af1ec785b36fd0045
|
|
| MD5 |
2ecece94ea78a5ec58fe90675a037ec9
|
|
| BLAKE2b-256 |
1a31c2eb81c20b9a171f9475fb15c0f2ebd90deafc9ff27d8fd50f97587f49a8
|
Provenance
The following attestation bundles were made for memsearch-0.1.0-py3-none-any.whl:
Publisher:
release.yml on zilliztech/memsearch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
memsearch-0.1.0-py3-none-any.whl -
Subject digest:
9601a80089eddfa218c7f779b2e57555586817cfde83b37af1ec785b36fd0045 - Sigstore transparency entry: 939936288
- Sigstore integration time:
-
Permalink:
zilliztech/memsearch@b6edaa3683b3c56a43a12fd022d3732960084af4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zilliztech
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b6edaa3683b3c56a43a12fd022d3732960084af4 -
Trigger Event:
push
-
Statement type: