Action Replay Is Not Enough: Why Agent Memory Must Be Content-Addressable
A deep dive into forensic memory design for autonomous agents
Action Replay Is Not Enough: Why Agent Memory Must Be Content-Addressable
When your autonomous agent makes a wrong decision, how do you know why? If you answer "I can replay the actions," you have a problem. Action replay—storing a chronological list of events—is the default for most agent frameworks. It's also the source of countless debugging nightmares, audit failures, and reasoning loops.
I've spent the past year building agents that operate on-prem, with full sovereignty. Every decision must be explainable, every failure traceable. Action replay is a dead end. The alternative is content-addressable memory (CAM): storing and retrieving memories by their semantic content, not by sequence number. This article explains why CAM is essential for production agents, and how to implement it with tools you already know.
The Action Replay Fallacy
Action replay sounds plausible: log every action, timestamp it, and replay the sequence to understand the agent's behavior. In practice, this breaks down in three ways.
First, replay doesn't capture state. An agent's decision depends on the entire context: the current system state, the embeddings of previous messages, the internal model's hidden states. Replaying actions without this context is like reading a play without the stage directions. You see what the agent did, but not why.
Second, replay is linear. Agents don't think linearly—they branch, backtrack, and explore. A single decision may be the result of dozens of parallel evaluations, only one of which is recorded. The rest vanish.
Third, replay scales poorly. As the agent runs for weeks, the log grows unbounded. Finding the specific moment where something went wrong requires scanning millions of entries. You're not debugging—you're data mining.
I've seen teams spend days reconstructing agent behavior from logs, only to realize they're missing the key piece: the content of the memory that triggered the action. Action replay gives you the "when" but not the "what."
Content-Addressable Memory: The Core Idea
Content-addressable memory flips the model. Instead of "find action #12345," you ask "find the memory that matches this semantic query." The memory is stored as a vector embedding, and retrieval is by similarity search.
In practice, every memory—whether it's a user message, a tool output, an internal thought, or a decision—is embedded into a high-dimensional vector using a model like BGE-M3 or E5. These vectors are stored in a vector database (pgvector, Qdrant) alongside the original text and metadata. When the agent needs to recall something, it embeds the query and performs a nearest-neighbor search.
The result is a memory system that is inherently semantic, temporal, and associative. You can ask "what did the agent think about compliance last week?" and get the exact memories, regardless of where they appear in the action sequence.
Why CAM Wins for Debugging and Audit
Forensic traceability. When an agent makes a mistake, you can query the memory store with the erroneous output. The nearest memories will show the context that led to that output. You can then trace backwards: what inputs, what tool results, what prior decisions. This is impossible with action replay because the relevant memories are scattered across the log.
Causal reasoning. CAM enables you to ask "why did the agent choose action A?" by retrieving the memories that are most similar to the decision point. These memories form a causal chain, not a chronological list. You can see the actual influences on the decision.
Audit compliance. The EU AI Act requires that high-risk AI systems provide "meaningful explanations" of decisions. Action replay does not satisfy this—it's just a sequence of events. CAM gives you the semantic content that drove each decision. You can export a memory graph that shows the reasoning path.
Long-term reasoning. Agents that operate over months need to recall information from weeks ago. With action replay, you either keep everything (and suffer performance) or prune aggressively (and lose context). CAM allows you to keep all memories efficiently via vector compression and indexing. Retrieval is O(log N) regardless of history size.
Implementing CAM with pgvector and BGE-M3
Let's get concrete. Here's a minimal implementation using PostgreSQL with pgvector and BGE-M3 for embeddings.
Schema
CREATE EXTENSION vector;
CREATE TABLE agent_memories (
id BIGSERIAL PRIMARY KEY,
agent_id TEXT NOT NULL,
memory_type TEXT NOT NULL, -- 'user_message', 'tool_output', 'thought', 'decision'
content TEXT NOT NULL,
embedding VECTOR(1024), -- BGE-M3 outputs 1024-dim vectors
created_at TIMESTAMPTZ DEFAULT NOW(),
metadata JSONB
);
CREATE INDEX idx_memories_agent ON agent_memories (agent_id);
CREATE INDEX idx_memories_embedding ON agent_memories USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);Embedding with BGE-M3
Use the BAAI/bge-m3 model via sentence-transformers or llama.cpp. For production, I run a small inference server with vLLM or llama.cpp.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-m3')
def embed(text: str) -> list[float]:
return model.encode(text, normalize_embeddings=True).tolist()Storing a memory
import psycopg2
from pgvector.psycopg2 import register_vector
conn = psycopg2.connect("dbname=agent_memory")
register_vector(conn)
cur = conn.cursor()
content = "User asked: 'What is our data retention policy?'"
embedding = embed(content)
cur.execute(
"""INSERT INTO agent_memories (agent_id, memory_type, content, embedding, metadata)
VALUES (%s, %s, %s, %s, %s)""",
('agent-01', 'user_message', content, embedding, '{"source": "chat"}')
)
conn.commit()Retrieving by semantic content
query = "data retention policy"
query_emb = embed(query)
cur.execute(
"""SELECT content, metadata, 1 - (embedding <=> %s) AS similarity
FROM agent_memories
WHERE agent_id = 'agent-01'
ORDER BY embedding <=> %s
LIMIT 5""",
(query_emb, query_emb)
)
for row in cur.fetchall():
print(f"{row[2]:.3f}: {row[0]}")This returns the most semantically related memories. The agent can then use these as context for its next decision.
Advanced: Temporal Decay and Importance Weighting
Not all memories are equal. A memory from five minutes ago is likely more relevant than one from five days ago. Implement a hybrid scoring function that combines semantic similarity with recency and importance.
-- Add importance and decay fields
ALTER TABLE agent_memories ADD COLUMN importance REAL DEFAULT 1.0;
-- Hybrid query: similarity * recency * importance
SELECT content,
(1 - (embedding <=> %s)) *
EXP(-0.01 * EXTRACT(EPOCH FROM (NOW() - created_at))) *
importance AS score
FROM agent_memories
WHERE agent_id = 'agent-01'
ORDER BY score DESC
LIMIT 5;Adjust the decay constant (0.01) based on your domain. For fast-moving agents, use a higher value. For long-term memory, use a lower value.
CAM in Agent Orchestration
CAM isn't just for debugging—it's the core of agent reasoning. When an agent needs to decide its next action, it queries its memory store for relevant context. This replaces the monolithic context window with a dynamic, retrievable memory.
Here's a pattern I use with systemd-managed agents:
class AgentWithMemory:
def __init__(self, agent_id: str):
self.agent_id = agent_id
self.conn = psycopg2.connect("dbname=agent_memory")
register_vector(self.conn)
def recall(self, query: str, top_k: int = 5) -> list[dict]:
query_emb = embed(query)
cur = self.conn.cursor()
cur.execute(
"""SELECT content, metadata, 1 - (embedding <=> %s) AS sim
FROM agent_memories
WHERE agent_id = %s
ORDER BY embedding <=> %s
LIMIT %s""",
(query_emb, self.agent_id, query_emb, top_k)
)
return [{"content": row[0], "metadata": row[1], "similarity": row[2]} for row in cur.fetchall()]
def decide(self, input_text: str) -> str:
memories = self.recall(input_text)
context = "\n".join([m["content"] for m in memories])
# Use context to prompt the LLM
prompt = f"Context:\n{context}\n\nInput: {input_text}\n\nAction:"
# Call your LLM (vLLM, llama.cpp)
return self.llm(prompt)The Cost of Not Going CAM
If you're building agents today and relying on action replay, you're accumulating technical debt. Every debugging session will be painful. Every audit will require manual reconstruction. Every long-running agent will degrade in performance as its log grows.
CAM is not harder to implement. It's a different mindset: store for retrieval, not for replay. The tools are mature—pgvector is production-ready, BGE-M3 is free and runs on CPU if needed. The pattern is proven in recommendation systems and search engines. It's time agents caught up.
Summary
- Action replay gives you sequence, not semantics. It fails for debugging, audit, and long-term reasoning.
- Content-addressable memory stores memories as vectors and retrieves by semantic similarity.
- Implement with pgvector and BGE-M3 for a robust, scalable memory system.
- Use hybrid scoring (similarity + recency + importance) for better retrieval.
- CAM is the foundation for explainable, sovereign agents.
Stop replaying. Start addressing.