Epistemological memory for AI agents — a knowledge graph where every belief carries a derivation chain back to ground-truth observations, confidence is derived from graph structure rather than asserted, and beliefs are tested, violated, or confirmed as new evidence lands.
raven is the successor to pantainos-memory (Cloudflare Workers + D1 +
Vectorize, TypeScript), rebuilt as a Python MCP service on Postgres +
pgvector with pluggable embeddings. The v1 implementation is preserved on
the v1-cloudflare-archive branch.
The core loop: remembering is cheap, believing is earned. Writing to memory just records raw text; a background worker later reads the surrounding graph and proposes how the new memory connects to what's already known — and a single gate function is the only thing allowed to mutate the graph.
flowchart TD
A[agent calls <b>remember</b>] --> B[persist raw text + embed<br/><i>no graph decisions yet</i>]
B --> Q[(job queue)]
Q --> W[enrichment worker claims job,<br/>builds context pack from the graph]
W --> LLM[agent proposes ops<br/>observe · derive · link · predict · verdict · merge]
LLM --> G{<b>apply_proposal</b><br/>the only writer}
G -->|valid| KG[(knowledge graph)]
G -->|reject| Q
KG --> R[<b>recall</b> / ask / surprising<br/>beliefs + derived confidence]
Proposals use a closed vocabulary — an op can say "this thought derives from those observations" but can never assert a number. Confidence, counters, and centrality are all computed in code from the graph's edges.
Which brings up the trust question — not all memories are equal:
flowchart TD
R[reality<br/>market data · news · tools · humans]
R -->|source| O["<b>observations</b><br/>ground truth, frozen once written<br/>trust ≈ source quality"]
O -->|derive| T["<b>thoughts</b><br/>derived from other memories<br/>trust inherited via edges"]
T -->|derive| T
T -->|+ deadline| P["<b>predictions</b><br/>resolve correct/incorrect on deadline<br/>→ track record per source"]
T -->|dream lane| F["<b>framings</b><br/>perspectives & theses<br/>synthesized over clusters"]
E[new evidence] -->|confirms / violates| T
T -.->|violations cascade<br/>to downstream beliefs| T
So when an agent recalls, it doesn't just get similar text back — it gets beliefs with a pedigree: what each one is derived from, how much of its foundation still stands, and how well its sources have scored before.
v2 rebuilds the write path around loose ingest + one apply gate:
rememberis loose ingest — persist the raw text, embed it, enqueue an enrichment job. No graph decisions at write time.- A queue-driven enrichment worker claims jobs (SKIP LOCKED, with park/fail/dead lanes), builds a codified context pack from the surrounding graph, and runs an agent that proposes mutations.
- The gate is the only writer. Proposals use a closed op vocabulary
(
gate/ops.py); onlygate/apply.py:apply_proposalturns them into graph mutations. Every number — confidence, counters, centrality — is derived in code from edges; no op can name one, andtests/test_vocabulary.pyasserts that property mechanically. - Everything is tenanted. Composite PKs and hash partitioning make untenanted queries physically impossible.
A dream lane consolidates further: a gardener enqueues reframe clusters and a dreamer worker synthesizes higher-level framings from them.
- Python, FastMCP — streamable-http on port 3005
- Postgres + pgvector (asyncpg)
- Embeddings via a pluggable
EmbeddingProvider: Ollama (default), TEI, or any hosted OpenAI-compatible API - LLM judge + enrichment runner are pluggable; a deterministic
FixtureRunnerbacks the zero-LLM end-to-end tests
raven/
├── PLAN.md # migration plan
├── backend/
│ ├── src/raven/
│ │ ├── mcp_server.py # FastMCP + tool registration
│ │ ├── gate/ # op vocabulary + the single apply gate
│ │ ├── workers/ # enrich, exposure, dispatch, dreamer
│ │ ├── db/ # pool, tenancy, migrations, repo/
│ │ ├── embeddings/ # provider protocol + ollama/tei/hosted
│ │ ├── services/ usecases/ tools/
│ ├── migrations/ # append-only, numbered SQL
│ └── tests/ # need a real pgvector Postgres (see below)
└── infra/
docker run -d --name memory-pg -p 5432:5432 \
-e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=raven \
pgvector/pgvector:pg16
cd backend
uv sync
POSTGRES_URL=postgres://postgres:postgres@localhost:5432/raven \
uv run raven-migrate
POSTGRES_URL=postgres://postgres:postgres@localhost:5432/raven \
uv run ravenEmbeddings default to an Ollama endpoint (EMBED_URL,
EMBED_MODEL=embeddinggemma); see CLAUDE.md for the full env table and
hosted-provider fallback.
docker run -d --name memtest-pg -p 5433:5432 \
-e POSTGRES_PASSWORD=testpass -e POSTGRES_DB=memtest \
pgvector/pgvector:pg16
cd backend
POSTGRES_TEST_URL=postgres://postgres:testpass@127.0.0.1:5433/memtest \
uv run pytest tests/ -qTests skip without a reachable pgvector Postgres. The acceptance test
(tests/test_e2e_t1.py) runs remember → worker → enriched graph
end-to-end with zero LLM calls.
- v1 — pantainos-memory: Cloudflare Workers + D1 + Vectorize.
Archived on
v1-cloudflare-archive. - v2 (this) — Postgres + pgvector, loose ingest + gated writes.
Canonical design doc:
pantainos-memory-v2-proposal.mdin the cassandra-stack architecture docs.