AgentState manages execution context
Every agent manages its execution context through AgentState, a typed dictionary that holds the current conversation history and any custom fields your tools and middleware need. AgentState is also the type signature for every node-style middleware hook (before_model, after_model, and similar). Hooks receive the current state and can return a dict of updates to merge back into it.
AgentState built-in messages field
The AgentState contains a built-in field 'messages' of type list[BaseMessage]. This field holds the full conversation history for the current thread. The messages field is append-only: new messages are added, never replaced.
Customize AgentState with custom fields
To add custom fields to AgentState (for example, a user_id or a counter), subclass AgentState and pass the subclass to create_agent via the state_schema parameter.
Custom state keys in agent type definitions
If your agent exposes custom state keys beyond messages, extend the TypeScript interface when passing a type parameter to useStream. For JavaScript agents, custom state keys are inferred automatically from the compiled graph with no manual interface required. For Python backends, manually extend the interface to include the additional custom state properties.
Checkpoint structure in LangGraph
Every state change in a LangGraph agent creates a checkpoint, which is a ThreadState object capturing: checkpoint (metadata with ID and timestamp), values (full agent state including messages and custom keys), tasks (graph nodes scheduled to run next), and next (names of upcoming nodes in the execution plan).
Linear timeline of agent checkpoints
LangGraph persists agent state after every node execution, creating a linear timeline of every decision the agent made, every tool it called, and every response it produced. This complete history enables UIs to render the timeline and let users jump to any point.
How to store and retrieve persistent data across multiple agent interactions using LangGraph
Use LangGraph's Store feature with FilesystemMiddleware and CompositeBackend. Configure FilesystemMiddleware with a CompositeBackend that routes paths like /memories/ to a StoreBackend instead of StateBackend. Files written to /memories/ prefix are saved to persistent storage via InMemoryStore or other store implementations, surviving across different threads and agent interactions. Files without the /memories/ prefix remain in ephemeral state storage.
State machine workflow determined by current_step field
The state machine pattern uses a current_step field in state to determine which configuration is applied. The value defaults to the first step (e.g., 'warranty_collector') and gets updated by tool calls that return Command objects. On each turn, middleware reads current_step and applies the corresponding prompt and tools.
Step configuration requires field prevents invalid transitions
Each step specifies a 'requires' list of state fields that must be set. The middleware validates this before applying the step. For example, 'issue_classifier' requires 'warranty_status', so it cannot be reached until warranty status has been recorded. This prevents partial workflows.
Result collection with reducers
Agent results flow back to the main state via a reducer. Each agent returns {"results": [{"source": "github", "result": "..."}]} or similar. The reducer (operator.add in Python or concat in JavaScript) concatenates these lists, collecting all parallel results into state["results"]. This allows results from multiple agents to be collected as they complete.
Agent state in router example
Three state schemas are used in the router example: AgentInput (simple state passed to each subagent with just a query field), AgentOutput (result returned by each subagent with source name and result), Classification (a single routing decision with source enum ['github', 'notion', 'slack'] and query string), and RouterState (main workflow state tracking the query, classifications list, results list with operator.add reducer, and final_answer string).
Skill middleware with state tracking
To enforce constraints that tools are only available after specific skills are loaded, use custom state tracking in middleware. Define a custom state class with a skills_loaded field to track which skills have been loaded (e.g., CustomState with NotRequired[list[str]] field). Modify the load_skill tool to return a Command that updates state to add the loaded skill name to the skills_loaded list. Create constrained tools that check the skills_loaded state before executing, returning an error message if the required skill has not been loaded. This pattern ensures the agent must load the appropriate skill before calling dependent tools.
Subagent checkpointing: inherited vs continuations mode
By default, subagents use inherited checkpointer mode—each invocation starts with fresh state, supports interrupts, and runs safely in parallel. If you need a subagent to maintain its own persistent conversation history across invocations, compile it with checkpointer=True (continuations mode).
Subagent state inspection limitation
Because subagents are called inside tool functions, LangGraph cannot statically discover them. This means get_state with subgraphs parameter will not return subagent state. If you need to read nested graph state (e.g., during an interrupt), invoke the subagent from a node function in a custom graph instead.
Short-term memory definition and purpose
Short-term memory is a system that lets an agent remember information about previous interactions within a single thread or conversation. A thread organizes multiple interactions in a session, similar to how email groups messages in a single conversation. Short-term memory is crucial for agents because it lets them remember previous interactions, learn from feedback, and adapt to user preferences.
Thread-level persistence requires checkpointer
To add short-term memory (thread-level persistence) to an agent, you need to specify a `checkpointer` when creating an agent. The checkpointer persists state to a database or memory so the thread can be resumed at any time.
LangGraph short-term memory via agent state
LangChain's agent manages short-term memory as part of the agent's state. By storing state in the graph's state, the agent can access the full context for a given conversation while maintaining separation between different threads. Short-term memory updates when the agent is invoked or a step (like a tool call) is completed, and the state is read at the start of each step.
Extend AgentState for custom state schema
Extend `AgentState` to add additional fields for custom state schemas. Pass custom state schemas to `create_agent` using the `state_schema` parameter. Custom state can be passed in invoke calls alongside standard fields like messages.
Long-term memory vs short-term memory
Long-term memory stores and recalls user-specific or application-level data across different threads and sessions. This is different from short-term memory which only remembers information within a single thread or conversation.
InMemorySaver checkpointer for testing persistence
The InMemorySaver checkpointer from langgraph.checkpoint.memory enables persistence during testing and allows simulation of multiple turns to test state-dependent behavior. It preserves agent state across invocations when using the same thread_id in the config's configurable dictionary.
InMemorySaver example for multi-turn testing
Example using InMemorySaver for multi-turn agent testing:
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model,
tools=[],
checkpointer=InMemorySaver()
)
# First invocation
agent.invoke(
{"messages": [HumanMessage(content="I live in Sydney, Australia")]},
config={"configurable": {"thread_id": "session-1"}}
)
# Second invocation: the first message is persisted (Sydney location), so the model returns GMT+10 time
agent.invoke(
{"messages": [HumanMessage(content="What's my local time?")]},
config={"configurable": {"thread_id": "session-1"}}
)