Alpha Notice: These docs cover the v1-alpha release. Content is incomplete and subject to change.For the latest stable version, see the v0 LangChain Python or LangChain JavaScript docs.
Agents combine language models with tools to create systems that can reason about tasks, decide which tools to use, and iteratively work towards solutions.create_agent() provides a production-ready ReAct (Reasoning + Acting) agent implementation based on the paper ReAct: Synergizing Reasoning and Acting in Language Models.ReAct frames an agent’s behavior as an interleaving of thought -> action -> observation steps, where the model writes out its reasoning, picks a tool, sees the tool’s result, and then repeats. ReAct reduces hallucinations and makes the decision process auditable: the agent can form hypotheses (thought), test them with tools (action), and update its plan based on feedback (observation).A ReAct loop runs until a stop condition - i.e., when the model emits a final answer or an iteration limit is reached.
create_agent() builds a graph-based agent runtime using LangGraph. A graph consists of nodes (steps) and edges (connections) that define how your agent processes information. The agent moves through this graph, executing nodes like the model node (which calls the model), the tools node (which executes tools), or pre/post model hook nodes.Learn more about the graph API.
Static models are configured once when creating the agent and remain unchanged throughout execution. This is the most common and straightforward approach. To initialize a static model from a :
Copy
from langchain.agents import create_agentagent = create_agent( "openai:gpt-5", tools=tools)
Model identifier strings support automatic inference (e.g., "gpt-5" will be inferred as "openai:gpt-5").
For more control over the model configuration, initialize a model instance directly using the provider package:
Model instances give you complete control over configuration. Use them when you need to set specific parameters like temperature, max tokens, timeouts, base URLs, and other provider-specific settings. Refer to the reference to see available params and methods on your model.
Dynamic models are selected at based on the current and context. This enables sophisticated routing logic and cost optimization.To use a dynamic model, you need to provide a function that receives the graph state and runtime and returns an instance of BaseChatModel with the tools bound to it using .bind_tools(tools), where tools is a subset of the tools parameter.
Copy
from langchain_openai import ChatOpenAIfrom langchain.agents import create_agent, AgentStatefrom langgraph.runtime import Runtimedef select_model(state: AgentState, runtime: Runtime) -> ChatOpenAI: """Choose model based on conversation complexity.""" messages = state["messages"] message_count = len(messages) if message_count < 10: return ChatOpenAI(model="gpt-4.1-mini").bind_tools(tools) else: return ChatOpenAI(model="gpt-5").bind_tools(tools) # Better model for longer conversationsagent = create_agent(select_model, tools=tools)
Alternatively, you can create a ToolNode directly and pass it to the agent. This allows you to customize the tool node’s behavior, such as handling tool errors:
Copy
tool_node = ToolNode( tools=[search, calculate], handle_tool_errors="Please check your input and try again.")agent = create_agent(model, tools=tool_node)result = agent.invoke({"messages": [...]})
If an error occurs within ToolNode, the agent will return a ToolMessage to the model with the custom error message:
Copy
# result["messages"][ ... ToolMessage(content="Please check your input and try again.", tool_call_id="..."), ...]
Agents follow the ReAct (Reasoning + Acting) pattern, alternating between brief reasoning steps with targeted tool calls and feeding the resulting observations into subsequent decisions until they can deliver a final answer.
Example of ReAct loop
Prompt: Identify the current most popular wireless headphones and verify availability.
Copy
================================ Human Message =================================Find the most popular wireless headphones right now and check if they're in stock
Reasoning: “Popularity is time-sensitive, I need to use the provided search tool.”Acting: Call search_products("wireless headphones")
================================= Tool Message =================================Product WH-1000XM5: 10 units in stock
Reasoning: “I have the most popular model and its stock status. I can now answer the user’s question.”Acting: Produce final answer
Copy
================================== Ai Message ==================================I found wireless headphones (model WH-1000XM5) with 10 units in stock...
For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use the modify_model_request decorator to create a simple custom middleware.Dynamic system prompt is especially useful for personalizing prompts based on user roles, conversation context, or other changing factors:
Copy
from typing import TypedDictfrom langchain.agents import create_agentfrom langchain.agents.middleware.types import modify_model_request, AgentState, ModelRequestfrom langgraph.runtime import Runtimeclass Context(TypedDict): user_role: str@modify_model_requestdef dynamic_system_prompt(request: ModelRequest, state: AgentState, runtime: Runtime[Context]) -> ModelRequest: user_role = runtime.context.get("user_role", "user") base_prompt = "You are a helpful assistant." if user_role == "expert": prompt = f"{base_prompt} Provide detailed technical responses." elif user_role == "beginner": prompt = f"{base_prompt} Explain concepts simply and avoid jargon." else: prompt = base_prompt request.system_prompt = prompt return requestagent = create_agent( model="openai:gpt-4o", tools=[web_search], middleware=[dynamic_system_prompt], context_schema=Context)# The system prompt will be set dynamically based on contextresult = agent.invoke( {"messages": [{"role": "user", "content": "Explain machine learning"}]}, context={"user_role": "expert"})
For more details on message types and formatting, see Messages. For comprehensive middleware documentation, see Middleware.
In some situations, you may want the agent to return an output in a specific format. LangChain provides a simple, universal way to do this with the response_format parameter.
Agents maintain conversation history automatically through the message state. You can also configure the agent to use a custom state schema to remember additional information during the conversation.Information stored in the state can be thought of as the short-term memory of the agent:
Copy
from typing import TypedDictfrom typing_extensions import Annotatedfrom langgraph.graph.message import add_messagesfrom langchain.agents import create_agentfrom langchain.agents import AgentStateclass CustomAgentState(AgentState): messages: Annotated[list, add_messages] user_preferences: dictagent = create_agent( model, tools=tools, state_schema=CustomAgentState)# The agent can now track additional state beyond messages. This custom state can be accessed and updated throughout the conversation.result = agent.invoke({ "messages": [{"role": "user", "content": "I prefer technical explanations"}], "user_preferences": {"style": "technical", "verbosity": "detailed"},})
To learn more about memory, see Memory. For information on implementing long-term memory that persists across sessions, see Long-term memory.
Pre-model hook is an optional node that can process state before the model is called. Use cases include message trimming, summarization, and context injection.It must be a callable or a runnable that takes in current graph state and returns a state update in the form of:
Copy
{ # Will UPDATE the `messages` in the state "messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES), ...], # Any other state keys that need to be propagated ...}
Example of a pre-model hook that trims messages to fit the context window:
Copy
from langchain_core.messages import RemoveMessagefrom langgraph.graph.message import REMOVE_ALL_MESSAGESfrom langchain.agents import create_agentdef trim_messages(state): """Keep only the last few messages to fit context window.""" messages = state["messages"] if len(messages) <= 3: return {"messages": messages} 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 ] }agent = create_agent( model, tools=tools, pre_model_hook=trim_messages)
messages must be provided and will be used as an input to the agent node (i.e., the node that calls the LLM). The rest of the keys will be added to the graph state.
If you are returning messages in the pre-model hook, you should overwrite the messages key by doing the following:
Post-model hook is an optional node that can process the model’s response before tool execution. Use cases include validation, guardrails, or other post-processing.It must be a callable or a runnable that takes in current graph state and returns a state update.Example of a post-model hook that filters out confidential information:
Copy
from langchain_core.messages import AIMessage, RemoveMessagefrom langgraph.graph.message import REMOVE_ALL_MESSAGESdef validate_response(state): """Check model response for policy violations.""" messages = state["messages"] last_message = messages[-1] if "confidential" in last_message.content.lower(): return { "messages": [ RemoveMessage(id=REMOVE_ALL_MESSAGES), *messages[:-1], AIMessage(content="I cannot share confidential information.") ] } return {}agent = create_agent( model, tools=tools, post_model_hook=validate_response)
We’ve seen how the agent can be called with .invoke to get a final response. If the agent executes multiple steps, this may take a while. To show intermediate progress, we can stream back messages as they occur.
Copy
for chunk in agent.stream({ "messages": [{"role": "user", "content": "Search for AI news and summarize the findings"}]}, stream_mode="values"): # Each chunk contains the full state at that point latest_message = chunk["messages"][-1] if latest_message.content: print(f"Agent: {latest_message.content}") elif latest_message.tool_calls: print(f"Calling tools: {[tc['name'] for tc in latest_message.tool_calls]}")