---
title: "LangChain"
sidebarTitle: "LangChain"
description: "Build AI agents with persistent memory using LangChain and Supermemory"
icon: "link"
---

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

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## 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 for context
    profile_result = memory.profile(container_tag=user_id, q=message)

    # 2. Build context from profile
    static_facts = profile_result.profile.static or []
    dynamic_context = profile_result.profile.dynamic or []
    search_results = profile_result.search_results.results if profile_result.search_results else []

    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(
        content=f"User: {message}\nAssistant: {response.content}",
        container_tag=user_id
    )

    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 profile with optional search
result = memory.profile(
    container_tag="user_123",
    q="optional search query"  # Also returns relevant memories
)

print(result.profile.static)   # ["User is a Python developer", "Prefers dark mode"]
print(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(
    content="User asked about async Python patterns. Explained asyncio basics.",
    container_tag="user_123",
    metadata={"topic": "python", "type": "conversation"}
)

# Store a document
memory.add(
    content="https://docs.python.org/3/library/asyncio.html",
    container_tag="user_123"
)
```

### Memory Search

Search returns both extracted memories and document chunks:

```python
results = memory.search.memories(
    q="async programming",
    container_tag="user_123",
    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 with search for similar code patterns
        result = self.memory.profile(
            container_tag=user_id,
            q=code[:500],  # Use code snippet for semantic search
            threshold=0.6
        )

        static = result.profile.static or []
        dynamic = result.profile.dynamic or []
        memories = result.search_results.results if result.search_results else []

        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}" 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(
            content=f"Code review feedback: {response.content[:500]}",
            container_tag=user_id,
            metadata={"type": "code_review", "language": language}
        )

        return response.content

    def learn_preference(self, user_id: str, preference: str):
        """Store a coding preference or style guideline."""
        self.memory.add(
            content=f"Developer preference: {preference}",
            container_tag=user_id,
            metadata={"type": "preference"}
        )


# 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."""
        result = self.memory.profile(
            container_tag=self.user_id,
            q=query,
            threshold=0.5
        )

        profile = result.profile
        memories = result.search_results.results if result.search_results else []

        return f"""You are a helpful assistant with memory of past conversations.

About this user:
{chr(10).join(profile.static) if profile.static else 'No profile yet.'}

Current context:
{chr(10).join(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(
            content=f"User: {message}\nAssistant: {response.content}",
            container_tag=self.user_id
        )

        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(
    content="Discussed React hooks and state management",
    container_tag="user_123",
    metadata={
        "topic": "react",
        "type": "discussion",
        "project": "frontend-redesign"
    }
)

# Search with filters
results = memory.search.memories(
    q="state management",
    container_tag="user_123",
    filters={
        "AND": [
            {"key": "topic", "value": "react"},
            {"key": "project", "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(
        content=note,
        container_tag="team_standup",
        metadata={"date": "2024-01-15", "type": "decision"}
    )
```

---

## Next Steps

<CardGroup cols={2}>
  <Card title="User Profiles" icon="user" href="/recall/user-profiles">
    Deep dive into automatic user profiling
  </Card>
  <Card title="Search API" icon="search" href="/recall/search">
    Advanced search patterns and filtering
  </Card>
  <Card title="OpenAI SDK" icon="robot" href="/integrations/openai">
    Native OpenAI integration with memory tools
  </Card>
  <Card title="Vercel AI SDK" icon="triangle" href="/integrations/ai-sdk">
    Memory middleware for Next.js apps
  </Card>
</CardGroup>
