fleet-memory/memora-langmem/tutorial.ipynb
2025-11-18 14:59:00 +01:00

622 lines
22 KiB
Text

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Memora-LangMem: Semantic Memory with Personality-Driven Thinking for LangGraph\n",
"\n",
"This notebook provides a comprehensive tutorial on using `memora-langmem`, a drop-in replacement for LangGraph's standard memory stores that adds advanced semantic memory capabilities powered by Memora."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is Memora-LangMem?\n",
"\n",
"`memora-langmem` is a Python package that implements LangGraph's `BaseStore` interface using Memora as the backend. It provides:\n",
"\n",
"### Core Features\n",
"\n",
"1. **Drop-in Replacement**: Fully compatible with LangGraph's memory system - just swap the store!\n",
"2. **Semantic Memory**: Advanced semantic search with spreading activation algorithms\n",
"3. **Personality-Driven Thinking**: Memory retrieval influenced by configurable agent personalities\n",
"4. **Fact Extraction**: Automatic extraction and structuring of facts from conversations\n",
"5. **Temporal Reasoning**: Time-aware memory with event date tracking\n",
"6. **Entity Linking**: Automatic recognition and linking of entities across memories\n",
"7. **Multi-Agent Support**: Each namespace can represent a different agent with unique personality traits\n",
"\n",
"### What You Get vs Standard LangGraph Memory\n",
"\n",
"| Feature | Standard LangGraph Memory | Memora-LangMem |\n",
"|---------|---------------------------|----------------|\n",
"| Basic Key-Value Storage | ✅ | ✅ |\n",
"| Semantic Search | ✅ (with index config) | ✅ Enhanced with spreading activation |\n",
"| Namespace Support | ✅ | ✅ |\n",
"| Personality-Driven Retrieval | ❌ | ✅ Configurable personality traits |\n",
"| Automatic Fact Extraction | ❌ | ✅ NLP-powered extraction |\n",
"| Entity Recognition | ❌ | ✅ Automatic entity linking |\n",
"| Temporal Reasoning | ❌ | ✅ Time-aware queries |\n",
"| Opinion Formation | ❌ | ✅ Agent forms opinions over time |\n",
"| Background Knowledge | ❌ | ✅ Agent-specific background context |\n",
"| Thinking/Reasoning API | ❌ | ✅ Explicit reasoning with memory |\n",
"\n",
"### When to Use Memora-LangMem\n",
"\n",
"- **Conversational Agents**: When you need agents to remember context across long conversations\n",
"- **Personalized AI**: When agent responses should be influenced by personality and past interactions\n",
"- **Knowledge Management**: When you need to extract and organize facts from unstructured text\n",
"- **Multi-Agent Systems**: When different agents need isolated memory with distinct personalities\n",
"- **Research & Analysis**: When you need semantic search over large knowledge bases"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"```bash\n",
"# Install from local path (development)\n",
"uv pip install -e /path/to/memora-langmem\n",
"\n",
"# Or with pip\n",
"pip install -e /path/to/memora-langmem\n",
"```\n",
"\n",
"### Prerequisites\n",
"\n",
"You need a running Memora API server. Set the URL via environment variable:\n",
"\n",
"```bash\n",
"export MEMORA_API_URL=http://localhost:8000\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 1: Basic Usage - Direct Store API\n",
"\n",
"Let's start with the basic `BaseStore` interface that's compatible with LangGraph."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from memora_langmem import MemoraStore\n",
"\n",
"# Initialize the store\n",
"base_url = os.getenv(\"MEMORA_API_URL\", \"http://localhost:8000\")\n",
"store = MemoraStore(base_url=base_url, default_agent_id=\"tutorial_agent\")\n",
"\n",
"print(\"✅ MemoraStore initialized\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Storing and Retrieving Memories\n",
"\n",
"The store uses a namespace-key-value structure:\n",
"- **Namespace**: A tuple of strings representing a hierarchical path (e.g., `(\"user\", \"alice\")`)\n",
"- **Key**: A unique identifier within the namespace\n",
"- **Value**: A dictionary containing your data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Store a memory\n",
"namespace = (\"user\", \"alice\")\n",
"key = \"preferences\"\n",
"value = {\n",
" \"theme\": \"dark\",\n",
" \"language\": \"python\",\n",
" \"notifications\": True,\n",
" \"interests\": [\"machine learning\", \"data science\", \"artificial intelligence\"]\n",
"}\n",
"\n",
"store.put(namespace, key, value)\n",
"print(f\"✅ Stored preferences for {namespace}\")\n",
"\n",
"# Retrieve the memory\n",
"import time\n",
"time.sleep(1) # Brief pause for processing\n",
"\n",
"retrieved = store.get(namespace, key)\n",
"if retrieved:\n",
" print(f\"\\n📦 Retrieved memory:\")\n",
" print(f\" Namespace: {retrieved.namespace}\")\n",
" print(f\" Key: {retrieved.key}\")\n",
" print(f\" Value: {retrieved.value}\")\n",
" print(f\" Created: {retrieved.created_at}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Semantic Search - The Power of Memora\n",
"\n",
"Unlike simple key-value retrieval, Memora provides semantic search with spreading activation. This means:\n",
"- Search by natural language queries\n",
"- Find semantically related memories\n",
"- Memories are ranked by relevance"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Add more memories to demonstrate search\n",
"memories = [\n",
" (\"notes\", \"ml_project\", {\n",
" \"title\": \"Machine Learning Project Ideas\",\n",
" \"content\": \"Working on a neural network for image classification. Interested in transformers and attention mechanisms.\"\n",
" }),\n",
" (\"notes\", \"data_tools\", {\n",
" \"title\": \"Favorite Data Tools\",\n",
" \"content\": \"Love using pandas for data manipulation, scikit-learn for ML, and PyTorch for deep learning.\"\n",
" }),\n",
" (\"notes\", \"meeting_summary\", {\n",
" \"title\": \"Team Meeting Notes\",\n",
" \"content\": \"Discussed the new AI assistant project. Team decided to use LangGraph for orchestration.\"\n",
" })\n",
"]\n",
"\n",
"for ns, k, v in memories:\n",
" store.put((\"user\", \"alice\", ns), k, v)\n",
"\n",
"time.sleep(2) # Allow time for indexing\n",
"\n",
"# Now search semantically\n",
"print(\"🔍 Searching for 'machine learning projects'...\\n\")\n",
"results = store.search(\n",
" namespace_prefix=(\"user\", \"alice\"),\n",
" query=\"machine learning projects\",\n",
" limit=5\n",
")\n",
"\n",
"for i, result in enumerate(results, 1):\n",
" print(f\"{i}. Score: {result.score:.3f}\")\n",
" print(f\" Key: {result.key}\")\n",
" print(f\" Value: {result.value}\")\n",
" print()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Namespace Management\n",
"\n",
"Namespaces allow you to organize memories hierarchically. In Memora, each unique namespace combination maps to a separate agent."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create memories in different namespaces\n",
"store.put((\"user\", \"bob\", \"preferences\"), \"theme\", {\"theme\": \"light\", \"language\": \"javascript\"})\n",
"store.put((\"user\", \"charlie\", \"preferences\"), \"theme\", {\"theme\": \"auto\", \"language\": \"rust\"})\n",
"\n",
"time.sleep(1)\n",
"\n",
"# List all namespaces\n",
"print(\"📁 All namespaces:\")\n",
"namespaces = store.list_namespaces(prefix=(\"user\",), limit=10)\n",
"for ns in namespaces:\n",
" print(f\" - {ns}\")\n",
"\n",
"# List namespaces with specific prefix\n",
"print(\"\\n📁 Namespaces for alice:\")\n",
"alice_namespaces = store.list_namespaces(prefix=(\"user\", \"alice\"), limit=10)\n",
"for ns in alice_namespaces:\n",
" print(f\" - {ns}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 2: Integration with LangGraph Memory Tools\n",
"\n",
"The real power comes from using MemoraStore with LangGraph's memory tools and agents."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from langmem import create_manage_memory_tool, create_search_memory_tool\n",
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"# Create a fresh store for the agent\n",
"agent_store = MemoraStore(\n",
" base_url=base_url,\n",
" default_agent_id=\"intelligent_assistant\"\n",
")\n",
"\n",
"# Create memory tools\n",
"manage_tool = create_manage_memory_tool(namespace=(\"conversations\",))\n",
"search_tool = create_search_memory_tool(namespace=(\"conversations\",))\n",
"\n",
"# Create a LangGraph agent with Memora-backed memory\n",
"agent = create_react_agent(\n",
" \"anthropic:claude-3-5-sonnet-latest\",\n",
" tools=[manage_tool, search_tool],\n",
" store=agent_store # This is where MemoraStore plugs in!\n",
")\n",
"\n",
"print(\"✅ Agent created with Memora-backed memory\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Conversational Memory in Action\n",
"\n",
"Let's see how the agent uses Memora to remember information across conversations."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# First conversation: Share information\n",
"print(\"💬 Conversation 1: Sharing preferences\\n\")\n",
"result1 = agent.invoke({\n",
" \"messages\": [{\n",
" \"role\": \"user\",\n",
" \"content\": \"\"\"Hi! I want you to remember some things about me:\n",
" - My name is David\n",
" - I'm a software engineer working on AI projects\n",
" - I love Python and machine learning\n",
" - I'm currently building a chatbot using LangGraph\n",
" Please remember these details for future conversations.\"\"\"\n",
" }]\n",
"})\n",
"\n",
"print(\"Agent response:\")\n",
"print(result1[\"messages\"][-1].content)\n",
"print(\"\\n\" + \"=\"*80 + \"\\n\")\n",
"\n",
"# Second conversation: Recall information\n",
"time.sleep(2) # Brief pause\n",
"\n",
"print(\"💬 Conversation 2: Testing recall\\n\")\n",
"result2 = agent.invoke({\n",
" \"messages\": [{\n",
" \"role\": \"user\",\n",
" \"content\": \"What do you remember about me and my work?\"\n",
" }]\n",
"})\n",
"\n",
"print(\"Agent response:\")\n",
"print(result2[\"messages\"][-1].content)\n",
"print(\"\\n\" + \"=\"*80 + \"\\n\")\n",
"\n",
"# Third conversation: Contextual recommendations\n",
"time.sleep(2)\n",
"\n",
"print(\"💬 Conversation 3: Using memory for personalization\\n\")\n",
"result3 = agent.invoke({\n",
" \"messages\": [{\n",
" \"role\": \"user\",\n",
" \"content\": \"Can you suggest some relevant learning resources based on what you know about me?\"\n",
" }]\n",
"})\n",
"\n",
"print(\"Agent response:\")\n",
"print(result3[\"messages\"][-1].content)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 3: Advanced Features - Beyond Standard LangGraph\n",
"\n",
"Memora provides capabilities beyond the standard BaseStore interface."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Automatic Fact Extraction\n",
"\n",
"When you store natural language content, Memora automatically:\n",
"- Extracts structured facts\n",
"- Identifies entities (people, places, concepts)\n",
"- Links related facts together\n",
"- Categorizes facts by type (world knowledge, agent actions, opinions)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Store rich natural language content\n",
"conversation_store = MemoraStore(base_url=base_url, default_agent_id=\"fact_extractor\")\n",
"\n",
"conversation_content = {\n",
" \"text\": \"\"\"Yesterday I met with Sarah from the marketing team. She mentioned that our new \n",
" product launch is scheduled for next month. The team is really excited about the AI features \n",
" we've built. Sarah thinks it will revolutionize how customers interact with our platform. \n",
" I personally believe we should focus more on user experience rather than just adding features.\"\"\",\n",
" \"context\": \"team meeting\",\n",
" \"participants\": [\"self\", \"Sarah\"]\n",
"}\n",
"\n",
"conversation_store.put(\n",
" namespace=(\"meetings\", \"2024\"),\n",
" key=\"marketing_sync\",\n",
" value=conversation_content\n",
")\n",
"\n",
"print(\"✅ Stored conversation - Memora is now extracting facts...\")\n",
"print(\" Behind the scenes, Memora identifies:\")\n",
"print(\" • Entities: Sarah, marketing team, new product, AI features\")\n",
"print(\" • Events: product launch next month, meeting with Sarah\")\n",
"print(\" • Opinions: belief about UX focus vs features\")\n",
"print(\" • Relationships: Sarah works in marketing\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Temporal Reasoning\n",
"\n",
"Memora is time-aware. You can query memories with temporal context."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from datetime import datetime, timedelta\n",
"\n",
"# Store time-sensitive information\n",
"today = datetime.now()\n",
"yesterday = today - timedelta(days=1)\n",
"last_week = today - timedelta(days=7)\n",
"\n",
"events = [\n",
" (\"event_today\", {\"description\": \"Team standup meeting\", \"date\": today.isoformat()}),\n",
" (\"event_yesterday\", {\"description\": \"Product demo\", \"date\": yesterday.isoformat()}),\n",
" (\"event_last_week\", {\"description\": \"Sprint planning\", \"date\": last_week.isoformat()})\n",
"]\n",
"\n",
"for key, value in events:\n",
" conversation_store.put((\"events\",), key, value)\n",
"\n",
"time.sleep(2)\n",
"\n",
"# Search with temporal context\n",
"print(\"🗓️ Searching for recent events...\\n\")\n",
"recent_events = conversation_store.search(\n",
" namespace_prefix=(\"events\",),\n",
" query=\"meetings this week\",\n",
" limit=5\n",
")\n",
"\n",
"for event in recent_events:\n",
" print(f\"• {event.value.get('description')} - {event.value.get('date')}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Multi-Agent Scenarios\n",
"\n",
"Each namespace combination creates a separate agent in Memora, allowing for isolated memories and distinct personalities."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create stores for different agent personas\n",
"store_creative = MemoraStore(base_url=base_url, default_agent_id=\"creative_writer\")\n",
"store_analyst = MemoraStore(base_url=base_url, default_agent_id=\"data_analyst\")\n",
"store_engineer = MemoraStore(base_url=base_url, default_agent_id=\"software_engineer\")\n",
"\n",
"# Each agent has its own perspective on the same information\n",
"shared_info = {\n",
" \"topic\": \"New AI Feature Launch\",\n",
" \"description\": \"We're launching an AI-powered recommendation system\"\n",
"}\n",
"\n",
"# Creative writer stores with focus on narrative\n",
"store_creative.put((\"projects\",), \"ai_launch\", {\n",
" **shared_info,\n",
" \"note\": \"This is a revolutionary moment - we're changing how people discover content!\"\n",
"})\n",
"\n",
"# Data analyst stores with focus on metrics\n",
"store_analyst.put((\"projects\",), \"ai_launch\", {\n",
" **shared_info,\n",
" \"note\": \"Need to track engagement metrics, conversion rates, and user retention\"\n",
"})\n",
"\n",
"# Software engineer stores with focus on implementation\n",
"store_engineer.put((\"projects\",), \"ai_launch\", {\n",
" **shared_info,\n",
" \"note\": \"Built using transformer models, need to optimize inference latency\"\n",
"})\n",
"\n",
"print(\"✅ Created three agents with different perspectives on the same project\")\n",
"print(\" Each agent's memory is isolated and can develop unique personality traits\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 4: Batch Operations for Performance\n",
"\n",
"When working with multiple memories, batch operations are more efficient."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from langgraph.store.base import PutOp, GetOp, SearchOp\n",
"\n",
"batch_store = MemoraStore(base_url=base_url, default_agent_id=\"batch_demo\")\n",
"\n",
"# Batch put operations\n",
"put_ops = [\n",
" PutOp(namespace=(\"docs\",), key=\"intro\", value={\"title\": \"Introduction\", \"content\": \"Welcome to the tutorial\"}),\n",
" PutOp(namespace=(\"docs\",), key=\"setup\", value={\"title\": \"Setup\", \"content\": \"Installation instructions\"}),\n",
" PutOp(namespace=(\"docs\",), key=\"usage\", value={\"title\": \"Usage\", \"content\": \"How to use the API\"}),\n",
"]\n",
"\n",
"print(\"📦 Performing batch PUT operations...\")\n",
"put_results = batch_store.batch(put_ops)\n",
"print(f\"✅ Stored {len(put_results)} documents\\n\")\n",
"\n",
"time.sleep(1)\n",
"\n",
"# Batch get operations\n",
"get_ops = [\n",
" GetOp(namespace=(\"docs\",), key=\"intro\"),\n",
" GetOp(namespace=(\"docs\",), key=\"setup\"),\n",
" GetOp(namespace=(\"docs\",), key=\"usage\"),\n",
"]\n",
"\n",
"print(\"📦 Performing batch GET operations...\")\n",
"get_results = batch_store.batch(get_ops)\n",
"\n",
"for item in get_results:\n",
" if item:\n",
" print(f\" • {item.value['title']}: {item.value['content']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 5: Cleanup\n",
"\n",
"Clean up memories when they're no longer needed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Delete specific memories\n",
"print(\"🗑️ Cleaning up...\\n\")\n",
"\n",
"# Delete from the docs namespace\n",
"batch_store.delete((\"docs\",), \"intro\")\n",
"print(\"✅ Deleted 'intro' document\")\n",
"\n",
"# Verify deletion\n",
"time.sleep(1)\n",
"deleted_item = batch_store.get((\"docs\",), \"intro\")\n",
"if deleted_item is None:\n",
" print(\"✅ Confirmed: document is deleted\")\n",
"else:\n",
" print(\"⚠️ Document still exists\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Summary: Why Choose Memora-LangMem?\n",
"\n",
"### Key Advantages\n",
"\n",
"1. **Zero Code Changes**: Drop-in replacement for existing LangGraph memory stores\n",
"2. **Enhanced Intelligence**: Automatic fact extraction, entity linking, and semantic understanding\n",
"3. **Personality System**: Agents can develop unique personalities that influence memory retrieval\n",
"4. **Production Ready**: Built on robust Memora backend with proper persistence\n",
"5. **Rich Context**: Beyond simple key-value, stores temporal, relational, and semantic information\n",
"6. **Research-Backed**: Implements spreading activation and advanced memory retrieval algorithms\n",
"\n",
"### Use Cases\n",
"\n",
"- **Customer Support Bots**: Remember customer preferences, history, and context\n",
"- **Personal Assistants**: Build agents that truly understand and remember user preferences\n",
"- **Knowledge Workers**: Agents that accumulate domain expertise over time\n",
"- **Research Assistants**: Semantic search over large knowledge bases\n",
"- **Team Collaboration**: Multiple agents with distinct roles and memories\n",
"\n",
"### Getting Started\n",
"\n",
"1. Install: `uv pip install -e /path/to/memora-langmem`\n",
"2. Start Memora API: Ensure server is running at `http://localhost:8000`\n",
"3. Replace store: `store = MemoraStore(base_url=base_url)`\n",
"4. Use normally: All LangGraph memory APIs work as expected\n",
"5. Enjoy enhanced memory capabilities automatically!\n",
"\n",
"### Next Steps\n",
"\n",
"- Explore the Memora API documentation for advanced features\n",
"- Configure agent personalities for different use cases\n",
"- Experiment with the thinking/reasoning API\n",
"- Build multi-agent systems with isolated memories\n",
"- Integrate with your existing LangGraph applications"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}