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.createAgent() 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 :
Model identifier strings use the format provider:model (e.g. "openai:gpt-5"). You may want more control over the model configuration, in which case you can initialize a model instance directly using the provider package:
Copy
import { createAgent } from "langchain";import { ChatOpenAI } from "@langchain/openai";const model = new ChatOpenAI({ model: "gpt-4o", temperature: 0.1, maxTokens: 1000, timeout: 30});const agent = createAgent({ model, tools: []});
Model instances give you complete control over configuration. Use them when you need to set specific parameters like temperature, max tokens, timeouts, or configure API keys, base URLs, and other provider-specific settings. Refer to the API 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 .bindTools(tools), where tools is a subset of the tools parameter.
Copy
import { createAgent, AgentState } from "langchain";import { ChatOpenAI } from "@langchain/openai";const selectModel = (state: AgentState) => { const messageCount = state.messages.length; if (messageCount > 10) { return new ChatOpenAI({ model: "gpt-4.1" }).bindTools(tools); } return new ChatOpenAI({ model: "gpt-4o" }).bindTools(tools);};const agent = createAgent({ llm: selectModel, 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
import { ToolNode, ToolMessage } from "langchain";const toolNode = new ToolNode([search, calculate], { handleToolErrors: (error, toolCall) => { return new ToolMessage({ content: "Please check your input and try again.", tool_call_id: toolCall.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 modifyModelRequest 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
import { z } from "zod";import { createAgent } from "langchain";import { dynamicSystemPromptMiddleware } from "langchain/middleware";const contextSchema = z.object({ userRole: z.enum(["expert", "beginner"]),});const agent = createAgent({ model: "openai:gpt-4o", tools: [/* ... */], contextSchema, middleware: [ dynamicSystemPromptMiddleware<z.infer<typeof contextSchema>>((state, runtime) => { const userRole = runtime.context.userRole || "user"; const basePrompt = "You are a helpful assistant."; if (userRole === "expert") { return `${basePrompt} Provide detailed technical responses.`; } else if (userRole === "beginner") { return `${basePrompt} Explain concepts simply and avoid jargon.`; } return basePrompt; }), ],});// The system prompt will be set dynamically based on contextconst result = await agent.invoke( { messages: [{ role: "user", content: "Explain machine learning" }] }, { context: { userRole: "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 responseFormat 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
import { z } from "zod";import { MessagesZodState } from "@langchain/langgraph";import { createAgent, type BaseMessage } from "langchain";const customAgentState = z.object({ messages: MessagesZodState.shape.messages, userPreferences: z.record(z.string(), z.string()),});const CustomAgentState = createAgent({ model: "openai:gpt-4o", tools: [], stateSchema: customAgentState,});
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:
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
import { createAgent, type AgentState, AIMessage, RemoveMessage } from "langchain";import { REMOVE_ALL_MESSAGES } from "@langchain/langgraph";const validateResponse = (state: AgentState) => { const lastMessage = state.messages.at(-1)?.content as string; if (lastMessage.toLowerCase().includes("confidential")) { return { messages: [ new RemoveMessage({ id: REMOVE_ALL_MESSAGES }), ...state.messages.slice(0, -1), new AIMessage("I cannot share confidential information."), ], }; } return {};};const agent = createAgent({ model: "openai:gpt-4o", tools, postModelHook: validateResponse,});
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
const stream = await agent.stream( { messages: [new HumanMessage("What's the weather in NYC?")], }, { streamMode: "values" });for await (const chunk of stream) { // Each chunk contains the full state at that point const latestMessage = chunk.messages.at(-1); if (latestMessage?.content) { console.log(`Agent: ${latestMessage.content}`); } else if (latestMessage?.tool_calls) { console.log(`Calling tools: ${latestMessage.tool_calls.map((tc: ToolCall) => tc.name).join(", ")}`); }}