letta/learning-sdk/SKILL.md
Integration patterns and best practices for adding persistent memory to LLM agents using the Letta Learning SDK
npx skillsauth add letta-ai/skills learning-sdk-integrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides universal patterns for adding persistent memory to LLM agents using the Learning SDK through a 3-line integration pattern that works with OpenAI, Anthropic, Gemini, and other LLM providers.
Use this skill when:
from agentic_learning import learning
# Wrap LLM SDK calls to enable memory
with learning(agent="my-agent"):
response = openai.chat.completions.create(...)
from agentic_learning import learning_async
# For async LLM SDK usage
async with learning_async(agent="my-agent"):
response = await claude.messages.create(...)
from openai import OpenAI
from agentic_learning import learning_async
class MemoryEnhancedOpenAIAgent:
def __init__(self, api_key: str, agent_name: str):
self.client = OpenAI(api_key=api_key)
self.agent_name = agent_name
async def chat(self, message: str, model: str = "gpt-4"):
async with learning_async(agent=self.agent_name):
response = await self.client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": message}]
)
return response.choices[0].message.content
from anthropic import Anthropic
from agentic_learning import learning_async
class MemoryEnhancedClaudeAgent:
def __init__(self, api_key: str, agent_name: str):
self.client = Anthropic(api_key=api_key)
self.agent_name = agent_name
async def chat(self, message: str, model: str = "claude-3-5-sonnet-20241022"):
async with learning_async(agent=self.agent_name):
response = await self.client.messages.create(
model=model,
max_tokens=1000,
messages=[{"role": "user", "content": message}]
)
return response.content[0].text
import google.generativeai as genai
from agentic_learning import learning_async
class MemoryEnhancedGeminiAgent:
def __init__(self, api_key: str, agent_name: str):
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-pro')
self.agent_name = agent_name
async def chat(self, message: str):
async with learning_async(agent=self.agent_name):
response = await self.model.generate_content_async(message)
return response.text
from pydantic_ai import Agent
from agentic_learning import learning
agent = Agent('anthropic:claude-sonnet-4-20250514')
with learning(agent="pydantic-demo"):
result = agent.run_sync("Hello!")
For detailed patterns including structured output, tool usage, and async examples, see references/pydantic-ai.md.
# Use capture_only=True to save conversations without memory injection
async with learning_async(agent="research-agent", capture_only=True):
# Conversation will be saved but no memory will be retrieved/injected
response = await llm_call(...)
# Define custom memory blocks for specific context
custom_memory = [
{"label": "project_context", "description": "Current project details"},
{"label": "user_preferences", "description": "User's working preferences"}
]
async with learning_async(agent="my-agent", memory=custom_memory):
response = await llm_call(...)
# Multiple agents can share memory by using the same agent name
agent1 = MemoryEnhancedOpenAIAgent(api_key, "shared-agent")
agent2 = MemoryEnhancedClaudeAgent(api_key, "shared-agent")
# Both agents will access the same memory context
response1 = await agent1.chat("Research topic X")
response2 = await agent2.chat("Summarize our research")
async def context_aware_tool_use():
async with learning_async(agent="tool-selector"):
# Memory will help agent choose appropriate tools
memories = await get_memories("tool-selector")
if "web_search_needed" in str(memories):
return use_web_search()
elif "data_analysis" in str(memories):
return use_data_tools()
else:
return use_default_tools()
email-processor, research-assistant, code-reviewer# Good: Specific, purposeful memory blocks
memory_blocks = [
{"label": "conversation_history", "description": "Recent conversation context"},
{"label": "task_context", "description": "Current task and goals"},
{"label": "user_preferences", "description": "User interaction preferences"}
]
async def robust_llm_call(message: str):
try:
async with learning_async(agent="my-agent"):
return await llm_sdk_call(...)
except Exception as e:
# Fallback without memory if learning fails
return await llm_sdk_call(...)
def choose_provider(task_type: str, budget: str, latency_requirement: str):
"""Select LLM provider based on task requirements"""
if task_type == "code_generation" and budget == "high":
return "claude-3-5-sonnet" # Best for code
elif task_type == "general_chat" and budget == "low":
return "gpt-3.5-turbo" # Cost-effective
elif latency_requirement == "ultra_low":
return "gemini-1.5-flash" # Fastest
else:
return "gpt-4" # Good all-rounder
from agentic_learning import AsyncAgenticLearning
async def get_conversation_context(agent_name: str):
client = AsyncAgenticLearning()
memories = await client.get_memories(agent_name)
return memories
# When starting fresh contexts
client = AsyncAgenticLearning()
await client.clear_memory(agent_name)
class UniversalResearchAgent:
def __init__(self, provider: str, api_key: str):
self.provider = provider
self.client = self._initialize_client(provider, api_key)
def _initialize_client(self, provider: str, api_key: str):
if provider == "openai":
from openai import OpenAI
return OpenAI(api_key=api_key)
elif provider == "claude":
from anthropic import Anthropic
return Anthropic(api_key=api_key)
elif provider == "gemini":
import google.generativeai as genai
genai.configure(api_key=api_key)
return genai.GenerativeModel('gemini-pro')
async def research(self, topic: str):
async with learning_async(
agent="universal-researcher",
memory=[
{"label": "research_history", "description": "Previous research topics"},
{"label": "current_session", "description": "Current research session"}
]
):
prompt = f"Research the topic: {topic}. Consider previous research context."
response = await self._make_llm_call(prompt)
return response
class CodeReviewAssistant:
def __init__(self, providers: dict):
self.providers = providers
self.clients = {name: self._init_client(name, key)
for name, key in providers.items()}
async def review_with_multiple_perspectives(self, code: str):
reviews = {}
for provider_name, client in self.clients.items():
async with learning_async(
agent=f"code-reviewer-{provider_name}",
memory=[
{"label": "review_history", "description": "Past code reviews"},
{"label": "coding_standards", "description": "Project standards"}
]
):
prompt = f"Review this code from {provider_name} perspective: {code}"
reviews[provider_name] = await self._make_llm_call(client, prompt)
# Synthesize multiple perspectives
return await self._synthesize_reviews(reviews)
import pytest
from agentic_learning import learning_async
async def test_memory_integration():
async with learning_async(agent="test-agent"):
# Test that memory is working
response = await llm_sdk_call("Remember this test")
# Verify memory was captured
client = AsyncAgenticLearning()
memories = await client.get_memories("test-agent")
assert len(memories) > 0
@pytest.mark.parametrize("provider", ["openai", "claude", "gemini"])
async def test_provider_memory_integration(provider):
# Test memory works with each provider
agent = create_agent(provider, api_key)
response = await agent.chat("Test message")
assert response is not None
capture_only=True for logging-only scenarioslearning_async with async SDK calls# Enable debug logging to see memory operations
import logging
logging.basicConfig(level=logging.DEBUG)
async with learning_async(agent="debug-agent"):
# Memory operations will be logged
response = await llm_sdk_call(...)
chat.completions endpointmessages endpoint for conversationgenerate_content_async for asyncreferences/pydantic-ai.md - PydanticAI integration patternsreferences/mem0-migration.md - Migrating from mem0 to Learning SDKtesting
Navigates archived ChatGPT or Claude-style conversation exports and a MemFS reference archive on demand. Use when recalling what a past assistant knew, searching old conversations, rendering specific chats, seeding reference memory from export sidecars, or mining historical context without doing a full import.
testing
Migrates deprecated Letta Filesystem folders/files to MemFS using markdown document corpora, chunking, local lexical search, and QMD semantic search via the memfs-search skill. Use when replacing folders.files.upload, working with PDFs or document QA, or emulating open_file, grep_file, and search_file behavior.
data-ai
Configures Letta agent compaction settings and custom summarization prompts. Use when a user asks to change an agent's compaction prompt, improve summaries after context eviction, tune sliding-window or all-message compaction, or design companion/coding-agent continuity summaries.
development
Semantic search over agent memory files. Use when you need to find conceptually related memory blocks, discover forgotten reference files, check what you already know before creating new memory, or search beyond exact keyword matching. Currently supports QMD (local, no API keys).