agent-memory-systems
SKILL.md
Agent Memory Systems
Design and implement memory systems that give agents persistent knowledge and context.
When to Use
- Agents need to remember across sessions
- Multiple agents share knowledge
- Long-running tasks require state persistence
- Building agents that learn from experience
Memory Types
┌─────────────────────────────────────────────────────────────┐
│ AGENT MEMORY TAXONOMY │
├─────────────────────────────────────────────────────────────┤
│ │
│ Working Memory (Active Context) │
│ └─ Current conversation, immediate task state │
│ │
│ Short-Term Memory (Session) │
│ └─ Recent interactions, temporary facts │
│ │
│ Long-Term Memory (Persistent) │
│ ├─ Episodic: Past events, experiences │
│ ├─ Semantic: Facts, knowledge, learned info │
│ └─ Procedural: How to do things, skills │
│ │
└─────────────────────────────────────────────────────────────┘
Working Memory
The agent's current context window.
interface WorkingMemory {
// Current task context
task: {
description: string;
requirements: string[];
progress: string[];
};
// Active entities being discussed
entities: Map<string, Entity>;
// Recent messages
conversationWindow: Message[];
// Scratchpad for reasoning
scratchpad: string;
}
class WorkingMemoryManager {
private memory: WorkingMemory;
private MAX_MESSAGES = 20;
addMessage(message: Message) {
this.memory.conversationWindow.push(message);
// Evict old messages
if (this.memory.conversationWindow.length > this.MAX_MESSAGES) {
const evicted = this.memory.conversationWindow.shift();
// Optionally summarize and store
this.summarizeToShortTerm(evicted);
}
}
updateEntity(id: string, entity: Entity) {
this.memory.entities.set(id, entity);
}
getContext(): string {
return `
Task: ${this.memory.task.description}
Progress: ${this.memory.task.progress.join(', ')}
Key Entities: ${[...this.memory.entities.values()].map(e => e.summary).join('\n')}
Recent Discussion: ${this.memory.conversationWindow.slice(-5).map(m => m.content).join('\n')}
`;
}
}
Short-Term Memory
Session-scoped, expires after inactivity.
interface ShortTermMemory {
sessionId: string;
startedAt: Date;
lastAccessedAt: Date;
expiresAt: Date;
// Conversation summary
summary: string;
// Key facts learned this session
facts: Fact[];
// Decisions made
decisions: Decision[];
// Files accessed
touchedFiles: string[];
}
class ShortTermStore {
private store = new Map<string, ShortTermMemory>();
private TTL_MS = 30 * 60 * 1000; // 30 minutes
async get(sessionId: string): Promise<ShortTermMemory | null> {
const memory = this.store.get(sessionId);
if (!memory) return null;
if (Date.now() > memory.expiresAt.getTime()) {
// Expired - archive to long-term
await this.archiveToLongTerm(memory);
this.store.delete(sessionId);
return null;
}
// Touch
memory.lastAccessedAt = new Date();
memory.expiresAt = new Date(Date.now() + this.TTL_MS);
return memory;
}
async addFact(sessionId: string, fact: Fact) {
const memory = await this.getOrCreate(sessionId);
memory.facts.push(fact);
// Check if fact should be promoted to long-term
if (this.shouldPromote(fact)) {
await this.promoteToLongTerm(fact);
}
}
}
Long-Term Memory
Episodic Memory (Events/Experiences)
interface Episode {
id: string;
timestamp: Date;
type: 'task_completed' | 'error_resolved' | 'user_feedback' | 'learning';
// What happened
description: string;
context: Record<string, unknown>;
// Outcome
outcome: 'success' | 'failure' | 'partial';
lessons: string[];
// For retrieval
embedding: number[];
tags: string[];
}
class EpisodicMemory {
private vectorStore: VectorStore;
async remember(episode: Episode): Promise<void> {
// Generate embedding for semantic search
episode.embedding = await this.embed(
`${episode.description} ${episode.lessons.join(' ')}`
);
await this.vectorStore.insert(episode);
}
async recall(query: string, limit: number = 5): Promise<Episode[]> {
const queryEmbedding = await this.embed(query);
return this.vectorStore.similaritySearch(queryEmbedding, {
limit,
threshold: 0.7
});
}
async recallByType(type: Episode['type'], limit: number = 10): Promise<Episode[]> {
return this.vectorStore.filter({ type }, { limit, orderBy: 'timestamp DESC' });
}
}
Semantic Memory (Facts/Knowledge)
interface Fact {
id: string;
subject: string;
predicate: string;
object: string;
confidence: number;
source: string;
learnedAt: Date;
lastVerified: Date;
embedding: number[];
}
class SemanticMemory {
private facts: VectorStore<Fact>;
async learn(fact: Omit<Fact, 'id' | 'embedding'>): Promise<void> {
// Check for conflicts
const existing = await this.findRelated(fact.subject, fact.predicate);
if (existing.length > 0) {
// Handle contradiction or update
await this.resolveConflict(existing, fact);
return;
}
// Store new fact
await this.facts.insert({
...fact,
id: generateId(),
embedding: await this.embed(`${fact.subject} ${fact.predicate} ${fact.object}`)
});
}
async query(question: string): Promise<Fact[]> {
// Semantic search for relevant facts
const embedding = await this.embed(question);
return this.facts.similaritySearch(embedding, { limit: 10 });
}
async getFactsAbout(subject: string): Promise<Fact[]> {
return this.facts.filter({ subject });
}
}
Procedural Memory (Skills/How-To)
interface Procedure {
id: string;
name: string;
description: string;
trigger: string; // When to use this
steps: string[];
examples: Example[];
successRate: number;
usageCount: number;
}
class ProceduralMemory {
private procedures: Map<string, Procedure> = new Map();
async findProcedure(task: string): Promise<Procedure | null> {
// Match task to known procedures
const candidates = await this.matchProcedures(task);
if (candidates.length === 0) return null;
// Return best match by success rate and relevance
return candidates.sort((a, b) =>
(b.successRate * b.relevanceScore) - (a.successRate * a.relevanceScore)
)[0];
}
async recordOutcome(procedureId: string, success: boolean): Promise<void> {
const proc = this.procedures.get(procedureId);
if (!proc) return;
// Update success rate with exponential moving average
const alpha = 0.1;
proc.successRate = alpha * (success ? 1 : 0) + (1 - alpha) * proc.successRate;
proc.usageCount++;
}
async learnProcedure(
name: string,
steps: string[],
fromEpisode: Episode
): Promise<void> {
// Create new procedure from successful episode
this.procedures.set(generateId(), {
id: generateId(),
name,
description: fromEpisode.description,
trigger: this.extractTrigger(fromEpisode),
steps,
examples: [{ input: fromEpisode.context, output: fromEpisode.outcome }],
successRate: 1.0,
usageCount: 1
});
}
}
Shared Memory (Multi-Agent)
interface SharedMemory {
// Namespace for isolation
namespace: string;
// Read/write with locking
read(key: string): Promise<unknown>;
write(key: string, value: unknown): Promise<void>;
// Atomic operations
compareAndSwap(key: string, expected: unknown, newValue: unknown): Promise<boolean>;
// Subscriptions
subscribe(pattern: string, callback: (key: string, value: unknown) => void): void;
}
class RedisSharedMemory implements SharedMemory {
constructor(
private redis: Redis,
public namespace: string
) {}
private key(k: string): string {
return `${this.namespace}:${k}`;
}
async read(key: string): Promise<unknown> {
const value = await this.redis.get(this.key(key));
return value ? JSON.parse(value) : null;
}
async write(key: string, value: unknown): Promise<void> {
await this.redis.set(this.key(key), JSON.stringify(value));
await this.redis.publish(`${this.namespace}:updates`, JSON.stringify({ key, value }));
}
subscribe(pattern: string, callback: (key: string, value: unknown) => void): void {
const subscriber = this.redis.duplicate();
subscriber.psubscribe(`${this.namespace}:${pattern}`);
subscriber.on('pmessage', (_, channel, message) => {
const { key, value } = JSON.parse(message);
callback(key, value);
});
}
}
Memory Retrieval Strategies
Strategy 1: Recency-Weighted
function recencyWeightedRetrieval(
memories: Memory[],
query: string,
recencyWeight: number = 0.3
): Memory[] {
const now = Date.now();
return memories
.map(m => ({
memory: m,
score: (1 - recencyWeight) * m.relevanceScore +
recencyWeight * Math.exp(-(now - m.timestamp.getTime()) / TIME_DECAY)
}))
.sort((a, b) => b.score - a.score)
.map(x => x.memory);
}
Strategy 2: Importance-Based
function importanceBasedRetrieval(
memories: Memory[],
query: string
): Memory[] {
return memories
.map(m => ({
memory: m,
score: m.relevanceScore * m.importance * (m.accessCount / 10)
}))
.sort((a, b) => b.score - a.score)
.map(x => x.memory);
}
Strategy 3: Contextual (RAG)
async function contextualRetrieval(
query: string,
currentContext: Context
): Promise<Memory[]> {
// Expand query with context
const expandedQuery = `
${query}
Current task: ${currentContext.task}
Related entities: ${currentContext.entities.join(', ')}
`;
// Vector search
const candidates = await vectorStore.search(expandedQuery, { limit: 20 });
// Rerank with cross-encoder
return reranker.rerank(query, candidates, { limit: 5 });
}
Memory Maintenance
class MemoryMaintenance {
// Consolidate short-term to long-term
async consolidate(): Promise<void> {
const sessions = await shortTermStore.getExpired();
for (const session of sessions) {
// Extract key learnings
const learnings = await this.extractLearnings(session);
// Store in appropriate long-term stores
for (const learning of learnings) {
if (learning.type === 'fact') {
await semanticMemory.learn(learning);
} else if (learning.type === 'procedure') {
await proceduralMemory.learnProcedure(learning);
} else {
await episodicMemory.remember(learning);
}
}
}
}
// Forget outdated/irrelevant memories
async forget(): Promise<void> {
// Remove low-value memories
await episodicMemory.prune({
olderThan: days(90),
accessCountBelow: 2,
importanceBelow: 0.2
});
// Update fact confidence based on verification
await semanticMemory.decayUnverified({
olderThan: days(30),
decayRate: 0.1
});
}
}
Best Practices
- Separate concerns - Different memory types for different purposes
- Index wisely - Use embeddings for semantic search
- Expire aggressively - Don't let memory grow unbounded
- Version memories - Track when facts were learned/updated
- Handle conflicts - New information may contradict old
- Secure sensitive data - Some memories shouldn't be shared
Weekly Installs
3
Repository
latestaiagents/…t-skillsGitHub Stars
2
First Seen
Feb 4, 2026
Installed on
mcpjam3
claude-code3
replit3
junie3
windsurf3
zencoder3