--- 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