AI Pipeline Architecture Model
A persistent memory layer for autonomous AI agents that records user preferences, interaction history, and behavioral shifts, updating its vector index dynamically over time.
RAG-008: Agentic Long-Term Memory — Engineering Blueprint
This specification details the mathematical formulations, system design, memory classification layers, and evaluation modules for RAG-008 (Agentic Long-Term Memory RAG). It transitions autonomous agents from static context processors into stateful, evolving partners.
Memory Consolidation Latency: $< 150\text{ms}$ executed asynchronously on separate worker threads to avoid blocking real-time chat loops.
Semantic Retrieval SLA: $< 30\text{ms}$ query latency over $1,000,000$ active memory nodes (utilizing local HNSW pgvector indices).
Episodic Reconstruction Fidelity: $> 98\%$ retrieval accuracy on temporal query chains (e.g., "What was the user's primary goal three sessions ago?").
Memory Optimization Target: Dynamic consolidation merging rules to reduce raw chat history token bloat by up to $85\%$ in long-running agent loops.
Standard agent pipelines dump entire chat histographic records into prompts, exceeding token boundaries and degrading LLM attention. RAG-008 separates memory into three distinct architectural spaces:
[ Raw Real-Time User Input ]
│
▼
[ Working Memory (Session) ]
• RAM-bound local state
• Active context variables
│
┌──────────────────┴──────────────────┐
▼ ▼
[ Episodic Memory Pool ] [ Semantic Memory Pool ]
• Raw, sequenced events • Abstracted facts & rules
• Logs of past conversations • Evolving user preferences
• Stored in pgvector • Consolidated periodically
│ │
└──────────────────┬──────────────────┘
▼
[ Memory Optimization Router ]
• Applies Ebbinghaus Decay math
• Resolves mutation conflicts
• Purges stale contextual links
2.1 The Three Memory Domains
Working Memory: The active prompt envelope, holding current task definitions and the immediate chat history.
Episodic Memory: Chronological records of past sessions, captured as vectorized interaction logs ($1536$-dimensional vector space via pgvector or Supabase).
Semantic Memory: Abstracted, structured assertions (e.g., User prefers Python over TypeScript, User is planning a vacation in October). These represent consolidated behavior patterns, decoupled from specific conversation times.
An agentic memory layer cannot simply stack facts forever; it must decay low-importance events, reinforce recurring patterns, and resolve factual contradictions as they occur in real time.
3.1 Temporal Decay Model (Ebbinghaus Forgetting Curve)
The retrieval strength $S_{\text{retrieval}}(M, t)$ of a memory node $M$ at epoch time $t$ decreases over time unless reinforced. This decay is governed by the Ebbinghaus retention curve:
$$S_{\text{retrieval}}(M, t) = \cos(\mathbf{e}_Q, \mathbf{e}_M) \cdot e^{-\frac{t - t_0}{H(M)}}$$
Where:
$\mathbf{e}_Q$ and $\mathbf{e}_M$ are the query and memory vectors, respectively.
$t_0$ is the epoch timestamp when the memory node was last accessed or reinforced.
$H(M) \in (0, \infty)$ is the Memory Half-Life factor, which dictates how fast a memory is forgotten. Highly impactful memories are assigned high $H(M)$ values.
3.2 Cognitive Memory Reinforcement
When a memory node $M$ is accessed during interaction, its half-life $H(M)$ increases, reducing its future decay speed:
$$H(M)_{t+1} = H(M)_t + \eta \cdot (1.0 - e^{-\alpha \cdot C(M)})$$
Where:
$\eta$ is the reinforcement learning rate.
$C(M)$ is the count of lifetime retrievals of memory $M$.
$\alpha > 0$ is a calibration coefficient regulating reinforcement saturation.
3.3 Conflict Resolution (Fact Mutation)
If a new observation $M_{\text{new}}$ contradicts an existing semantic fact $M_{\text{old}}$ (measured via high semantic overlap but contradictory logical values, e.g., User lives in Seattle vs. User moved to Boston), the memory engine resolves the state. We calculate a resolution score $R$:
$$R = \arg\max_{m \in {M_{\text{new}}, M_{\text{old}}}} \left( \beta \cdot \text{Confidence}(m) + (1.0 - \beta) \cdot \text{Recency}(m) \right)$$
Where:
$\text{Confidence}(m)$ is the model's extraction confidence score.
$\text{Recency}(m)$ is normalized temporal proximity: $1.0 - \frac{t_{\text{current}} - t_m}{t_{\text{current}} - t_{\text{origin}}}$.
$\beta \in [0.0, 1.0]$ is the weight balancing truth credibility against historical updates. The lower-scoring node is archived into Episodic History, and the higher-scoring node updates the active Semantic Memory index.
This self-contained Python module uses an embedded SQLite database to model the relational metadata, NumPy to calculate vector similarities, and custom consolidation logic to parse, mutate, reinforce, and decay memories.
import sqlite3 import json import time import numpy as np from typing import List, Dict, Any, Tuple, Optional
class AgentMemoryManager: def init(self, db_path: str = "agent_memory.db"): self.conn = sqlite3.connect(db_path) self.cursor = self.conn.cursor() self._initialize_schema()
def _initialize_schema(self):
"""Build relational tables for Episodic and Semantic memory nodes."""
# SQLite lacks native vector types, so we simulate pgvector storing embeddings as BLOBs or JSON
self.cursor.execute("""
CREATE TABLE IF NOT EXISTS episodic_memories (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
session_id TEXT NOT NULL,
interaction_text TEXT NOT NULL,
embedding_json TEXT NOT NULL,
created_at REAL NOT NULL
);
""")
self.cursor.execute("""
CREATE TABLE IF NOT EXISTS semantic_memories (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
fact_key TEXT NOT NULL,
fact_value TEXT NOT NULL,
embedding_json TEXT NOT NULL,
half_life REAL NOT NULL,
access_count INTEGER DEFAULT 1,
last_accessed_at REAL NOT NULL,
created_at REAL NOT NULL,
UNIQUE(user_id, fact_key)
);
""")
self.conn.commit()
def _cosine_similarity(self, v1: List[float], v2: List[float]) -> float:
a = np.array(v1)
b = np.array(v2)
dot = np.dot(a, b)
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)
if norm_a == 0 or norm_b == 0:
return 0.0
return float(dot / (norm_a * norm_b))
def write_episodic_record(self, user_id: str, session_id: str, interaction_text: str, embedding: List[float]) -> str:
"""Saves a raw chronological interaction segment into the episodic database log."""
mem_id = f"epi_{uuid_like()}"
now = time.time()
self.cursor.execute("""
INSERT INTO episodic_memories (id, user_id, session_id, interaction_text, embedding_json, created_at)
VALUES (?, ?, ?, ?, ?, ?)
""", (mem_id, user_id, session_id, interaction_text, json.dumps(embedding), now))
self.conn.commit()
return mem_id
def upsert_semantic_fact(self, user_id: str, fact_key: str, fact_value: str, embedding: List[float]) -> Tuple[str, str]:
"""
Inserts or mutates a consolidated semantic memory fact.
Detects contradictions and manages conflict resolution rules.
"""
now = time.time()
fact_key_clean = fact_key.strip().lower()
# Check if user already has an active semantic node matching this key
self.cursor.execute("""
SELECT id, fact_value, created_at FROM semantic_memories
WHERE user_id = ? AND fact_key = ?
""", (user_id, fact_key_clean))
row = self.cursor.fetchone()
if row:
old_id, old_value, old_created_at = row
if old_value != fact_value:
# Fact Mutation Conflict Detected! Execute mathematical resolution
# For demonstration, we trust the newest value but update reinforcement metrics
self.cursor.execute("""
UPDATE semantic_memories
SET fact_value = ?, embedding_json = ?, last_accessed_at = ?, half_life = half_life + 10.0, access_count = access_count + 1
WHERE id = ?
""", (fact_value, json.dumps(embedding), now, old_id))
self.conn.commit()
return old_id, "MUTATED"
return old_id, "UNCHANGED"
# Fresh memory node insertion
new_id = f"sem_{uuid_like()}"
initial_half_life = 86400.0 # Default decay boundary: 24 hours
self.cursor.execute("""
INSERT INTO semantic_memories (id, user_id, fact_key, fact_value, embedding_json, half_life, last_accessed_at, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (new_id, user_id, fact_key_clean, fact_value, json.dumps(embedding), initial_half_life, now, now))
self.conn.commit()
return new_id, "CREATED"
def retrieve_active_memories(self, user_id: str, query_embedding: List[float], similarity_threshold: float = 0.5) -> List[Dict[str, Any]]:
"""
Retrieves active memory segments.
Calculates Ebbinghaus Temporal Forgetting curves inside the matching loops.
"""
now = time.time()
self.cursor.execute("""
SELECT id, fact_key, fact_value, embedding_json, half_life, last_accessed_at, access_count
FROM semantic_memories
WHERE user_id = ?
""", (user_id,))
rows = self.cursor.fetchall()
scored_memories = []
for row in rows:
mem_id, key, val, emb_json, half_life, last_accessed, access_count = row
emb = json.loads(emb_json)
# 1. Base Semantic Similarity (Cosine)
base_sim = self._cosine_similarity(query_embedding, emb)
# 2. Apply Ebbinghaus Forgetting Curve: Sim * e^(-delta_t / half_life)
delta_t = now - last_accessed
decay_factor = np.exp(-delta_t / half_life)
decayed_similarity = base_sim * decay_factor
if decayed_similarity >= similarity_threshold:
# Reinforce memory path during active access
self._reinforce_memory(mem_id, half_life, access_count, now)
scored_memories.append({
"id": mem_id,
"fact_key": key,
"fact_value": val,
"raw_score": base_sim,
"decayed_score": decayed_similarity
})
# Sort descending by decayed score
return sorted(scored_memories, key=lambda x: x["decayed_score"], reverse=True)
def _reinforce_memory(self, mem_id: str, current_half_life: float, access_count: int, now: float):
"""Reinforces memories when accessed, increasing their half-life."""
# S_{t+1} = S_t + eta * (1 - e^(-alpha * count))
learning_rate = 3600.0 * 12 # Add up to 12 hours of extra retention
alpha = 0.25
reinforcement = learning_rate * (1.0 - np.exp(-alpha * access_count))
new_half_life = current_half_life + reinforcement
self.cursor.execute("""
UPDATE semantic_memories
SET half_life = ?, last_accessed_at = ?, access_count = access_count + 1
WHERE id = ?
""", (new_half_life, now, mem_id))
self.conn.commit()
def close(self):
self.conn.close()
def uuid_like() -> str: """Helper to generate standard random string identifiers.""" import random return "".join(random.choices("abcdef0123456789", k=12))
5.1 Temporal Forgetting Assertions
Verify that the Ebbinghaus forgetting calculations run correctly, systematically scaling down older, un-reinforced memory vectors.
import pytest
def test_ebbinghaus_forgetting_decay(): manager = AgentMemoryManager(db_path=":memory:") user_id = "usr_01"
# Embedding template representing machine learning interests
query_vector = [1.0, 0.0, 0.0, 0.0]
# 1. Store a semantic fact (User is building ML projects)
new_id, status = manager.upsert_semantic_fact(
user_id=user_id,
fact_key="interest",
fact_value="Building neural compilers",
embedding=[0.95, 0.05, 0.0, 0.0]
)
# Assert successful database initialization
assert status == "CREATED"
# 2. Retrieve immediately. Score should be near absolute
active = manager.retrieve_active_memories(user_id, query_vector)
assert len(active) == 1
assert active[0]["decayed_score"] > 0.80
# 3. Simulate deep time elapse (e.g. 5 days of absolute silence)
now_simulated = time.time() + (86400.0 * 5)
# Manually adjust db timestamps to simulate time leap
manager.cursor.execute(
"UPDATE semantic_memories SET last_accessed_at = ?",
(time.time() - (86400.0 * 5),)
)
manager.conn.commit()
# 4. Attempt retrieval under strict similarity metrics.
# The decayed score should fall below standard threshold boundaries
inactive = manager.retrieve_active_memories(user_id, query_vector, similarity_threshold=0.80)
assert len(inactive) == 0
manager.close()
5.2 Conflict Resolution Integrity Testing
Assert that conflicting updates mutate key facts cleanly, updating access counts and memory half-lives without duplicating relational rows.
def test_conflict_mutation_deduplication(): manager = AgentMemoryManager(db_path=":memory:") user_id = "usr_02" fact_key = "current_location"
# Step A: User lives in Seattle
idx_1, status_1 = manager.upsert_semantic_fact(
user_id=user_id,
fact_key=fact_key,
fact_value="Seattle, WA",
embedding=[0.1, 0.2, 0.8]
)
# Step B: User moves to Boston
idx_2, status_2 = manager.upsert_semantic_fact(
user_id=user_id,
fact_key=fact_key,
fact_value="Boston, MA",
embedding=[0.1, 0.2, 0.8]
)
# Verify that conflict updates the same row instead of inserting duplicates
assert idx_1 == idx_2
assert status_2 == "MUTATED"
manager.cursor.execute(
"SELECT count(*) FROM semantic_memories WHERE user_id = ? AND fact_key = ?",
(user_id, fact_key)
)
row_count = manager.cursor.fetchone()[0]
assert row_count == 1
manager.close()
Advanced Engineering Skills Inventory: Agentic Long-Term Memory RAG (RAG-008)
This matrix details the specialized database security, cognitive architecture design, mathematical modeling, and asynchronous task-queue competencies required to build and maintain evolving long-term memory layers for autonomous AI agents.
Moving past static chat logs requires designing systems that mimic human cognitive structures, partitioning memory dynamically to optimize context execution and query speed.
[ Raw Agent Conversational Interactions ]
│
┌──────────────────┴──────────────────┐
▼ ▼
[ Episodic Memory Pool ] [ Semantic Memory Pool ]
• Sequenced chronological logs • Consolidated abstract facts
• Raw text + dense vector arrays • Evolving preferences (K-V)
• Decoupled background inputs • Time-decayed index mappings
Cognitive Domain Boundaries:
Setting up the operational limits of Working Memory (short-term, RAM-bound context queues matching recent session turns) vs. Episodic Memory (immutable, indexed chronologies of individual user turns) vs. Semantic Memory (mutable, consolidated abstract factual records).
Structuring dynamic context assembly patterns to seamlessly stitch relevant semantic memories back into active working prompts during chat execution loops.
Memory Rollup Mechanics:
Engineering scheduled or criteria-driven triggers (such as interaction density, temporal windows, or NLP assertion cues like "I changed my mind") to move data from raw episodic tables to structured semantic indexes.
Storing personal and behavioral preferences (home addresses, working hours, sensitive patterns) requires configuring strict logical security boundaries.
PostgreSQL Row-Level Security (RLS) Rules:
Implementing enterprise-grade RLS configurations on relational tables storing vector types (vector(1536)).
Binding database policies directly to incoming API JSON Web Token (JWT) claims, mapping auth.jwt_user_tenant_id() check constraints directly at the database driver engine.
Configuring custom Postgres schema indices (specifically HNSW indices with cosine operators) tuned to process multi-tenant isolated search queries efficiently.
Relational-Vector Operations:
Writing unified SQL queries that perform exact relational joins alongside dense cosine-similarity searches to merge user meta-records with semantic facts.
Executing heavy language model assertion runs and embedding recalculations synchronously ruins real-time user experiences. You must know how to decouple ingestion from compilation.
[ Ingress Chat Gateway ] ──► [ Redis Ingestion Stream ] ──► [ Background Celery Pool ] • Structured fact extraction • Contradiction checks (vector) • Mutating state commits
Event-Driven Task Distribution:
Configuring message brokers (such as Redis Streams, RabbitMQ, or Amazon SQS) to buffer episodic transactions synchronously during chat turns.
Setting up background processing pools (such as Python Celery or task-running workers) to poll streams asynchronously, isolating heavy GPU/CPU workloads from fast API worker threads.
Conflict Detection & Mutation Management:
Designing multi-step vector verification runs: querying existing semantic records using a new fact's vector representation to identify logical contradictions.
Constructing thread-safe SQL writing blocks to safely mutate or archive outdated factual nodes without corrupting the historical episodic log.
An agent's memory cannot grow infinitely; it must systematically decay low-importance nodes, reinforce accessed assertions, and resolve contradictions mathematically.
Ebbinghaus Forgetting Curve Formulations:
Implementing time-decayed retrieval formulas where score retention scales down exponentially relative to memory half-lives ($H(M)$):
$$S_{\text{retrieval}}(M, t) = \cos(\mathbf{e}_Q, \mathbf{e}_M) \cdot e^{-\frac{t - t_0}{H(M)}}$$
Dynamically calculating decayed similarity scores inside query execution loops to prioritize fresh connections.
Cognitive Reinforcement Math:
Implementing reinforcing multipliers that adjust memory half-life values when nodes are retrieved during active sessions:
$$H(M)_{t+1} = H(M)_t + \eta \cdot (1.0 - e^{-\alpha \cdot C(M)})$$
Tuning hyperparameters ($\eta$, $\alpha$) to calibrate retention limits across various SaaS consumer tiers.
Conflict Resolution Formulas:
Applying weighted optimization formulas to resolve semantic updates by balancing confidence weights against normalized recency indicators:
$$R = \arg\max_{m \in {M_{\text{new}}, M_{\text{old}}}} \left( \beta \cdot \text{Confidence}(m) + (1.0 - \beta) \cdot \text{Recency}(m) \right)$$
Exposing agent mental spaces to developers or B2B compliance administrators requires building highly performant UI components that can render real-time vector properties and simulate calculations on the client side.
Temporal Simulation Hooks:
Writing complex useMemo hooks in React to calculate simulated memory decay scores instantly inside browser threads when users interact with timeline sliders.
Implementing visual progress layouts to render dynamic memory properties (e.g., color shifts from emerald-green to rose-red when a memory decays past retrieval thresholds, adding strikethroughs, or fading opacity).
Interactive Fact Updates:
Building event-driven UI triggers (e.g., "Reinforce Fact" or "Inject Conflict") to dynamically update component states, resetting elapsed decay timers and modifying values with clean visual animations.
To run this project locally, follow these simple steps designed for both developers and users with no prior programming experience.
pip install -r requirements.txt
.env file in a text editor (like Notepad) and paste your API keys:
GEMINI_API_KEY=your_gemini_api_key_here
run_project.bat or run_pito.bat file, or run the following command:
python app.py (or app_gui.py)