Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 7 additions & 100 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# memU

### Personal memory, stored as files
### Personal memory, stored as an LLM wiki

**Across Sessions. Across Agents. Across Devices.**

Expand All @@ -20,90 +20,29 @@

---

memU is a 500-line memory system for AI agents. Agents write what's worth keeping as Markdown; memU stores it, embeds it, and retrieves ranked context in a single call β€” embeddings are the only model calls it makes. The entire memory logic lives in [`agentic.py`](src/memu/app/agentic.py) + [`service.py`](src/memu/app/service.py); everything else is pluggable storage and embedding transport.
memU is a lightweight, agent-driven memory system that gives users a shared LLM wiki across sessions, agents, and devices. Its core memory logic is only 500 lines β€” compact enough to inspect, understand, and adapt. It uses embedding-only retrieval with fully pluggable storage and embedding infrastructure.

**Installation is agent-driven.** The guides are written for the agent, not for you. One message is the whole setup β€” tell your agent:

> Read https://raw.githubusercontent.com/NevaMind-AI/MemU/main/SKILL.md and follow it to install memU.

It works for Codex, Claude Code, Cursor, OpenClaw, Hermes, WorkBuddy β€” and any other agent, via detection. Details in [Host adapters](#host-adapters-memory-for-desktop-coding-agents).

Want to follow the latest development instead? Install from the newest source (git `main`) β€” tell your agent:

> Read https://raw.githubusercontent.com/NevaMind-AI/MemU/main/INSTALL-LATEST.md and follow it to install the latest memU.

## Quick start

```python
from memu.app import MemoryService

service = MemoryService(
database_config={"metadata_store": {"provider": "sqlite", "dsn": "sqlite:///memu.sqlite3"}},
)

# 1. Persist agent-prepared memory: recall files (memory/skill tracks) + resources
await service.commit_results(
recall_files=[
{
"name": "Profile",
"track": "memory",
"description": "who the user is",
"content": "# Profile\n- prefers dark roast coffee\n- ships on Fridays",
},
{
"name": "deploy-checklist",
"track": "skill",
"description": "how to deploy this repo",
"content": "1. run tests\n2. tag\n3. push",
},
],
resource=[{"path": "/abs/path/notes.md", "description": "meeting notes from the launch review"}],
)
Your memory lives in the shared store configured by `MEMU_DB` in `~/.memu/config.env` β€” typically `~/.memu/memu.sqlite3` for local SQLite, or a Postgres DSN.

# 2. See what is stored, across every track
files = await service.list_all_recall_files()

# 3. Single-shot embedding retrieval over segments / files / resources
context = await service.progressive_retrieve("What should I know about this user's launch preferences?")
```

Or straight from the terminal β€” no code:
Once installed, your agent retrieves relevant memory automatically before answering. To retrieve manually, run the adapter for your host:

```bash
export OPENAI_API_KEY=sk-... # embedding API key β€” the only model calls memU makes

npx memu-cli commit results.json # {"recall_files": [...], "resource": [...]}
npx memu-cli list-files
npx memu-cli retrieve "What should I know about this user's launch preferences?"
memu-codex retrieve "What should I remember about this project?"
# or: memu-claude-code / memu-cursor / memu-openclaw / memu-hermes / memu-workbuddy / memu-agent
```

State persists in a local SQLite database (`./data/memu.sqlite3` by default), so commit in one invocation and retrieve in the next.

## How it works

![memU memory system architecture](assets/structure-v2.png)

### The data model

Memory is a set of **recall files** β€” one Markdown document per topic (`track="memory"`) or per learned skill (`track="skill"`). Committing a file also writes its search index:

| Record | What it is | How it's embedded |
|---|---|---|
| **RecallFile** | The Markdown document itself (`name`, `track`, `description`, `content`) | `name: description`, once at creation |
| **RecallFileSegment** | Searchable slices of a file | memory track: one per content line (headings skipped); skill track: one `name: description` segment per skill |
| **Resource** | A raw source on disk (`url`, `caption`) | its one-line caption |

Segments are reconciled on every commit: lines that disappeared are deleted, only genuinely new lines are embedded, unchanged lines keep their vectors β€” so re-committing a lightly edited file is nearly free.

### Retrieval

`progressive_retrieve(query)` embeds the query **once** and returns three ranked layers:

- `segments` β€” the matched slices, narrowest and usually most on-point, each with a `score`
- `files` β€” the documents those segments belong to (usually what you want), each scored by its best segment and carrying its linked `resource_urls`
- `resources` β€” matching raw sources, for when summaries are not enough

There is no intention routing, sufficiency checking, or summarization β€” one embedding call in, ranked context out.

## Host adapters: memory for desktop coding agents

Expand All @@ -130,7 +69,7 @@ Installation is the one-message setup at the top of this README. [SKILL.md](SKIL

Afterwards `<binary> doctor` proves the whole loop resolves: config, store, and a live retrieval.

Adding another host means implementing one `TranscriptSource` (where its session logs live, how its records are shaped) plus a `HostSpec`-sized CLI β€” the pipeline, verbs, and instruction text are shared (ADR 0010).
Adding another host means implementing one `TranscriptSource` (where its session logs live, how its records are shaped) plus a `HostSpec`-sized CLI β€” the pipeline, verbs, and instruction text are shared.

## Installation

Expand Down Expand Up @@ -166,38 +105,6 @@ service = MemoryService(
embedding_profiles={"default": {"provider": "jina"}},
)
```

### Multi-tenancy

Every record carries optional scope fields (`user_id`, `agent_id` by default). Pass `user=` on writes and `where=` on reads to partition one store:

```python
await service.commit_results(recall_files=[...], user={"user_id": "alice"})
await service.progressive_retrieve("launch preferences", where={"user_id": "alice"})
```

Need different scope fields? Supply your own model β€” filters are validated against it, unknown fields raise:

```python
from pydantic import BaseModel

class TeamScope(BaseModel):
team_id: str | None = None
user_id: str | None = None

service = MemoryService(user_config={"model": TeamScope})
```

## Development

```bash
make install # uv sync + pre-commit hooks
make test # pytest with coverage
make check # lock check, pre-commit, mypy, deptry
```

Architecture decisions live in [`docs/adr/`](docs/adr/) β€” notably tracked workspace memorization (ADR 0006), the segment/file/resource retrieval lines (ADR 0007), and the host-adapter seams (ADR 0008/0009).

## License

Apache-2.0
Loading