Principle:Langchain ai Langgraph Agent State Schema
| Attribute | Value |
|---|---|
| Concept | Defining the state structure for agent-based graphs including message history and structured responses |
| Workflow | ReAct_Agent_Creation |
| Type | Principle |
| Repository | Langchain_ai_Langgraph |
| Source | libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py
|
Overview
The agent state schema defines the data structure that flows through a ReAct agent graph, determining what information is tracked between steps of the reasoning-and-action loop. At its core, a ReAct agent state must include a messages field for conversation history and a remaining_steps field for recursion safety. The AgentState TypedDict and its variant AgentStateWithStructuredResponse provide the default schemas used by create_react_agent. Users can also define custom state schemas that extend these defaults with application-specific fields.
Description
Message Accumulation
The central element of agent state is the messages field, which accumulates the full conversation history as the agent loop progresses. This field uses the add_messages reducer from langgraph.graph.message, declared via Python's Annotated type:
messages: Annotated[Sequence[BaseMessage], add_messages]
The add_messages reducer controls how state updates merge with existing state:
- When a node returns
{"messages": [new_message]}, the reducer appends the new message to the existing list rather than replacing it. - This ensures the conversation history grows naturally as the agent reasons and tools respond.
- Special message types like
RemoveMessagecan be used to trim history when needed.
The message types that accumulate in this field include:
HumanMessage: User input that initiates or continues the conversation.AIMessage: Model responses, potentially containingtool_calls.ToolMessage: Results from tool execution, linked to their originating tool call viatool_call_id.SystemMessage: System-level instructions (typically prepended by the prompt).
Remaining Steps
The remaining_steps field is a managed value of type RemainingSteps that tracks how many execution steps remain before hitting the recursion limit. It is calculated roughly as recursion_limit - total_steps_taken and serves as a safety mechanism:
- When
remaining_steps < 2and the model produces tool calls, the agent returns a message saying "Sorry, need more steps to process this request." instead of executing the tools. - This prevents
GraphRecursionErrorby gracefully degrading rather than crashing. - The field is declared as
NotRequiredin the TypedDict, meaning it is managed by the framework and does not need to be provided in input.
Structured Response Extraction
When a response_format is configured, the agent state must include a structured_response field. The AgentStateWithStructuredResponse TypedDict adds this field:
class AgentStateWithStructuredResponse(AgentState):
structured_response: StructuredResponse # dict | BaseModel
After the agent loop completes (model stops calling tools), a separate "generate_structured_response" node makes an additional LLM call using with_structured_output to extract a response matching the provided schema. The result is stored in this field, enabling applications to work with typed, validated data rather than raw text.
Custom State Schemas
Users can define custom state schemas that extend the base agent state with application-specific fields. Custom schemas must include the required keys (messages and remaining_steps), plus structured_response if response_format is used:
from typing import Annotated, Sequence
from typing_extensions import TypedDict, NotRequired
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from langgraph.managed import RemainingSteps
class CustomAgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
user_id: str # Custom field
search_results: list # Custom field
Custom state fields can be accessed by tools (via InjectedState), pre/post model hooks, and dynamic model selectors, enabling rich context sharing across the agent loop.
State as TypedDict vs. Pydantic
LangGraph supports two approaches for defining state schemas:
- TypedDict: The default approach. Lightweight, dictionary-based. Used by
AgentState. - Pydantic BaseModel: Provides runtime validation, default values, and richer type constraints. Was used by the now-deprecated
AgentStatePydantic.
Both approaches are compatible with the agent framework, though TypedDict is the recommended default.
Usage
from langgraph.prebuilt import create_react_agent
# Default state schema (AgentState) - no explicit schema needed
agent = create_react_agent("openai:gpt-4", tools=[my_tool])
# Custom state schema
from typing import Annotated, Sequence
from typing_extensions import TypedDict, NotRequired
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from langgraph.managed import RemainingSteps
class MyState(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
context: str
agent = create_react_agent(
"openai:gpt-4",
tools=[my_tool],
state_schema=MyState,
)
# With structured response
from pydantic import BaseModel
class Answer(BaseModel):
result: str
confidence: float
class MyStateWithResponse(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
structured_response: dict # Required when response_format is used
agent = create_react_agent(
"openai:gpt-4",
tools=[my_tool],
state_schema=MyStateWithResponse,
response_format=Answer,
)
Theoretical Basis
The agent state schema implements the state machine formalism that underpins LangGraph. In a state machine, the state is the complete description of the system at any point in time, and transitions (graph edges) are pure functions of the current state. By defining the state schema explicitly, LangGraph ensures:
- Deterministic reproducibility: Given the same state, the same transitions will occur.
- Serializability: State can be checkpointed and restored for persistence, debugging, and human-in-the-loop workflows.
- Type safety: The schema provides compile-time and runtime guarantees about the shape of data flowing through the graph.
The add_messages reducer implements a conflict-free replicated data type (CRDT) pattern for message lists. Rather than using last-writer-wins semantics (which would lose conversation history), the reducer treats the messages field as an append-only log. This ensures that concurrent updates from parallel tool executions are merged correctly without data loss.
The RemainingSteps managed value implements the bounded liveness property from formal verification -- ensuring that the agent loop will eventually terminate, even in adversarial cases where the model continuously generates tool calls.