Context Memory Demo¶
This example demonstrates how to build a sophisticated chatbot that can remember context across multiple turns using Intent Kit's declarative context management system.
Overview¶
The Context Memory Demo shows how to: - Use declarative context read/write configuration - Remember user information across conversations - Use context to personalize responses - Handle multiple intents with a single DAG - Maintain conversation state with persistent context
Full Example¶
import os
from dotenv import load_dotenv
from intent_kit import DAGBuilder, run_dag
from intent_kit.core.context import DefaultContext
load_dotenv()
def remember_name(name: str, **kwargs) -> str:
"""Remember the user's name in context for future interactions."""
return f"Nice to meet you, {name}! I'll remember your name."
def get_weather(location: str, user_name: Optional[str] = None, **kwargs) -> str:
"""Get weather for a location, using remembered name if available."""
# Check for user.name from context first, then user_name parameter
context_user_name = kwargs.get('user.name')
if context_user_name:
return f"Hey {context_user_name}! The weather in {location} is sunny and 72°F."
elif user_name:
return f"Hey {user_name}! The weather in {location} is sunny and 72°F."
else:
return f"Hey there! The weather in {location} is sunny and 72°F."
def get_remembered_name(user_name: Optional[str] = None, **kwargs) -> str:
"""Get the remembered name from context."""
# Check for user.name from context first, then user_name parameter
context_user_name = kwargs.get('user.name')
if context_user_name:
return f"I remember you! Your name is {context_user_name}."
elif user_name:
return f"I remember you! Your name is {user_name}."
else:
return "I don't remember your name yet. Try introducing yourself first!"
def create_memory_dag():
"""Create a DAG that can remember context across turns."""
builder = DAGBuilder()
# Set default LLM configuration for the entire graph
builder.with_default_llm_config({
"provider": "openrouter",
"api_key": os.getenv("OPENROUTER_API_KEY"),
"model": "google/gemma-2-9b-it"
})
# Add classifier node to determine intent
builder.add_node("classifier", "classifier",
output_labels=["greet", "weather", "remember", "unknown"],
description="Classify user intent")
# Add extractor for name extraction
builder.add_node("extract_name", "extractor",
param_schema={"name": str},
description="Extract name from greeting",
output_key="name_params") # Use specific key to avoid conflicts
# Add extractor for location extraction
builder.add_node("extract_location", "extractor",
param_schema={"location": str},
description="Extract location from weather request",
output_key="location_params") # Use specific key to avoid conflicts
# Add action nodes with context read/write configuration
builder.add_node("remember_name_action", "action",
action=remember_name,
description="Remember the user's name",
param_keys=["name_params", "extracted_params"], # Look for name parameters
context_write=["user.name", "user.first_seen"]) # Write name to context
builder.add_node("weather_action", "action",
action=get_weather,
description="Get weather information",
param_keys=["location_params", "extracted_params"], # Look for location parameters
context_read=["user.name"], # Read user name from context
context_write=["weather.requests", "weather.last_location"]) # Write weather data
builder.add_node("get_name_action", "action",
action=get_remembered_name,
description="Get remembered name from context",
param_keys=["name_params", "extracted_params"], # Look for name parameters
context_read=["user.name"]) # Read user name from context
# Add clarification node
builder.add_node("clarification", "clarification",
clarification_message="I'm not sure what you'd like me to do. You can greet me, ask about weather, or ask me to remember your name!",
available_options=[
"Say hello", "Ask about weather", "Ask me to remember your name"],
description="Ask for clarification when intent is unclear")
# Connect nodes
builder.add_edge("classifier", "extract_name", "greet")
builder.add_edge("extract_name", "remember_name_action", "success")
builder.add_edge("classifier", "extract_location", "weather")
builder.add_edge("extract_location", "weather_action", "success")
builder.add_edge("classifier", "get_name_action", "remember")
builder.add_edge("classifier", "clarification", "unknown")
builder.set_entrypoints(["classifier"])
return builder.build()
def simulate_conversation():
"""Simulate a multi-turn conversation with context memory."""
dag = create_memory_dag()
context = DefaultContext()
print("=== Context Memory Demo ===\n")
# Turn 1: User introduces themselves
print("User: Hi, my name is Alice")
result, context = run_dag(dag, "Hi, my name is Alice")
print(f"Bot: {result.data}\n")
# Turn 2: User asks about weather (bot remembers name)
print("User: What's the weather like in San Francisco?")
result, context = run_dag(dag, "What's the weather like in San Francisco?", context)
print(f"Bot: {result.data}\n")
# Turn 3: User asks bot to remember their name
print("User: Do you remember my name?")
result, context = run_dag(dag, "Do you remember my name?", context)
print(f"Bot: {result.data}\n")
# Turn 4: Different user introduces themselves
print("User: Hello, I'm Bob")
result, context = run_dag(dag, "Hello, I'm Bob", context)
print(f"Bot: {result.data}\n")
# Turn 5: Bob asks about weather (bot uses Bob's name)
print("User: How's the weather in New York?")
result, context = run_dag(dag, "How's the weather in New York?", context)
print(f"Bot: {result.data}\n")
if __name__ == "__main__":
simulate_conversation()
Running the Demo¶
When you run this demo, you'll see output like:
=== Context Memory Demo ===
User: Hi, my name is Alice
Bot: Nice to meet you, Alice! I'll remember your name.
User: What's the weather like in San Francisco?
Bot: Hey Alice! The weather in San Francisco is sunny and 72°F.
User: Do you remember my name?
Bot: I remember you! Your name is Alice.
User: Hello, I'm Bob
Bot: Nice to meet you, Bob! I'll remember your name.
User: How's the weather in New York?
Bot: Hey Bob! The weather in New York is sunny and 72°F.
Key Features Demonstrated¶
1. Declarative Context Management¶
Nodes explicitly declare their context dependencies:
- context_read: Specifies which context keys to read before execution
- context_write: Specifies which context keys to write after execution
- param_keys: Specifies which parameter keys to check for parameters
2. Context-Aware Action Functions¶
Action functions receive context data through **kwargs:
- Access context values like kwargs.get('user.name')
- Fallback to function parameters if context not available
- No need for global variables or manual context management
3. Parameter Key Configuration¶
Action nodes can specify multiple parameter sources:
- param_keys=["name_params", "extracted_params"] checks multiple keys
- Prevents parameter conflicts between different extractors
- Enables flexible parameter sourcing
4. Context Persistence¶
The bot remembers user information across multiple turns:
- Names are stored and retrieved using user.name context key
- Weather requests are tracked with weather.requests counter
- Last location is remembered with weather.last_location
5. Multi-Intent Classification¶
The classifier can handle multiple types of requests: - Greetings with name introduction - Weather inquiries - Memory retrieval requests
6. Parameter Extraction¶
Different extractors handle different parameter types:
- Name extraction from greetings (stored in name_params)
- Location extraction from weather requests (stored in location_params)
7. Flexible Routing¶
The DAG routes to different paths based on classification: - Greeting → Name extraction → Remember action - Weather → Location extraction → Weather action - Memory → Direct memory retrieval action
8. Error Handling¶
Clarification node handles unclear requests gracefully.
Advanced Context Features¶
Declarative Context Management¶
This example demonstrates Intent Kit's declarative approach to context management: - Explicit Dependencies: Nodes declare exactly what context they read and write - Clear Data Flow: Context modifications are visible in node configuration - Prevent Conflicts: No accidental overwrites of important context data - Self-Documenting: Context usage is part of the node's configuration
Context Persistence Patterns¶
The demo shows several context persistence patterns:
- User Information: user.name, user.first_seen for persistent user data
- Request Tracking: weather.requests for analytics and counting
- State Management: weather.last_location for conversation state
- Parameter Isolation: name_params, location_params to avoid conflicts
Context-Aware Functions¶
Action functions can access context data seamlessly: - Automatic Injection: Context data is automatically passed to action functions - Fallback Logic: Functions can check both context and parameters - Type Safety: Context values maintain their original types - Error Handling: Graceful handling of missing context data
Production Context Management¶
In production applications, you might: - Use a database for persistent storage across sessions - Implement user session management with context isolation - Add context expiration and cleanup policies - Use distributed context storage for scalability - Implement context encryption for sensitive data
Context Security and Auditing¶
The context system provides: - Namespace Protection: System keys are protected from accidental modification - Audit Trails: All context modifications are tracked with provenance - Type Validation: Context values are validated against expected types - Immutable Snapshots: Context state can be captured for debugging - Access Control: Fine-grained control over context read/write permissions
Extending the Demo¶
You can extend this demo by: - Adding more intents (e.g., calendar, reminders) - Implementing more sophisticated memory (e.g., conversation history) - Adding user preferences and settings - Implementing multi-user support - Adding natural language generation for responses