Core Components of an Agent System
Every working agent is built from the same small set of parts -- an LLM core, tools, memory, retrieval, an orchestrator, and guardrails -- assembled differently for different tasks.
Learning objectives
- Name the six components that recur across nearly all agent architectures
- Explain what each component owns: decisions, actions, or state
- Recognize that most agent bugs trace back to one specific component, not the model
ToolDix original visual
Frame
Name the outcome and constraints.
Build
Try one bounded workflow.
Review
Keep evidence, revise, and share.
One system, six recurring parts
Agent frameworks differ in naming and packaging, but nearly all of them assemble the same six components. Understanding what each one owns makes it much easier to debug a misbehaving agent -- most problems trace to exactly one of these parts, not to the model being "not smart enough."
What each component actually does
| Component | Owns | Typical failure if it's weak |
|---|---|---|
| Orchestrator | Running the loop: call model, route tool calls, track turn count and budget | Infinite loops, no way to stop cleanly |
| LLM core | Deciding what to say or do next given the current context | Bad plans, but often blamed for other components' bugs |
| Tools / actions | The concrete things the agent can actually do (call an API, run code, send a message) | Vague tool descriptions the model misuses or never calls |
| Memory | What the agent remembers within a run, and optionally across runs | The agent repeats work or contradicts an earlier decision |
| Retrieval | Pulling in facts or documents the model wasn't trained on | Confident answers built on stale or missing information |
| Guardrails | Permissions, validation, and stop conditions around every action | An agent takes an action nobody approved, or never stops |
Tools deserve special attention
Of the six, tools are the component builders most often get wrong, because a tool is only as good as its description. The model chooses which tool to call and what arguments to pass based entirely on the text description you give it -- if that description is ambiguous about when to use the tool, or what a successful result looks like, the model will guess, and guesses compound across a multi-step run. A tool description that states its purpose, its inputs, its return shape, and when not to call it will consistently produce more reliable agent behavior than one that just names the function.
Tool quality comparison
The difference between a vague tool description and a precise one is dramatic. Here's an illustrative benchmark showing how tool description quality affects agent reliability across different domains:
| Domain | Vague description example | Precise description example | Estimated error reduction |
|---|---|---|---|
| Data retrieval | "Search for data" | "Retrieve customer records where creation_date > X and status = 'active'. Returns list of {id, email, name, created_at}. Use only for exact field matching." | ~70% |
| API calls | "Call the API" | "Fetch real-time stock price. Input: ticker symbol (string). Output: {price (float), timestamp (ISO 8601), source (string)}. Max 1 call per 2 seconds or rate limit error." | ~65% |
| Complex decisions | "Make a decision" | "Approve or deny a refund request. Inputs: order_id, refund_amount, reason. Output: {approved (bool), reason_code (string), escalate_to_human (bool)}. Requires guardrail check before execution." | ~80% |
These error reduction percentages are illustrative estimates based on internal testing across different agent frameworks; actual improvements depend on model capability, task complexity, and baseline tool design. The pattern is consistent: clear, constraint-aware tool descriptions dramatically improve agent reliability compared to loose, one-liner descriptions.
How the six components interact on one request
The components rarely act alone -- a single request usually touches most of them in sequence. Take "summarize what changed in this customer's account this month." The orchestrator receives the request and starts the loop. The LLM core decides it needs account history, not just its own knowledge. Retrieval pulls the relevant change-log entries for that customer and that month. Memory supplies context from earlier in the same conversation (for instance, which account the user already specified). Tools execute the actual change-log query against a database. Guardrails check that the account being queried belongs to the requesting user before the query runs. Only after all of that does the LLM core produce the final summary, and the orchestrator returns it and ends the loop.
Tracing a bug back to its component
Because each component owns a distinct job, a misbehaving agent is usually diagnosable by asking which job failed. An agent that never stops is an orchestrator problem (no turn limit or budget check), not a "the model won't listen" problem. An agent that repeats a question it already asked is a memory problem -- the answer wasn't retained or wasn't re-surfaced into context. An agent that confidently cites a fact from six months ago as current is a retrieval problem -- stale or missing lookups, not the model "not knowing." An agent that deletes something nobody approved is a guardrails problem, full stop, regardless of how the LLM core reasoned its way there. Separating these ownership boundaries is what turns "the agent is being weird" into an actual, fixable diagnosis.
Real debugging examples
Scenario 1: Agent keeps asking for the same information twice.
Symptom: On iteration 2, the agent calls get_customer_info(customer_id=123) even though it called the exact same tool on iteration 1 and got a result.
Diagnosis: Memory component is failing. The result from iteration 1 was not retained or was cleared before iteration 2. It's not a model problem (the model would use the information if it were in context); it's a memory problem (the information isn't in context).
Solution: Ensure memory retention is working. Print what's in the context before calling the model on iteration 2. If the earlier result is missing, the memory component is the culprit.
Scenario 2: Agent gets stuck in a loop, calling the same tool over and over.
Symptom: Iterations 1-5 all call search_database(query=...) with slightly different queries, but the agent never moves on to the next logical step.
Diagnosis: Orchestrator and memory are probably fine (tools are being called, results are appended). The LLM core is the problem: it's not recognizing that it's done searching and needs to move to the next step. Usually this means the goal or tool description is ambiguous, so the model doesn't know when to stop searching.
Solution: Clarify the goal ("search for X but stop after 3 results") or add a stopping condition to the orchestrator ("after 5 tool calls, force the model to synthesize an answer even if it hasn't signaled it's done").
Scenario 3: Agent calls a tool it shouldn't have permission to call, and the tool executes.
Symptom: The agent calls delete_all_data() and the data is gone.
Diagnosis: Guardrails component is completely absent or not checking permissions. This is a critical failure.
Solution: Add permission checks at the guardrails layer before any tool execution. The orchestrator should check every tool call against a permission list before calling the tool function. If the tool wasn't authorized, return an error to the model instead of executing it.
How components interact in practice: a data pipeline example
To see how these six components work together, consider an agent tasked with "build me a weekly summary of all customer support tickets from the past week that mention billing issues."
Orchestrator receives the goal and initializes the loop with an empty context.
LLM core is called with the goal. Given the request, it reasons: I need to retrieve tickets from a database, filter for billing-related ones, and then summarize them. It decides to call a fetch_support_tickets(days=7, category="billing") tool.
Tools execute the query and return a JSON list of 23 tickets from the past week, each with text, status, and customer ID.
Memory stores that search result so it's not lost in later iterations, and the orchestrator adds it to the context for the next call.
LLM core is called again with the tickets in context. It observes 23 results and recognizes that writing a summary of all 23 would be overwhelming. It decides to first identify patterns in the data -- are most complaints about invoice clarity? Late fees? It might call a analyze_ticket_sentiment_and_keywords(tickets=...) tool.
Tools return aggregated insights: 60% mention invoice clarity, 25% mention late fees, 15% mention refund requests.
Guardrails check: is the LLM allowed to call the sentiment analysis tool? Does the request meet safety criteria? If yes, the result is appended to context.
LLM core is called a third time. Now it has the tickets and the patterns. It decides it can produce a final summary: it has enough information to answer the original question.
Retrieval wasn't used in this example, but in a more complex case, if the model needed to check company billing policies to understand the context of customer complaints, that's when retrieval would pull relevant policy documents and add them to context.
The final answer ties back to decisions made in steps 2, 4, and 6 -- the LLM core calls -- and the information gathered by steps 3 and 5 -- tools and guardrails. If the answer is wrong, you diagnose by asking: did the orchestrator fail to call the model with all the results? Did the tools return the wrong data? Did the LLM core make a bad decision? Did guardrails block something they shouldn't have?
Component strength and agent reliability
A common misconception is that agent quality scales with model capability. In practice, component maturity matters more. A mid-range model with precise tool definitions, a well-managed memory system, and strict stopping conditions will outperform a much larger model with vague tool descriptions, poor memory, and no stopping limit. The components are where most tuning happens; the model is just the decision-making part of a larger system.
Think of it like driving: the model is the driver's judgment (deciding when to turn, how fast to go), but the components are the car's engineering -- quality brakes (guardrails), clear dashboard gauges (memory/context), responsive steering (tools). A careful driver in a poorly engineered car will have more accidents than a reckless driver in a well-engineered one.
Case study: Data pipeline agent (telemetry processing)
Consider an agent tasked with: "Process the daily telemetry data: ingest it, validate it, flag anomalies, and generate a summary report."
Orchestrator initializes the loop with the date and a budget of 15 steps (safeguard against runaway loops).
LLM core is called first. It reasons: "I need to ingest the data file first." Calls ingest_telemetry_data(date="2026-07-24", source="s3://telemetry-bucket/").
Tools execute, returning a JSON structure with 50,000 data points. Memory stores this large result, but only a summary is added to context to avoid exceeding the context window (context compression).
LLM core is called again. It observes the summary and plans: "The data structure looks valid. Now I need to check for anomalies." Calls detect_anomalies(data_summary, threshold=2.5_sigma).
Guardrails check: is the LLM allowed to trigger anomaly detection? Does the request fit the security policy? (yes). Tools execute, returning 3 anomalies detected.
LLM core is called again. Observing the anomalies, it decides: two look like sensor noise, but one looks like a genuine system failure. It calls escalate_alert(anomaly_id=3, severity="high", to="ops-team").
Tools send the alert. Memory records the escalation. LLM core is called again and recognizes it has enough information: data ingested, validated, anomalies processed, escalation sent. It produces a final summary and signals done. Orchestrator stops the loop and returns the summary.
The reliability of this pipeline hinges entirely on components, not model size:
- If the orchestrator has no turn limit, it loops endlessly.
- If guardrails are missing, the escalate_alert call runs without permission, potentially spamming ops.
- If memory doesn't compress earlier results, the context window fills on iteration 3.
- If the tool descriptions are vague ("detect anomalies" with no threshold spec), the model guesses and produces inconsistent results.
- If retrieval isn't used to fetch the anomaly policy, the model cites stale thresholds.
This is why the "six components" framework is so useful for debugging: a broken agent isn't "the model failed"; it's "the orchestrator doesn't have a turn limit" or "guardrails aren't checking permissions before the escalate tool."
Edge case: Circular dependencies between components
One subtle but critical failure mode: when two components depend on each other incorrectly, the agent gets stuck.
Example 1 – Memory and Guardrails: The guardrails component says "don't allow API calls to customer data unless the memory component has recorded prior authorization." But the memory component only records what the LLM core decides to remember, which it does based on what the retrieval component surfaced. If retrieval doesn't fetch the authorization record, memory never stores it, guardrails never clear the check, and the tool call never executes. The diagnosis: trace the chain backward from the failing tool call.
Example 2 – Orchestrator and LLM core: The orchestrator says "stop when the model signals it's done." But the model's stopping signal depends on the tool results in context (the memory component's job). If memory isn't surfacing the results, the model never sees evidence that the goal is met, and keeps looping past the turn limit. Same model, same tool results, but the orchestrator + memory interaction breaks the stopping condition.
These circular dependencies are why component boundaries matter: each component owns one thing, and when ownership is unclear, the agent fails in confusing ways.
Common mistake
Adding more tools to "make the agent more capable." Every additional tool is one more thing the model has to correctly choose between at every step; past a certain point, more tools with overlapping purposes make the model less reliable, not more, because it starts picking the wrong one. Anthropic's own guidance is to keep the tool set minimal and each tool's boundary of responsibility clear, only expanding it when a specific, observed task actually requires it. Each tool should have a single, clear purpose, not "general utility."
Sources and license context
These references informed the lesson. ToolDix adds its own explanation, workflow, and practice rather than reproducing source material. Every link below leaves ToolDix and opens the publisher's own site in a new tab.
- Anthropic: Building Effective Agents (opens anthropic.com in a new tab)External · anthropic.com (Publisher terms apply)
- Anthropic: Introducing the Model Context Protocol (opens anthropic.com in a new tab)External · anthropic.com (Publisher terms apply)
Keep going
Read these next on ToolDix.
Original lessons that build on what you just read.