Memory Management
Agent Kernel provides a sophisticated memory management system with multiple layers designed for different use cases and lifecycles. Understanding these layers helps you build efficient, context-aware agents that don't bloat LLM prompts.
Memory Architecture
Memory Layers
Agent Kernel provides three distinct memory layers, each with different purposes and lifecycles.
1. Conversational State (Short-term Memory)
Purpose: Store conversation history and agent state that becomes part of the LLM context.
Lifecycle: Session-scoped, persists across multiple requests within a session.
Characteristics:
- ✅ Automatically managed by framework adapters
- ✅ Included in LLM context window
- ✅ Persists across agent invocations
- ⚠️ Contributes to token usage
- ⚠️ Should not contain large data (files, documents)
Managed By: Framework-specific runners (OpenAI, LangGraph, CrewAI, ADK)
What's Stored:
- User messages and agent responses
- Multi-turn conversation history
- Agent state and context
- Framework-specific metadata
You Don't Manage This Directly: The framework adapters automatically handle conversation persistence.
Learn about session management →
2. Auxiliary Memory (Smart Caching)
Purpose: Store additional data needed by tools and hooks without bloating the LLM context.
Agent Kernel provides two types of auxiliary caches with identical APIs but different lifecycles:
Volatile Cache (Request-Scoped)
Lifecycle: Cleared automatically after each request completes.
Perfect For:
- 📄 RAG context retrieved from knowledge bases
- 📁 File contents loaded for processing
- 🧮 Intermediate calculation results
- 🔄 Temporary processing data
- 🎯 Request-specific metadata
Benefits:
- ✅ Keeps LLM prompts clean and focused
- ✅ Reduces token usage and costs
- ✅ Auto-cleanup - no memory leaks
- ✅ Fast access during request lifecycle
Example Use Cases:
# Store RAG context in pre-hook
v_cache.set("rag_context", retrieved_documents)
# Access in tool without passing through LLM
def my_tool():
context = v_cache.get("rag_context")
# Process context...
# Automatically cleared after request
Non-Volatile Cache (Session-Scoped)
Lifecycle: Persists throughout the entire session, across multiple requests.
Perfect For:
- ⚙️ User preferences and settings
- 🏷️ Session metadata and tags
- 📊 Analytics and tracking data
- 🔐 Authorization context
- 💾 Configuration data
Benefits:
- ✅ Survives across multiple requests
- ✅ Shared between hooks and tools
- ✅ Not included in LLM context
- ✅ Same backend as session storage
Example Use Cases:
# Store user preferences
nv_cache.set("user_language", "es")
nv_cache.set("user_timezone", "America/New_York")
# Access in subsequent requests
def my_hook():
lang = nv_cache.get("user_language")
# Customize response based on preference...
3. Long-term Memory (Coming Soon)
Purpose: Persistent knowledge base and user profiles that span multiple sessions.
Planned Features:
- User profile storage
- Historical interaction analysis
- Knowledge base integration
- Cross-session learning
Status: Under development
Using Auxiliary Memory
Accessing Caches
You can access auxiliary memory in two ways:
Method 1: From Session Object
When you have direct access to the session:
from agentkernel import PreHook, Session
from agentkernel.core.memory import KeyValueCache
class MyHook(PreHook):
async def on_run(self, session: Session, agent, requests):
# Get caches from session
v_cache: KeyValueCache = session.get_volatile_cache()
nv_cache: KeyValueCache = session.get_non_volatile_cache()
# Use the caches
v_cache.set("temp_data", "value")
user_pref = nv_cache.get("user_language")
return requests
Method 2: From Current Session or Runtime
When you don't have direct session access (e.g., in tools):
from agentkernel import Session, Runtime
from agentkernel.core.util.key_value_cache import KeyValueCache
def my_tool():
# Get caches from the current session
session = Session.current()
v_cache: KeyValueCache = session.get_volatile_cache()
nv_cache: KeyValueCache = session.get_non_volatile_cache()
# Or load a session by ID from the runtime
# session = Runtime.current().sessions().load(session_id)
# Use the caches
context = v_cache.get("rag_context")
settings = nv_cache.get("user_settings")
KeyValueCache API
Both volatile and non-volatile caches implement the same KeyValueCache interface:
# Set a value
cache.set(key: str, value: Any)
# Get a value (returns None if not found)
value = cache.get(key: str) -> Any | None
# Get with default value
value = cache.get(key: str, default: Any) -> Any
# Check if key exists
exists = cache.has(key: str) -> bool
# Delete a key
cache.delete(key: str)
# Clear all keys
cache.clear()
# Get all keys
keys = cache.keys() -> list[str]
Complete Example
See a comprehensive example with RAG context injection:
examples/memory/key-value-cache/
This example demonstrates:
- Using volatile cache for RAG context
- Using non-volatile cache for user preferences
- Accessing caches from hooks and tools
- Best practices for memory management
Common Patterns and Use Cases
Pattern 1: RAG Context Injection
Use volatile cache to inject retrieved context without bloating prompts:
from agentkernel import PreHook
from agentkernel.core.model import AgentRequestText
class RAGHook(PreHook):
def __init__(self, knowledge_base):
self.knowledge_base = knowledge_base
async def on_run(self, session, agent, requests):
if requests and isinstance(requests[0], AgentRequestText):
prompt = requests[0].text
else:
return requests
# Retrieve relevant context
context = self.knowledge_base.search(prompt)
# Store in volatile cache (not in LLM context)
v_cache = session.get_volatile_cache()
v_cache.set("rag_context", context)
v_cache.set("search_query", prompt)
# Inject concise reference into prompt
enriched_prompt = f"""[Context available in cache]
Question: {prompt}
Use the context from the knowledge base to answer."""
return [AgentRequestText(text=enriched_prompt)]
def name(self):
return "RAGHook"
Benefits: