memsearch

memsearch

A Markdown-first memory system, a standalone library for any AI agent. Inspired by OpenClaw.

Stars: 188

Visit
 screenshot

Memsearch is a tool that allows users to give their AI agents persistent memory in a few lines of code. It enables users to write memories as markdown and search them semantically. Inspired by OpenClaw's markdown-first memory architecture, Memsearch is pluggable into any agent framework. The tool offers features like smart deduplication, live sync, and a ready-made Claude Code plugin for building agent memory.

README:

Β  memsearch

OpenClaw's memory, everywhere.

PyPI Python License Docs Stars Discord X (Twitter)

https://github.com/user-attachments/assets/31de76cc-81a8-4462-a47d-bd9c394d33e3

πŸ’‘ Give your AI agents persistent memory in a few lines of code. Write memories as markdown, search them semantically. Inspired by OpenClaw's markdown-first memory architecture. Pluggable into any agent framework.

✨ Why memsearch?

  • πŸ“ Markdown is the source of truth β€” human-readable, git-friendly, zero vendor lock-in. Your memories are just .md files
  • ⚑ Smart dedup β€” SHA-256 content hashing means unchanged content is never re-embedded
  • πŸ”„ Live sync β€” File watcher auto-indexes changes to the vector DB, deletes stale chunks when files are removed
  • 🧩 Ready-made Claude Code plugin β€” a drop-in example of agent memory built on memsearch

πŸ“¦ Installation

pip install memsearch
Optional 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 β€” Give Your Agent Memory

from memsearch import MemSearch

mem = MemSearch(paths=["./memory"])

await mem.index()                                      # index markdown files
results = await mem.search("Redis config", top_k=3)    # semantic search
print(results[0]["content"], results[0]["score"])       # content + similarity
πŸš€ Full example β€” agent with memory (OpenAI) β€” click to expand
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
mem = 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 mem.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 mem.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 mem.index()  # or mem.watch() to auto-index in the background

    # 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()
mem = 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 mem.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 mem.index()
    return answer

async def main():
    save_memory("## Team\n- Alice: frontend lead\n- Bob: backend lead")
    await mem.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"
mem = 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 mem.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 mem.index()
    return answer

async def main():
    save_memory("## Team\n- Alice: frontend lead\n- Bob: backend lead")
    await mem.index()
    print(await agent_chat("Who is our frontend lead?"))

asyncio.run(main())

πŸ–₯️ CLI Usage

memsearch index ./memory/                          # index markdown files
memsearch search "how to configure Redis caching"  # semantic search
memsearch watch ./memory/                          # auto-index on file changes
memsearch compact                                  # LLM-powered memory summarization
memsearch config init                              # interactive config wizard
memsearch stats                                    # show index statistics

πŸ“– Full command reference with all flags and examples β†’ CLI Reference

πŸ” 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  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  β”Œβ”€β”€β”€ Compact ─────────────────────────────────────────────────────────┐
  β”‚  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 backend or a cloud embedding provider.

🧩 Claude Code Plugin

memsearch ships with a Claude Code plugin β€” a real-world example of agent memory in action. 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.

# 1. Install the memsearch CLI
pip install memsearch

# 2. Set your embedding API key (OpenAI is the default provider)
export OPENAI_API_KEY="sk-..."

# 3. In Claude Code, add the marketplace and install the plugin
/plugin marketplace add zilliztech/memsearch
/plugin install memsearch

# 4. Restart Claude Code for the plugin to take effect, then start chatting!
claude

πŸ“– Architecture, hook details, and development mode β†’ Claude Code Plugin docs

βš™οΈ Configuration

Settings are resolved in priority order (lowest β†’ highest):

  1. Built-in defaults β†’ 2. Global ~/.memsearch/config.toml β†’ 3. Project .memsearch.toml β†’ 4. CLI flags

API keys for embedding/LLM providers are read from standard environment variables (OPENAI_API_KEY, GOOGLE_API_KEY, VOYAGE_API_KEY, ANTHROPIC_API_KEY, etc.).

πŸ“– Config wizard, TOML examples, and all settings β†’ Getting Started β€” Configuration

πŸ”Œ Embedding Providers

Provider Install Default Model
OpenAI memsearch (included) text-embedding-3-small
Google memsearch[google] gemini-embedding-001
Voyage memsearch[voyage] voyage-3-lite
Ollama memsearch[ollama] nomic-embed-text
Local memsearch[local] all-MiniLM-L6-v2

πŸ“– Provider setup and env vars β†’ CLI Reference β€” Embedding Provider Reference

πŸ—„οΈ Milvus Backend

memsearch supports three deployment modes β€” just change milvus_uri:

Mode milvus_uri Best for
Milvus Lite (default) ~/.memsearch/milvus.db Personal use, dev β€” zero config
Milvus Server http://localhost:19530 Multi-agent, team environments
Zilliz Cloud https://in03-xxx.api.gcp-us-west1.zillizcloud.com Production, fully managed

πŸ“– Code examples and setup details β†’ Getting Started β€” Milvus Backends

πŸ“š Links

  • Documentation β€” Getting Started, CLI Reference, Architecture
  • Claude Code Plugin β€” hook details, progressive disclosure, comparison with claude-mem
  • OpenClaw β€” the memory architecture that inspired memsearch
  • Milvus β€” the vector database powering memsearch
  • Changelog β€” release history

Contributing

Bug reports, feature requests, and pull requests are welcome on GitHub. For questions and discussions, join us on Discord.

πŸ“„ License

MIT

For Tasks:

Click tags to check more tools for each tasks

For Jobs:

Alternative AI tools for memsearch

Similar Open Source Tools

For similar tasks

For similar jobs