Use this file to discover all available pages before exploring further.
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 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.
createAgent() 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 :
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:
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_url, 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, create middleware with wrapModelCall that modifies the model in the request:
import { ChatOpenAI } from "@langchain/openai";import { createAgent, createMiddleware } from "langchain";const basicModel = new ChatOpenAI({ model: "gpt-4o-mini" });const advancedModel = new ChatOpenAI({ model: "gpt-4o" });const dynamicModelSelection = createMiddleware({ name: "DynamicModelSelection", wrapModelCall: (request, handler) => { // Choose model based on conversation complexity const messageCount = request.messages.length; return handler({ ...request, model: messageCount > 10 ? advancedModel : basicModel, }); },});const agent = createAgent({ model: "gpt-4o-mini", // Base model (used when messageCount ≤ 10) tools, middleware: [dynamicModelSelection],});
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.
================================ 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
================================== 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 systemPrompt parameter can be provided as a string:
const agent = createAgent({ model, tools, systemPrompt: "You are a helpful assistant. Be concise and accurate.",});
When no systemPrompt is provided, the agent will infer its task from the messages directly.The systemPrompt parameter accepts either a string 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:
import { createAgent } from "langchain";import { SystemMessage, HumanMessage } from "@langchain/core/messages";const literaryAgent = createAgent({ model: "anthropic:claude-sonnet-4-5", systemPrompt: new 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" } } ] })});const result = await literaryAgent.invoke({ messages: [new 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.
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:
await 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 a simple, universal way to do this with the responseFormat parameter.
import * as z from "zod";import { createAgent } from "langchain";const ContactInfo = z.object({ name: z.string(), email: z.string(), phone: z.string(),});const agent = createAgent({ model: "gpt-4o", responseFormat: ContactInfo,});const result = await agent.invoke({ messages: [ { role: "user", content: "Extract contact info from: John Doe, john@example.com, (555) 123-4567", }, ],});console.log(result.structuredResponse);// {// name: 'John Doe',// email: 'john@example.com',// phone: '(555) 123-4567'// }
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:
import * as z from "zod";import { MessagesZodState } from "@langchain/langgraph";import { createAgent } from "langchain";import { type BaseMessage } from "@langchain/core/messages";const customAgentState = z.object({ messages: MessagesZodState.shape.messages, userPreferences: z.record(z.string(), z.string()),});const CustomAgentState = createAgent({ model: "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.
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.
const stream = await agent.stream( { messages: [{ role: "user", content: "Search for AI news and summarize the findings" }], }, { 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) { const toolCallNames = latestMessage.tool_calls.map((tc) => tc.name); console.log(`Calling tools: ${toolCallNames.join(", ")}`); }}
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.
For comprehensive middleware documentation including hooks like beforeModel, afterModel, and wrapToolCall, see Middleware.