Skip to main content
Glama
Polign

Recall by Polign

Official

Polign Recall

Memory for agents whose facts change. When a user corrects something, Recall replaces the old fact, tells the agent what it replaced, and keeps the old value in a history you can read back. Nothing is deleted, and no model decides on its own what to forget.

Recall, mem0 and Zep answering questions about a user whose facts changed over nine months

The demo is a real run: one user over nine months, with three homes, three employers, and three work emails, then six questions about her now. Opus 5.5 extracted, answered, and judged for all three systems, three runs each. Right answers: Recall 18/18, mem0 18/18, Zep 12/18. Tokens to save one message: Recall 3.0k, mem0 12.7k, Zep 14.7k.

Install

pip install polign-recall          # Python client; brings the server with it
go get github.com/Polign/recall    # Go
recall mcp -write                  # MCP over stdio: remember(text), recall(question), forget(text)

The Quickstart runs without a model or an API key.

Related MCP server: widemem-ai

What a correction looks like

Every change is kept. Three commands read that record:

$ recall history "which editor do I use"
user preferred editor
  believed now: user preferred editor: zed
  2026-09-01 10:00  stated, user_stated
      + user preferred editor: vim
  2026-09-20 10:00  replaced, user_stated
      - user preferred editor: vim
      + user preferred editor: emacs
  2026-10-05 10:00  replaced, user_stated
      - user preferred editor: emacs
      + user preferred editor: zed

$ recall ask -as-of 2026-09-25 "which editor do I use"
Believed as of 2026-09-25 00:00:
user preferred editor: emacs  (since 2026-09-20)
  replaced: vim  (since 2026-09-01)

$ recall diff -from 2026-09-15 -to 2026-10-31
Changed between 2026-09-15 00:00 and 2026-10-31 00:00:
- user home city: Lisbon
+ user home city: Porto
- user preferred editor: vim
+ user preferred editor: zed

Why Recall

  • Corrections are records. A new value replaces the old one by a rule you can read, and the answer arrives with what it replaced.

  • Ask about any moment. as_of, history, and diff answer what the agent believed then, and when that changed.

  • Every answer has a source. recall explain shows the message a fact came from, the rule that decided it, and its history.

  • Shared and resumable. Agents in different processes read and correct the same memory, and a crashed agent resumes from its records.

  • Your storage. polign_db, PostgreSQL with pgvector, MongoDB, or Qdrant. Apache 2.0.

Benchmark

LongMemEval user-facts subset (267 questions about a user's own facts, preferences, updates, and dates), Recall 0.19.0, Sonnet 5.5 as reader and judge, upstream prompts:

What the reader is given

Score

Nothing

6.0

The full chat history

88.0

BM25 over sessions, top 10

89.9

Recall, built-in word search

88.8

Recall with a model embedder (OpenAI text-embedding-3-small, 512 dims)

92.9

Only the sessions that hold the answer

96.6

With its built-in word search, Recall is about even with BM25 overall and behind it on preference questions (70.0 against 83.3). A model embedder puts it ahead, and it is the default since 0.20.0 when the extractor is an OpenAI model. Recall hands the reader about 36k tokens per question, against about 168k for the full history.

Choose an integration

Integration

Get started

Python

Follow the quickstart below, then see the client guide.

Go

Run go get github.com/Polign/recall and follow the Go client guide.

MCP

Connect an agent to tools for remembering, recalling, and forgetting facts.

LangGraph

pip install recall-langgraph to resume LangGraph agents from their own records.

LiveKit

pip install recall-livekit for caller memory in voice agents.

The Claude Code plugin is a ready-made MCP integration for memory across coding sessions.

Quickstart

You'll need Python 3.10+. Install the client:

pip install polign-recall

pip also brings the polign_db package, which holds the polign and polign-server binaries, so there is nothing else to download or start. This example needs no model or embedding API key.

One process saves an editor preference and then changes it:

python - <<'PY'
from polign_recall import Client

with Client(local_dir="./recall-data") as memory:
    memory.remember("user", "prefers_editor", "vim")
    memory.remember("user", "prefers_editor", "neovim")
PY

Run a second process in the same terminal. It reads the current preference, the full history, and what was believed before the correction:

python - <<'PY'
from polign_recall import Client

with Client(local_dir="./recall-data") as memory:
    print(memory.recall("user", "prefers_editor")[0].value)  # neovim

    events = memory.history("user", "prefers_editor")
    print([event.value for event in events])  # ['vim', 'neovim'] in a fresh directory

    # Ask what was believed at the moment of the first statement.
    before = memory.recall("user", "prefers_editor", as_of=events[0].observed_at)
    print(before[0].value)  # vim
PY

The correction did not erase vim. It is still there to read, which is how you answer "what did the agent believe yesterday, and when did that change?"

The first client to open ./recall-data starts a local polign-server for it in the background, and later clients share it. The memories stay in that directory after your program exits. Managed local databases work on Linux and macOS.

To share memory between machines, run polign-server yourself (requires Polign v0.8.0 or later), leave local_dir out, and set POLIGN_URL, POLIGN_COLLECTION, and POLIGN_API_KEY when authentication is required.

For examples covering forgetting as well, see the Python session example or Go session example.

How memory works

A memory has a subject, a predicate, and a typed value:

Subject

Predicate

Value

user

prefers_editor

neovim

recall-demo

prefers_test_framework

pytest

recall-demo

uses_technology

Go

Predicates live in a registry that declares each one's value type, whether it holds one value or several, and which predicates it contradicts (dislikes excludes likes, gave_away excludes owns). Recall includes starter registries for coding, personal assistant, support, sales, and voice agents. Add your own predicates (the registry), and the extractor files statements under them; you can change the registry later without changing what the agent remembered before. A value can be text, a number, a boolean, an enum, a date, or a reference to another subject.

For a single-valued predicate such as prefers_editor, a new assertion replaces the current value. A multi-valued predicate such as uses_technology can hold both Python and Go. Corrections preserve earlier statements, so you can read current values, inspect their history, or query what was known at an earlier time.

Forgetting records a withdrawal and removes the affected fact from current answers. It does not permanently delete the record.

An agent can also propose facts from text. Recall validates those proposals against the registry before writing them. A statement that fits no predicate is kept as a note instead of being dropped. Once you add a predicate for that kind of fact, Promote files each note under it and keeps the note in history.

Without a registry

You don't have to define predicates at all. With an open vocabulary, a write may name any predicate and its first use defines it, starting from the coding and personal starters. recall explain "<question>" shows, for each answer, the text it came from, the predicate it was filed under, the rules that decide it, and its history.

With a model configured, the MCP server goes one step further and takes plain text, with no predicates in any tool or answer:

recall setup -extract-model anthropic:claude-haiku-4-5-20251001
remember("I switched from helix to zed last week")
recall("which editor do I use?")  ->  "user prefers editor: zed" (before: helix)
forget("my editor")

The model decides how each statement is filed: which predicate, and whether a new value replaces the old one or adds to it. Once filed, replacement follows the predicate's rule as before, every filing is recorded, and a wrong one is corrected with one event (Redefine, Merge, Split) that every answer follows. The model is called on every remember and forget, and the text is sent to its provider. See text-only tools.

The benchmark above uses these text tools with an open vocabulary seeded from the personal starter.

Typed tools

recall mcp -write -typed serves tools over a fixed vocabulary instead, such as remember(subject, predicate, value). A session with the personal starter registry:

remember(user, likes, mangoes)
remember(user, dislikes, mangoes)
  -> superseded: [likes: mangoes]          # the registry says dislikes excludes likes

recall(user, dislikes)
  -> mangoes   replaced: [likes: mangoes]  # the correction arrives with the answer

remember(user, goal, "run a marathon")
remember(user, did,  "run a marathon")
recall(user, goal)
  -> run a marathon   elsewhere: [did: run a marathon]   # no rule joins goal and did,
                                                         # so the read shows both

remember(user, did, "run a marathon",
         replaces=[goal: "run a marathon", goal: "learn piano"])
  -> replaced:   [goal: run a marathon]
  -> unreplaced: [goal: learn piano]       # never believed, so reported, not dropped

No model reconciles memory on its own. Recall applies the rules the registry declares, shows the candidates that may conflict, and lets your agent confirm the rest, so every correction can be read back to the rule or the statement that made it.

Reading the record

Each step of a history is the log folded just before and just after that statement, so it agrees with a read at that instant. Agents get the same record through the history and diff MCP tools (timeline and diff with -typed), recall serve answers them over HTTP at /v1/history, /v1/timeline and /v1/diff, and the Python client has history_text, timeline and diff.

Resuming an agent

Resume gives an agent back the context it should start from, built from what it wrote down while it worked. The new process resumes from records, not from a process or container snapshot:

  • Working state: its goal, plan, progress, focus, and open questions.

  • Pointers to where its work lives, such as a git branch or an object key.

  • Turns, verbatim, and large tool outputs stored by reference.

The next process gets those records, plus memories that match its focus, sized to a token budget and rendered as one briefing for the model. A lease keeps one process per agent, so a second copy cannot write over the first. Leases need Polign v0.8.0 or later.

agent, err := client.Resume(ctx, recall.ResumeRequest{AgentID: "coder-1", TokenBudget: 8000})
if err != nil {
    return err
}
defer agent.Release(context.Background())

briefing := agent.Resumed().Briefing // hand this to the model

Resume and actions are a beta surface: the calls around the records may still change in a minor release, with the change and what to do about it in the changelog. The records themselves are stable. See STABILITY.md.

The resume example crashes an agent partway through a task and continues it in a new process. See Resuming an agent for the full API, and the Python client guide for resume in Python.

Serving many agents

recall mcp is one agent, one process, one store. recall serve is the same memory for many agents at once, over HTTP, each inside the namespace its key names. The server holds the store's credentials; agents hold Recall keys.

recall -url http://polign:23000 serve -bootstrap-key-file admin.key   # admin.key holds one rcl_... line
recall -url http://polign:23000 key create -namespace acme:agent-7 -expires 168h

Each namespace gets its own collection, so nothing a caller sends can reach another namespace's records. Keys are stored hashed, expire if asked, renew at POST /v1/keys/renew, and are revoked by a tombstone that nothing can overwrite. POST /mcp is MCP over streamable HTTP; POST /v1/remember, /v1/recall, /v1/forget and /v1/history take a tool's arguments as the body. Any MCP client that speaks stdio reaches the server through recall connect, with RECALL_URL and RECALL_API_KEY set.

On Kubernetes a pod needs no key at all. With recall serve -kubernetes, it posts its projected service account token (audience recall) and gets a short-lived key for <tenant>:<pod name>, fixed to that agent, which recall connect and the Python client obtain and renew by themselves from RECALL_TOKEN_FILE. The Helm chart is charts/recall in Polign/polign; the agent-sandbox demo runs it on kind. The contract every client is written against is docs/memory-api-v1.md.

Recall provides memory rules, validation, and history; the backend provides persistence and access to the data. Agents share memory by using the same backend, collection, and namespace, with compatible Recall versions and memory definitions. Search also requires compatible embedding methods.

Concurrent reads and writes follow the backend's consistency guarantees. Recall does not add transaction isolation or replication.

Backends

polign_db (default)

PostgreSQL + pgvector

MongoDB

Qdrant

Package

polign/

backend/pgvector/

backend/mongodb/

backend/qdrant/

recall mcp

default

-backend pgvector

-backend mongodb

-backend qdrant

URL and key variables

POLIGN_URL, POLIGN_API_KEY

DATABASE_URL, PGPASSWORD

MONGODB_URI, MONGODB_PASSWORD

QDRANT_URL, QDRANT_API_KEY

Server version

0.8.0 or later

PostgreSQL 13 or later with pgvector 0.6.0 or later; 0.8.0 or later recommended

6.0 or later, Atlas or self-hosted

1.10 or later

Remember, recall, forget, history, as-of

yes

yes

yes

yes

Evidence links, audit export

yes

yes

yes

yes

Keyword search

yes, BM25

yes, PostgreSQL full-text search

yes, MongoDB text search

no, vector search only

Cached reads

yes

yes

yes

no, every read refolds the history

Agent leases (recall mcp -agent)

yes

yes

yes

no; Go Resume with Unleased: true only

Agent writes fenced on the server

yes

yes, in the write's transaction

yes on a replica set or Atlas, in the write's transaction; checked just before the write on a standalone server

no

recall setup and the Claude plugin

yes

no, add recall mcp by hand

no, add recall mcp by hand

no, add recall mcp by hand

To run Recall on a PostgreSQL database:

export DATABASE_URL=postgres://user:password@localhost:5432/mydb
recall -backend pgvector mcp -write

The first write creates a recall schema with one table per collection and two bookkeeping tables. The role needs CREATE on the database, and the vector extension must be installed or creatable by that role.

Pitfalls on PostgreSQL:

  • A collection takes its vector size from the first write. Changing embedders needs a new collection.

  • Every write to a collection takes a row lock on that collection's revision, so writes to one collection are serialized. That is fine at conversation pace but slows a bulk import.

  • Keyword search uses the english text search configuration unless the Go Config.TextSearchConfig says otherwise. It is fixed when a collection is created.

  • On pgvector older than 0.8.0, a filtered vector search can return fewer hits than asked for, because the index stops scanning at ef_search candidates before the filter applies.

To run Recall on MongoDB:

export MONGODB_URI=mongodb://user:password@localhost:27017/recall
recall -backend mongodb mcp -write

The database is the connection string's path, recall when it has none. The first write to a collection creates a MongoDB collection named rc_ and the collection's name, and two bookkeeping collections, recall_collections and recall_leases. The user needs readWrite on the database.

Pitfalls on MongoDB:

  • Vector search is exact: the similarity is computed in the server over the events a filter keeps. No Atlas Search index is needed and the answer is the true nearest events, but each search reads the whole filtered collection. Agent memory collections hold thousands of events, which is fast; a collection of millions is not what this backend is for.

  • Run a replica set (Atlas always is) for agents. On a standalone server there are no transactions, so a fenced agent write is checked just before it lands rather than with it, and listings are not read from one snapshot. A single-node replica set is enough.

  • A collection takes its vector size from the first write. Changing embedders needs a new collection.

  • Keyword search uses MongoDB's english text index, fixed when a collection is created.

Pitfalls on Qdrant:

  • Use a collection only Recall writes to. A point without the _recall_id payload field makes reads fail.

  • A collection takes its vector size from the first write. Changing embedders needs a new collection.

  • Unleased: true is safe only when something else keeps one process per agent. Without a lease, a crashed process that comes back can overwrite its replacement.

  • With the built-in lexical embedder, exact-word queries rank worse than on polign_db. A model embedder (-embed-model) helps paraphrases, not keywords.

To add a backend, implement Put, List and Search (contract), register it with recall.RegisterBackend in init, pass backendtest, and import it in cmd/recall/backends.go.

The Go library has no external module dependencies. The pgvector backend uses pgx and the MongoDB backend the official MongoDB driver, which a program pulls in only by importing that package. Its built-in lexical embedder supports word-overlap search; applications can supply a model-based embedder for semantic search. See the embedding guide.

Repository layout

Path

Contents

recall.go, doc.go

Public Go API at github.com/Polign/recall

internal/engine/

Memory implementation and unit tests

polign/

polign_db backend

backend/pgvector/

PostgreSQL + pgvector backend

backend/mongodb/

MongoDB backend

backend/qdrant/

Qdrant backend

backendtest/

Conformance suite for backends

python/

Python package and tests

cmd/

Command-line tools, including recall-audit

examples/

Runnable Go and Python examples, including a crash-and-resume demo

docs/

Guides and API reference

testdata/

Shared audit fixtures

See the development guide for the implementation map and test commands.

Documentation

What is stable and what is beta · Developer reference · Resuming an agent · Connecting through MCP · Caching memory reads · Exporting and replaying history

Support the project

If Recall kept your agent from acting on a stale fact, a star on this repository helps other people find it. Bugs, questions, and ideas go in issues.

License

Apache 2.0.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Persistent semantic memory for AI agents. SQLite-backed, local-first, zero config. Semantic search via Ollama embeddings with keyword fallback. Tools: remember, recall, history, forget, stats.
    17
    37 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Open-source AI memory layer for LLM agents. Importance scoring, temporal decay, hierarchical memory (facts, summaries, themes), YMYL prioritization, and active retrieval with contradiction detection. Supports OpenAI, Anthropic, Ollama. Local-first with SQLite + FAISS.
    51 PyPI
    49
    Apache 2.0
  • F
    license
    Not graded
    quality
    A
    maintenance
    Local memory for AI agents in one SQLite file on your own machine. Facts carry a validity window and a confidence score, so an outdated one is superseded and downweighted rather than deleted, and retrieval fuses BM25, vector similarity and a knowledge graph.
    2
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Self-hosted long-term memory for AI agents on your own Postgres. Facts are dated, versioned and superseded rather than silently overwritten, every recall ships with an inspectable trace, and agents can correct wrong memories from inside the conversation.
    2
    Apache 2.0