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 agent implementation.An LLM Agent runs tools in a loop to achieve a goal.
An agent runs until a stop condition is met - i.e., when the model emits a final output 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 middleware.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
Ask AI
from langchain.agents import create_agentagent = create_agent( "gpt-5", tools=tools)
Model identifier strings support automatic inference (e.g., "gpt-5" will be inferred as "openai:gpt-5"). Refer to the reference to see a full list of model identifier string mappings.
For more control over the model configuration, initialize a model instance directly using the provider package. In this example, we use ChatOpenAI. See Chat models for other available chat model classes.
Model instances give you complete control over configuration. Use them when you need to set specific parameters like temperature, max_tokens, timeouts, base_url, 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, create middleware using the @wrap_model_call decorator that modifies the model in the request:
Copy
Ask AI
from langchain_openai import ChatOpenAIfrom langchain.agents import create_agentfrom langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponsebasic_model = ChatOpenAI(model="gpt-4o-mini")advanced_model = ChatOpenAI(model="gpt-4o")@wrap_model_calldef dynamic_model_selection(request: ModelRequest, handler) -> ModelResponse: """Choose model based on conversation complexity.""" message_count = len(request.state["messages"]) if message_count > 10: # Use an advanced model for longer conversations model = advanced_model else: model = basic_model return handler(request.override(model=model))agent = create_agent( model=basic_model, # Default model tools=tools, middleware=[dynamic_model_selection])
Pre-bound models (models with bind_tools already called) are not supported when using structured output. If you need dynamic model selection with structured output, ensure the models passed to the middleware are not pre-bound.
Tools can be specified as plain Python functions or .The tool decorator can be used to customize tool names, descriptions, argument schemas, and other properties.
Copy
Ask AI
from langchain.tools import toolfrom langchain.agents import create_agent@tooldef search(query: str) -> str: """Search for information.""" return f"Results for: {query}"@tooldef get_weather(location: str) -> str: """Get weather information for a location.""" return f"Weather in {location}: Sunny, 72°F"agent = create_agent(model, tools=[search, get_weather])
If an empty tool list is provided, the agent will consist of a single LLM node without tool-calling capabilities.
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
Ask AI
================================ 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.”
================================= 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
Ask AI
================================== Ai Message ==================================I found wireless headphones (model WH-1000XM5) with 10 units in stock...
You can shape how your agent approaches tasks by providing a prompt. The system_prompt parameter can be provided as a string:
Copy
Ask AI
agent = create_agent( model, tools, system_prompt="You are a helpful assistant. Be concise and accurate.")
When no system_prompt is provided, the agent will infer its task from the messages directly.The system_prompt parameter accepts either a str or a SystemMessage. Using a SystemMessage gives you more control over the prompt structure, which is useful for provider-specific features like Anthropic’s prompt caching:
Copy
Ask AI
from langchain.agents import create_agentfrom langchain.messages import SystemMessage, HumanMessageliterary_agent = create_agent( model="anthropic:claude-sonnet-4-5", system_prompt=SystemMessage( content=[ { "type": "text", "text": "You are an AI assistant tasked with analyzing literary works.", }, { "type": "text", "text": "<the entire contents of 'Pride and Prejudice'>", "cache_control": {"type": "ephemeral"} } ] ))result = literary_agent.invoke( {"messages": [HumanMessage("Analyze the major themes in 'Pride and Prejudice'.")]})
The cache_control field with {"type": "ephemeral"} tells Anthropic to cache that content block, reducing latency and costs for repeated requests that use the same system prompt.
For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use middleware.The @dynamic_prompt decorator creates middleware that generates system prompts based on the model request:
Copy
Ask AI
from typing import TypedDictfrom langchain.agents import create_agentfrom langchain.agents.middleware import dynamic_prompt, ModelRequestclass Context(TypedDict): user_role: str@dynamic_promptdef user_role_prompt(request: ModelRequest) -> str: """Generate system prompt based on user role.""" user_role = request.runtime.context.get("user_role", "user") base_prompt = "You are a helpful assistant." if user_role == "expert": return f"{base_prompt} Provide detailed technical responses." elif user_role == "beginner": return f"{base_prompt} Explain concepts simply and avoid jargon." return base_promptagent = create_agent( model="gpt-4o", tools=[web_search], middleware=[user_role_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.
You can invoke an agent by passing an update to its State. All agents include a sequence of messages in their state; to invoke the agent, pass a new message:
Copy
Ask AI
result = agent.invoke( {"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]})
For streaming steps and / or tokens from the agent, refer to the streaming guide.Otherwise, the agent follows the LangGraph Graph API and supports all associated methods, such as stream and invoke.
In some situations, you may want the agent to return an output in a specific format. LangChain provides strategies for structured output via the response_format parameter.
ProviderStrategy uses the model provider’s native structured output generation. This is more reliable but only works with providers that support native structured output (e.g., OpenAI):
Copy
Ask AI
from langchain.agents.structured_output import ProviderStrategyagent = create_agent( model="gpt-4o", response_format=ProviderStrategy(ContactInfo))
As of langchain 1.0, simply passing a schema (e.g., response_format=ContactInfo) is no longer supported. You must explicitly use ToolStrategy or ProviderStrategy.
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:Custom state schemas must extend AgentState as a TypedDict.There are two ways to define custom state:
Use the state_schema parameter as a shortcut to define custom state that is only used in tools.
Copy
Ask AI
from langchain.agents import AgentStateclass CustomState(AgentState): user_preferences: dictagent = create_agent( model, tools=[tool1, tool2], state_schema=CustomState)# The agent can now track additional state beyond messagesresult = agent.invoke({ "messages": [{"role": "user", "content": "I prefer technical explanations"}], "user_preferences": {"style": "technical", "verbosity": "detailed"},})
As of langchain 1.0, custom state schemas must be TypedDict types. Pydantic models and dataclasses are no longer supported. See the v1 migration guide for more details.
Defining custom state via middleware is preferred over defining it via state_schema on create_agent because it allows you to keep state extensions conceptually scoped to the relevant middleware and tools.state_schema is still supported for backwards compatibility on create_agent.
To learn more about memory, see Memory. For information on implementing long-term memory that persists across sessions, see Long-term memory.
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
Ask AI
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]}")
Middleware provides powerful extensibility for customizing agent behavior at different stages of execution. You can use middleware to:
Process state before the model is called (e.g., message trimming, context injection)
Modify or validate the model’s response (e.g., guardrails, content filtering)
Handle tool execution errors with custom logic
Implement dynamic model selection based on state or context
Add custom logging, monitoring, or analytics
Middleware integrates seamlessly into the agent’s execution, allowing you to intercept and modify data flow at key points without changing the core agent logic.