By Sagar Shankaran, Founder of CallSphere
Build a version-controlled memory system for AI agents that tracks every change, supports rollback to previous states, and provides audit trails for debugging knowledge issues.
Key takeaways
Agent memory is mutable. User preferences change, facts get corrected, and tasks evolve. When the agent updates a memory — say, changing a user's preferred language from Python to Rust — the old value is typically overwritten and lost. If the update was wrong (the agent misinterpreted the user), there is no way to recover.
Memory versioning solves this by treating every change as a new version rather than an overwrite. Like git for agent knowledge, it lets you inspect the history of any memory, understand how knowledge evolved, and roll back mistakes.
Each memory item has a unique key. Every write creates a new version with an incrementing version number. The current state is the latest version.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart TD
MSG(["New message"])
WORKING["Working memory<br/>rolling window"]
EPISODIC[("Episodic memory<br/>past sessions")]
SEMANTIC[("Semantic memory<br/>facts and preferences")]
SUM["Summarizer<br/>compresses old turns"]
ROUTER{"Retrieve<br/>needed memories"}
PROMPT["Assembled context"]
LLM["LLM"]
UPD["Memory updater<br/>writes new facts"]
MSG --> WORKING --> ROUTER
ROUTER -->|Past sessions| EPISODIC
ROUTER -->|User facts| SEMANTIC
EPISODIC --> SUM --> PROMPT
SEMANTIC --> PROMPT
WORKING --> PROMPT --> LLM --> UPD
UPD --> EPISODIC
UPD --> SEMANTIC
style ROUTER fill:#4f46e5,stroke:#4338ca,color:#fff
style LLM fill:#f59e0b,stroke:#d97706,color:#1f2937
style EPISODIC fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style SEMANTIC fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
from dataclasses import dataclass, field
from datetime import datetime
from copy import deepcopy
@dataclass
class MemoryVersion:
version: int
content: str
timestamp: datetime
author: str = "agent"
change_reason: str = ""
metadata: dict = field(default_factory=dict)
@dataclass
class VersionedMemory:
key: str
versions: list[MemoryVersion] = field(default_factory=list)
@property
def current(self) -> MemoryVersion | None:
return self.versions[-1] if self.versions else None
@property
def version_count(self) -> int:
return len(self.versions)
class VersionedMemoryStore:
def __init__(self, max_versions_per_key: int = 50):
self.memories: dict[str, VersionedMemory] = {}
self.max_versions = max_versions_per_key
self.global_changelog: list[dict] = []
def write(
self,
key: str,
content: str,
author: str = "agent",
reason: str = "",
metadata: dict | None = None,
) -> int:
if key not in self.memories:
self.memories[key] = VersionedMemory(key=key)
mem = self.memories[key]
version_num = mem.version_count + 1
version = MemoryVersion(
version=version_num,
content=content,
timestamp=datetime.now(),
author=author,
change_reason=reason,
metadata=metadata or {},
)
mem.versions.append(version)
# Trim old versions if needed
if len(mem.versions) > self.max_versions:
mem.versions = mem.versions[-self.max_versions:]
# Log to global changelog
self.global_changelog.append({
"key": key,
"version": version_num,
"timestamp": version.timestamp.isoformat(),
"author": author,
"reason": reason,
})
return version_num
The changelog provides a complete audit trail of every modification. You can query it to understand how knowledge evolved and who made each change.
def read(self, key: str) -> str | None:
mem = self.memories.get(key)
if mem and mem.current:
return mem.current.content
return None
def history(self, key: str) -> list[MemoryVersion]:
mem = self.memories.get(key)
return mem.versions if mem else []
def diff(self, key: str, v1: int, v2: int) -> dict | None:
mem = self.memories.get(key)
if not mem:
return None
ver1 = next(
(v for v in mem.versions if v.version == v1), None
)
ver2 = next(
(v for v in mem.versions if v.version == v2), None
)
if not ver1 or not ver2:
return None
return {
"key": key,
"from_version": v1,
"to_version": v2,
"old_content": ver1.content,
"new_content": ver2.content,
"changed_by": ver2.author,
"reason": ver2.change_reason,
"time_between": str(ver2.timestamp - ver1.timestamp),
}
Rollback creates a new version with the content from a previous version. It does not delete the intermediate versions — the history is preserved, and the rollback itself is tracked.
def rollback(
self, key: str, to_version: int, reason: str = ""
) -> int | None:
mem = self.memories.get(key)
if not mem:
return None
target = next(
(v for v in mem.versions if v.version == to_version),
None,
)
if not target:
return None
rollback_reason = (
reason or f"Rolled back to version {to_version}"
)
return self.write(
key=key,
content=target.content,
author="system",
reason=rollback_reason,
metadata={"rolled_back_from": mem.current.version},
)
The global changelog lets you reconstruct exactly how the agent's knowledge changed over any time window. This is invaluable for debugging unexpected behavior.
def audit_trail(
self,
start: datetime | None = None,
end: datetime | None = None,
author: str | None = None,
) -> list[dict]:
trail = self.global_changelog
if start:
trail = [
e for e in trail
if datetime.fromisoformat(e["timestamp"]) >= start
]
if end:
trail = [
e for e in trail
if datetime.fromisoformat(e["timestamp"]) <= end
]
if author:
trail = [e for e in trail if e["author"] == author]
return trail
store = VersionedMemoryStore()
# Initial knowledge
store.write(
"user_language",
"Python",
author="onboarding",
reason="User stated preference during setup",
)
# Agent updates based on conversation
store.write(
"user_language",
"Rust",
author="conversation_agent",
reason="User said they switched to Rust",
)
# Oops — agent misunderstood. Roll back.
store.rollback(
"user_language",
to_version=1,
reason="Agent misinterpreted — user meant Rust for a side project only",
)
# Inspect the full history
for v in store.history("user_language"):
print(f"v{v.version}: {v.content} ({v.change_reason})")
# v1: Python (User stated preference during setup)
# v2: Rust (User said they switched to Rust)
# v3: Python (Rolled back to version 1)
Keep 20 to 50 versions for frequently updated keys. For rarely changed keys like user preferences, keep all versions. Use the max_versions parameter to cap storage. When trimming, always keep the first version so you can see the original value.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
The storage overhead is modest — each version is just a content string plus metadata. The write latency is negligible because it is an append operation. The main cost is in history queries, which scan the version list. With 50 versions per key, this is instant.
For production agents handling sensitive data, yes. Implement a rollback request that an admin reviews before it executes. For development and testing, automatic rollback is fine. The audit trail provides accountability either way.
#MemoryVersioning #Rollback #AuditTrail #Python #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
The 2026 desktop AI agent landscape — ServiceNow Project Arc, Anthropic Claude offerings, OpenAI agents, and Google Mariner. A buyer's map.
Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.
An agentic-AI perspective on Anthropic Skills system, covering orchestration patterns, tool use, and how agent tooling fits production agent stacks.
Enterprise CIO Guide perspective on Comet's general-availability launch put an agentic browser in front of millions of consumers, and it works better than the demos suggested.
Enterprise CIO Guide perspective on Harvey AI's enterprise rollout numbers show legal agents have moved past the pilot stage at AmLaw 100 firms.
Enterprise CIO Guide perspective on Hippocratic AI's deployment numbers show healthcare voice agents are moving from pilot to production across major US health systems.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco