Large Language Models (LLMs) are often perceived as having human-like memory because they can hold coherent conversations. However, technically, LLMs are stateless mathematical functions. This blog explores how developers bridge the gap between this inherent statelessness and the complex memory systems required for production-grade AI agents using frameworks like LangGraph.
1. The Core Paradox: Why LLMs are Stateless #
At its fundamental level, an LLM at inference is a parameterized mathematical function: y=f(θ,x).
- θ: Billions of fixed parameters (the model’s “brain” trained on the internet).
- x: The input tokens (your prompt).
- y: The output tokens.
Because θ is fixed after training and the function only looks at the current x, it has no “internal” memory of past interactions. If you tell an LLM your name in one call and ask for it in the next, it will fail unless that context is provided again
2. Short-Term Memory (STM): The Conversation Buffer #
To make an LLM “remember,” we create a Conversation Buffer. Instead of sending just the new message (x2), we concatenate the entire history (x1,y1,x2) and send it as the new input.
LangGraph Implementation: Checkpointers and Threads
In LangGraph, STM is managed through Checkpointers and Thread IDs.
- Checkpointer: Saves the state of the graph at every “superstep”.
- Thread ID: A unique identifier for a specific conversation session. Memory is typically thread-scoped, meaning it exists only within that specific conversation boundary.
Diagram: STM Workflow

Code Example (In-Memory STM):
# ==========================================================
# Example 1: LangGraph WITHOUT Memory
# ==========================================================
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, MessagesState
# Load OpenAI API key from .env file
load_dotenv()
# Create the LLM
model = ChatOpenAI()
# ----------------------------------------------------------
# Node Function
# ----------------------------------------------------------
# This node receives the current state (messages),
# sends it to the LLM, and returns the AI response.
def call_model(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": [response]}
# ----------------------------------------------------------
# Build the Graph
# ----------------------------------------------------------
builder = StateGraph(MessagesState)
# Add one node
builder.add_node("call_model", call_model)
# Connect START → call_model
builder.add_edge(START, "call_model")
# Compile graph
graph = builder.compile()
# ----------------------------------------------------------
# First Conversation
# ----------------------------------------------------------
graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Hi! My name is Sanjit"
}
]
}
)
"""
Output
Human : Hi! My name is Sanjit
AI : Nice to meet you, Sanjit!
"""
# ----------------------------------------------------------
# Second Conversation
# ----------------------------------------------------------
graph.invoke(
{
"messages": [
{
"role": "user",
"content": "What is my name?"
}
]
}
)
"""
Output
Human : What is my name?
AI : I don't know your name.
Reason:
Every invoke() starts a NEW conversation.
The previous message ("My name is Sanjit")
was NOT stored anywhere.
"""
# ==========================================================
# Example 2: LangGraph WITH Memory
# ==========================================================
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.checkpoint.memory import InMemorySaver
# Load environment variables
load_dotenv()
# Create LLM
model = ChatOpenAI()
# ----------------------------------------------------------
# Node Function
# ----------------------------------------------------------
def call_model(state: MessagesState):
"""
Receives all conversation messages,
sends them to the LLM,
returns the AI response.
"""
response = model.invoke(state["messages"])
return {"messages": [response]}
# ----------------------------------------------------------
# Build Graph
# ----------------------------------------------------------
builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_edge(START, "call_model")
# ----------------------------------------------------------
# Create Checkpointer
# ----------------------------------------------------------
# InMemorySaver stores conversation history
# in RAM using a thread_id.
checkpointer = InMemorySaver()
# Compile graph with memory
graph = builder.compile(checkpointer=checkpointer)
# ----------------------------------------------------------
# Thread Configurations
# ----------------------------------------------------------
config1 = {
"configurable": {
"thread_id": "thread-1"
}
}
config2 = {
"configurable": {
"thread_id": "thread-2"
}
}
# ----------------------------------------------------------
# First Message (Thread 1)
# ----------------------------------------------------------
graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Hi! My name is Sanjit."
}
]
},
config=config1,
)
"""
Output
Human : Hi! My name is Sanjit.
AI : Nice to meet you Sanjit.
"""
# ----------------------------------------------------------
# Ask Again in SAME Thread
# ----------------------------------------------------------
graph.invoke(
{
"messages": [
{
"role": "user",
"content": "What is my name?"
}
]
},
config=config1,
)
"""
Output
Human : What is my name?
AI : Your name is Sanjit.
"""
# ----------------------------------------------------------
# Ask in a DIFFERENT Thread
# ----------------------------------------------------------
graph.invoke(
{
"messages": [
{
"role": "user",
"content": "What is my name?"
}
]
},
config=config2,
)
"""
Output
Human : What is my name?
AI : I don't know your name.
"""
3. Solving the Context Window Crisis #
LLMs have a Context Window—a limit on the number of tokens they can process at once. As conversations grow, the buffer can exceed this limit, leading to hallucinations or errors.
Technique A: Trimming
We set a max_token_limit and keep only the most recent messages that fit within it, discarding the oldest ones.
- Pros: Simple to implement using functions like
trim_messages. - Cons: The LLM completely loses the context of early parts of the conversation.
# ==========================================================
# LangGraph Memory Trimming Example
# ==========================================================
# This example demonstrates how to limit the conversation
# history sent to the LLM using trim_messages().
#
# Even though the complete conversation is stored in memory,
# only the most recent messages that fit within MAX_TOKENS
# are sent to the model.
# ==========================================================
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.messages.utils import (
trim_messages,
count_tokens_approximately,
)
# ----------------------------------------------------------
# Load OpenAI API Key
# ----------------------------------------------------------
load_dotenv()
# Create LLM
model = ChatOpenAI()
# ----------------------------------------------------------
# Maximum Tokens Allowed
# ----------------------------------------------------------
# Only messages that fit within this budget
# will be sent to the LLM.
MAX_TOKENS = 150
# ----------------------------------------------------------
# Graph Node
# ----------------------------------------------------------
def call_model(state: MessagesState):
# Trim the conversation.
# Strategy = "last"
# Keep the latest messages until MAX_TOKENS is reached.
messages = trim_messages(
state["messages"],
strategy="last",
token_counter=count_tokens_approximately,
max_tokens=MAX_TOKENS,
)
# Print token count after trimming
print("\nCurrent Token Count ->",
count_tokens_approximately(messages))
# Print messages actually sent to LLM
print("\nMessages Sent To LLM\n")
for message in messages:
print(message.content)
print("-" * 60)
# Send trimmed conversation
response = model.invoke(messages)
return {"messages": [response]}
# ----------------------------------------------------------
# Build Graph
# ----------------------------------------------------------
builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_edge(START, "call_model")
# ----------------------------------------------------------
# Add Memory
# ----------------------------------------------------------
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
# ----------------------------------------------------------
# Thread Configuration
# ----------------------------------------------------------
config = {
"configurable": {
"thread_id": "chat-1"
}
}
# ==========================================================
# Conversation 1
# ==========================================================
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Hi, my name is Sanjit."
}
]
},
config,
)
print(result["messages"][-1].content)
"""
Output
Current Token Count -> 10
Messages Sent To LLM
Hi, my name is Sanjit.
------------------------------------------------------------
AI:
Hello Sanjit! Nice to meet you.
"""
# ==========================================================
# Conversation 2
# ==========================================================
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "I am learning LangGraph."
}
]
},
config,
)
print(result["messages"][-1].content)
"""
Output
Current Token Count -> 40
Messages Sent To LLM
Hi, my name is Sanjit.
AI:
Hello Sanjit!
I am learning LangGraph.
------------------------------------------------------------
AI:
That's great!
"""
# ==========================================================
# Conversation 3
# ==========================================================
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Can you explain short-term memory?"
}
]
},
config,
)
print(result["messages"][-1].content)
"""
Output
Current Token Count -> 108
Messages Sent To LLM
Hi, my name is Sanjit.
AI:
Hello Sanjit!
I am learning LangGraph.
AI:
That's great!
Can you explain short-term memory?
------------------------------------------------------------
AI:
Short-term memory is ...
"""
# ==========================================================
# Conversation 4
# ==========================================================
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "What is my name?"
}
]
},
config,
)
print(result["messages"][-1].content)
"""
Output
Current Token Count -> 8
Messages Sent To LLM
What is my name?
------------------------------------------------------------
AI:
I'm sorry, I don't know your name.
"""
# ==========================================================
# View Complete Stored Memory
# ==========================================================
snapshot = graph.get_state(config)
print("\nComplete Conversation Stored In Memory\n")
for message in snapshot.values["messages"]:
print(message.content)
print("-" * 80)
Technique B: Summarization & Deletion
Instead of deleting old messages, we send them to another LLM to generate a Summary. We then pass this summary plus the most recent messages to the main LLM. This preserves the “essence” of the past without bloating the token count.
# ==========================================================
# LangGraph Conversation Summarization Memory Example
# ==========================================================
#
# This example demonstrates how LangGraph automatically
# summarizes long conversations.
#
# Instead of sending the entire chat history to the LLM,
# it:
#
# 1. Stores all messages.
# 2. When conversation becomes long (>6 messages),
# creates a summary.
# 3. Deletes old messages.
# 4. Keeps only the latest two messages.
# 5. Uses the summary in future conversations.
#
# ==========================================================
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, RemoveMessage
from langgraph.graph import MessagesState, StateGraph, START
from langgraph.checkpoint.memory import InMemorySaver
# ----------------------------------------------------------
# Load OpenAI API Key
# ----------------------------------------------------------
load_dotenv()
# Create LLM
model = ChatOpenAI()
# ==========================================================
# Custom State
# ==========================================================
#
# MessagesState already contains:
#
# messages
#
# We extend it by adding
#
# summary
#
# Final State
#
# {
# "messages": [...],
# "summary": "..."
# }
#
# ==========================================================
class ChatState(MessagesState):
summary: str
# ==========================================================
# Summarization Node
# ==========================================================
#
# This node runs only when the conversation
# becomes longer than 6 messages.
#
# It creates/updates the summary and
# removes old messages.
#
# ==========================================================
def summarize_conversation(state: ChatState):
# Previous summary
existing_summary = state["summary"]
# -------------------------------------
# Build Prompt
# -------------------------------------
if existing_summary:
prompt = (
f"Existing summary:\n{existing_summary}\n\n"
"Extend the summary using the new conversation above."
)
else:
prompt = "Summarize the conversation above."
# Conversation + Summarization Prompt
messages_for_summary = state["messages"] + [
HumanMessage(content=prompt)
]
# Ask LLM to summarize
response = model.invoke(messages_for_summary)
# -------------------------------------
# Delete old messages
# Keep last 2 messages
# -------------------------------------
messages_to_delete = state["messages"][:-2]
return {
# Store summary
"summary": response.content,
# Remove old conversation
"messages": [
RemoveMessage(id=m.id)
for m in messages_to_delete
],
}
# ==========================================================
# Chat Node
# ==========================================================
def chat_node(state: ChatState):
messages = []
# If summary exists,
# inject it as System Message
if state["summary"]:
messages.append({
"role": "system",
"content":
f"Conversation Summary:\n{state['summary']}"
})
# Add latest conversation
messages.extend(state["messages"])
print("\nMessages Sent To LLM\n")
for message in messages:
print(message)
print("-" * 80)
# Call LLM
response = model.invoke(messages)
return {
"messages": [response]
}
# ==========================================================
# Decide whether summarization is required
# ==========================================================
def should_summarize(state: ChatState):
return len(state["messages"]) > 6
# ==========================================================
# Build Graph
# ==========================================================
builder = StateGraph(ChatState)
builder.add_node("chat", chat_node)
builder.add_node(
"summarize",
summarize_conversation
)
builder.add_edge(
START,
"chat"
)
builder.add_conditional_edges(
"chat",
should_summarize,
{
True: "summarize",
False: "__end__"
}
)
builder.add_edge(
"summarize",
"__end__"
)
# ==========================================================
# Memory
# ==========================================================
checkpointer = InMemorySaver()
graph = builder.compile(
checkpointer=checkpointer
)
# ==========================================================
# Thread
# ==========================================================
config = {
"configurable": {
"thread_id": "t1"
}
}
# ==========================================================
# Helper Function
# ==========================================================
def run_turn(text):
return graph.invoke(
{
"messages": [
HumanMessage(content=text)
],
"summary": ""
},
config=config
)
# ==========================================================
# Show Current Memory
# ==========================================================
def show_state():
snapshot = graph.get_state(config)
values = snapshot.values
print("\n========== CURRENT STATE ==========")
print("\nSummary\n")
print(values.get("summary", ""))
print("\nStored Messages:", len(values["messages"]))
print("\nConversation\n")
for message in values["messages"]:
print(type(message).__name__)
print(message.content)
print("-" * 80)
# ==========================================================
# Conversation 1
# ==========================================================
run_turn("Quantum Physics")
show_state()
"""
OUTPUT
Summary
(empty)
Stored Messages
2
Human:
Quantum Physics
AI:
Quantum Physics is ...
"""
# ==========================================================
# Conversation 2
# ==========================================================
run_turn("How is Albert Einstein related?")
show_state()
"""
OUTPUT
Summary
(empty)
Stored Messages
4
Human:
Quantum Physics
AI:
...
Human:
How is Albert Einstein related?
AI:
...
"""
# ==========================================================
# Conversation 3
# ==========================================================
run_turn("What are some of Einstein's famous works?")
show_state()
"""
OUTPUT
Summary
(empty)
Stored Messages
6
No summarization yet.
"""
# ==========================================================
# Conversation 4
# ==========================================================
run_turn("Explain Special Theory of Relativity")
show_state()
"""
OUTPUT
Conversation becomes
8 Messages
↓
Condition
8 > 6
↓
Summarize Node Executes
↓
Summary Generated
↓
Old Messages Deleted
↓
Final State
Summary
The conversation discussed:
• Quantum Physics
• Albert Einstein
• Photoelectric Effect
• Special Relativity
• General Relativity
• E = mc²
• Brownian Motion
• Quantum Mechanics
Stored Messages
2
Human:
Explain Special Theory of Relativity
AI:
Special Theory of Relativity is...
"""
Summarization Logic

4. Long-Term Memory #
In the world of AI agents, memory is what transforms a generic chatbot into a personalized assistant. While short-term memory handles the immediate conversation flow, long-term memory (LTM) allows an agent to remember user preferences, names, and past projects across different sessions and threads.
This blog post explores how to implement LTM in LangGraph, moving from basic concepts to production-grade persistent storage.
1. What is Long-Term Memory? #
Unlike short-term memory which exists within a single thread, LTM is persistent. It acts as a “Memory Store” (like a database) where the agent extracts useful information from conversations and stores it for future use. For example, if a user mentions they prefer Python, the agent can store this in LTM and always provide code examples in Python, even in a completely new chat thread.
2. The Store Architecture #
LangGraph implements memory through a class hierarchy based on an abstract class called BaseStore. This class defines standard activities like creating, searching, editing, and deleting memories.
- InMemoryStore: Stores data in RAM. It is excellent for quick prototyping but loses all data when the program stops.
- PostgresStore / RedisStore: These are production-grade implementations that store memories in a persistent database.
3. Name Spaces: The Organization Logic #
To keep memories organized, LangGraph uses Name Spaces, which function like folders in a directory. A common structure is ('users', 'user_id', 'sub_folder'). This ensures that User A’s preferences never leak into User B’s sessions.
Step 1: Basic Memory Operations #
You can interact with a store using three primary methods: put (create/update), get (retrieve by key), and search (retrieve multiple).
from langgraph.store.memory import InMemoryStore
# Initialize the store
store = InMemoryStore()
# Define a namespace for a specific user
namespace = ("users", "u1")
# 1. Put (Create) memory
store.put(namespace, "pref_1", {"data": "User likes Python"})
store.put(namespace, "pref_2", {"data": "User prefers dark mode"})
# 2. Get (Retrieve specific)
memory = store.get(namespace, "pref_1")
print(memory.value) # Output: User likes Python
# 3. Search (Retrieve all in namespace)
items = store.search(namespace)
for item in items:
print(item.value)
Step 2: Advanced Semantic Search #
In production, you might have hundreds of memories. Instead of loading them all, you can use Semantic Search to fetch only the memories relevant to the current query. This is done by passing an embedding model to the store.
# Initialize store with an embedding model for semantic search
store = InMemoryStore(
index={
"embed": embedding_model, # e.g., OpenAI text-embedding-3-small
"dims": 1536
}
)
# Search based on meaning, not just keys
results = store.search(namespace, query="What are the user's coding preferences?", limit=1)
Step 3: Implementing the “Remember” and “Chat” Workflow #
A complete LTM-enabled agent typically follows a two-node workflow:
- Remember Node: Analyzes the user’s message to see if there is anything worth saving to the long-term store.
- Chat Node: Fetches existing memories from the store and injects them into the system prompt to personalize the response.
Extracting Memories with Pydantic
To ensure the LLM extracts memories accurately, we use a Pydantic model to structure the output.
from pydantic import BaseModel, Field
from typing import List
class MemoryItem(BaseModel):
text: str
is_new: bool # Used for de-duplication
class MemoryDecision(BaseModel):
should_write: bool
memories: List[MemoryItem]
The “Remember” Node Logic
This node uses an “Extractor LLM” to decide what to save. To avoid cluttering the store, a de-duplication strategy is used: the LLM compares the new message against existing memories and only marks truly new information for storage.
def remember_node(state, config, store):
user_id = config['configurable']['user_id']
namespace = ("users", user_id, "details")
# Get last message
last_msg = state['messages'][-1].content
# LLM extracts new memories (using structured output)
decision = extractor_llm.with_structured_output(MemoryDecision).invoke(...)
if decision.should_write:
for item in decision.memories:
if item.is_new:
store.put(namespace, str(uuid.uuid4()), {"data": item.text})
return {"messages": [AIMessage(content="Memory updated.")]}
Step 4: Moving to Production with PostgreSQL #
For a production system, you must switch from InMemoryStore to a persistent database like PostgreSQL. This ensures memories survive server restarts.
- Set up Postgres: Use Docker to run a PostgreSQL container.
- Configure the Store: Connect LangGraph to the database using a connection string.
from langgraph.store.postgres import PostgresStore
# Database URL for a local Docker setup
DB_URL = "postgresql://postgres:postgres@localhost:5432/langgraph"
# Using a context manager to handle the connection
with PostgresStore.from_conn_string(DB_URL) as store:
# Setup the tables (only needed once)
store.setup()
# Compile graph with the persistent store
app = graph.compile(store=store)
Conclusion #
By implementing Long-Term Memory, you allow your LangGraph agents to build a “relationship” with the user over time. Using a combination of Semantic Search for relevance and PostgreSQL for persistence, you can build production-ready systems that offer deeply personalized experiences.
LLM Memory Architecture Quiz #
Q.1 In the mathematical representation of an LLM at inference, y=f(θ,x), what does the component θ represent?
The fixed weights or parameters learned during training.
The temporary memory buffer of the current conversation.
The input tokens provided by the user.
The output tokens generated by the model.
Explanation
θ (theta) represents the model’s learned parameters (weights and biases). These parameters are fixed during inference and contain the knowledge acquired during training.
Q.2 What is the primary reason why LLMs are described as stateless systems?
They can only process one token at a time.
The model's parameters are updated after every user interaction.
Each invocation of the model is independent and lacks intrinsic memory of past interactions.
They do not have a context window for processing text.
Explanation
LLMs are stateless because each invocation is independent. Without external memory, they do not remember previous conversations or user interactions.
Q.3 How does Short-Term Memory (STM) typically function in a conversation buffer setup?
By permanently updating the model's θ parameters with new facts.
By storing the user's name in a global variable across all model versions.
By using a camera lens to capture the physical state of the user.
By concatenating past conversation history with the current user message into the prompt.
Explanation
Short-Term Memory works by including previous conversation history in the prompt so the LLM can use it as context during inference.
Q.4 In LangGraph, what component is used to save the state of a graph at every superstep to enable memory?
The Thread ID
A Checkpointer
The BaseStore
The MessageState
Explanation
A Checkpointer saves the graph state after each superstep, enabling persistence, recovery, and conversational memory.
Q.5 When dealing with the Context Overflow problem, how does Trimming differ from Summarization?
Trimming deletes the state, while summarization saves it to a database.
Trimming increases the context window size, while summarization reduces the token count.
Trimming keeps the last n messages and ignores the rest, while summarization condenses old messages into a brief overview.
Trimming keeps the oldest messages, while summarization keeps the newest ones.
Explanation
Trimming removes older messages while keeping the most recent ones. Summarization compresses older conversations into a concise summary so important information is retained.
Q.6 Which type of Long-Term Memory (LTM) is specifically concerned with how things are done, such as preferred workflows or rules?
Procedural Memory
Episodic Memory
Semantic Memory
In-Memory Store
Explanation
Procedural Memory stores processes, workflows, habits, and instructions describing how tasks should be performed.
Q.7 In the LangGraph Store implementation, what is a Namespace?
A function that generates embeddings for semantic search.
The name of the database where the memory is stored.
A way to organize memories into hierarchical structures, similar to folders in a file system.
A unique identifier for a single message.
Explanation
A Namespace organizes memories into logical groups, making it easier to separate user memories, application data, or different domains.
Q.8 Which LangGraph component is primarily responsible for storing Long-Term Memory across multiple conversations?
Checkpointer
BaseStore (Store)
MessageState
Thread ID
Explanation
The LangGraph Store (BaseStore) is designed for Long-Term Memory, allowing information to persist across multiple conversations and sessions.
Q.9 In the four-step workflow of Long-Term Memory, what happens during the Injection stage?
The model's weights are fine-tuned with the user's preferences.
New information is extracted from the user's message.
The retrieved long-term memory is placed into the current conversation buffer (Short-Term Memory).
Data is permanently saved to a Postgres database.
Explanation
During Injection, retrieved Long-Term Memory is inserted into the current prompt or Short-Term Memory so the LLM can use it when generating a response.
Q.10 What is a major challenge when implementing a Remember node to extract memories in real-time?
Ensuring the user is always asked for permission before a memory is saved.
Preventing the creation of redundant or duplicate memories in the store.
The Postgres database cannot handle high-frequency writes.
LLMs cannot generate JSON or structured output.
Explanation
A Remember node must avoid storing duplicate or redundant memories; otherwise, the memory store becomes cluttered and retrieval quality decreases.
Q.11 What is the 'Context Window' of a Large Language Model?
- The total number of parameters the model was trained on.
- The amount of text an LLM can read and remember at one time before answering.
- The physical screen size where the chat interface is displayed.
- The time limit the model has to generate a response.
Explanation
The context window is the maximum amount of text/tokens an LLM can process in a single invocation.
Q.12 How is 'Short-Term Memory' technically achieved in a stateless LLM?
- By updating the model's weights in real-time.
- By storing information in the model's intrinsic RAM.
- By concatenating the conversation history into the input prompt for each new call.
- LLMs are not actually stateless; they have built-in memory.
Explanation
Since LLMs are stateless, developers maintain a conversation buffer by appending previous messages to the current input so the model has the context.
Q.13 What does it mean for short-term memory to be 'thread-scoped'?
- Memory is shared across all users in the system.
- Memory is deleted immediately after a single word is generated.
- Memory exists only within the boundary of a specific conversation or session.
- Memory is stored in the global weights of the LLM.
Explanation
Short-term memory is typically restricted to a single ‘thread’ or conversation ID, meaning context from one chat does not leak into another.
Q.14 What is a significant drawback of using 'trimming' to solve the context window problem?
- It requires expensive vector databases to function.
- The LLM completely loses the context of the oldest parts of the conversation.
- It makes the LLM significantly slower at generating text.
- It causes the LLM to forget the user's name even if it was just mentioned.
Explanation
Trimming removes the oldest messages once a token limit is reached, which can lead to a ‘breaking point’ in the conversation if early context was important.
Q.15 Why is 'summarization' often more effective than 'trimming' for long conversations?
- It is cheaper because it uses fewer API calls.
- It removes the need for any persistent database.
- It preserves the essence of the entire past conversation while staying within token limits.
- It allows the LLM to remember every single word ever spoken.
Explanation
Summarization uses an LLM to condense old messages into a brief summary, which is then passed alongside recent messages to maintain context without exceeding the window.
Q.16 Which type of Long-Term Memory is responsible for remembering 'facts' about a user, such as their job or language preference?
- Episodic Memory
- Semantic Memory
- Volatile Memory
Explanation
Semantic memory consists of facts about the user or the system, such as a user’s preference for Python or their profession.
Q.17 In LangGraph, why is 'PostgresStore' preferred over 'InMemoryStore' for production?
- PostgresStore is faster for processing tokens.
- InMemoryStore loses all data when the program or server restarts.
- PostgresStore allows the LLM to run without any parameters.
- InMemoryStore is only compatible with Gemini models.
Explanation
InMemoryStore saves state in RAM, which is lost upon restart; PostgresStore provides a durable, persistent layer for production-grade applications.
Q.18 What is the purpose of 'Namespaces' in LangGraph memory stores?
- To give the AI agent a specific personality name.
- To define the programming language used for the graph.
- To organize memories into logical 'folders' like ('users', 'user_id', 'preferences').
- To set the maximum number of tokens allowed per thread.
Explanation
Namespaces (represented as tuples) allow developers to organize and categorize memories within a store, making them easier to manage and retrieve.
Q.19 What is required to enable 'semantic search' in a LangGraph memory store?
- An embedding model and vector indexing.
- A higher token limit in the system prompt.
- Deleting all old messages periodically.
- Using only the GPT-4o model.
Explanation
To perform semantic search (searching by meaning), the store must be initialized with an embedding model to convert memories and queries into vectors for comparison.
Q.20 What are the four high-level steps of the Long-Term Memory (LTM) workflow?
- Input, Output, Tokenization, Training.
- Trimming, Summarization, Deletion, Resetting.
- Creation, Storage, Retrieval, Injection.
- Login, Query, Response, Logout.
Explanation
The LTM workflow involves creating/extracting a memory, storing it in a database, retrieving it based on relevance, and injecting it into the current prompt.
Q.21 In the mathematical equation y = f(θ, x), what does the 'θ' (theta) represent in an LLM?
- The input tokens provided by the user.
- The output tokens generated by the model.
- The billions of fixed parameters/weights trained into the model.
- The time it takes to process a single request.
Explanation
Theta represents the billions of parameters that define the LLM’s knowledge, which are fixed after the training phase.
Q.22 What is a 'Checkpointer' in the context of LangGraph?
- A tool that checks if the LLM is generating hallucinations.
- A mechanism that saves the state of the graph at every superstep.
- A database specifically for storing image files.
- A function that resets the conversation history to zero.
Explanation
A checkpointer is a concept in LangGraph used to implement short-term memory by saving the state of the graph at every superstep.
Q.23 Which phenomenon allows an LLM to answer questions about a private PDF that wasn't in its training data?
- Parametric Retrieval
- In-Context Learning
- Gradient Descent
- Supervised Fine-Tuning
Explanation
In-context learning is an emergent ability where the LLM uses information and patterns present in the current prompt to generate an answer.
Q.24 Why is 'In-Memory Store' (InMemoryStore) unsuitable for production-grade applications?
- It is too slow to handle user queries.
- It can only store up to 10 messages at a time.
- It is volatile and loses all stored data if the server restarts.
- It requires a specialized Docker container to function.
Explanation
InMemoryStore saves data in RAM; once the program or server restarts, all the memories are lost, making it unsuitable for production.
Q.25 In LangGraph's Long-Term Memory, what is a 'Namespace'?
- The legal name of the AI agent.
- A way to organize memories into logical 'folders' using tuples.
- A specific programming language used to build the graph.
- The total number of tokens allowed in a thread.
Explanation
Namespaces, typically created as tuples like (‘users’, ‘user_id’), are used to organize and categorize memories within a memory store.
Q.26 Which type of memory specifically remembers 'how to do things' or user-preferred strategies?
- Episodic Memory
- Semantic Memory
- Procedural Memory
- Parametric Memory
Explanation
Procedural memory stores strategies, rules, and learned behaviors, such as a user’s preference for step-by-step explanations.
Q.27 What must be provided to a LangGraph Store to enable 'Semantic Search' functionality?
- A list of forbidden keywords.
- An embedding model (like text-embedding-3-small).
- A high-speed internet connection.
- A specific Thread ID for every user.
Explanation
To conduct semantic search (searching by meaning), you must pass an embedding model when creating the memory store.
Q.28 In the Long-Term Memory workflow, what does the 'Injection' step involve?
- Saving a new fact into the Postgres database.
- Filtering out noise from the user's chat history.
- Pulling relevant long-term memories into the current short-term context/prompt.
- Deleting old messages from the conversation buffer.
Explanation
Injection is the step where retrieved long-term memories are added to the short-term memory (prompt) so the LLM can use them.
Q.29 What is a 'Remember Node' in an agentic workflow?
- A node that reminds the user of their appointments.
- A node that extracts useful information from the user's message to save in LTM.
- A node that counts how many tokens the user has consumed.
- A node that deletes all history to save database space.
Explanation
A ‘Remember’ node is used to identify and extract stable, user-specific information from a message and store it in the long-term memory.
Q.30 To prevent duplicate memories in a store, what strategy is suggested in the sources?
- Deleting the entire database every 24 hours.
- Using an LLM to compare the new message against existing memories before saving.
- Only allowing the user to save one memory per day.
- Limiting memories to 10 words or less.
Explanation
To avoid redundancy, the system can send existing memories to the ‘extractor’ LLM so it can decide if a new message contains a truly new fact or just a duplicate of what is already stored.