139 lines
6.5 KiB
SQL
139 lines
6.5 KiB
SQL
-- Enable the pgvector extension and uuid extension
|
|
CREATE EXTENSION IF NOT EXISTS vector;
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
|
|
-- ============================================================================
|
|
-- TEMPORAL + SEMANTIC + ENTITY MEMORY ARCHITECTURE
|
|
-- ============================================================================
|
|
|
|
-- Documents: Source of memory units (for tracking, updates, and deletion)
|
|
CREATE TABLE IF NOT EXISTS documents (
|
|
id TEXT NOT NULL, -- User-provided document ID
|
|
agent_id TEXT NOT NULL,
|
|
PRIMARY KEY (id, agent_id),
|
|
original_text TEXT, -- Full original content (for context expansion)
|
|
content_hash TEXT, -- SHA256 hash for deduplication
|
|
metadata JSONB DEFAULT '{}'::jsonb, -- User-provided metadata
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Memory Units: Individual sentence-level memories
|
|
CREATE TABLE IF NOT EXISTS memory_units (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
agent_id TEXT NOT NULL,
|
|
document_id TEXT, -- Link to source document
|
|
text TEXT NOT NULL,
|
|
embedding vector(384), -- bge-small-en-v1.5 dimension
|
|
context TEXT, -- What was happening when this memory was formed
|
|
event_date TIMESTAMPTZ NOT NULL, -- When the event occurred
|
|
access_count INTEGER DEFAULT 0, -- For recency/frequency weighting
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Entities: Resolved entities (people, organizations, locations, etc.)
|
|
CREATE TABLE IF NOT EXISTS entities (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
canonical_name TEXT NOT NULL, -- "Alice", "Google", "San Francisco"
|
|
entity_type TEXT NOT NULL, -- PERSON, ORG, GPE, etc.
|
|
agent_id TEXT NOT NULL, -- Entities are scoped to agents
|
|
metadata JSONB DEFAULT '{}'::jsonb, -- Additional entity info
|
|
first_seen TIMESTAMPTZ DEFAULT NOW(),
|
|
last_seen TIMESTAMPTZ DEFAULT NOW(),
|
|
mention_count INTEGER DEFAULT 1
|
|
);
|
|
|
|
-- Unit-Entity associations: Which entities appear in which units
|
|
CREATE TABLE IF NOT EXISTS unit_entities (
|
|
unit_id UUID REFERENCES memory_units(id) ON DELETE CASCADE,
|
|
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (unit_id, entity_id)
|
|
);
|
|
|
|
-- Entity Co-occurrences: Materialized cache of which entities appear together
|
|
-- This dramatically speeds up entity resolution by avoiding expensive joins
|
|
CREATE TABLE IF NOT EXISTS entity_cooccurrences (
|
|
entity_id_1 UUID REFERENCES entities(id) ON DELETE CASCADE,
|
|
entity_id_2 UUID REFERENCES entities(id) ON DELETE CASCADE,
|
|
cooccurrence_count INTEGER DEFAULT 1,
|
|
last_cooccurred TIMESTAMPTZ DEFAULT NOW(),
|
|
PRIMARY KEY (entity_id_1, entity_id_2),
|
|
CHECK (entity_id_1 < entity_id_2) -- Enforce ordering to avoid duplicates
|
|
);
|
|
|
|
-- Memory Links: Temporal, semantic, AND entity connections
|
|
CREATE TABLE IF NOT EXISTS memory_links (
|
|
from_unit_id UUID REFERENCES memory_units(id) ON DELETE CASCADE,
|
|
to_unit_id UUID REFERENCES memory_units(id) ON DELETE CASCADE,
|
|
link_type TEXT NOT NULL, -- 'temporal', 'semantic', or 'entity'
|
|
weight FLOAT NOT NULL DEFAULT 1.0, -- Link strength
|
|
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE, -- Set for entity links
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Unique constraint to prevent duplicate links
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_memory_links_unique
|
|
ON memory_links (from_unit_id, to_unit_id, link_type, COALESCE(entity_id, '00000000-0000-0000-0000-000000000000'::uuid));
|
|
|
|
-- ============================================================================
|
|
-- FOREIGN KEY CONSTRAINTS
|
|
-- ============================================================================
|
|
|
|
-- Add foreign key from memory_units to documents (composite key)
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint WHERE conname = 'memory_units_document_fkey'
|
|
) THEN
|
|
ALTER TABLE memory_units
|
|
ADD CONSTRAINT memory_units_document_fkey
|
|
FOREIGN KEY (document_id, agent_id)
|
|
REFERENCES documents(id, agent_id)
|
|
ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- INDEXES
|
|
-- ============================================================================
|
|
|
|
-- Document indexes
|
|
CREATE INDEX IF NOT EXISTS idx_documents_agent_id ON documents(agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_documents_content_hash ON documents(content_hash);
|
|
|
|
-- Memory unit indexes
|
|
CREATE INDEX IF NOT EXISTS idx_memory_units_agent_id ON memory_units(agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_units_document_id ON memory_units(document_id);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_units_event_date ON memory_units(event_date DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_units_agent_date ON memory_units(agent_id, event_date DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_units_access_count ON memory_units(access_count DESC);
|
|
|
|
-- Vector similarity index (HNSW for fast approximate nearest neighbor)
|
|
CREATE INDEX IF NOT EXISTS idx_memory_units_embedding ON memory_units
|
|
USING hnsw (embedding vector_cosine_ops);
|
|
|
|
-- Entity indexes
|
|
CREATE INDEX IF NOT EXISTS idx_entities_agent_id ON entities(agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_entities_canonical_name ON entities(canonical_name);
|
|
CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
|
|
CREATE INDEX IF NOT EXISTS idx_entities_agent_name_type ON entities(agent_id, canonical_name, entity_type);
|
|
|
|
-- Unit-entity indexes
|
|
CREATE INDEX IF NOT EXISTS idx_unit_entities_unit ON unit_entities(unit_id);
|
|
CREATE INDEX IF NOT EXISTS idx_unit_entities_entity ON unit_entities(entity_id);
|
|
|
|
-- Entity co-occurrence indexes for fast lookups
|
|
CREATE INDEX IF NOT EXISTS idx_entity_cooccurrences_entity1 ON entity_cooccurrences(entity_id_1);
|
|
CREATE INDEX IF NOT EXISTS idx_entity_cooccurrences_entity2 ON entity_cooccurrences(entity_id_2);
|
|
CREATE INDEX IF NOT EXISTS idx_entity_cooccurrences_count ON entity_cooccurrences(cooccurrence_count DESC);
|
|
|
|
-- Link indexes for graph traversal
|
|
CREATE INDEX IF NOT EXISTS idx_memory_links_from ON memory_links(from_unit_id);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_links_to ON memory_links(to_unit_id);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_links_type ON memory_links(link_type);
|
|
CREATE INDEX IF NOT EXISTS idx_memory_links_entity ON memory_links(entity_id) WHERE entity_id IS NOT NULL;
|
|
|
|
-- Composite index for spreading activation neighbor queries (from_unit_id + weight filter)
|
|
CREATE INDEX IF NOT EXISTS idx_memory_links_from_weight ON memory_links(from_unit_id, weight DESC)
|
|
WHERE weight >= 0.1;
|