TellWorldAI World Archive
Ask中文

Home›Practice›Practice

Practice

Short-term memory in LangChain agents

Give a LangChain agent thread-level memory with a checkpointer, persist it in a database for production, and control long histories by trimming, deleting or summarizing messages.

Who this is for and what you will be able to do

This guide is for developers who already have a simple LangChain agent and want it to remember what was said earlier in the same conversation. After reading it, you can add thread-level short-term memory with a checkpointer, persist that state in a database, and choose between trimming, deleting and summarizing when a conversation gets long.

What short-term memory is

Short-term memory lets an application remember previous interactions within a single thread or conversation.[1] A thread works like an email conversation: it groups several interactions into one session.[1]

Conversation history is the most common form of short-term memory.[1] It runs into limits as conversations grow. A full history may not fit inside the model's context window, which leads to lost context or errors.[1] Even when a model supports the full context length, most LLMs still perform poorly over long contexts. They get distracted by stale or off-topic content, and responses become slower and more expensive.[1]

Chat models receive context as messages. A system message carries the instructions, and human messages carry the inputs. In a chat application the list of messages grows longer over time.[1]

Short-term memory is not the same as long-term memory. Long-term memory stores user-specific or application-level data across different threads and sessions.[1]

Adding memory to an agent

To add thread-level persistence, specify a checkpointer when you create the agent.[1] The agent keeps this memory as part of its state, stored in the graph's state. That lets it see the full context of one conversation while keeping different threads separate.[1] The state is persisted through the checkpointer, so a thread can be resumed at any time.[1]

  1. Create a checkpointer. The Python example uses InMemorySaver from langgraph.checkpoint.memory.[1]
  2. Pass it to create_agent as the checkpointer argument.
  3. Pass a thread_id in the config on each call. Calls with the same thread_id share the same memory.[1]

The complete Python example below is a custom state that also shows InMemorySaver and the thread_id config:

from langchain.agents import create_agent, AgentState
from langgraph.checkpoint.memory import InMemorySaver

class CustomAgentState(AgentState):  # [!code highlight]
    user_id: str  # [!code highlight]
    preferences: dict  # [!code highlight]

agent = create_agent(
    "gpt-6.1-sol",
    tools=[get_user_info],
    state_schema=CustomAgentState,  # [!code highlight]
    checkpointer=InMemorySaver(),
)

# Custom state can be passed in invoke
result = agent.invoke(
    {
        "messages": [{"role": "user", "content": "Hello"}],
        "user_id": "user_123",  # [!code highlight]
        "preferences": {"theme": "dark"}  # [!code highlight]
    },
    {"configurable": {"thread_id": "1"}})

State is updated when the agent is invoked and when a step such as a tool call completes. The state is read at the start of each step.[1]

Moving to a database in production

The in-memory checkpointer suits experiments. For production, the docs recommend a checkpointer backed by a database. The options listed include SQLite, Postgres and Azure Cosmos DB.[1] For Postgres with Python, install with pip:

pip install -U langgraph-checkpoint-postgres "psycopg[binary]"

With uv:

uv add langgraph-checkpoint-postgres "psycopg[binary]"

By default, langgraph-checkpoint-postgres installs psycopg without extras. Most users should use psycopg[binary].[1] Then create the PostgresSaver and hand it to the agent:

from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver  # [!code highlight]

def get_user_info() -> str:
    """Look up information about the current user."""
    return "No user profile on file."

DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
    checkpointer.setup() # auto create tables in PostgreSQL
    agent = create_agent(
        "gpt-6.1-sol",
        tools=[get_user_info],
        checkpointer=checkpointer,  # [!code highlight]
    )

creates the tables in PostgreSQL automatically.[1] The JavaScript version creates the saver from a connection string:

import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";

const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);

Customizing agent state

By default an agent uses AgentState, and the conversation history lives under the messages key.[1] To store extra fields such as a user ID or preferences, extend AgentState. In Python, pass the custom state schema to create_agent with the state_schema parameter.[1] In JavaScript, add the state through middleware with a stateSchema. The docs prefer the StateSchema class, though plain Zod objects also work.[1]

Handling long conversations

Four common approaches appear in the docs: trimming messages, deleting messages, summarizing messages, and custom strategies such as message filtering.[1]

Trimming. In Python, the @before_model decorator turns a function into middleware that runs before each model call.[1] The example below keeps the first message and the most recent few:

@before_model
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    """Keep only the last few messages to fit context window."""
    messages = state["messages"]

    if len(messages) <= 3:
        return None  # No changes needed

    first_msg = messages[0]
    recent_messages = messages[-3:] if len(messages) % 2 == 0 else messages[-4:]
    new_messages = [first_msg] + recent_messages

    return {
        "messages": [
            RemoveMessage(id=REMOVE_ALL_MESSAGES),
            *new_messages
        ]
    }

Deleting. RemoveMessage only takes effect on a state key that uses the add_messages reducer. The default AgentState provides this.[1] To clear every message, return a RemoveMessage with REMOVE_ALL_MESSAGES, imported from langgraph.graph.message.[1] After deleting, keep the history valid. Some providers expect a user message first, and most require each assistant tool call to be followed by its tool result.[1]

Summarizing. Trimming and deleting can drop useful information. The built-in SummarizationMiddleware uses a chat model to summarize earlier messages. The example sets trigger=("tokens", 4000) and keep=("messages", 20).[1]

Reading and writing memory from tools and prompts

A tool reads state through the runtime parameter, typed as ToolRuntime. The parameter is hidden from the tool signature, so the model never sees it.[1] Tools can also write state: return a Command with updates, such as a user name, and a ToolMessage added to the history.[1] Middleware can build dynamic prompts from state or runtime context, for example addressing the user by name.[1]

Limits and common questions

  • Short-term memory is scoped to one thread. Cross-conversation data needs long-term memory.[1]
  • The in-memory checkpointer is for experiments. Use a database-backed checkpointer in production.[1]
  • Long conversations can exceed the context window, so trimming, deleting or summarizing is needed.[1]
  • After deleting messages, the history must still be valid for the provider, or requests may fail.[1]

Next steps

For the full list of checkpointers, persistence details and the complete middleware options, see the corresponding sections of the LangChain documentation.[1]

Adapted from the LangChain documentation page *Short-term memory*[1], released under the MIT License.

Sources

  1. 1
    Short-term memoryLangChain docs (MIT)

Cite this page: Short-term memory in LangChain agents. TellWorld, https://www.tellworld.net/practice/practice-langchain-memory. Updated 2026-11-14

Related news

View all