GitHub
Cookbook

Companion with Memory

Build a fitness coach that remembers goals, injuries, and preferences across sessions.

Domain: Fitness Coaching
Key Concepts: Persistent Memory, Categorization, Temporal Facts

Problem Statement

Standard LLMs are stateless. If a user tells a fitness bot "I hurt my knee" on Tuesday, the bot shouldn't suggest squats on Thursday. Creating a true companion requires a memory system that can:

  • Persist critical facts (injuries, goals) across app restarts.
  • Filter noise (ignore "hello", "ok thanks") to save costs.
  • Organize memories by type (goals vs. constraints).

Architecture: The Memory Loop

The core of this pattern is the Retrieve-Generate-Store loop that runs on every interaction.

Implementation Steps

1. Initializing Memory

First, we check if the user has a history. If not, we can "seed" the memory with initial goals.

from functor_sdk import FunctorClient
client = FunctorClient(api_key="sk-...")
USER_ID = "user_max"
# Check for existing history
history = client.memory.episodic.list(user_id=USER_ID, limit=1)
if not history['episodes']:
# Seed initial goal
client.memory.semantic.add_fact(
content="Goal: Run a sub-4 hour marathon",
kg_name="fitness_kg",
metadata={
"category": "goals",
"user_id": USER_ID,
"status": "active"
}
)

2. The Retrieval Phase

Before calling the LLM, we search for relevant context. Notice we can filter by relevance or recency.

# User asks a question
user_query = "Can I do hill repeats today?"
# Search specifically for constraints and physical status
context = client.memory.search(
query=user_query,
user_id=USER_ID,
filters={
"categories": ["constraints", "injuries", "recovery"]
},
limit=3
)
print(context)
# Output: ["Right knee soreness reported 2 days ago", "Doctor advised 1 week rest"]

3. Storing Interactions

After the conversation, we save the interaction. We distinguish between Episodic (the conversation flow) and Semantic (facts extracted).

# Store the conversation turn
episode = client.memory.episodic.create(
session_id="session_123",
user_id=USER_ID,
event_data={
"query": user_query,
"response": agent_response
},
summary="User asked about hill repeats; advised against due to knee injury."
)
# Explicitly store new facts if detected
if "knee feels better" in user_query:
client.memory.semantic.add_fact(
content="Knee injury is improving",
user_id=USER_ID,
metadata={"category": "recovery", "sentiment": "positive"}
)

Advanced Features

Time-Bound Memories

Some facts expire. An injury is temporary. Use ttl_days or explicit expiration dates to prevent stale context.

client.memory.semantic.add_fact(
content="Taking ibuprofen for inflammation",
user_id=USER_ID,
# This fact will self-destruct after 7 days
ttl_days=7
)

Memory Categorization

Tagging memories allows for precise retrieval. A "Planning Agent" might only need goals, while a "Support Agent" needs preferences.

# Retrieval for a specific agent persona
nutrition_facts = client.memory.search(
query="What does he eat?",
user_id=USER_ID,
filters={"category": "diet_preferences"} # Ignores training data
)