lesson-25

19 MIN READ | UPDATED: 2026-05-07

Hello, future AI architects! It's your old friend, the AI Technical Mentor. Welcome to Episode 25 of the LangGraph Multi-Agent Masterclass.

We have already built quite a powerful "Universal AI Content Creation Agency." The Planner strategizes, the Researcher gathers extensive knowledge, the Writer crafts brilliant prose, and the Editor stands as the final line of defense for our content. But have you ever run into this situation: for the same client, you have to re-explain their brand tone, preferred terminology, or even their unique definition of "humor" every single time? It's like going to the barbershop and having to explain your preferred haircut from scratch every visit—highly inefficient and a terrible user experience.

Today, we are going to solve this exact pain point—equipping our AI Editor Agent with "long-term memory" so it can remember users' historical preferences and truly "understand" them. This will be the critical step that takes your AI Agency from merely "capable of working" to "highly perceptive and proactive."

🎯 Learning Objectives for This Episode

In this episode, you won't just learn the technology; you will understand how to use it to solve real business problems. Specifically, you will gain:

  1. Understand the Core Value of Long-Term Memory: Why short-term memory (Graph State) in multi-agent systems is insufficient for complex user interactions, and how long-term memory enhances user experience and system efficiency.
  2. Master the Paradigm of Building Long-Term Memory in LangGraph: Learn how to cleverly integrate a VectorStore (vector database) to store unstructured user preferences, and utilize LangGraph's Checkpointers to manage conversational states and memory pointers.
  3. Hands-on Practice with Personalized Memory for the AI Editor Agent: Build a workflow for our Editor Agent that "remembers" a user's specific tone, style, and historical feedback, turning it into a truly thoughtful "personal editor."
  4. Identify and Avoid Common Pitfalls of Long-Term Memory: Dive deep into advanced issues like memory bloat, memory conflicts, and retrieval accuracy, providing practical solutions.

Ready? Let's install a "memory chip" into our AI!

📖 Principle Analysis

In previous lessons, we primarily relied on LangGraph's Graph State to maintain short-term dialogue and task context between agents. This is akin to human "working memory," processing the task currently at hand. However, once a session ends, or when we need to remember personalized user needs across multiple sessions, short-term memory falls short.

Imagine our AI Editor Agent. One client might prefer a humorous and witty tone, while another favors a rigorous and professional style. If the Editor Agent has to "re-learn" these preferences at the start of every editing task, it will never become a truly efficient and personalized service provider. This is why we need to introduce "long-term memory."

Long-term memory, as the name suggests, is information that can be persistently stored, retrieved, and utilized when needed. In the context of LangGraph, we typically implement it through a combination of two mechanisms:

  1. VectorStore (Vector Database): This is a database that stores unstructured data (like text, images, audio) and enables semantic retrieval. We embed users' personalized preferences, brand style guides, historical feedback, etc., into vectors as text, and store them in the VectorStore. When needed, we can perform a semantic similarity search based on the current task or user ID, retrieve the most relevant preference information, and inject it as context into the LLM's prompt. This solves the problems of "what to remember" and "how to retrieve it."

  2. Checkpointers: LangGraph's built-in Checkpointer mechanism allows us to persist the current state of the Graph into a database (like SQLite). It is primarily used to resume interrupted sessions or maintain state continuity across multiple iterations. In this episode, we will use the Checkpointer to save each user's session ID (thread_id), along with indices or summaries that might point to specific memory entries in the VectorStore. This solves the problems of "where the memory is" and "how to associate it."

The core idea is: The Checkpointer is responsible for remembering "whose session this is, and where they left off last time," while the VectorStore is responsible for storing "all the long-term preferences and historical experiences of this user." When an editing task arrives, the Graph first identifies the user via the Checkpointer, then uses this user ID to retrieve their exclusive long-term preferences from the VectorStore, and finally uses these preferences as additional context to guide the Editor Agent in personalized editing. If the user is unsatisfied with the result and provides feedback, this feedback can be added to the VectorStore in a structured or unstructured way, thereby "teaching" the AI Editor Agent to do better next time.

The Long-Term Memory Workflow of the AI Editor Agent:

  1. Task Reception: The AI Content Agency receives a new content editing request, accompanied by a user_id and content_to_edit.
  2. Memory Retrieval (Retrieve Preferences):
    • The Graph first identifies the user_id corresponding to the current thread_id via the Checkpointer.
    • Using the user_id as a query condition, it retrieves all historical preferences, style guides, and previous feedback for that user from the VectorStore.
    • It consolidates the retrieved preference information into a clear set of instructions.
  3. Personalized Editing (Editor Agent):
    • It injects the original content and the retrieved personalized preferences into the Editor Agent's LLM prompt.
    • The LLM polishes and optimizes the content based on these instructions.
  4. User Feedback (Process Feedback):
    • If the user provides feedback on the edited result (e.g., "Too formal, make it livelier!").
    • The Graph receives the feedback and processes it (this could be summarized by another LLM or stored directly).
    • It adds the processed feedback as a new memory entry into the VectorStore, associated with the user_id.
  5. Memory Update: The Checkpointer automatically saves the latest state of the Graph, including any potentially updated memory pointers or summaries.

Through this cycle, our AI Editor Agent will no longer be a "disposable" tool, but a "personal assistant" capable of continuously learning and adapting with every interaction.

Core Architecture Mermaid Diagram

Let's intuitively understand this process through a Mermaid diagram:

graph TD
    A[User Submits Edit Request] --> B{Identify User/Session ID}
    B -- thread_id --> C(LangGraph Checkpointer)
    C -- user_id --> D[Retrieve User Long-Term Preferences]
    D -- Query --> E(VectorStore: Preferences/Feedback)
    E -- Retrieval Results --> D
    D -- Inject Context --> F[Editor Agent (LLM)]
    F --> G[Generate Edited Content]
    G --> H{User Review/Feedback?}
    H -- Yes --> I[Process User Feedback]
    I -- Update Memory --> E
    I --> G'[[Edit Complete/Re-edit]]'
    H -- No --> J[Complete Edit, Deliver]

    subgraph LangGraph Workflow
        B --- F
        F --- I
    end

    style C fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#ccf,stroke:#333,stroke-width:2px
    style D fill:#afa,stroke:#333,stroke-width:2px
    style I fill:#afa,stroke:#333,stroke-width:2px

Diagram Legend:

  • User Submits Edit Request (A): The starting point of the workflow, containing the content and user identifier.
  • Identify User/Session ID (B): LangGraph's internal mechanism, identifying the current session via configurable: thread_id.
  • LangGraph Checkpointer (C): Persists the session state, saving the user_id or its associated information to ensure session continuity.
  • Retrieve User Long-Term Preferences (D): This is a LangGraph node responsible for fetching relevant memories from the VectorStore based on the user_id.
  • VectorStore: Preferences/Feedback (E): Our long-term memory bank, storing vector representations of all users' personalized preferences, style guides, and historical feedback.
  • Editor Agent (LLM) (F): Our core editing agent, which receives the original content and retrieved personalized preferences to perform the edit.
  • Generate Edited Content (G): The output of the Editor Agent.
  • User Review/Feedback? (H): Simulates the user's judgment of the edited result.
  • Process User Feedback (I): Another LangGraph node responsible for receiving user feedback, converting it into storable memory, and updating the VectorStore.
  • Complete Edit, Deliver (J): The endpoint of the workflow.

This architecture clearly demonstrates how the Checkpointer and VectorStore work together to provide our agents with powerful long-term memory capabilities.

💻 Hands-on Code Practice (Specific Application in the Agency Project)

Alright, enough theory—let's roll up our sleeves and get to work. Now, we will translate the above principles into code, injecting long-term memory into the Editor Agent within our AI Content Agency. We will use Python and some popular LangChain/LangGraph components.

1. Environment Setup and Dependency Installation

First, ensure your environment has the necessary libraries installed:

pip install -U langchain-openai langgraph langchain_community chromadb tiktoken

2. Code Implementation

import os
import sqlite3
from typing import TypedDict, Annotated, List, Literal
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter # Used for processing large texts
from langgraph.graph import StateGraph, END
from langgraph.checkpoint import MemorySaver, SqliteSaver # Used for persisting state

# Set your OpenAI API key
# Recommended to set via environment variables, e.g.: export OPENAI_API_KEY="sk-..."
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY" # Please replace with your actual key or set via environment variable

# Ensure API key is set
if not os.getenv("OPENAI_API_KEY"):
    raise ValueError("Please set the OPENAI_API_KEY environment variable")

print("--- Environment setup complete, API key loaded ---")

### 1. Define Graph State ###
# TypedDict is used to define the state schema for LangGraph, ensuring type safety and clarity
class AgentState(TypedDict):
    """
    Define the shared state in our multi-agent workflow.
    This state will be passed between all nodes.
    """
    content: str # 待编辑的原始内容 (Original content to be edited)
    user_id: str # 用户的唯一标识符,用于检索个性化偏好 (Unique user ID for retrieving preferences)
    preferences: str # 从 VectorStore 检索到的用户偏好 (User preferences retrieved from VectorStore)
    edited_content: str # AI 编辑后的内容 (Content after AI editing)
    feedback: str # 用户对编辑内容的反馈 (User feedback on the edited content)
    # You can add a history message list for short-term dialogue context, but this episode focuses on long-term memory
    # chat_history: List[BaseMessage]

print("--- AgentState definition complete ---")

### 2. Initialize VectorStore (Long-Term Memory Bank) ###
# We use ChromaDB as the vector database to store user preferences and historical feedback.
# It supports local persistence, making it convenient for demonstration.

# Initialize OpenAI embeddings model to convert text into vectors
embeddings = OpenAIEmbeddings()

# Initialize ChromaDB. We persist data to the local file system to maintain memory across sessions.
# persist_directory is the path to store the database files
VECTOR_DB_PATH = "./chroma_db_editor_memory"
vectorstore = Chroma(embedding_function=embeddings, persist_directory=VECTOR_DB_PATH)

# Simulate some initial user preference documents
# In a real scenario, this data might come from the client's brand guidelines, style manuals, or initial communications
user_preferences_data = [
    {"user_id": "client_A", "preference": "口吻:活泼、幽默、简洁。避免冗长和过于正式的表达。", "doc_id": "pref_A_1"},
    {"user_id": "client_A", "preference": "关键词:创新、前沿技术、未来趋势。多用比喻和类比。", "doc_id": "pref_A_2"},
    {"user_id": "client_B", "preference": "口吻:专业、严谨、细节丰富。强调数据支持和逻辑推理。", "doc_id": "pref_B_1"},
    {"user_id": "client_B", "preference": "关键词:市场分析、投资回报、风险管理、合规性。避免主观判断。", "doc_id": "pref_B_2"},
    {"user_id": "client_C", "preference": "口吻:平易近人、通俗易懂。避免行业术语和复杂句式。目标读者是普通大众。", "doc_id": "pref_C_1"},
    {"user_id": "client_C", "preference": "关键词:健康生活、美食、旅行、亲子教育。内容要积极向上。", "doc_id": "pref_C_2"},
]

# Check if VectorStore is already populated to avoid duplicate additions
# This is a simple check; a more rigorous approach would be to check if doc_id exists
existing_docs = vectorstore.get()
if not existing_docs["ids"]: # If the database is empty, populate it
    for data in user_preferences_data:
        vectorstore.add_texts(
            texts=[data["preference"]],
            metadatas=[{"user_id": data["user_id"], "doc_id": data["doc_id"]}]
        )
    print(f"--- VectorStore initialized and populated with {len(user_preferences_data)} initial user preferences. ---")
else:
    print(f"--- VectorStore already contains {len(existing_docs['ids'])} records, skipping initial population. ---")


### 3. Define Agent (LLM) and Graph Nodes ###

# Initialize ChatOpenAI model as our editing brain
llm = ChatOpenAI(model="gpt-4o", temperature=0.5) # Set temperature lower for more stable editing

# --- Graph Node 1: Retrieve User Preferences ---
def retrieve_preferences(state: AgentState):
    """
    Retrieve the user's long-term preferences and historical feedback from the VectorStore based on user_id.
    """
    user_id = state["user_id"]
    if not user_id:
        print("Warning: user_id not provided, using generic editing mode.")
        return {"preferences": "无特定用户偏好。请以通用、专业且清晰的风格编辑。"}

    # Construct a query to attempt retrieving preferences related to this user
    # More complex query logic can be added here, such as retrieving based on the current content type
    query = f"用户 {user_id} 的内容创作偏好、风格要求以及历史反馈。"
    
    # Execute similarity search and filter documents matching the current user_id
    # LangChain's Chroma defaults do not support filtering metadata directly in similarity_search
    # So we retrieve first, then filter in code, or use Chroma's advanced query interface (if needed)
    # For simplicity in this demo, we assume the retrieval results contain enough relevant information and will be refined by the LLM
    
    # In actual production, a better approach is to use Chroma's `similarity_search_with_score` or `similarity_search_by_vector`
    # combined with the `where` parameter for filtering, but here for demonstration, we retrieve first and filter manually
    
    # Retrieve all relevant documents, then filter manually
    results = vectorstore.similarity_search_with_score(query, k=5) # Retrieve the top 5 most relevant
    
    # Filter out preferences that truly belong to the current user_id
    filtered_preferences = []
    for doc, score in results:
        if doc.metadata.get("user_id") == user_id:
            filtered_preferences.append(doc.page_content)
            # print(f"  - Matched preference (Score: {score:.2f}): {doc.page_content[:50]}...")
    
    preferences_str = "\n".join(filtered_preferences)
    if not preferences_str:
        preferences_str = "无特定用户偏好。请以通用、专业且清晰的风格编辑。"
        print(f"--- No specific preferences retrieved for user {user_id}, using generic mode. ---")
    else:
        print(f"--- Successfully retrieved preferences for user {user_id}:\n{preferences_str}\n---")
    
    return {"preferences": preferences_str}

# --- Graph Node 2: AI Editor ---
def editor_agent_node(state: AgentState):
    """
    AI Editor node, edits based on the original content and retrieved user preferences.
    """
    content = state["content"]
    preferences = state["preferences"]
    user_id = state["user_id"]

    prompt = f"""
    你是一位经验丰富、洞察力敏锐的资深内容编辑。
    你的任务是为用户 {user_id} 润色和优化以下内容。
    
    请务必严格遵循以下用户个性化偏好和风格要求进行编辑。
    这些偏好是用户长期积累的,代表了他们的品牌声音和期望。
    
    --- 用户偏好和风格要求 ---
    {preferences}
    ---
    
    原始