Implementation:Langchain ai Langgraph AgentState Schema
| Attribute | Value |
|---|---|
| API | AgentState and AgentStateWithStructuredResponse TypedDicts
|
| Workflow | ReAct_Agent_Creation |
| Type | Pattern Doc |
| Repository | Langchain_ai_Langgraph |
| Source File | libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py
|
| Source Lines | L57-62 (AgentState), L88-91 (AgentStateWithStructuredResponse) |
Overview
AgentState is a TypedDict that defines the default state schema for ReAct agents built with create_react_agent. It contains two fields: messages (an annotated sequence of messages with the add_messages reducer) and remaining_steps (a managed counter for recursion safety). AgentStateWithStructuredResponse extends AgentState with a structured_response field for agents that produce typed output.
Note: Both AgentState and AgentStateWithStructuredResponse are deprecated in favor of equivalents in the langchain.agents package. They remain functional for backward compatibility.
Description
AgentState
The AgentState TypedDict defines the minimal state required for a ReAct agent:
class AgentState(TypedDict):
"""The state of the agent."""
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
messages: The conversation history. TheAnnotated[..., add_messages]annotation attaches theadd_messagesreducer, which appends new messages to the existing list rather than replacing it. This is critical for the ReAct loop where each step adds new messages (AIMessage, ToolMessage) to the growing conversation.
remaining_steps: ANotRequiredmanaged value of typeRemainingSteps. This is automatically populated by the framework based on the recursion limit and steps taken. It is used by the agent's internal logic to detect when the step budget is nearly exhausted, allowing graceful termination instead of aGraphRecursionError.
AgentStateWithStructuredResponse
This extends AgentState with a field for structured output:
class AgentStateWithStructuredResponse(AgentState):
"""The state of the agent with a structured response."""
structured_response: StructuredResponse # dict | BaseModel
When create_react_agent is called with a response_format parameter, this schema is used as the default state. The structured_response field holds the output of the "generate_structured_response" node, which makes a separate LLM call after the agent loop to produce typed output matching the provided schema.
Default Schema Selection
Within create_react_agent, the state schema is selected as follows (lines 547-552):
if state_schema is None:
state_schema = (
AgentStateWithStructuredResponse
if response_format is not None
else AgentState
)
If a custom state_schema is provided, it is validated to ensure it contains the required keys (messages, remaining_steps, and structured_response if applicable).
The add_messages Reducer
The add_messages reducer (from langgraph.graph.message) is central to agent state behavior. When a node returns {"messages": [new_msg]}, the reducer appends new_msg to the existing messages list in state. It also handles special cases:
RemoveMessage: Removes specific messages by ID from the list.- Message deduplication by ID when messages with matching IDs are provided.
This reducer pattern is what enables the "accumulating conversation history" behavior that the ReAct loop depends on.
Usage
# Default usage - AgentState is used automatically
from langgraph.prebuilt import create_react_agent
agent = create_react_agent("openai:gpt-4", tools=[my_tool])
# Uses AgentState internally
# With structured response - uses AgentStateWithStructuredResponse
from pydantic import BaseModel
class Result(BaseModel):
answer: str
agent = create_react_agent(
"openai:gpt-4",
tools=[my_tool],
response_format=Result,
)
# Uses AgentStateWithStructuredResponse internally
# 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 CustomState(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
user_preferences: dict
agent = create_react_agent(
"openai:gpt-4",
tools=[my_tool],
state_schema=CustomState,
)
Code Reference
Source Location
| File | libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py
|
| AgentState | Lines 57-62 |
| AgentStateWithStructuredResponse | Lines 88-91 |
| StructuredResponse type | BaseModel |
| Schema selection logic | Lines 538-552 |
Signature
class AgentState(TypedDict):
"""The state of the agent."""
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
class AgentStateWithStructuredResponse(AgentState):
"""The state of the agent with a structured response."""
structured_response: StructuredResponse
Import
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.prebuilt.chat_agent_executor import AgentStateWithStructuredResponse
I/O Contract
AgentState Fields
| Field | Type | Required | Reducer | Description |
|---|---|---|---|---|
messages |
Annotated[Sequence[BaseMessage], add_messages] |
Yes | add_messages |
Conversation history. New messages are appended by the reducer. Contains HumanMessage, AIMessage, ToolMessage, and SystemMessage objects.
|
remaining_steps |
RemainingSteps |
No (NotRequired) |
Managed | Steps remaining before recursion limit. Managed by the framework; typically not set by users. |
AgentStateWithStructuredResponse Additional Fields
| Field | Type | Required | Reducer | Description |
|---|---|---|---|---|
structured_response |
BaseModel) | Yes | Default (replace) | Typed output produced by the "generate_structured_response" node. Schema determined by the response_format parameter.
|
Input Format
When invoking an agent, the input should match the state schema:
# Minimal input
{"messages": [("user", "Hello")]}
# With HumanMessage
{"messages": [HumanMessage(content="Hello")]}
# With custom state fields
{"messages": [("user", "Hello")], "user_preferences": {"language": "en"}}
Output Format
# Standard output (AgentState)
{
"messages": [
HumanMessage(content="Hello"),
AIMessage(content="Hi there! How can I help?"),
],
"remaining_steps": 23,
}
# With structured response (AgentStateWithStructuredResponse)
{
"messages": [...],
"remaining_steps": 21,
"structured_response": Result(answer="42"),
}
Usage Examples
Accessing State in Tools via InjectedState
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState, create_react_agent
@tool
def message_count(state: Annotated[dict, InjectedState]) -> str:
"""Report the number of messages in the conversation."""
return f"There are {len(state['messages'])} messages"
agent = create_react_agent("openai:gpt-4", tools=[message_count])
result = agent.invoke({"messages": [("user", "How many messages are there?")]})
Custom State with Structured Response
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
from pydantic import BaseModel
class AnalysisResult(BaseModel):
sentiment: str
key_topics: list[str]
class AnalysisState(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
remaining_steps: NotRequired[RemainingSteps]
structured_response: dict # Required for response_format
document_text: str # Custom field for analysis context
agent = create_react_agent(
"openai:gpt-4",
tools=[my_analysis_tool],
state_schema=AnalysisState,
response_format=AnalysisResult,
)
result = agent.invoke({
"messages": [("user", "Analyze this document")],
"document_text": "The document content...",
})
print(result["structured_response"])
# AnalysisResult(sentiment="positive", key_topics=["AI", "agents"])
Remaining Steps Safety Check
# The remaining_steps field is managed automatically.
# When steps are nearly exhausted, the agent returns gracefully:
agent = create_react_agent(
"openai:gpt-4",
tools=[complex_tool],
)
result = agent.invoke(
{"messages": [("user", "Do complex analysis")]},
config={"recursion_limit": 5}, # Low limit for demonstration
)
# If the agent needs more steps than allowed, the last message will be:
# AIMessage(content="Sorry, need more steps to process this request.")