Open up any agent — Aria included — and you'll find the same six organs. A customer-support copilot, a coding assistant, a flight-booking bot: under the chrome, they're built from the identical parts, wired the identical way. Learn the anatomy once, and you'll recognize it everywhere — and know exactly which part to reach for when something breaks.
The six components
An agent is not one monolithic thing. It's six cooperating parts, each with a single job:
- Input — what the agent receives: a user message, a ticket, an event. The trigger that starts a run.
- Reasoning — the LLM "brain." It reads everything available and decides the next move: answer directly, or call a tool.
- Memory — what the agent remembers within a run (the running conversation) and across runs (past sessions, retrieved knowledge). It feeds context back into reasoning.
- Tools — the abilities the agent could use: look up an order, search docs, call an API. Just capabilities sitting on a shelf until reasoning picks one.
- Actions — reasoning's decision turned into something real: the chosen tool actually runs, and its result comes back.
- Output — the final response handed back to the user (or the next system) once the agent decides it's done.
The magic isn't any one part — it's how they loop. Input enters reasoning. Reasoning, informed by memory, either produces output or requests a tool. A tool call becomes an action; the result lands back in memory; reasoning runs again. The cycle repeats until reasoning decides the task is complete.
These six concepts map almost one-to-one onto the LangGraph constructs you'll build in Section 3. Memory becomes the graph's state (a running list of messages). Reasoning and actions become nodes (an llm_call node and a tool_node). The loop becomes a conditional edge that routes back to reasoning whenever a tool was called. We plant the vocabulary now; we pay it off in §3.
Aria handles one ticket
A customer at Northwind Co. writes: "Where's my order #4471? It was supposed to arrive yesterday." Watch all six parts light up as that one ticket flows through Aria:
- Input → the ticket text arrives.
- Reasoning → the LLM reads it and concludes: "I need the order status before I can answer."
- Memory → the conversation so far (the ticket + Aria's running notes) is in context.
- Tools → Aria has a
get_order_statustool available. - Action → that tool runs with
order_id="4471"and returns{"status": "delayed", "eta": "tomorrow"}. - Reasoning (again) → with the result now in memory, the LLM decides it has enough to reply.
- Output → "Order #4471 is delayed and now expected tomorrow — apologies for the wait."
Here's that same anatomy expressed as the LangGraph constructs we'll formalize later. Don't memorize the API — just notice each labeled part lining up with a component above:
from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
# REASONING — the LLM brain
model = init_chat_model("anthropic:claude-sonnet-4-5")
# TOOLS — an ability sitting on the shelf
@tool
def get_order_status(order_id: str) -> dict:
"""Look up the shipping status of a Northwind order."""
return {"status": "delayed", "eta": "tomorrow"}
# MEMORY lives in the message list we carry through the loop
# INPUT is the first message the user sends
messages = [{"role": "user", "content": "Where's my order #4471?"}]
reasoner = model.bind_tools([get_order_status])
ai = reasoner.invoke(messages) # REASONING decides
if ai.tool_calls: # ACTION: a tool was requested
messages.append(ai)
for call in ai.tool_calls:
result = get_order_status.invoke(call["args"])
messages.append({"role": "tool",
"content": str(result),
"tool_call_id": call["id"]})
ai = reasoner.invoke(messages) # REASONING runs again, now informed
print(ai.content) # OUTPUT back to the customer
The LLM never runs a tool itself — it only asks for one. When ai.tool_calls is populated, the model is saying "please call get_order_status for me." Your code (or LangGraph) executes it and feeds the result back. Skip that step and the agent stalls, waiting on an action that never happens.
Try it
Consider a "book me a flight" agent. A user says: "Find me the cheapest flight from Boston to Lisbon next Friday and book it." Fill in all six boxes for this agent: what's the input, what does the reasoning decide, what's in memory, which tools does it need, what actions fire, and what's the final output?
A filled-in reference
- Input → "Find the cheapest flight Boston→Lisbon next Friday and book it."
- Reasoning → "First I need to search flights; once I have options, pick the cheapest, then book it. Booking is high-stakes — I may confirm with the user first."
- Memory → the request, the user's travel preferences, and (as the loop turns) the search results and chosen flight.
- Tools →
search_flights(origin, dest, date)andbook_flight(flight_id, passenger). - Actions → call
search_flights→ read results → callbook_flighton the cheapest option. - Output → "Booked! TAP Air flight TP218, Fri 7:40 AM, $612. Confirmation #X9K2."
Notice the loop turned twice: search, then book. The number of laps isn't fixed in advance — reasoning keeps cycling until the goal is met. That's the defining trait of an agent.
Did you flag book_flight as risky? Good instinct. Actions that spend money or change the world deserve a human checkpoint — a pattern we'll wire in as a human-in-the-loop interrupt in Section 5. The anatomy tells you where the dangerous step lives.
Takeaway
Next up
You can now dissect any agent into its six parts. But here's the tension: how much should the agent decide on its own? Sometimes a rigid, hard-coded pipeline beats a free-thinking agent — it's cheaper, faster, and never goes rogue.
Every agent is the same loop: input → reasoning (+ memory + tools) → action → output, cycling until the task is done.