docs
Complete API reference for creating and executing LangChain agents.
/**
* Creates a production-ready ReAct (Reasoning + Acting) agent with comprehensive configuration options
*
* ReAct agents iteratively reason about tasks and call tools to gather information and take actions.
* This function creates a fully-configured agent with support for tools, structured outputs, state management,
* middleware, and more.
*
* @template TConfig - Agent type configuration (inferred automatically from params)
* @param params - Agent configuration parameters. At minimum, must include a model.
* @param params.model - Language model identifier (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet-20241022")
* or a ChatModel instance for full control. This is effectively required - the agent cannot
* function without a model. String format is "provider:model-name".
* @param params.llm - Alias for params.model (either can be used)
* @param params.tools - Array of tools the agent can call, created with tool() function or a configured ToolNode.
* Tools give agents the ability to take actions like searching, calculating, or making API calls.
* @param params.systemPrompt - System instructions that guide the agent's behavior. Can be a string or SystemMessage.
* Should explain the agent's role, constraints, and available capabilities.
* @param params.prompt - Alias for params.systemPrompt (either can be used)
* @param params.responseFormat - Structured output schema using Zod schemas, JSON schemas, or response format strategies.
* When set, the agent will return a validated structuredResponse in addition to messages.
* Supports single schemas, array of schemas (union types), or strategy instances.
* @param params.stateSchema - Custom state schema for adding persistent state properties beyond built-in state (messages).
* State persists across invocations when using a checkpointer. Can be a Zod object or
* LangGraph AnnotationRoot. Properties from this schema become required/optional parameters
* in agent.invoke() and are accessible in the returned state.
* @param params.contextSchema - Context schema for read-only data provided per invocation but not persisted.
* Useful for request-scoped data like request IDs, user info, or IP addresses.
* Can be a Zod object or LangGraph AnnotationRoot. Passed via config.context in invoke().
* @param params.middleware - Array of middleware instances for extending agent behavior with cross-cutting concerns
* like logging, retries, human-in-the-loop, PII detection, etc. Middleware can add hooks,
* state, context, and tools. Executes in array order for lifecycle hooks.
* @param params.checkpointer - BaseCheckpointSaver instance for persisting agent state across invocations.
* Required for conversation memory and resuming interrupted executions.
* Use thread_id in config to identify separate conversation threads.
* @param params.store - BaseStore instance for long-term memory storage separate from checkpointer.
* Useful for facts, preferences, or data that outlives conversation threads.
* @param params.name - Optional agent name for identification in multi-agent systems or logging
* @param params.description - Optional agent description explaining its purpose or capabilities
* @param params.includeAgentName - How to expose agent name to the model:
* - true: Include in all messages
* - false: Never include (default)
* - "tool_messages": Include only in tool messages
* @param params.signal - AbortSignal for cancelling agent execution. Use AbortSignal.timeout(ms) for timeouts.
* @param params.version - LangGraph version to use ("v1" or "v2"). Defaults to "v2". Usually not needed.
* @returns ReactAgent instance with methods for execution (invoke, stream, streamEvents, batch)
*
* @example Basic agent
* ```typescript
* const agent = createAgent({
* model: "openai:gpt-4o",
* tools: [],
* systemPrompt: "You are a helpful assistant.",
* });
* ```
*
* @example Agent with tools and structured output
* ```typescript
* const searchTool = tool(
* async ({ query }) => `Results for: ${query}`,
* { name: "search", description: "Search", schema: z.object({ query: z.string() }) }
* );
*
* const ResultSchema = z.object({
* answer: z.string(),
* sources: z.array(z.string()),
* });
*
* const agent = createAgent({
* model: "openai:gpt-4o",
* tools: [searchTool],
* responseFormat: ResultSchema,
* systemPrompt: "You are a research assistant.",
* });
* ```
*
* @example Agent with state and persistence
* ```typescript
* import { MemorySaver } from "@langchain/langgraph";
*
* const StateSchema = z.object({
* userId: z.string(),
* preferences: z.object({ theme: z.string() }).optional(),
* });
*
* const agent = createAgent({
* model: "openai:gpt-4o",
* tools: [],
* stateSchema: StateSchema,
* checkpointer: new MemorySaver(),
* });
*
* // First invocation
* await agent.invoke(
* { messages: [{ role: "user", content: "Hi, I'm Alice" }], userId: "user-123" },
* { configurable: { thread_id: "thread-1" } }
* );
*
* // Later invocation - state is restored
* const result = await agent.invoke(
* { messages: [{ role: "user", content: "What's my name?" }], userId: "user-123" },
* { configurable: { thread_id: "thread-1" } }
* );
* ```
*/
function createAgent<TConfig>(params: CreateAgentParams): ReactAgent<TConfig>;
interface CreateAgentParams<
StructuredResponseFormat = any,
StateSchema = any,
ContextSchema = any,
TResponseFormat = any
> {
/**
* Language model as string identifier (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet-20241022")
* or model instance for full control
* Required parameter - must be provided
* Note: "llm" is an alias for "model" (both are accepted)
*/
model?: string | ChatModel;
/**
* Alias for model parameter (either model or llm can be used)
*/
llm?: string | ChatModel;
/**
* Array of tools created with tool() function or configured ToolNode
*/
tools?: Tool[] | ToolNode;
/**
* System instructions as string or SystemMessage
*/
systemPrompt?: string | SystemMessage;
/**
* Alias for systemPrompt parameter
*/
prompt?: string | SystemMessage;
/**
* Structured output schema using Zod schema, JSON schema, or response format strategy
* When set, agent will return typed structuredResponse in state
*/
responseFormat?: TResponseFormat;
/**
* Custom state schema for memory and additional state properties (persisted)
* Can be Zod object or LangGraph AnnotationRoot
*/
stateSchema?: StateSchema;
/**
* Context schema for read-only data not persisted across invocations
* Can be Zod object or LangGraph AnnotationRoot
*/
contextSchema?: ContextSchema;
/**
* Array of middleware instances for extending agent behavior
*/
middleware?: AgentMiddleware[];
/**
* Checkpointer for state persistence across invocations
*/
checkpointer?: BaseCheckpointSaver;
/**
* Long-term memory storage (separate from checkpointer)
*/
store?: BaseStore;
/**
* Agent name for identification
*/
name?: string;
/**
* Agent description
*/
description?: string;
/**
* How to expose agent name to the model
* - true: Include in all messages
* - false: Never include
* - "tool_messages": Include only in tool messages
*/
includeAgentName?: boolean | "tool_messages";
/**
* Abort signal for cancellation
*/
signal?: AbortSignal;
/**
* Graph version to use ("v1" or "v2")
* @default "v2"
*/
version?: "v1" | "v2";
}/**
* ReAct agent instance with execution methods
*/
class ReactAgent<TConfig> {
/**
* Execute agent synchronously and return final state
* @param input - User input with messages and optional state
* @param config - Configuration including context and thread_id
* @returns Promise resolving to final state with messages and structuredResponse
*/
invoke(
input: UserInput<TConfig>,
config?: InvokeConfiguration<TConfig>
): Promise<State<TConfig>>;
/**
* Stream agent execution with real-time updates
* @param input - User input with messages and optional state
* @param config - Stream configuration with streamMode option
* @returns Async generator yielding state updates
*/
stream(
input: UserInput<TConfig>,
config?: StreamConfiguration<TConfig>
): AsyncGenerator<State<TConfig>>;
/**
* Stream detailed events during agent execution
* @param input - User input with messages and optional state
* @param config - Stream configuration
* @returns Async generator yielding execution events
*/
streamEvents(
input: UserInput<TConfig>,
config?: StreamConfiguration<TConfig>
): AsyncGenerator<StreamEvent>;
/**
* Execute agent on multiple inputs in parallel
* @param inputs - Array of user inputs
* @param config - Optional batch configuration
* @returns Promise resolving to array of states
*/
batch(
inputs: UserInput<TConfig>[],
config?: BatchConfiguration<TConfig>
): Promise<State<TConfig>[]>;
}/**
* User input for agent invocation
*/
type UserInput<TStateSchema = any> = {
/**
* Array of messages to process
*/
messages: MessageInput[];
} & Partial<InferSchemaInput<TStateSchema>>;
/**
* Message input formats
*/
type MessageInput =
| string // Simple string message (treated as user message)
| BaseMessage // Full message object
| {
role: "user" | "assistant" | "system" | "tool";
content: string;
tool_call_id?: string; // Required for tool role
};/**
* Configuration for agent invocation
*/
interface InvokeConfiguration<ContextSchema = any> {
/**
* Read-only context data not persisted across invocations
*/
context?: InferContextInput<ContextSchema>;
/**
* Configurable options including thread_id for persistence
*/
configurable?: {
/**
* Thread ID for state persistence (requires checkpointer)
*/
thread_id?: string;
[key: string]: any;
};
/**
* Abort signal for cancellation
*/
signal?: AbortSignal;
}
/**
* Configuration for streaming
*/
interface StreamConfiguration<ContextSchema = any> extends InvokeConfiguration<ContextSchema> {
/**
* Stream mode
* - "values": Full state updates
* - "updates": Only changed values
* - "messages": Only new messages
*/
streamMode?: "values" | "updates" | "messages";
}
/**
* Configuration for batch processing
*/
interface BatchConfiguration<ContextSchema = any> extends InvokeConfiguration<ContextSchema> {
/**
* Maximum concurrency for batch processing
*/
maxConcurrency?: number;
}/**
* Agent state returned from invoke/stream
*/
interface State<TConfig = any> {
/**
* Conversation message history
*/
messages: BaseMessage[];
/**
* Structured response when responseFormat is set
*/
structuredResponse?: InferStructuredResponse<TConfig>;
/**
* Additional state properties from stateSchema
*/
[key: string]: any;
}
/**
* Built-in state properties
*/
interface BuiltInState {
/**
* Message history
*/
messages: BaseMessage[];
/**
* Structured response (when responseFormat is used)
*/
structuredResponse?: any;
}/**
* Information about a tool call
*/
interface ToolCall {
id: string;
name: string;
args: Record<string, any>;
}
/**
* Information about a tool result
*/
interface ToolResult {
tool_call_id: string;
content: string;
error?: string;
}
/**
* Executed tool call with result
*/
interface ExecutedToolCall extends ToolCall {
result: string;
error?: Error;
}/**
* Interrupt information from middleware
*/
interface Interrupt<TValue = any> {
value: TValue;
resumable: boolean;
when: "before-tools" | "after-tools" | "before-model" | "after-model";
}
/**
* Jump targets for controlling execution flow
*/
type JumpToTarget = "model" | "tools" | "end";/**
* Runtime context available to middleware
*/
interface Runtime<TContext = any> {
/**
* Read-only context data
*/
context: TContext;
/**
* Configurable options
*/
configurable: {
thread_id?: string;
[key: string]: any;
};
/**
* Additional LangGraph runtime properties
*/
[key: string]: any;
}/**
* Type guard to check if value is a BaseChatModel
*/
function isBaseChatModel(value: any): value is BaseChatModel;
/**
* Type guard to check if model is configurable
*/
function isConfigurableModel(value: any): value is ConfigurableModelInterface;
/**
* Interface for configurable models
*/
interface ConfigurableModelInterface {
model?: string | ChatModel;
[key: string]: any;
}
/**
* Check if tool is a client tool (should run on client)
*/
function isClientTool(tool: Tool): boolean;
/**
* Validate that LLM has no tools bound
*/
function validateLLMHasNoBoundTools(llm: ChatModel): void;
/**
* Check if message has tool calls
*/
function hasToolCalls(message: BaseMessage): boolean;
/**
* Normalize system prompt input
*/
function normalizeSystemPrompt(
prompt: string | SystemMessage | ((state: State) => string | SystemMessage),
state?: State
): SystemMessage;/**
* Thrown when attempting to bind structured output to a model that already has tools bound
*/
class MultipleToolsBoundError extends Error {
name: "MultipleToolsBoundError";
message: "The model already has tools bound to it. Cannot bind structured output format.";
}
/**
* Thrown when multiple structured outputs are returned but only one was expected
*/
class MultipleStructuredOutputsError extends Error {
name: "MultipleStructuredOutputsError";
message: "Multiple structured outputs returned when one was expected";
outputs: any[];
}
/**
* Thrown when structured output parsing fails
*/
class StructuredOutputParsingError extends Error {
name: "StructuredOutputParsingError";
message: string;
cause?: Error;
rawOutput?: string;
}
/**
* Thrown when tool invocation fails
*/
class ToolInvocationError extends Error {
name: "ToolInvocationError";
message: string;
toolName: string;
toolInput: any;
cause?: Error;
}/**
* How to include agent name in messages
*/
type AgentNameMode = boolean | "tool_messages";Complete type inference system for extracting types from agents at compile time.
/**
* Complete agent type configuration
*/
type AgentTypeConfig<TResponse, TState, TContext, TMiddleware, TTools> = {
response: TResponse;
state: TState;
context: TContext;
middleware: TMiddleware;
tools: TTools;
};
/**
* Extract any agent type property
* @template T - Agent type to extract from
* @template K - Key of AgentTypeConfig to extract
*/
type InferAgentType<T, K extends keyof AgentTypeConfig<any, any, any, any, any>> =
T extends ReactAgent<infer Config>
? Config extends AgentTypeConfig<any, any, any, any, any>
? Config[K]
: never
: never;
/**
* Extract response type from agent
* Returns the inferred type of the structured response
* @template T - Agent type
*/
type InferAgentResponse<T> = InferAgentType<T, "response">;
/**
* Extract full merged state from agent (includes built-in state)
* Merges agent state schema with middleware states and built-in state
* @template T - Agent type
*/
type InferAgentState<T> = InferAgentType<T, "state">;
/**
* Extract raw state schema from agent
* Returns the state schema without merging
* @template T - Agent type
*/
type InferAgentStateSchema<T> = T extends ReactAgent<infer Config>
? Config extends { stateSchema: infer Schema }
? Schema
: undefined
: never;
/**
* Extract full merged context from agent
* Merges agent context schema with middleware contexts
* @template T - Agent type
*/
type InferAgentContext<T> = InferAgentType<T, "context">;
/**
* Extract raw context schema from agent
* Returns the context schema without merging
* @template T - Agent type
*/
type InferAgentContextSchema<T> = T extends ReactAgent<infer Config>
? Config extends { contextSchema: infer Schema }
? Schema
: undefined
: never;
/**
* Extract middleware array from agent
* @template T - Agent type
*/
type InferAgentMiddleware<T> = InferAgentType<T, "middleware">;
/**
* Extract tools array from agent
* Includes both agent tools and middleware tools
* @template T - Agent type
*/
type InferAgentTools<T> = InferAgentType<T, "tools">;Usage Example:
import { createAgent, type InferAgentState, type InferAgentResponse } from "langchain";
import { z } from "zod";
const StateSchema = z.object({
userId: z.string(),
count: z.number(),
});
const ResponseSchema = z.object({
answer: z.string(),
confidence: z.number(),
});
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
stateSchema: StateSchema,
responseFormat: ResponseSchema,
});
// Infer state type (includes built-in state)
type State = InferAgentState<typeof agent>;
// { messages: BaseMessage[]; userId: string; count: number; structuredResponse?: { answer: string; confidence: number } }
// Infer response type
type Response = InferAgentResponse<typeof agent>;
// { answer: string; confidence: number }
// Type-safe access
const result = await agent.invoke({
messages: [{ role: "user", content: "Hello" }],
userId: "user123",
count: 1,
});
const answer: string = result.structuredResponse.answer;
const userId: string = result.userId;/**
* Extract union type from Zod schema array
* Useful for inferring union response types
* @template T - Array of Zod schemas
*/
type ExtractZodArrayTypes<T extends readonly any[]> = T extends readonly [
infer First,
...infer Rest
]
? z.infer<First> | ExtractZodArrayTypes<Rest>
: never;
/**
* Convert tools array to message tool set
* Creates a mapped type with tool names as keys
* @template T - Array of tools
*/
type ToolsToMessageToolSet<T extends readonly Tool[]> = {
[K in T[number]["name"]]: Extract<T[number], { name: K }>;
};
/**
* Merge agent and middleware tools
* Combines tools from agent and all middleware
* @template TAgentTools - Agent's tools array
* @template TMiddleware - Middleware array
*/
type CombineTools<
TAgentTools extends readonly Tool[],
TMiddleware extends readonly AgentMiddleware[]
> = [...TAgentTools, ...InferMiddlewareTools<TMiddleware>];
/**
* Infer tools from middleware array
* Recursively extracts tools from all middleware
* @template T - Middleware array
*/
type InferMiddlewareTools<T extends readonly AgentMiddleware[]> =
T extends readonly [infer First, ...infer Rest]
? First extends AgentMiddleware<any, any, any, infer Tools>
? Tools extends readonly Tool[]
? [...Tools, ...(Rest extends readonly AgentMiddleware[] ? InferMiddlewareTools<Rest> : [])]
: Rest extends readonly AgentMiddleware[]
? InferMiddlewareTools<Rest>
: []
: []
: [];
/**
* Infer tools array from middleware array
* @template T - Middleware array or other type
*/
type InferMiddlewareToolsArray<T> = T extends readonly AgentMiddleware[]
? InferMiddlewareTools<T>
: [];
/**
* Check if all properties are optional
* Used for context optionality checking
* @template T - Object type to check
*/
type IsAllOptional<T> = {} extends Required<T> ? true : false;
/**
* Context optionality helper
* Makes context optional if all properties are optional
* @template TContext - Context type
*/
type WithMaybeContext<TContext> = IsAllOptional<TContext> extends true
? { context?: TContext }
: { context: TContext };
/**
* Infer input type from schema
* Handles both Zod schemas and plain objects
* @template T - Schema type
*/
type InferSchemaInput<T> = T extends z.ZodType<infer Output, any, infer Input>
? Input
: T extends { [key: string]: any }
? T
: {};
/**
* Infer context input type
* Handles both Zod schemas and plain objects
* @template ContextSchema - Context schema type
*/
type InferContextInput<ContextSchema> = ContextSchema extends z.ZodType<
any,
any,
infer Input
>
? Input
: ContextSchema extends { [key: string]: any }
? ContextSchema
: {};
/**
* Resolve agent type configuration
* Maps CreateAgentParams to AgentTypeConfig
* @template T - CreateAgentParams type
*/
type ResolveAgentTypeConfig<T> = T extends CreateAgentParams<
infer Response,
infer State,
infer Context,
infer ResponseFormat
>
? AgentTypeConfig<Response, State, Context, any, any>
: DefaultAgentTypeConfig;
/**
* Default agent type configuration
* Used when no custom types are specified
*/
type DefaultAgentTypeConfig = AgentTypeConfig<
undefined,
Record<string, never>,
Record<string, never>,
readonly AgentMiddleware[],
readonly Tool[]
>;
/**
* Any annotation root type
* Placeholder for LangGraph annotation roots
*/
type AnyAnnotationRoot = {
[key: string]: any;
};
/**
* Normalized schema input
* Extracts input type from Zod schema or returns schema as-is
* @template TSchema - Schema type
*/
type NormalizedSchemaInput<TSchema> = TSchema extends z.ZodType<
any,
any,
infer Input
>
? Input
: TSchema;Advanced Type Inference Examples:
// Union response types
const EmailSchema = z.object({ type: z.literal("email"), to: z.string() });
const TaskSchema = z.object({ type: z.literal("task"), title: z.string() });
const agent = createAgent({
model: "openai:gpt-4o",
tools: [],
responseFormat: [EmailSchema, TaskSchema],
});
type Response = InferAgentResponse<typeof agent>;
// { type: "email", to: string } | { type: "task", title: string }
// Tool name extraction
const searchTool = tool(async ({ query }) => "...", {
name: "search",
description: "Search",
schema: z.object({ query: z.string() }),
});
const tools = [searchTool] as const;
type ToolNames = typeof tools[number]["name"];
// "search"
// Tools to message set
type ToolSet = ToolsToMessageToolSet<typeof tools>;
// { search: typeof searchTool }See Type Inference Guide for complete documentation and examples.
/**
* Type guard to check if value is a BaseChatModel
* @param value - Value to check
* @returns True if value is a BaseChatModel instance
*/
function isBaseChatModel(value: any): value is BaseChatModel;
/**
* Type guard to check if model is configurable
* @param value - Value to check
* @returns True if value implements ConfigurableModelInterface
*/
function isConfigurableModel(value: any): value is ConfigurableModelInterface;
/**
* Interface for configurable models
*/
interface ConfigurableModelInterface {
model?: string | ChatModel;
[key: string]: any;
}/**
* Check if tool is a client tool (should run on client)
* @param tool - Tool to check
* @returns True if tool should run on client side
*/
function isClientTool(tool: Tool): boolean;
/**
* Validate that LLM has no tools bound
* Throws error if tools are already bound to prevent conflicts
* @param llm - Chat model to validate
* @throws MultipleToolsBoundError if tools already bound
*/
function validateLLMHasNoBoundTools(llm: ChatModel): void;
/**
* Check if message has tool calls
* @param message - Message to check
* @returns True if message contains tool calls
*/
function hasToolCalls(message: BaseMessage): boolean;/**
* Normalize system prompt input to SystemMessage
* @param prompt - System prompt as string, SystemMessage, or function
* @param state - Optional state for function prompts
* @returns Normalized SystemMessage
*/
function normalizeSystemPrompt(
prompt: string | SystemMessage | ((state: State) => string | SystemMessage),
state?: State
): SystemMessage;/**
* Runtime context available to middleware and agent internals
*/
interface Runtime<TContext = any> {
/**
* Read-only context data
*/
context: TContext;
/**
* Configurable options including thread_id
*/
configurable: {
thread_id?: string;
[key: string]: any;
};
/**
* Additional LangGraph runtime properties
*/
[key: string]: any;
}The agent execution follows this precise flow:
Initialization Phase
Pre-Execution Phase (per invoke/stream)
beforeAgent hooks from all middleware (in array order)Execution Loop (ReAct pattern)
beforeModel hooks from all middlewarewrapModelCall hooks (innermost to outermost)afterModel hooks from all middlewarewrapToolCall hooks for each tool (innermost to outermost)Post-Execution Phase
afterAgent hooks from all middleware (in array order)State Persistence:
thread_id in config.configurableState Merging Order:
State Schema Validation:
Context (read-only, per-invocation):
config.context in invoke/stream callsruntime.contextState (persisted, mutable):
Tool Invocation:
Tool Call Limiting:
toolCallLimitMiddleware to enforce limitsTool Arguments:
Response Format Strategies:
Extraction Process:
structuredResponse in stateUnion Type Handling:
Message Normalization:
Message History Management:
trimMessages or summarizationMiddleware for long conversationsTool Messages:
Hook Execution Order:
beforeAgent: Middleware array order (0 → N)beforeModel: Middleware array order (0 → N)afterModel: Middleware array order (0 → N)afterAgent: Middleware array order (0 → N)wrapToolCall: Nested (innermost middleware wraps outermost)wrapModelCall: Nested (innermost middleware wraps outermost)State/Context Merging:
Tool Merging:
Token Usage:
summarizationMiddleware to manage conversation lengthLatency:
Memory:
Concurrency:
Automatic Recovery:
modelRetryMiddleware)Manual Recovery:
Error Propagation:
For Advanced Users:
The agent is implemented as a LangGraph StateGraph with nodes:
__start__: Entry pointagent: Model invocation nodetools: Tool execution node__end__: Exit pointEdges:
__start__ → agentagent → tools (if tool calls present)agent → __end__ (if no tool calls)tools → agent (loop back after tool execution)Middleware is implemented as graph transforms that:
This architecture allows: