PostgreSQL + pgvector backend for Mnemosyne, the task-agnostic agentic memory library.
This library implements the Mnemosyne.GraphBackend behaviour, persisting the knowledge graph in PostgreSQL with vector similarity search powered by pgvector.
Add mnemosyne_postgres to your list of dependencies in mix.exs:
def deps do
[
{:mnemosyne_postgres, "~> 0.1.0"} # x-release-please-version
]
endEnsure the pgvector extension is available in your PostgreSQL instance (v0.5+).
defmodule MyApp.Repo.Migrations.AddMnemosyne do
use Ecto.Migration
def up, do: MnemosynePostgres.Migrations.up(version: 1, embedding_dimensions: 1536)
def down, do: MnemosynePostgres.Migrations.down(version: 1)
end| Option | Default | Description |
|---|---|---|
:version |
1 |
Target migration version |
:embedding_dimensions |
-- (required) | Dimensionality of your embedding vectors |
:index_type |
:hnsw |
:hnsw or :ivfflat |
:hnsw_m |
pgvector default | Max connections per HNSW layer |
:hnsw_ef_construction |
pgvector default | Dynamic candidate list size for HNSW |
:ivfflat_lists |
pgvector default | Number of inverted lists for IVFFlat |
:prefix |
"mnemosyne_" |
Table name prefix |
Set the default backend in your supervision tree so you don't repeat it on every open_repo call:
children = [
{Mnemosyne.Supervisor,
config: config,
llm: MyApp.LLM,
embedding: MyApp.Embedding,
backend: {MnemosynePostgres.Backend, repo: MyApp.Repo}}
]Then open repos -- repo_id is injected automatically from the first argument:
# Single-tenant (tenant_id defaults to "default")
{:ok, _pid} = Mnemosyne.open_repo("my-project")
# Multi-tenant
{:ok, _pid} = Mnemosyne.open_repo("my-project", tenant_id: "org-123")| Option | Default | Description |
|---|---|---|
:repo |
-- (required) | Your Ecto repo module |
:tenant_id |
"default" |
Tenant identifier for multi-tenant isolation |
:prefix |
"mnemosyne_" |
Table name prefix (must match migration) |
Nodes are stored in a single polymorphic nodes table with JSONB data, vector embeddings, and JSONB link maps. Metadata lives in a separate node_metadata table.
nodes node_metadata
+-----------+-----------+ +-------------+----------+
| id (uuid) | type | | node_id | accessed |
| data | embedding | | reward | recency |
| links | tenant_id | | frequency | |
| repo_id | inserted | +-------------+----------+
+-----------+-----------+
Vector indexes (HNSW or IVFFlat) are created per node type via partial indexes, avoiding the performance penalty of post-filtering across the full table.
When a new schema version is released, create a new migration pointing to the next version:
defmodule MyApp.Repo.Migrations.UpgradeMnemosyneV2 do
use Ecto.Migration
def up, do: MnemosynePostgres.Migrations.up(version: 2, embedding_dimensions: 1536)
def down, do: MnemosynePostgres.Migrations.down(version: 2)
endThe migration system tracks the current version via table comments and runs only the deltas.
All backend operations emit [:mnemosyne_postgres, ...] telemetry events. See MnemosynePostgres.Telemetry for the full list of events and their measurements.