create_agent is the main harness factory
create_agent is a highly configurable harness used to build agents. At its simplest, you can create an agent with just a model and tools. It can be configured with basic parameters directly (model, tools, system_prompt) or extended with middleware for more advanced capabilities.
Agent equals model plus harness
An agent consists of two parts: a model and a harness. The harness is everything around the core agent loop, including the prompt, the tools, and any middleware that shapes the model's behavior. The job of a harness is to get the model the right context at the right time for the given task.
Agent definition and loop
An agent is a model calling tools in a loop until a given task is complete. The agent loop consists of the model making decisions and calling tools repeatedly until the task is finished.
Why agents fail
When agents fail, it is usually because the LLM call inside the agent took the wrong action or did not do what was expected. LLMs fail for two reasons: (1) the underlying LLM is not capable enough, or (2) the right context was not passed to the LLM. More often than not, it is the second reason that causes agents to not be reliable.
Agent loop consists of two main steps
A typical agent loop consists of two main steps: (1) Model call - calls the LLM with a prompt and available tools, returns either a response or a request to execute tools; (2) Tool execution - executes the tools that the LLM requested, returns tool results. This loop continues until the LLM decides to finish.
CopilotKit installation for Python backend
Install: uv add copilotkit ag-ui-langgraph fastapi uvicorn. Install middleware with chat model package: pip install -U deepagents copilotkit langchain-openai or uv add deepagents copilotkit langchain-openai.
Frontend SDK capabilities for agent UIs
LangChain frontend SDKs expose these runtime semantics for production agents: durable threads allowing page reload and device switching without losing conversation state; typed agent state to render any state key like todos, pipeline outputs, citations, or custom objects; tool-call lifecycle showing pending, completed, and failed states as UI cards; interrupts to pause for human approval then resume; checkpoints enabling edit, retry, branch, audit, and time-travel flows; nested execution visualization for deep agents and subagents; and framework-native reactivity using idiomatic patterns for React, Vue, Svelte, or Angular.
Extracting checkpoint metadata for timeline display
Transform raw checkpoint data using: index (position in history), id (cp.checkpoint.checkpoint_id), taskName (cp.tasks[0].name or 'unknown'), messageCount (cp.values.messages.length), hasInterrupts (cp.tasks.some(t => t.interrupts.length)), and nextNodes (cp.next array). This enables display-friendly timeline entries instead of raw IDs.
Time travel use cases
Time travel is valuable for: debugging agent behavior by stepping through decisions; undoing actions by resuming from earlier checkpoints; exploring alternatives by forking from mid-conversation checkpoints; auditing complete history for compliance and quality assurance; and teaching by walking through execution step by step.
React useStream example for time travel
import { useStream } from "@langchain/react";
import { useEffect, useState } from "react";
const AGENT_URL = "http://localhost:2024";
export function TimeTravelChat() {
const [threadId, setThreadId] = useState<string | null>(null);
const [history, setHistory] = useState<ThreadState[]>([]);
const stream = useStream<typeof myAgent>({
apiUrl: AGENT_URL,
assistantId: "time_travel",
threadId,
onThreadId: setThreadId,
});
useEffect(() => {
if (!threadId || stream.isLoading) return;
stream.client.threads.getHistory(threadId).then(setHistory);
}, [stream.client, threadId, stream.isLoading]);
function resumeFrom(cp: ThreadState) {
stream.submit({}, {
forkFrom: { checkpointId: cp.checkpoint.checkpoint_id },
});
}
return (
<div className="flex h-screen">
<ChatPanel messages={stream.messages} />
<TimelineSidebar history={history} onSelect={resumeFrom} />
</div>
);
}
This example shows how to set up useStream, fetch checkpoint history, and handle resumption from a selected checkpoint.
Checkpoint history is not destructive
Resuming from a checkpoint does not delete the original timeline. Previous checkpoints remain available in the history, allowing users to always go back and try a different path without losing any prior work.
useStream setup for time travel with checkpoint history
To set up time travel, create a stream using useStream with apiUrl, assistantId, and threadId parameters. After the stream is ready and not loading, fetch checkpoint history explicitly from the LangGraph client using stream.client.threads.getHistory(threadId).
Resume execution from checkpoint using forkFrom
To resume execution from a checkpoint, call stream.submit({}, { forkFrom: { checkpointId: selectedCheckpoint.checkpoint.checkpoint_id } }). This rolls back to the selected checkpoint's state, re-executes the graph from that point forward, and streams new results to the client. The existing messages after the selected checkpoint are replaced by the new execution path, creating a branch in the conversation timeline.
Best practices for time travel UI
Load history lazily by paginating or loading only recent N entries for threads with hundreds of checkpoints. Show meaningful labels like node names and message counts instead of raw checkpoint IDs. Confirm before resuming since it replaces the current execution path. Highlight the current checkpoint visually. Support keyboard navigation with arrow keys for stepping through checkpoints. For advanced users, show state diffs between consecutive checkpoints to reveal how agent state evolved.
Time travel debugging with checkpoints
Time travel lets you inspect any checkpoint, view the exact state the agent held, and resume execution from that point to explore alternative paths. It acts as a debugger, undo button, and audit log combined.
Accessing checkpoint data in timeline
For each checkpoint in history, extract taskName from cp.tasks[0].name (fallback to 'unknown'), messageCount from (cp.values.messages).length, checkpoint ID from cp.checkpoint.checkpoint_id, and upcoming nodes from cp.next array.
Composing multiple architectures in custom workflows
You can compose other architectures within a custom workflow by embedding them as single nodes. For example, a multi-agent system can be embedded as a single node within a custom workflow.
Basic custom workflow implementation TypeScript
Example showing how to build a basic workflow with a LangChain agent in a LangGraph node:
```typescript
import { z } from "zod";
import { createAgent } from "langchain";
import { StateGraph, START, END, StateSchema, MessagesValue } from "@langchain/langgraph";
const agent = createAgent({ model: "openai:gpt-5.5", tools: [...] });
const AgentState = new StateSchema({
messages: MessagesValue,
query: z.string(),
});
const agentNode: GraphNode<typeof AgentState> = (state) => {
// A LangGraph node that invokes a LangChain agent
const result = await agent.invoke({
messages: [{ role: "user", content: state.query }]
});
return { answer: result.messages.at(-1)?.content };
}
// Build a simple workflow
const workflow = new StateGraph(State)
.addNode("agent", agentNode)
.addEdge(START, "agent")
.addEdge("agent", END)
.compile();
```
When to use custom workflows
Use custom workflows when standard patterns (subagents, skills, etc.) do not fit your requirements, you need to mix deterministic logic with agentic behavior, or your use case requires complex routing or multi-stage processing. Each node in a custom workflow can be a simple function, an LLM call, or an entire agent with tools.
Custom workflow architecture definition
Custom workflow is an architecture pattern where you define your own bespoke execution flow using LangGraph, with complete control over the graph structure including sequential steps, conditional branches, loops, and parallel execution.
Custom workflow key characteristics
Custom workflows provide complete control over graph structure, support mixing deterministic logic with agentic behavior, enable sequential steps, conditional branches, loops, and parallel execution, and allow embedding other patterns as nodes in the workflow.
Calling LangChain agent inside LangGraph node
You can call a LangChain agent directly inside any LangGraph node by creating an agent with create_agent(), then invoking it within a node function that takes the graph state and returns a dictionary with the result.
Customer support workflow example with four steps
The example builds a customer support agent with states: warranty_collector (asks about warranty status, calls record_warranty_status to transition), issue_classifier (classifies as hardware/software, calls record_issue_type to transition), and resolution_specialist (provides solutions or escalates). Each step has its own prompt template and tools.
Multi-domain request performance comparison
For a multi-domain request comparing multiple domains (e.g., 'Compare Python, JavaScript, and Rust for web development' with ~2000 tokens per domain agent/skill): Subagents (5 calls, ~9K tokens, best fit), Handoffs (7+ calls, ~14K+ tokens), Skills (3 calls, ~15K tokens), Router (5 calls, ~9K tokens, best fit). For multi-domain tasks, patterns with parallel execution (Subagents, Router) are most efficient. Skills has fewer calls but high token usage due to context accumulation—every subsequent call processes all domain documentation. Handoffs is inefficient for multi-domain—it must execute sequentially and cannot leverage parallel tool calling to consult multiple domains simultaneously.
One-shot request performance: model calls by pattern
For a simple one-shot request (e.g., 'Buy coffee'), model call counts are: Subagents (4 calls), Handoffs (3 calls, best fit), Skills (3 calls, best fit), Router (3 calls, best fit). Handoffs, Skills, and Router are most efficient for single tasks at 3 calls each. Subagents adds one extra call because results flow back through the main agent—this overhead provides centralized control.
Repeat request performance: model calls by pattern
For a repeated request in the same conversation (e.g., 'Buy coffee' then 'Buy coffee again'), model call counts for turn 2 and total: Subagents (4 calls turn 2, 8 total), Handoffs (2 calls turn 2, 5 total, best fit), Skills (2 calls turn 2, 5 total, best fit), Router (3 calls turn 2, 6 total). Stateful patterns (Handoffs, Skills) save 40-50% of calls on repeat requests because they maintain state—the skill context is already loaded in conversation history or the agent is still active from the previous turn. Subagents are stateless by design, so each invocation follows the same flow.
Pattern selection matrix: feature support
Pattern feature support matrix for distributed development, parallelization, multi-hop, and direct user interaction capabilities: Subagents supports distributed development (5/5), parallelization (5/5), multi-hop (5/5), direct user interaction (1/5). Handoffs supports distributed development (0/5), parallelization (0/5), multi-hop (5/5), direct user interaction (5/5). Skills supports distributed development (5/5), parallelization (3/5), multi-hop (5/5), direct user interaction (5/5). Router supports distributed development (3/5), parallelization (5/5), multi-hop (0/5), direct user interaction (3/5).
Deep Agents provides built-in multi-agent support
For built-in multi-agent support, use Deep Agents: a higher-level harness built on LangChain that ships with subagents, skills, planning, a virtual filesystem, and context management.
Multi-agent patterns overview table
There are five main patterns for building multi-agent systems: Subagents (a main agent coordinates subagents as tools, all routing passes through the main agent), Handoffs (behavior changes dynamically based on state, tool calls update a state variable that triggers routing or configuration changes), Skills (specialized prompts and knowledge loaded on-demand, a single agent stays in control while loading context from skills as needed), Router (a routing step classifies input and directs it to one or more specialized agents, results are synthesized into a combined response), and Custom workflow (build bespoke execution flows with LangGraph, mixing deterministic logic and agentic behavior).
Performance summary: pattern comparison across all scenarios
Complete performance comparison table across one-shot, repeat request, and multi-domain scenarios: Subagents (4 calls one-shot, 8 calls repeat, 5 calls/9K tokens multi-domain). Handoffs (3 calls one-shot, 5 calls repeat, 7+ calls/14K+ tokens multi-domain). Skills (3 calls one-shot, 5 calls repeat, 3 calls/15K tokens multi-domain). Router (3 calls one-shot, 6 calls repeat, 5 calls/9K tokens multi-domain).
Pattern mixing is supported
You can mix patterns. For example, a subagents architecture can invoke tools that invoke custom workflows or router agents. Subagents can even use the skills pattern to load context on-demand.
Router: stateless routing design
Routers are stateless—each request requires an LLM routing call. For a repeated request, the router makes the same routing call as the first request, resulting in consistent calls per request with no savings on repeat requests. Can be optimized by wrapping as a tool in a stateful agent.
Pattern selection optimization matrix
Optimization matrix for selecting patterns: For single requests, best patterns are Handoffs, Skills, Router. For repeat requests, best patterns are Handoffs, Skills. For parallel execution, best patterns are Subagents, Router. For large-context domains, best patterns are Subagents, Router. For simple focused tasks, best pattern is Skills.
Handoffs: state persistence across turns
In the handoffs pattern, agent state persists across turns. If a coffee agent is still active from turn 1, no handoff is needed on turn 2—the agent directly calls the buy_coffee tool, saving one model call by skipping the handoff.
Subagents: stateless design tradeoff
Subagents are stateless by design—each invocation follows the same flow. The main agent maintains conversation context, but subagents start fresh each time. This provides strong context isolation but repeats the full flow, resulting in consistent cost per request with no savings on repeated requests.
Multi-agent system fundamentals and when to use
Multi-agent systems coordinate specialized components to tackle complex workflows. However, not every complex task requires this approach—a single agent with the right (sometimes dynamic) tools and prompt can often achieve similar results. Developers seeking multi-agent capabilities are usually looking for one or more of: context management (provide specialized knowledge without overwhelming the model's context window), distributed development (allow different teams to develop and maintain capabilities independently), or parallelization (spawn specialized workers for subtasks and execute them concurrently).
Skills: context loading and reuse
In the skills pattern, skill context is loaded once in conversation history. On repeated requests, there is no need to reload—the agent directly calls tools based on the already-loaded skill documentation, saving one call by reusing the loaded skill.
Agent initialization with skill middleware and checkpointer
Create an agent with skill support by: (1) Initialize your chat model, (2) Create an instance of SkillMiddleware, (3) Call create_agent with the model, system_prompt, middleware list containing SkillMiddleware, and a checkpointer (e.g., InMemorySaver) for state persistence. The middleware injects skill descriptions into the system prompt automatically. The checkpointer maintains conversation history across turns. The agent then has access to skill descriptions and can call load_skill to retrieve full skill content.
TypeScript example of skills pattern implementation
import { tool, createAgent } from "langchain";
import * as z from "zod";
const loadSkill = tool(
async ({ skillName }) => {
// Load skill content from file/database
return "";
},
{
name: "load_skill",
description: `Load a specialized skill.
Available skills:
- write_sql: SQL query writing expert
- review_legal_doc: Legal document reviewer
Returns the skill's prompt and context.`,
schema: z.object({
skillName: z
.string()
.describe("Name of skill to load")
})
}
);
const agent = createAgent({
model: "gpt-5.5",
tools: [loadSkill],
systemPrompt: (
"You are a helpful assistant. " +
"You have access to two skills: " +
"write_sql and review_legal_doc. " +
"Use load_skill to access them."
),
});
This example shows the TypeScript implementation of the skills pattern with Zod schema validation for the skill name parameter.
Skills pattern relationship to Agent Skills and llms.txt
The skills pattern is conceptually identical to Agent Skills and llms.txt (introduced by Jeremy Howard). While llms.txt uses tool calling for progressive disclosure of documentation pages, the skills pattern applies progressive disclosure to specialized prompts and domain knowledge rather than just documentation pages.
Reference awareness in skills
Skills can have reference awareness where each skill's prompt references the location of other assets and provides information on when the agent should use those assets. When those assets become relevant, the agent will know that those files exist and read them into memory as needed to complete tasks. This follows the progressive disclosure pattern and limits the information in the context window.
Hierarchical skills structure
Skills can define other skills in a tree structure, creating nested specializations. For instance, loading a data_science skill might make available sub-skills like pandas_expert, visualization, and statistical_analysis. Each sub-skill can be loaded independently as needed, allowing for fine-grained progressive disclosure of domain knowledge. This hierarchical approach helps manage large knowledge bases by organizing capabilities into logical groupings that can be discovered and loaded on-demand.
Dynamic tool registration pattern for skills
Dynamic tool registration combines progressive disclosure with state management to register new tools as skills load. For example, loading a database_admin skill could both add specialized context and register database-specific tools like backup, restore, and migrate. This uses the same tool-and-state mechanisms used across multi-agent patterns where tools update state to dynamically change agent capabilities.
Basic skill implementation with load_skill tool
A basic skills implementation uses a load_skill tool that loads a specialized skill prompt and context. The tool takes a skill_name parameter and returns the skill's prompt and context. Skills can be registered in the agent's system prompt, and the agent uses the load_skill tool to access them on-demand.
When to use the skills pattern
Use the skills pattern when you want a single agent with many possible specializations, you don't need to enforce specific constraints between skills, or different teams need to develop capabilities independently. Common examples include coding assistants (skills for different languages or tasks), knowledge bases (skills for different domains), and creative assistants (skills for different formats).
Python example of skills pattern implementation
from langchain.tools import tool
from langchain.agents import create_agent
@tool
def load_skill(skill_name: str) -> str:
"""Load a specialized skill prompt.
Available skills:
- write_sql: SQL query writing expert
- review_legal_doc: Legal document reviewer
Returns the skill's prompt and context.
"""
# Load skill content from file/database
...
agent = create_agent(
model="gpt-5.5",
tools=[load_skill],
system_prompt=(
"You are a helpful assistant. "
"You have access to two skills: "
"write_sql and review_legal_doc. "
"Use load_skill to access them."
),
)
This example shows how to create an agent with a load_skill tool that makes available specialized skills like write_sql and review_legal_doc that the agent can invoke on-demand.
Key characteristics of skills pattern
Skills have the following key characteristics: prompt-driven specialization where skills are primarily defined by specialized prompts; progressive disclosure where skills become available based on context or user needs; team distribution where different teams can develop and maintain skills independently; lightweight composition where skills are simpler than full sub-agents; and reference awareness where skills can reference scripts, templates, and other resources.
Skills architecture definition
In the skills architecture, specialized capabilities are packaged as invocable skills that augment an agent's behavior. Skills are primarily prompt-driven specializations that an agent can invoke on-demand.
Async job completion notification pattern
When an async job finishes, surface a notification that, when clicked, sends a HumanMessage like 'Check job_123 and summarize the results' to notify the user and fetch the results.
Parallel execution capability of subagents
The main agent can invoke multiple subagents in a single turn, enabling parallel execution.
Single dispatch tool: context isolation as primary reason
An interesting aspect of the single dispatch approach is that sub-agents may have the exact same capabilities as the main agent. In such cases, invoking a sub-agent is really about context isolation as the primary reason—allowing complex, multi-step tasks to run in isolated context windows without bloating the main agent's conversation history. The sub-agent completes its work autonomously and returns only a concise summary, keeping the main thread focused and efficient.
Subagents vs routers: supervisor pattern
A supervisor agent (subagents pattern) is different from a router. The supervisor is a full agent that maintains conversation context and dynamically decides which subagents to call across multiple turns. A router is typically a single classification step that dispatches to agents without maintaining ongoing conversation state.
Asynchronous subagent execution: background jobs
Use asynchronous execution when the subagent's work is independent—the main agent does not need the result to continue conversing with the user. The main agent kicks off a background job and remains responsive. Use async when subagent work is independent of the main conversation flow, users should be able to continue chatting while work happens, or you want to run multiple independent tasks in parallel. The three-tool pattern: (1) Start job—kicks off the background task and returns a job ID; (2) Check status—returns current state (pending, running, completed, failed); (3) Get result—retrieves the completed result.
Synchronous subagent execution: default mode
By default, subagent calls are synchronous: the main agent waits for each subagent to complete before continuing. Use sync when the main agent's next action depends on the subagent's result, tasks have order dependencies (e.g., fetch data → analyze → respond), or subagent failures should block the main agent's response. Tradeoff: simple implementation, but blocks the conversation and user sees no response until all subagents complete.
Subagent execution modes: sync vs async design decision
Subagent execution can be synchronous (blocking) or asynchronous (background). Choose sync when the main agent needs the result to continue; choose async for independent tasks where users should not wait. Note: 'async' here means the main agent kicks off a background job and continues without blocking, not Python's async/await.
User interaction within subagents using interrupts
While subagents typically return results to the main agent rather than conversing directly with users, you can use interrupts within a subagent to pause execution and gather user input. This is useful when a subagent needs clarification or approval before proceeding. The main agent remains the orchestrator, but the subagent can collect information from the user mid-task.
When to use subagents pattern
Use the subagents pattern when you have multiple distinct domains (e.g., calendar, email, CRM, database), subagents do not need to converse directly with users, or you want centralized workflow control. For simpler cases with just a few tools, use a single agent instead.
Key characteristics of subagents architecture
Key characteristics: (1) Centralized control—all routing passes through the main agent; (2) No direct user interaction—subagents return results to the main agent, not the user (though you can use interrupts within a subagent to allow user interaction); (3) Subagents via tools—subagents are invoked via tools; (4) Parallel execution—the main agent can invoke multiple subagents in a single turn.
Subagents architecture: supervisor coordinates subagents as tools
In the subagents architecture, a central main agent (called a supervisor) coordinates subagents by calling them as tools. The main agent decides which subagent to invoke, what input to provide, and how to combine results. Subagents are stateless—they do not remember past interactions. All conversation memory is maintained by the main agent, providing context isolation where each subagent invocation works in a clean context window, preventing context bloat in the main conversation.
LangSmith tracing automatic support in agents
LangChain agents built with create_agent (Python) or createAgent (JavaScript) automatically support tracing through LangSmith. No extra code is required to enable tracing; agents trace all steps from initial user input to final response including all tool calls, model interactions, and decision points.