The Core Components of a Real AI Agent
An AI agent isn’t just a single call to ChatGPT. That’s a glorified function. A real agent has three parts that work in a loop: a brain, tools, and state. If you miss one of these, you don’t have an agent; you have a script that will break the first time it sees something unexpected.
- The Brain (LLM): This is the decision-making core. It’s an LLM like OpenAI’s GPT-4o or Anthropic’s Claude 3 Opus. Its job isn’t to do the work, but to decide what work needs to be done next. It takes in the current situation and chooses a tool.
- The Tools (APIs): These are the agent’s hands. They are functions the agent can call to interact with the world. This could be a `send_email` function that uses the SendGrid API, a `get_stock_price` function using Finnhub, or a `scrape_website` function. Each tool should do one thing well and return a clear result.
- The State (Memory): This is the most critical and most overlooked part. State is the agent’s short-term memory. It’s a record of the overall goal, the steps taken so far, and the results of those steps. Without it, the agent has no context and can’t perform multi-step tasks. It’s the difference between asking a person to bake a cake versus giving them one instruction at a time with no memory of the previous one.
These three components run in a loop. The brain looks at the state and the goal, picks a tool, the tool runs, and its result is saved back to the state. The loop repeats until the goal is met. That’s it. That’s the core of every useful AI agent I’ve ever built.
What Most Guides Get Wrong
Almost every online tutorial on how to build an AI agent gets the most important part wrong. They show you how to write a clever prompt that can choose between a few functions in a single turn. They call an LLM, it returns a JSON object saying which function to run, the code runs it, and the demo ends. Impressive, but useless in production.
That’s not an agent. It’s a stateless router.
The real work happens in the second, third, and fourth steps. A real agent needs to take the output of the first tool, add it to its memory (the state), and then re-evaluate the goal to decide on the next action. It’s a conversation with itself, mediated by the tools you give it. This requires an orchestrator—a control loop that manages the state and repeatedly calls the LLM brain. Most guides conveniently skip this part because it’s less flashy than a single, clever prompt.
Another mistake is cramming all the logic into one massive god-prompt. Don’t do that. Your agent will be brittle and impossible to debug. Instead, use multiple, smaller, specialized prompts. You might have one prompt for routing (choosing the next tool), another for summarizing scraped text, and a third for drafting an email. This makes the system modular and much easier to fix when one part goes off the rails.
A Practical Build: The Lead Qualification Agent
Let’s build something real. Imagine an agent that takes a company URL, qualifies it as a potential lead, and drafts a personalized email intro. This is a perfect task for an agent because it’s a multi-step process involving data gathering and synthesis.
The Goal: Given `example.com`, find out what the company does, what tech it uses, and draft a two-sentence email intro.
Here’s the process, step-by-step:
- The Orchestrator: This is the master function. It can be a simple serverless function on Vercel or Cloudflare Workers. It initializes an empty state object, like `state = {}`, and starts the main loop.
- The Router Prompt: Inside the loop, the orchestrator calls the LLM with a prompt that tells it to choose the next tool. It’s not a conversational prompt; it’s a command.
You are a task routing system. Based on the goal and the current state, choose the next tool to use.Available Tools:1. `scrape_website(url)`: Scrapes the text content of a given URL.2. `check_tech_stack(url)`: Uses an API to identify technologies on a website.3. `draft_email_intro(context)`: Drafts a personalized email intro based on the provided context.4. `finish(output)`: Completes the task and provides the final output.Goal: Qualify example.com and draft an email intro.Current State: {{state_json}}Respond with ONLY the JSON for the tool to use, like this: {"tool": "scrape_website", "params": {"url": "example.com"}}
- First Loop: The `state_json` is initially empty (`{}`). The LLM will logically choose `scrape_website`. The orchestrator receives `{“tool”: “scrape_website”, “params”: {“url”: “example.com”}}`.
- Tool Execution: The orchestrator calls your actual Python or Node.js function for scraping. Let’s say it returns a big blob of text from the homepage.
- Update State: The orchestrator updates the state object: `state = {“scraped_content”: “ExampleCorp is a leader in synergistic blockchain solutions…”}`.
- Second Loop: The orchestrator runs the loop again. This time, it sends the updated state to the router prompt. The LLM sees the scraped content and now chooses the next logical step: `{“tool”: “check_tech_stack”, “params”: {“url”: “example.com”}}`.
- Rinse and Repeat: The `check_tech_stack` tool (which could be calling the BuiltWith API) runs and its output is added to the state. The state now has scraped content and tech data. In the next loop, the LLM will see that it has enough information and will finally call `draft_email_intro`. Once that’s done, it calls `finish`. The loop terminates.
This stateful, iterative process is what makes an agent powerful. It can reason, gather information over multiple steps, and react to the information it finds.
