Polign Recall
Typed, durable memory for agents. Every fact is a record. Every correction is either a rule you declared or a statement the agent made. No model decides what to forget.
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) recall mcp -write -typed # the typed tools instead: remember(subject, predicate, value), history
recall mcp serves the text tools: pass a passage to remember, and the model
named by -extract-model works out the facts in it. Without a model the text is
kept as written and replaces nothing. -typed serves the typed tools over a
fixed vocabulary, which is what the rules below are written in.
What a session looks like with the typed tools and 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
Nothing is deleted. Every replaced value stays in history, and as_of answers
what was believed at any earlier moment.
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.
Agents share memory across processes and sessions: one agent records a preference, another corrects it, and both read the same current value and the same history. Recall can also resume an agent from its records when its process crashes or moves to another machine.
Use it through Go, Python, or MCP, with Polign as the storage backend. Go applications can also supply their own backend.
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.
On the LongMemEval user-facts subset (267 questions, gpt-4o-mini extraction, gpt-4o reading and judging), an open vocabulary scored 79.4% against 81.3% for a hand-tuned registry, a difference within noise at that size (p = 0.36).
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
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 168hEach 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.
Storage and search
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) | Qdrant | |
|---|---|---|
| Package | polign/ |
backend/qdrant/ |
recall mcp |
default | -backend qdrant |
| URL and key variables | POLIGN_URL, POLIGN_API_KEY |
QDRANT_URL, QDRANT_API_KEY |
| Server version | 0.8.0 or later | 1.10 or later |
| Remember, recall, forget, history, as-of | yes | yes |
| Evidence links, audit export | yes | yes |
| Keyword (BM25) search | yes | no, vector search only |
| Cached reads | yes | no, every read refolds the history |
Agent leases (recall mcp -agent) |
yes | no; Go Resume with Unleased: true only |
recall setup and the Claude plugin |
yes | no, add recall mcp by hand |
Pitfalls on Qdrant:
- Use a collection only Recall writes to. A point without the
_recall_idpayload field makes reads fail. - A collection takes its vector size from the first write. Changing embedders needs a new collection.
Unleased: trueis 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-url) 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. 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/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
Developer reference · Resuming an agent · Connecting through MCP · Caching memory reads · Exporting and replaying history