Module 3: Agents with create_agent

Introduction: From Manual Tools to Autonomous Agents

Overview

In Module 2 you built the tool execution loop by hand: the user asks, the model decides which tool to call, you execute the tool, wrap the result in a ToolMessage, hand it back to the model, and the model produces the final answer. If it needed more data, you ran the whole cycle again inside a while loop.

It worked. But every time you wanted an assistant with tools, you had to write the same boilerplate: the tool_map, the while loop, the ToolMessage handling, the max_rounds safety net. For one tool that's manageable; for a research agent that chains five tools, it turns tedious and error-prone.

create_agent solves this. One line replaces the entire manual loop: you give it a model and a list of tools, and you get an agent that runs the full cycle on its own. The model reasons about what to do, calls tools, observes results, and repeats until it has enough information to answer. This pattern is called ReAct (Reason + Act), and it's the foundation of modern LLM agents.


Where are we in the guide?

This is Module 3 of LangChain & LangGraph: From Chains to Agents. It's the last module of Block 1 (LangChain Core) before we move on to Middleware and Customization.

Block 1: LangChain Core (Modules 1-4)      ← YOU ARE HERE (Module 3)
Block 2: LangGraph Fundamentals (Modules 5-7)
Block 3: Advanced LangGraph (Modules 8-10)
Block 4: Production (Modules 11-12)
Your progress in Block 1:

Module 1: Models and Providers            ✅ Done
    │
    ▼
Module 2: Tools and Tool Calling          ✅ Done
    │
    ▼
Module 3: Agents (create_agent)           ← YOU ARE HERE
    │
    ▼
Module 4: Middleware and Customization    🔒 Next

In Module 1 you learned to connect to models. In Module 2 you gave them tools and wrote the manual loop to run them. Now you're going to automate that loop: the agent takes care of the entire reasoning-and-execution cycle.


The bridge: from manual loop to autonomous agent

What you already know

In Module 2 you implemented this:

from dotenv import load_dotenv
load_dotenv()

from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, ToolMessage

@tool
def search(query: str) -> str:
    """Search the web for information."""
    return f"Results for '{query}': LangChain is a framework for LLMs."

tools = [search]
tool_map = {t.name: t for t in tools}

model = init_chat_model("openai:gpt-4.1-mini")
model_with_tools = model.bind_tools(tools)

messages = [HumanMessage(content="What is LangChain?")]

for _ in range(5):
    response = model_with_tools.invoke(messages)
    messages.append(response)

    if not response.tool_calls:
        break

    for tc in response.tool_calls:
        try:
            result = str(tool_map[tc["name"]].invoke(tc["args"]))
        except Exception as e:
            result = f"Error: {e}"
        messages.append(ToolMessage(content=result, tool_call_id=tc["id"]))

print(response.content)
# LangChain is a framework for building applications with LLMs.

That's ~20 lines of boilerplate for every assistant you want to build. The tool_map, the for loop, the try/except, the ToolMessage, the max_rounds... all of it repeats every single time.

What you'll learn here

With create_agent, the same result in 5 lines:

from dotenv import load_dotenv
load_dotenv()

from langchain.agents import create_agent
from langchain_core.tools import tool

@tool
def search(query: str) -> str:
    """Search the web for information."""
    return f"Results for '{query}': LangChain is a framework for LLMs."

agent = create_agent("openai:gpt-4.1-mini", tools=[search])
result = agent.invoke({"messages": [("user", "What is LangChain?")]})
print(result["messages"][-1].content)
# LangChain is a framework for building applications with LLMs.

No tool_map. No while loop. No manual ToolMessage. No max_rounds. The agent handles it all internally.


Why agents: the problem with the manual loop

The manual loop works for simple cases, but it runs into real problems in complex scenarios:

ProblemManual loopAgent
Repetitive boilerplate15-20 lines for every assistant2-3 lines
Complex multi-roundYou have to manage the while and max_roundsAutomatic with recursion_limit
Error handlingManual try/except on every tool callBuilt in (returns the error as a ToolMessage)
Parallel tool callsYou have to iterate over tool_callsHandled internally
StreamingNeeds significant extra logicagent.stream() built in
PersistenceNot available without extra codecheckpointer as a parameter

The manual loop is valuable for understanding how tool calling works. The agent is what you use to build real systems.

An analogy: the dispatcher

In the manual loop, you are the dispatcher. The model tells you "I need to call search", you run search, hand back the result, and ask "anything else?". You're the middleman wiring each piece together.

With an agent, the model is the dispatcher. It takes the question, decides which tools to call, runs the full cycle, and hands you the final answer. You just define the tools and ask the question — the agent takes care of the rest.

It's the difference between driving stick and driving automatic. Both get you there, but one makes you handle every step of the process.


The ReAct pattern: Reason + Act

create_agent agents use the ReAct pattern (Reasoning + Acting). The model alternates between two phases in a loop:

┌──────────────────────────────────────────────────────────┐
│                    ReAct Loop                            │
│                                                          │
│  ┌──────────┐     ┌──────────┐     ┌──────────┐        │
│  │  Reason  │────▶│   Act    │────▶│ Observe  │───┐    │
│  │ (Model)  │     │  (Tool)  │     │ (Result) │   │    │
│  └──────────┘     └──────────┘     └──────────┘   │    │
│       ▲                                            │    │
│       └────────────────────────────────────────────┘    │
│                                                          │
│  Stop condition: the model answers with no tool_calls    │
└──────────────────────────────────────────────────────────┘

Step by step:

  1. Reason — The model looks at the question and decides what to do. "The user wants the weather in Madrid. I'm going to call get_weather."
  2. Act — The model emits a tool_call and the agent runs the tool.
  3. Observe — The tool's result gets appended as a ToolMessage and the model receives it.
  4. Repeat — The model decides whether it needs more information. If it does, back to Reason. If not, it writes the final answer.

The ReAct flow in action

User: "What's Japan's population and its GDP per capita?"

Round 1 — Reason:
  "I need two data points: population and GDP. I'll look up both."
  → tool_call: search("Japan population 2024")
  → tool_call: search("Japan GDP per capita 2024")

Round 1 — Act:
  → search("Japan population 2024") = "125 million people"
  → search("Japan GDP per capita 2024") = "USD 33,800"

Round 1 — Observe:
  The model receives both results.

Round 2 — Reason:
  "I have everything I need. Time to answer."
  → No tool_calls → produces the final answer

Answer: "Japan has roughly 125 million people and a GDP per
capita of USD 33,800."

The model chose to run two searches in parallel (parallel tool calls), observed the results, and decided it had enough information to answer. All of that happened automatically inside the agent.


create_agent as a high-level abstraction

create_agent isn't magic — it's an abstraction over LangGraph. Under the hood it builds a graph with two nodes wired in a loop:

┌─────────────┐       tool_calls        ┌─────────────┐
│   Model     │─────────────────────────▶│    Tools    │
│   Node      │                          │    Node     │
│             │◀─────────────────────────│             │
└─────────────┘      ToolMessages       └─────────────┘
       │
       │ no tool_calls
       ▼
    [Answer]
  • Model Node: Calls the LLM with the message list. If the response contains tool_calls, it routes to the Tools Node. If not, it returns the answer (done).
  • Tools Node: Runs each tool call and appends the results as ToolMessage. Back to the Model Node.

This is exactly the while loop you wrote by hand in Module 2, only packaged as a compiled LangGraph graph. Which means it inherits everything LangGraph can do: checkpointing, streaming, interrupts, and more.

You don't need to know LangGraph to use create_agent. But knowing it's a graph underneath explains why it takes parameters like checkpointer, interrupt_before, and recursion_limit.


What you'll master in this module

By the end of these 8 capsules, you'll be able to:

  • ✅ Create agents with create_agent(model, tools) in a single line
  • ✅ Understand the ReAct loop and its stop conditions
  • ✅ Configure static and dynamic system prompts
  • ✅ Manage agent state with state_schema (TypedDict)
  • ✅ Stream the agent's reasoning process
  • ✅ Get structured output from agents with response_format
  • ✅ Translate legacy code (AgentExecutor, LLMChain) to modern APIs

Module map

CapsuleTopicWhat you'll learn
02create_agent and the ReAct loopcreate_agent(model, tools), how it works internally, stop conditions, recursion_limit
03Static and dynamic system promptssystem_prompt as a string/SystemMessage, @dynamic_prompt for contextual prompts
04Agent state and memoryAgentState, state_schema with TypedDict, custom state, conversation history
05Streaming agentsagent.stream() with stream_mode, processing chunks, streaming tool calls inside agents
06Structured output in agentsresponse_format, ToolStrategy vs ProviderStrategy, structured_response
07Legacy vs modern: API mappingEquivalence table, why migrate, how to translate legacy code
08Project: Research agent with toolsAn agent that searches, extracts structured data, and writes a report

Learning flow: First you'll master basic agent creation and understand the ReAct loop (02). Then you'll learn to shape its behavior with prompts (03) and custom state (04). Next you'll explore streaming (05) and structured output (06) for production. Finally, you'll learn to translate legacy code (07) and build the capstone project (08).


Connection to the project

This module's mini-project: Research Agent with Tools

In Capsule 08 you'll build a research agent that:

  1. Uses create_agent with 3+ tools (web search, data extraction, calculator)
  2. Has a system prompt that shapes how it does research
  3. Streams the reasoning process — you watch the agent think and act in real time
  4. Produces a structured output as its final report (a Pydantic model with title, findings, sources, conclusion)

Every concept from capsules 02-07 comes together in this project.

Connection to the full guide

The agents you build here are the foundation for everything that follows:

  • Module 4: The middleware system lets you intercept and change an agent's behavior without rewriting it. Want it to use a different model depending on complexity? Filter tools by permission? Middleware handles that.
  • Modules 5-7: In LangGraph, you'll build workflows more complex than a single agent. But create_agent remains your tool for 80% of cases.
  • Modules 8-10: Persistent memory, human-in-the-loop, and multi-agent. The checkpointer, interrupt_before, and name parameters of create_agent are your entry point into those topics.
  • Module 11: Deep Agents push autonomy to the limit — planning, filesystem, subagents. Understanding create_agent is a prerequisite for understanding what Deep Agents adds on top.

Limits: what this module does NOT cover

  • Custom LangGraph workflows — Covered in Modules 5-7. Here you use create_agent as an abstraction; you don't build graphs by hand.
  • Middleware and customization — Covered in Module 4. Here you configure agents with direct parameters; the middleware system comes later.
  • Multi-agent systems — Covered in Module 10. Here you work with a single autonomous agent.
  • Human-in-the-loop — Covered in Module 9. We'll mention interrupt_before/interrupt_after without going deep.
  • Persistent memory across sessions — Covered in Module 8. Here the conversation lives in memory for the duration of the session.

Technical setup

Prerequisites

Before you continue, make sure you have:

  • Module 2 completed — you know how to create tools with @tool, use bind_tools, and run the tool execution loop
  • Python 3.11+ installed
  • ✅ At least one API key from a provider that supports tool calling (OpenAI or Anthropic recommended)

Installation

create_agent lives in langchain but uses langgraph internally to build the agent's graph. You need both packages:

pip install langchain langgraph langchain-openai python-dotenv

If you already had langchain and langchain-openai from Module 1, you just need to add langgraph:

pip install langgraph

Check that everything works

from dotenv import load_dotenv
load_dotenv()

from langchain.agents import create_agent
from langchain_core.tools import tool

@tool
def greet(name: str) -> str:
    """Greet a person by name."""
    return f"Hello, {name}!"

agent = create_agent("openai:gpt-4.1-mini", tools=[greet])
result = agent.invoke({"messages": [("user", "Say hi to María")]})
print(result["messages"][-1].content)
# Expected output: Hello, María! (or something close)

If you see a response with the greeting in it, your setup is ready.

If something breaks:

ErrorCauseFix
ImportError: cannot import name 'create_agent'Old langchain versionpip install --upgrade langchain (you need v1.2+)
ModuleNotFoundError: No module named 'langgraph'langgraph not installedpip install langgraph
NotImplementedError: ... does not support tool callingThe model doesn't support tool callingUse openai:gpt-4.1-mini, anthropic:claude-sonnet-4-20250514, or another model with support

Signs you got it

By the end of this module, you'll know you succeeded if:

  • ✅ You can create an agent with create_agent and have it answer questions using tools
  • ✅ You understand the ReAct loop: reason → act → observe → repeat
  • ✅ Your agent stops on its own once it has enough information
  • ✅ You can configure system prompts and get structured output
  • ✅ You know when to use create_agent vs the manual loop vs a custom LangGraph
  • ✅ Your research project turns autonomous searches into a structured report

A look ahead: from the agent to middleware

In this module you'll build working agents with create_agent. But what happens when you need to customize the behavior without rewriting the agent?

In Module 4, you'll learn the middleware system — interceptors that modify the agent at specific points:

# Module 3: basic agent (what you'll learn here)
agent = create_agent(
    "openai:gpt-4.1-mini",
    tools=[search, calculator],
    system_prompt="You are a research assistant."
)

# Module 4: agent with middleware (what comes next)
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse

@wrap_model_call
def smart_routing(request: ModelRequest, handler) -> ModelResponse:
    """Use a cheap model for simple questions, a powerful one for complex ones."""
    message_count = len(request.state["messages"])
    if message_count > 10:
        return handler(request.override(model=advanced_model))
    return handler(request)

agent = create_agent(
    "openai:gpt-4.1-mini",
    tools=[search, calculator],
    middleware=[smart_routing]
)

Middleware gives you control over the agent without touching its internal logic. It's like adding filters to a camera — the camera works exactly the same, but the photos come out different.


Summary

  • In Module 2 you built the tool execution loop by hand — it worked, but it was tedious and repetitive
  • create_agent automates the whole loop: you give it a model and tools, and the agent reasons, acts, observes, and repeats until it has the answer
  • The ReAct pattern (Reason + Act) is the foundation: the model alternates between reasoning about what to do and executing actions
  • create_agent is an abstraction over LangGraph — under the hood it builds a graph with a model node and a tools node in a loop
  • The key analogy: manual loop = you're the dispatcher; agent = the model is the dispatcher
  • create_agent inherits LangGraph's capabilities: checkpointing, streaming, interrupts, and more
  • This module covers agents with create_agent; Modules 5-7 cover custom workflows with LangGraph for when you need more control
  • You need langchain + langgraph installed (create_agent uses langgraph internally)
  • The capstone project is a research agent with web search, streaming, and structured output

Further reading

  1. LangChain Agents Documentation — Official guide to agents with create_agent
  2. create_agent API Reference — Full reference for parameters and types
  3. ReAct: Synergizing Reasoning and Acting in Language Models — The original ReAct paper (Yao et al., 2022)
  4. LangGraph Agents Overview — How create_agent is built on top of LangGraph
  5. Tool Calling — LangChain Docs — The conceptual basis for the tools the agent uses internally
  6. What's New in LangGraph v1 — Changes from create_react_agent to create_agent

Module 3 — LangChain & LangGraph: From Chains to Agents

Next capsule: create_agent and the ReAct Loop — you'll learn to create agents, understand how they work under the hood, and control their stop conditions.