---
title: "LangChain"
sidebarTitle: "LangChain"
description: "Build AI agents with persistent memory using LangChain and Supermemory"
icon: "/icons/hugeicons/link-01.svg"
---
Build AI applications with LangChain that remember context across conversations. Supermemory handles memory storage, retrieval, and user profiling while LangChain manages your conversation flow.
## Overview
This guide shows how to integrate Supermemory with LangChain to create AI agents that:
- Maintain user context through automatic profiling
- Store and retrieve relevant memories semantically
- Personalize responses based on conversation history
## Setup
Install the required packages:
```bash
pip install langchain langchain-openai supermemory python-dotenv
```
Configure your environment:
```bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key
```
Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).
## Basic integration
Initialize both clients and set up a simple chat function with memory:
```python
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from supermemory import Supermemory
from dotenv import load_dotenv
load_dotenv()
# Initialize clients
llm = ChatOpenAI(model="gpt-4o")
memory = Supermemory()
def chat(user_id: str, message: str) -> str:
# 1. Get user profile and relevant memories
profile_result = memory.profile(user_id)
search_result = memory.search(user_id, query=message)
# 2. Build context from profile
static_facts = [m.memory for m in profile_result.profile.static]
dynamic_context = [m.memory for m in profile_result.profile.dynamic]
search_results = search_result.results
context = f"""
User Background:
{chr(10).join(static_facts) if static_facts else 'No profile yet.'}
Recent Context:
{chr(10).join(dynamic_context) if dynamic_context else 'No recent activity.'}
Relevant Memories:
{chr(10).join([r.memory or r.chunk for r in search_results]) if search_results else 'None found.'}
"""
# 3. Generate response
prompt = ChatPromptTemplate.from_messages([
SystemMessage(content=f"You are a helpful assistant. Use this context to personalize your response:\n{context}"),
HumanMessage(content=message)
])
chain = prompt | llm
response = chain.invoke({})
# 4. Store the interaction as memory
memory.add(
user_id,
content=f"User: {message}\nAssistant: {response.content}",
dreaming="instant"
)
return response.content
```
---
## Core concepts
### User profiles
Supermemory automatically maintains user profiles with two types of information:
- **Static facts**: Long-term information about the user (preferences, expertise, background)
- **Dynamic context**: Recent activity and current focus areas
```python
# Fetch the profile for a namespace
result = memory.profile("user_123")
print([m.memory for m in result.profile.static]) # ["User is a Python developer", "Prefers dark mode"]
print([m.memory for m in result.profile.dynamic]) # ["Currently working on API integration", "Debugging auth issues"]
```
### Memory storage
Content you add is automatically processed into searchable memories:
```python
# Store a conversation
memory.add(
"user_123",
content="User asked about async Python patterns. Explained asyncio basics.",
metadata={"topic": "python", "type": "conversation"},
dreaming="instant"
)
# Store a document
memory.add(
"user_123",
content="https://docs.python.org/3/library/asyncio.html",
dreaming="instant"
)
```
### Memory search
Search returns both extracted memories and document chunks:
```python
results = memory.search(
"user_123",
query="async programming",
search_mode="hybrid", # Searches memories + document chunks
limit=5
)
for r in results.results:
print(r.memory or r.chunk, r.similarity)
```
---
## Complete example: Code review assistant
Here's a full example of a code review assistant that learns from past reviews and adapts to the user's coding style:
```python
import os
from typing import Optional
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate
from supermemory import Supermemory
from dotenv import load_dotenv
load_dotenv()
class CodeReviewAssistant:
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o", temperature=0.3)
self.memory = Supermemory()
def get_context(self, user_id: str, code: str) -> str:
"""Retrieve user profile and relevant past reviews."""
# Get profile, then search for similar code patterns
profile = self.memory.profile(user_id).profile
memories = self.memory.search(
user_id,
query=code[:500], # Use code snippet for semantic search
threshold=0.6
).results
static = [m.memory for m in profile.static]
dynamic = [m.memory for m in profile.dynamic]
return f"""
## Developer Profile
{chr(10).join(f"- {fact}" for fact in static) if static else "New developer, no profile yet."}
## Current Focus
{chr(10).join(f"- {ctx}" for ctx in dynamic) if dynamic else "No recent context."}
## Relevant Past Reviews
{chr(10).join(f"- {m.memory or m.chunk}" for m in memories[:3]) if memories else "No similar reviews found."}
"""
def review(self, user_id: str, code: str, language: Optional[str] = None) -> str:
"""Review code with personalized feedback."""
context = self.get_context(user_id, code)
prompt = ChatPromptTemplate.from_messages([
SystemMessage(content=f"""You are a code review assistant. Provide constructive feedback
tailored to the developer's experience level and preferences.
{context}
Guidelines:
- Reference past feedback when relevant patterns appear
- Adapt explanation depth to the developer's expertise
- Focus on issues that matter most to this developer"""),
HumanMessage(content=f"Review this {language or 'code'}:\n\n```\n{code}\n```")
])
chain = prompt | self.llm
response = chain.invoke({})
# Store the review for future context
self.memory.add(
user_id,
content=f"Code review feedback: {response.content[:500]}",
metadata={"type": "code_review", "language": language},
dreaming="instant"
)
return response.content
def learn_preference(self, user_id: str, preference: str):
"""Store a coding preference or style guideline."""
self.memory.add(
user_id,
content=f"Developer preference: {preference}",
metadata={"type": "preference"},
dreaming="instant"
)
# Usage
if __name__ == "__main__":
assistant = CodeReviewAssistant()
user_id = "dev_alice"
# Teach the assistant about preferences
assistant.learn_preference(user_id, "Prefers functional programming patterns")
assistant.learn_preference(user_id, "Values descriptive variable names over comments")
# Review some code
code = """
def calc(x, y):
r = []
for i in x:
if i in y:
r.append(i)
return r
"""
review = assistant.review(user_id, code, language="python")
print(review)
```
---
## Advanced patterns
### Conversation history with memory
Maintain multi-turn conversations while building long-term memory:
```python
from langchain_core.messages import BaseMessage
class ConversationalAgent:
def __init__(self, user_id: str):
self.user_id = user_id
self.llm = ChatOpenAI(model="gpt-4o")
self.memory = Supermemory()
self.messages: list[BaseMessage] = []
def _build_system_prompt(self, query: str) -> str:
"""Build system prompt with user context."""
profile = self.memory.profile(self.user_id).profile
memories = self.memory.search(self.user_id, query=query, threshold=0.5).results
return f"""You are a helpful assistant with memory of past conversations.
About this user:
{chr(10).join(m.memory for m in profile.static) if profile.static else 'No profile yet.'}
Current context:
{chr(10).join(m.memory for m in profile.dynamic) if profile.dynamic else 'No recent context.'}
Relevant memories:
{chr(10).join(m.memory or m.chunk for m in memories[:5]) if memories else 'None.'}
Use this context to provide personalized, contextual responses."""
def chat(self, message: str) -> str:
"""Process a message and return response."""
# Add user message to conversation
self.messages.append(HumanMessage(content=message))
# Build prompt with memory context
system = SystemMessage(content=self._build_system_prompt(message))
# Generate response
response = self.llm.invoke([system] + self.messages)
self.messages.append(response)
# Store interaction for long-term memory
self.memory.add(
self.user_id,
content=f"User: {message}\nAssistant: {response.content}",
dreaming="instant"
)
return response.content
def clear_session(self):
"""Clear conversation but keep long-term memory."""
self.messages = []
```
### Metadata filtering
Use metadata to organize and filter memories:
```python
# Store with metadata
memory.add(
"user_123",
content="Discussed React hooks and state management",
dreaming="instant",
metadata={
"topic": "react",
"type": "discussion",
"project": "frontend-redesign"
}
)
# Search with filters
results = memory.search(
"user_123",
query="state management",
filter={
"operator": "and",
"operands": [
{"field": "topic", "operator": "eq", "value": "react"},
{"field": "project", "operator": "eq", "value": "frontend-redesign"}
]
}
)
```
### Batch memory operations
Efficiently store multiple memories:
```python
# Store meeting notes as separate memories
notes = [
"Decided to use PostgreSQL for the new service",
"Timeline: MVP ready by end of Q2",
"Alice will lead the database migration"
]
for note in notes:
memory.add(
"team_standup",
content=note,
metadata={"date": "2024-01-15", "type": "decision"},
dreaming="instant"
)
```
---
## Next steps
Deep dive into automatic user profiling
Advanced search patterns and filtering
Native OpenAI integration with memory tools
Memory middleware for Next.js apps