homeresume
 
   
🔍

Conversation memory for LangChain agents

June 18, 2026

This post extends the support triage agent from Building AI agents with LangChain into a multi-turn flow: turn 1 looks up the customer and invoice; turn 2 creates the ticket without the user repeating IDs. It is post #5 in the LangChain series, following the overview, loaders/chunking, RAG, and agents posts.

Prerequisites

  • OpenAI account
  • Generated API key
  • Enabled billing
  • Node.js version 26
  • Packages from the agents post, plus the checkpoint package:
npm i langchain @langchain/openai @langchain/core @langchain/langgraph-checkpoint zod
  • OPENAI_API_KEY set in the environment

Mental model

Three related concepts:

  • Checkpointer - short-term session memory. Saves messages and graph state after each step so the next invoke on the same thread can resume.
  • thread_id - conversation key passed in configurable. Same ID = same history; different ID = isolated session.
  • Store - long-term memory across threads (user preferences, facts learned over time). LangGraph stores are separate from checkpointers; this post focuses on checkpointers only.

Typical support flow with memory:

  1. Turn 1 - rep asks to look up cus_1042 and inv_8891; agent calls lookup tools and summarizes findings.
  2. Turn 2 - rep says "create the ticket we discussed"; agent recalls prior tool results and calls create_support_ticket.

MemorySaver

For demos and tests, use MemorySaver - an in-memory checkpointer that persists state for the lifetime of the process:

import { MemorySaver } from '@langchain/langgraph-checkpoint';
const checkpointer = new MemorySaver();

State is lost when the Node process exits. That is fine for local scripts; production apps need a durable backend (see below).

Attach a checkpointer to createAgent

Pass the checkpointer when creating the agent. Reuse the same triage tools and instructions from the agents post:

import { createAgent } from 'langchain';
import { MemorySaver } from '@langchain/langgraph-checkpoint';
const agent = createAgent({
model: 'gpt-5.5',
tools: supportTools,
systemPrompt: TRIAGE_INSTRUCTIONS,
checkpointer: new MemorySaver(),
});

The agent loop is unchanged - the checkpointer hooks into LangGraph beneath createAgent.

First turn - lookup

Pass a stable thread_id in the invoke config:

const threadConfig = { configurable: { thread_id: 'support-cus-1042' } };
const turn1 = await agent.invoke(
{
messages: [
{
role: 'user',
content:
'Look up customer cus_1042 and invoice inv_8891 for a possible duplicate charge. Summarize what you find. Do not create a ticket yet.',
},
],
},
threadConfig,
);
console.log(turn1.messages.at(-1)?.content);

The agent calls get_customer, get_invoice, and search_knowledge_base. LangGraph saves the full message history (including tool results) to the checkpointer.

Second turn - follow-up without IDs

Send only the new user message on the same thread_id. Prior context is restored automatically:

const turn2 = await agent.invoke(
{
messages: [
{
role: 'user',
content: 'Create the support ticket we discussed.',
},
],
},
threadConfig,
);
console.log(turn2.messages.at(-1)?.content);

The agent should call create_support_ticket using customer and invoice details from turn 1 - the user does not repeat cus_1042 or inv_8891.

Read the final answer from result.messages as in the agents post:

const lastAi = [...turn2.messages]
.reverse()
.find((message) => message.type === 'ai');
console.log(lastAi?.content);

Thread isolation

Different thread_id values do not share history. Two support reps working different cases should use separate thread IDs:

await agent.invoke(
{ messages: [{ role: 'user', content: 'Look up cus_1042.' }] },
{ configurable: { thread_id: 'rep-alice-case-1' } },
);
await agent.invoke(
{ messages: [{ role: 'user', content: 'Create the ticket we discussed.' }] },
{ configurable: { thread_id: 'rep-bob-case-2' } },
);

The second invoke on rep-bob-case-2 has no knowledge of Alice's lookup - Bob's thread starts empty.

Production checkpointers

MemorySaver is process-local and not suitable for production. LangGraph supports durable checkpointers backed by Postgres, SQLite, and other stores via @langchain/langgraph-checkpoint integrations. Swap the checkpointer implementation; the thread_id API stays the same.

Pick a backend that matches your deployment: Postgres for multi-instance apps, SQLite for single-node services.

Demo

See the langchain-agent-memory-nodejs-demo folder for multi-turn triage and thread-isolation scripts.