An agent that decides its own steps โ in about 20 lines of LangChain.
Swipe โ
01
A chain follows your script
from langchain.chat_models import init_chat_model
model = init_chat_model(
"anthropic:claude-sonnet-5")
chain = prompt | model | parser
chain.invoke({"question": q})
# three hops. always three.
LCEL is not deprecated. For a fixed pipeline it is still exactly the right tool.
02
An agent decides
scripted โ a chainprompt | model | parser
decided โ an agentlookup_order โ search_policy โ days_since
"Where is my order 4471, and can I still return it?" took three tool calls โ because the model chose three.
03
Three arguments. That's the agent.
from langchain.agents import create_agent
agent = create_agent(
model="anthropic:claude-sonnet-5",
tools=[lookup_order, search_policy,
days_since],
system_prompt="You are a support agent.",
)
Back comes a compiled graph you call .invoke() on. And it is system_prompt=, never prompt=.
04
A tool is a function
from langchain.tools import tool
@tooldef lookup_order(order_id: str) -> str:
"""Look up an order by its ID."""
return "Order 4471: delivered 12 Sept, โน2,499"
The type hint builds the input schema. No hint, no schema, no call.
What it returns arrives as a ToolMessage.
05
The docstring is the description
vague โ never called"""Order tool."""
specific โ called"""Look up an order's status, delivery date, item and amount by its ID."""
That one line is all the model reads before deciding your tool is worth a call.
A tool nobody picks never raises an error.
06
One trip round the loop
Ask, run what it asked for, feed the results back, ask again. It stops when the model wants no more tools.
07
Messages are the state
1SystemMessageyour system_prompt, once at the top
2HumanMessagethe question, exactly as typed
3AIMessage.text โ or .tool_calls when it wants one
4ToolMessagewhat came back, tagged with tool_call_id