ToolNode, tools_condition and create_agent

LangGraph · Practical · 10 min

Prebuilts remove boilerplate and handle edge cases; knowing the current names avoids deprecated APIs.

LangGraph's langgraph.prebuilt module packages the agent-loop pieces:

  • ToolNode(tools) is a ready tools node. It runs all tool calls from the last message (in parallel), returns matching ToolMessages, and by default turns tool exceptions into error messages the model can read.
  • tools_condition is the routing function: it returns "tools" when the last message has tool calls, otherwise END.

So the hand-built loop shrinks to adding ToolNode as the "tools" node and calling add_conditional_edges("model", tools_condition). Your graph stays fully customizable around them.

For a standard agent with no custom flow, use LangChain's `create_agent` (from langchain.agents import create_agent). It builds and compiles a LangGraph graph for you, accepts a model name or object, tools and a system prompt, and supports middleware (hooks that run before or after model and tool calls, for example summarizing history or requiring human approval). Because the result is a compiled LangGraph graph, everything in this course (checkpointers, streaming, interrupts, deployment) applies to it.

Older tutorials use langgraph.prebuilt.create_react_agent. It was deprecated in LangGraph 1.0 in favour of create_agent; avoid it in new code.

When to choose what: create_agent first; your own graph with ToolNode when you need extra steps, branches or several agents; a fully hand-built tools node when tool execution itself needs custom logic.

Example

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.prebuilt import ToolNode, tools_condition


@tool
def get_weather(city: str) -> str:
    """Current weather for a city."""
    return f"Sunny in {city}"


# Option 1: your own graph with prebuilt pieces
model = init_chat_model("claude-sonnet-4-6").bind_tools([get_weather])
b = StateGraph(MessagesState)
b.add_node("model", lambda s: {"messages": [model.invoke(s["messages"])]})
b.add_node("tools", ToolNode([get_weather]))
b.add_edge(START, "model")
b.add_conditional_edges("model", tools_condition)   # -> "tools" or END
b.add_edge("tools", "model")
graph = b.compile()

# Option 2: a complete agent (also a compiled LangGraph graph)
agent = create_agent("claude-sonnet-4-6", tools=[get_weather],
                     system_prompt="You are a helpful travel assistant.")
agent.invoke({"messages": [{"role": "user", "content": "Weather in Oslo?"}]})

Remember

  • ToolNode runs tool calls; tools_condition routes to 'tools' or END
  • create_agent (LangChain) builds a full agent graph with middleware
  • create_react_agent is deprecated since LangGraph 1.0
  • Prebuilt agents are compiled graphs: all LangGraph features apply

Watch out for

  • Copying create_react_agent from an old tutorial; migrate to langchain.agents.create_agent.

Check yourself

  1. Which is the current recommended way to get a standard tool-calling agent?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.langchain.com/oss/python/releases/langgraph-v1