Using Recall with Claude
The Problem
Claude has a large context window (200K tokens), but every token costs. Long conversations bloat your prompts: including the full history of 100 turns at 500 tokens each consumes 50,000 tokens before Claude sees the current question. At $3 per 1M input tokens, that's inefficient.
Solution: Selective Memory with Recall
Recall stores conversation history, decisions, and learned patterns outside Claude's context window. Before calling Claude, retrieve only the relevant memories. Your prompt shrinks from 50K tokens to 5K, while Claude remains fully informed about past context.
Install
# Python Recall SDK
pip install recall-sdk
# Claude SDK
pip install anthropic
# (Optional) Async support
pip install httpx aiohttpIntegration Steps
Step 1: Initialize Recall for Your Claude Application
Create a Recall instance to store conversation context, decisions, and learned patterns for this application.
from recall_sdk import Recall
# Memory for this Claude application
app_memory = Recall(
name="claude-app",
metadata={
"application": "customer-support-agent",
"model": "claude-3-5-sonnet-20241022"
}
)Step 2: Store Conversation and Decisions
After each Claude API call, store the turn and any decisions made. This builds your memory of the conversation.
from anthropic import Anthropic
client = Anthropic()
def claude_turn_with_storage(
app_memory,
conversation_history,
user_message,
user_id,
system_prompt=None
):
"""Call Claude and store the turn in memory."""
# 1. Call Claude with full conversation (first approach)
messages = [
{"role": "user" if i % 2 == 0 else "assistant", "content": msg}
for i, msg in enumerate(conversation_history + [user_message])
]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=system_prompt or "You are a helpful assistant.",
messages=messages
)
assistant_message = response.content[0].text
# 2. Extract key decision or insight
decision_markers = ["decided", "concluded", "recommend", "should", "next step"]
has_decision = any(word in assistant_message.lower() for word in decision_markers)
# 3. Store the turn
app_memory.store(
text=f"User: {user_message}\n\nAssistant: {assistant_message}",
memory_type="conversation" if not has_decision else "decision",
metadata={
"user_id": user_id,
"turn": len(conversation_history),
"has_decision": has_decision,
"model": "claude-3-5-sonnet"
}
)
return assistant_message
# Example usage
conversation = []
user_id = "user_1001"
# Turn 1
response_1 = claude_turn_with_storage(
app_memory,
conversation,
"I'm building an AI assistant. Where should I start?",
user_id,
system_prompt="You are an AI architect advising startups."
)
conversation.append(response_1)
# Turn 2
response_2 = claude_turn_with_storage(
app_memory,
conversation,
"What about memory? Should I use Recall?",
user_id,
system_prompt="You are an AI architect advising startups."
)
conversation.append(response_2)Step 3: Retrieve Selective Context Before Claude Calls
Before calling Claude, query Recall for relevant memories instead of including full conversation history. Inject only the essential context.
def retrieve_context(app_memory, current_query, user_id, top_k=5):
"""Retrieve relevant memories to include in Claude's context."""
memories = app_memory.retrieve(
query=current_query,
filters={"user_id": user_id},
top_k=top_k
)
return "\n".join([m.text for m in memories])
def claude_turn_selective(
app_memory,
current_message,
user_id,
system_prompt=None
):
"""Call Claude with selective memory instead of full history."""
# 1. Retrieve only relevant past context
context = retrieve_context(
app_memory,
query=current_message,
user_id=user_id,
top_k=5
)
# 2. Build prompt with selective context, not full history
context_prompt = ""
if context:
context_prompt = f"Relevant prior context:\n{context}\n\n"
# 3. Call Claude
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=(system_prompt or "You are a helpful assistant.") + "\n" + context_prompt,
messages=[{"role": "user", "content": current_message}]
)
assistant_message = response.content[0].text
# 4. Store this turn
app_memory.store(
text=f"User: {current_message}\n\nAssistant: {assistant_message}",
memory_type="conversation",
metadata={"user_id": user_id}
)
return assistant_message
# After storing context from earlier turns, now use selective retrieval
response_3 = claude_turn_selective(
app_memory,
"Should I build my own memory system or use Recall?",
user_id,
system_prompt="You are an AI architect advising startups."
)
# Claude receives only the most relevant prior turns (via Recall),
# not the entire conversation history.Step 4: Implement Async for Production
For production applications, use async to avoid blocking on Recall queries.
import asyncio
from anthropic import AsyncAnthropic
async_client = AsyncAnthropic()
async def claude_turn_async(
app_memory,
current_message,
user_id,
system_prompt=None
):
"""Async Claude call with selective memory."""
# 1. Retrieve context (async)
memories = await app_memory.retrieve_async(
query=current_message,
filters={"user_id": user_id},
top_k=5
)
context = "\n".join([m.text for m in memories])
# 2. Call Claude (async)
response = await async_client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=(system_prompt or "You are helpful.") + f"\nContext:\n{context}",
messages=[{"role": "user", "content": current_message}]
)
return response.content[0].textStep 5: Monitor Token Savings
Track the difference between full context and selective context. This shows your cost reduction.
def estimate_tokens(text):
"""Rough estimate: ~1 token per 4 characters."""
return len(text) / 4
# With full conversation history (naive approach)
full_history_tokens = estimate_tokens(
"\n".join([f"Turn {i}: {msg}" for i, msg in enumerate(conversation)])
)
print(f"Full history tokens: {full_history_tokens:.0f}")
# With selective memory (Recall approach)
selective_context = retrieve_context(
app_memory,
query="Should I use Recall?",
user_id=user_id,
top_k=5
)
selective_tokens = estimate_tokens(selective_context)
print(f"Selective context tokens: {selective_tokens:.0f}")
# Savings
savings = (full_history_tokens - selective_tokens) / full_history_tokens
print(f"Token reduction: {savings:.1%}")Code Comparison
Without Recall: Full Conversation in Every Prompt
from anthropic import Anthropic
client = Anthropic()
# Store entire conversation in memory
conversation_history = []
def chat_no_memory(user_message, system_prompt):
"""Claude sees entire conversation history."""
# Add user message to history
conversation_history.append({
"role": "user",
"content": user_message
})
# Send entire history to Claude
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=system_prompt,
messages=conversation_history # Full history: expensive
)
assistant_message = response.content[0].text
# Add to history
conversation_history.append({
"role": "assistant",
"content": assistant_message
})
return assistant_message
# Conversation grows...
for i in range(20):
response = chat_no_memory(
f"Follow-up question {i+1}...",
"You are a helpful assistant."
)
# By turn 20, conversation_history has 40 entries
# At 500 tokens per turn: 20,000 tokens sent on EVERY API call
# Cost: 20,000 tokens × $3 per 1M = $0.06 per turn
# 20 turns = $1.20 in input tokens aloneWith Recall: Selective Memory Only
from recall_sdk import Recall
from anthropic import Anthropic
client = Anthropic()
# 1. Initialize Recall
app_memory = Recall(
name="claude-app",
metadata={"application": "chat"}
)
def chat_with_recall(user_message, system_prompt, user_id):
"""Claude sees only relevant prior context."""
# 2. Retrieve relevant memories (not full history)
memories = app_memory.retrieve(
query=user_message,
filters={"user_id": user_id},
top_k=5 # Only top 5 relevant turns
)
# 3. Build context from memories
context = ""
if memories:
context = "Relevant prior context:\n" + "\n".join([m.text for m in memories]) + "\n\n"
# 4. Call Claude with selective context
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=system_prompt + "\n" + context,
messages=[{"role": "user", "content": user_message}]
)
assistant_message = response.content[0].text
# 5. Store this turn for future retrieval
app_memory.store(
text=f"User: {user_message}\n\nAssistant: {assistant_message}",
memory_type="conversation",
metadata={"user_id": user_id}
)
return assistant_message
# Conversation grows...
user_id = "user_123"
for i in range(20):
response = chat_with_recall(
f"Follow-up question {i+1}...",
"You are a helpful assistant.",
user_id
)
# By turn 20, Recall stores 20 memories
# But each API call only retrieves top 5 relevant (2,500 tokens)
# Cost: 2,500 tokens × $3 per 1M = $0.0075 per turn
# 20 turns = $0.15 in input tokens (87.5% savings vs. naive approach)Implementation Checklist
- Install Recall SDK (
pip install recall-sdk) and Claude SDK (pip install anthropic) - Create a Recall instance for your Claude application
- After each Claude API call, store the turn and any decisions using
app_memory.store() - Before Claude calls, retrieve relevant memories with
app_memory.retrieve()instead of including full history - Include user_id in metadata and filters to isolate per-user conversations
- Estimate token savings: compare full history vs. selective memory size
- Test a 10-turn conversation and verify memory retrieval surfaces relevant prior context
- (Production) Use async calls with
retrieve_async()andAsyncAnthropic()
Next Steps
- Long-term memory strategies: Longterm Memory
- Cost optimization with memory: Cost Optimization Agent Memory
- LangChain integration: Using Recall with LangChain
- Memory architectures: Memory Architectures
Metric
50% reduction in context window usage. By storing full conversation history outside Claude's context window and retrieving only relevant memories, you cut prompt tokens in half. A 20-turn conversation drops from 20,000 tokens to 2,500 tokens per call, saving 87.5% in input costs while keeping Claude fully context-aware.