Skip to main content

Semantic memory search for markdown knowledge bases

Project description

ย  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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

memsearch-0.1.6.tar.gz (2.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

memsearch-0.1.6-py3-none-any.whl (34.4 kB view details)

Uploaded Python 3

File details

Details for the file memsearch-0.1.6.tar.gz.

File metadata

  • Download URL: memsearch-0.1.6.tar.gz
  • Upload date:
  • Size: 2.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for memsearch-0.1.6.tar.gz
Algorithm Hash digest
SHA256 8082d4e8dd0897f4bf9f26b3d2258c6b0b7eb4f77cef7ea591c3a59c1e1daf93
MD5 15fd699ba7a45b25865184117a1f4c4b
BLAKE2b-256 63cb0dfe1c38a939f3480e856c971882471a5a22d6722e2e901b9cd8d715f2b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for memsearch-0.1.6.tar.gz:

Publisher: release.yml on zilliztech/memsearch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file memsearch-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: memsearch-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 34.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for memsearch-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 e292a7d33757a7f1bc54d08507cb78b37e95d4d705f9ad2a3ebaa7281c780869
MD5 75c6d7ac4fcf9361aeada319e452cd56
BLAKE2b-256 81df045db42501155fb25016c41dccbe133486ebe59ee6d28b655fffa8ecae8a

See more details on using hashes here.

Provenance

The following attestation bundles were made for memsearch-0.1.6-py3-none-any.whl:

Publisher: release.yml on zilliztech/memsearch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page