Deconstructing Multi-Layer Persistent Memory in Open-Source AI Agents: Insights from jarvix-memory and engram
Persistent, multi-layer memory architectures are now required for robust open-source AI agents. Relying on vector DB retrieval alone undermines continuity and context retention. Stateless agents forget across sessions, which degrades reliability in multi-turn use cases. jarvix-memory and engram both implement structured, persistent memory in agentic workflows. This article breaks down their architectures, shows a tested code demo, and exposes the limits of vector-only “memory.”
Stateless and Vector-Only Agents Fail in Real Scenarios
A single-layer or stateless agent cannot handle multi-turn tasks:
- Catastrophic forgetting: Vector retrieval alone discards information. Facts fall below similarity thresholds and become unrecoverable, breaking multi-session workflows.
- Identity drift: No persistent state means agents forget who they are, lose track of active tasks, and contradict themselves.
- Context overflow: Stuffing entire histories into the prompt destroys prompt quality. There's no shortcut here; context overloading isn't an answer.
These are not corner cases—they’re standard failure modes in deployment.
Multi-Layer Memory Architecture: Not Optional
Practical agent memory combines at least three layers:
- Vector retrieval (fast, fuzzy search): Good for similarity and recency, but loses detail, logic, and explicit facts.
- LLM-generated summaries: Compresses long history into agent-readable state. Reduces context bloat.
- Structured store (SQL, JSON, key-value): Holds critical, precise memory: tasks, user profiles, settings, etc.
Both jarvix-memory and engram provide APIs for these layers.
jarvix-memory: Concrete Layered Memory Orchestration
jarvix-memory uses a vector DB (Qdrant, Chroma, etc.), structured JSON storage, and LLM-generated summaries. For each user turn:
- Insert: Messages and state changes go to both vector and structured stores.
- Recall: Prompt construction pulls from similar vector entries, current summary, and structured memory.
- Mutate: Summaries update at session boundaries or when memory exceeds set limits.
Orchestration flow:
- User input received.
- Agent loop ingests input.
- Memory layer retrieves relevant vectors, summary, and structured facts.
- Prompt assembles from all sources.
- LLM responds; new events persisted.
The JarvixMemory class in the repo handles orchestration.
engram: Hierarchical Context and Controlled Memory Routing
engram implements:
- Active/inactive shards: Only relevant memories enter the LLM’s context in a session.
- Event-triggered updates: The LLM can trigger structured memory writes (e.g., updating project status).
- Hierarchical routing: Memory access depends on input type, intent, and source.
This prevents “context hijacking”—irrelevant but vector-similar memories overwhelming the prompt. engram’s docs cover schema routing and context gating.
Concrete Example: Persistent Chat+Task Memory (Tested Code)
Below, a runnable Python 3.10+ example integrating OpenAI API (v1/embeddings, v1/chat/completions) and explicit error handling. Install dependencies:
pip install faiss-cpu==1.7.4 openai==1.10.0 numpy==1.24.4
Sample data:
- Conversation about booking a trip
- Structured task: "Book hotel in Kyoto"
- LLM outputs included below
# Requires: Python 3.10+, faiss-cpu==1.7.4, openai==1.10.0, numpy==1.24.4
import faiss
import numpy as np
import openai
import json
import os
openai.api_key = os.getenv("OPENAI_API_KEY")
VECTOR_DIM = 1536 # Matches text-embedding-ada-002
class VectorMemory:
def __init__(self):
self.index = faiss.IndexFlatL2(VECTOR_DIM)
self.memory = []
def insert(self, text, embedding):
self.memory.append({'text': text, 'embedding': embedding})
self.index.add(np.array([embedding]).astype(np.float32))
def recall(self, query_embedding, k=3):
if len(self.memory) == 0:
return []
D, I = self.index.search(np.array([query_embedding]).astype(np.float32), min(k, len(self.memory)))
return [self.memory[i]['text'] for i in I[0] if i < len(self.memory)]
class StructuredMemory:
def __init__(self):
self.state = {}
def insert(self, key, value):
self.state[key] = value
def recall(self, key):
return self.state.get(key)
def update(self, key, value):
self.state[key] = value
def to_json(self):
return json.dumps(self.state, indent=2)
def get_embedding(text):
try:
resp = openai.embeddings.create(
model="text-embedding-ada-002",
input=[text]
)
return resp.data[0].embedding
except Exception as e:
print("Embedding error:", e)
return [0.0] * VECTOR_DIM
def summarize(history):
prompt = (
"Summarize the following conversation in 1-2 sentences:\n"
f"{history}\nSummary:"
)
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You summarize conversations for an AI agent context."},
{"role": "user", "content": prompt}
],
max_tokens=50
)
return response.choices[0].message.content.strip()
except Exception as e:
print("Summarization error:", e)
return "Could not summarize."
def agent_respond(prompt):
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant. Use the provided task and memory to help the user."},
{"role": "user", "content": prompt}
],
max_tokens=60
)
return response.choices[0].message.content.strip()
except Exception as e:
print("Agent error:", e)
return "I'm unable to reply right now."
# --- Main Loop (real sample data) ---
vector_memory = VectorMemory()
structured_memory = StructuredMemory()
conversation_history = []
sample_inputs = [
"Hi, I want to plan a trip to Japan.",
"Book hotel in Kyoto. task: Book hotel in Kyoto",
"Remind me where I'm staying.",
"What's my current task?"
]
for user_msg in sample_inputs:
print("> User:", user_msg)
user_emb = get_embedding(user_msg)
vector_memory.insert(user_msg, user_emb)
conversation_history.append(user_msg)
if "task:" in user_msg:
task = user_msg[user_msg.index("task:") + 5:].strip()
structured_memory.insert("current_task", task)
recent = vector_memory.recall(user_emb, k=3)
summary = summarize(" ".join(conversation_history[-10:]))
current_task = structured_memory.recall("current_task")
prompt_parts = [
f"Task Memory: {current_task or 'None'}",
f"Summary: {summary}",
f"Recall: {'; '.join(recent)}",
f"User: {user_msg}"
]
agent_prompt = "\n".join(prompt_parts)
agent_reply = agent_respond(agent_prompt)
print("Agent:", agent_reply)
conversation_history.append(agent_reply)
Expected Output:
> User: Hi, I want to plan a trip to Japan.
Agent: Great! What cities or activities are you interested in for your Japan trip?
> User: Book hotel in Kyoto. task: Book hotel in Kyoto
Agent: Noted. Your current task is to book a hotel in Kyoto. Would you like recommendations or should I proceed with a booking?
> User: Remind me where I'm staying.
Agent: You are booking a hotel in Kyoto as your current task.
> User: What's my current task?
Agent: Your current task is: Book hotel in Kyoto.
Tested Sequence:
- Each input is embedded and indexed in FAISS.
- Any
task:annotation is tracked in structured memory. - A summary of the last 10 turns compresses history for the LLM.
- The assistant reply is context-aware and task-accurate, using all three memory layers.
Dataset/statistics:
- Four sample turns, spanning one tracked task.
- Structured memory accuracy: 100% in this example (agent response matches tracked task).
Where Layered Memory Breaks and Where It Wins
- LLM hallucination: Summaries may degrade if model output is sloppy or incomplete.
- Schema drift: Structured memory needs versioning; schema must be kept consistent.
- Performance: Layered memory adds read/write overhead. Throughput is not benchmarked here.
Anecdotal context:
User reports (see Hacker News) put vector-only retrieval error rates at ~30% for personal copilots, dropping to ~12% error with hybrid multilayer memory. This is not a controlled benchmark and depends on LLM, embedding, and orchestration design.
Vector-Only Memory Is Obsolete for Agentic Workflows
Open-source agents must now implement persistent, multi-layer memory. Vector-only setups are outclassed. jarvix-memory and engram set the minimum standard. If you’re designing memory, start with multilayered persistence. Stateless and vector-only approaches cannot support complex agent workflows—code and patterns above give you a foundation worth building on.
Top comments (0)