1. Home
  2. Blog
  3. Artificial Intelligence

Artificial Intelligence

What Are AI Agents? How They Work, With a Simple Agent Loop You Can Run

An AI agent does more than answer. It takes a goal, chooses actions, checks the results and keeps going. Learn what is inside one, run a small loop in Python, and understand the risks before you trust one.

9 min read
What are AI agents? How they work, with a simple agent loop you can run

Key takeaways

  • An AI agent is typically a language model running in a loop, with tools and a goal.
  • The loop is simple: decide, act, observe the result and repeat until finished.
  • The model only suggests actions. Your code runs them, which is where you enforce the rules.
  • Agents can loop or misuse tools, so set step limits and require approval for risky actions.

Ask a chatbot about a late order, and it gives you some advice. Ask an AI agent, and it can look up the order, check the delay, apply a voucher and report back. The difference is that an agent can take actions and react to what happens, instead of producing just one reply.

The term gets stretched in marketing, so this guide sticks to how these systems actually work. To make it concrete, we built a small agent loop in Python 3.13 and ran it, and the output below comes from those runs. Our “model” is a scripted function, not a real AI model, so that the loop can be run and tested by anyone. It shows the control structure, not intelligence. New to the basics? Read what is a large language model first.

What is an AI agent?

There is no single agreed definition. In current practice, an AI agent is software that pursues a goal by repeatedly deciding what to do next, using tools to act on the world and adjusting based on results. Usually, the decision-making comes from a large language model.

Think of the difference between a person who gives directions and a person who runs the errand. The first answers once. The second heads out, finds the shop closed, tries another one and only comes back when the job is done.

Chatbot, workflow or agent?

Type Who decides the steps? Can it act? Example
Chatbot The user drives each turn Usually not Answers questions from text
Fixed workflow The developer, in advance Yes, on set paths Summarize each ticket, then route it
AI agent The model, at run time Yes, within limits Investigates and resolves a ticket

These exist on a spectrum, and many useful systems are a mix. The key question is how much of the path gets decided at run time.

What is inside an agent

Part Role
Model Decides the next step, based on the goal and what has happened so far
Instructions The goal, rules and limits it should follow
Tools Functions it can request, like searching or calling an API
Memory What it knows from earlier steps or past sessions
Control loop Ordinary code that runs tools and enforces limits

The agent loop

  1. Decide. The model looks at the goal and history, and picks an action or decides it is finished.
  2. Act. Your code runs the requested tool.
  3. Observe. The result is added to the history.
  4. Repeat, until the goal is met or a limit is reached.

That is the core of most agents. The sophistication lies mainly in the model, the tools and the safeguards around this loop.

A loop you can run

Our small agent handles delayed orders. It has two tools, and one of them needs human approval because it costs money:

ORDERS = {
    "A100": {"status": "delayed", "days_late": 6},
    "B200": {"status": "shipped", "days_late": 0},
}

def get_order(order_id):
    return ORDERS.get(order_id, {"error": "order not found"})

def issue_voucher(order_id, amount):
    return {"voucher": f"V-{order_id}", "amount": amount}

TOOLS = {"get_order": get_order, "issue_voucher": issue_voucher}
NEEDS_APPROVAL = {"issue_voucher"}

And here is the loop itself. It is ordinary code, and every safety rule lives here rather than in the model:

def run_agent(goal, model, approve, max_steps=5):
    history = []
    for step in range(1, max_steps + 1):
        decision = model(goal, history)
        if "finish" in decision:
            return decision["finish"]

        name, args = decision["tool"], decision["args"]
        if name in NEEDS_APPROVAL and not approve(name, args):
            result = {"denied": True}
        else:
            result = TOOLS[name](**args)

        history.append({"tool": name, "result": result})
    return None   # stopped: step limit reached

To use a real model, you would replace the model function with a call to an LLM API, passing it the goal, the history and descriptions of your tools. The loop stays almost the same.

What happened in our runs

We tried five situations with the scripted decision function, and logged each step.

A delayed order, with the voucher approved

step 1: get_order({'order_id': 'A100'}) -> {'status': 'delayed', 'days_late': 6}
step 2: issue_voucher({'order_id': 'A100', 'amount': 200}) -> {'voucher': 'V-A100', 'amount': 200}
step 3: finish -> Sorry for the delay. Voucher V-A100 issued.

Each step used the result of the one before it. The voucher was only considered once the delay had been confirmed.

The same order, with approval denied

step 2: issue_voucher({'order_id': 'A100', 'amount': 200}) -> {'denied': True}
step 3: finish -> Your order is late. A person will review compensation.

The human said no, the tool never ran, and the agent adapted its reply.

An unknown order

step 1: get_order({'order_id': 'Z999'}) -> {'error': 'order not found'}
step 2: finish -> I could not find that order.

Giving tools clear error messages helps. A real model can read them and respond sensibly, rather than guessing.

A model that gets stuck

We also swapped in a faulty decision function that repeated the same action forever:

step 1: get_order({'order_id': 'B200'}) -> {'status': 'shipped', 'days_late': 0}
step 2: get_order({'order_id': 'B200'}) -> {'status': 'shipped', 'days_late': 0}
step 3: get_order({'order_id': 'B200'}) -> {'status': 'shipped', 'days_late': 0}
stopped: step limit reached

Agents really can fall into loops like this, and each step with a real model costs money. Without max_steps, this one would never have ended.

How tools work

This is the most misunderstood part. A language model cannot run anything by itself. It produces structured text saying which tool it wants, with what arguments. Your code decides whether to run it. That is why the loop is the right place for permissions, validation and logging.

Model providers offer tool or function calling for this. The Model Context Protocol (MCP) is an open standard for connecting models to tools and data, and our article on Playwright MCP shows one use. Write clear tool names and descriptions, because the model relies on them to choose correctly.

Tip: Start with read-only tools. An agent that can only look things up is far less dangerous than one that can send, delete or pay. Add write access one tool at a time, once you have seen how it behaves.

Memory

Type Holds Example
Short-term The current task’s history The history list in our loop
Long-term Facts kept between sessions User preferences in a database
Knowledge Documents that are retrieved when needed RAG using embeddings

Long tasks can outgrow a model’s context window, so systems often summarize older steps. Summaries can lose details, which is one reason long-running agents may drift off course.

Multi-agent systems

Some systems split work between several specialized agents, such as a planner, a researcher and a reviewer. This can help with large tasks that divide neatly. It also adds cost, more ways to fail and harder debugging. Try a single agent with good tools first, and split it only when there is a clear reason.

Risks and limits

  • Wrong actions. A chatbot’s mistake is a bad sentence. An agent’s mistake might be a sent email or a deleted record.
  • Prompt injection. A web page or email the agent reads could contain hidden instructions. Treat all tool output as untrusted data, not as commands.
  • Compounding errors. A small error early on can send every later step in the wrong direction.
  • Cost and latency. Each step can be another model call, so a task may be much slower and more expensive than a single prompt.
  • Inconsistency. The same goal can take a different path on each run, which makes testing harder.
  • Accountability. People remain responsible for what the system does, so keep records.

Guardrails that matter

  1. Least privilege. Give each agent only the tools and access it needs.
  2. Human approval for anything costly, irreversible or external, as our demo does.
  3. Hard limits on steps, time and spending.
  4. Validate tool inputs in code, and do not rely on instructions to the model.
  5. Log every step, so that you can see why something happened. See monitoring vs observability.
  6. Test with real scenarios, including failures. Check final outcomes as well as individual replies. Our guide to AI agent best practices goes further on deployment.

When to use an agent

Use an agent when Use something simpler when
Steps cannot be known in advance The steps are always the same
The task needs several tools One prompt can do the job
Results change what happens next A fixed workflow would work
Mistakes are cheap, or reviewable Errors are costly and hard to undo

Coding assistants are one of the most mature examples today, since they can read files, run tests, see failures and try again, with a human reviewing the result. A test suite gives them a clear signal of success. Our guide to prompt engineering also applies to writing good agent instructions. Start with the simplest thing that could work, because a well-prompted single call often beats a complicated agent.

Final thoughts

An AI agent is less mysterious than it sounds: a model, some tools and a loop with limits. The hard part is trust, not the code. To learn how they behave, take our small loop, connect it to a real model with read-only tools, and watch the logs as it works. You will soon see where it shines and where it needs a human.

Frequently asked questions

What is an AI agent in simple words?

It is software that works towards a goal by choosing actions, using tools and checking results in a loop, rather than giving only a single reply.

What is the difference between a chatbot and an AI agent?

A chatbot mainly responds to each message. An agent can plan several steps, call tools and change course based on what happens along the way.

Do AI agents need a large language model?

Most modern ones use an LLM for decisions, though the broader idea of software agents is older and can use rules or other AI methods.

Are AI agents safe?

They carry real risks, including wrong actions and prompt injection. Limit their permissions, require approval for risky steps, set hard limits and log everything.

Can an AI agent replace a developer or tester?

Not today in general. They can handle many well-defined tasks, but people still need to set goals, review the output and take responsibility for the result.

Keep learning

Work with Stack Quora

Need help with testing, WordPress or SEO?

We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.

Keep reading

More guides on testing, AI & modern development

Enjoyed this article? Pick a topic and jump straight into our newest tutorials.

Artificial Intelligence

LLM Structured Output Explained: Getting Reliable JSON From AI Models

LLM structured output explained with validation we ran in Pydantic: JSON mode and schemas, constrained decoding, the ways model output…

Read article →
DevOps

SSH Keys Explained: How They Work, ssh-keygen, Ed25519 and Safe Habits

SSH keys explained with keys we generated: public vs private keys, ssh-keygen, Ed25519 vs RSA, fingerprints, passphrases, ssh-agent, authorized_keys, config…

Read article →
DevOps

HTTP Caching Explained: Cache-Control, ETag and 304 Responses, With a Live Test

HTTP caching explained with a small server we ran: Cache-Control directives, max-age, no-cache vs no-store, ETag revalidation and 304 responses,…

Read article →
Test Automation

API Contract Testing Explained: Catch Breaking Changes Before They Ship

API contract testing explained with a schema check we ran: what a contract is, consumer-driven contracts, Pact, OpenAPI, which changes…

Read article →
Software Testing

Risk-Based Testing Explained: Score Risks, Build a Matrix and Test What Matters

Risk-based testing explained step by step: identifying risks, scoring likelihood and impact, building a risk matrix, mapping risk to test…

Read article →
Databases & SQL

SQL Constraints Explained: NOT NULL, UNIQUE, CHECK and FOREIGN KEY, Tested

SQL constraints explained with tests we ran: NOT NULL, UNIQUE, CHECK, DEFAULT, PRIMARY and FOREIGN KEY, the NULL loopholes, SQLite's…

Read article →
Databases & SQL

SQL Date Functions Explained: Adding, Comparing and Grouping Dates Without Bugs

SQL date functions explained with queries we ran: adding intervals, month-end surprises, date differences, the BETWEEN trap, grouping by day,…

Read article →
Software Development

Python asyncio Explained: async, await, gather and TaskGroup With Real Timings

Python asyncio explained with code we timed: coroutines, await, gather, TaskGroup, error handling, timeouts, limiting concurrency and the blocking-call mistake…

Read article →
Software Development

Debounce vs Throttle in JavaScript: How They Work, When to Use Each, Tested

Debounce vs throttle in JavaScript explained with timings we measured in Node.js: how each works, simple implementations, leading and trailing…

Read article →
Software Development

Python Context Managers Explained: The with Statement, contextlib and Real Uses

Python context managers explained with code we ran: how with works, __enter__ and __exit__, contextlib.contextmanager, suppress, ExitStack, database transactions and…

Read article →
Artificial Intelligence

LLM Temperature and Top-p Explained: How AI Models Choose Their Next Word

LLM temperature and top-p explained with a simulation we ran: how probabilities are reshaped, what top-p keeps, recommended settings by…

Read article →
DevOps

YAML Explained: Syntax, the Norway Problem and Other Traps We Tested

YAML explained with a parser test: basic syntax, lists and maps, the Norway problem, versions turning into floats, octal zip…

Read article →
DevOps

Cron Jobs Explained: Syntax, Real Schedules and the Mistakes That Break Them

Cron jobs explained with schedules we verified: the five fields, operators, real examples, the day-of-month OR day-of-week trap, environment problems,…

Read article →
Software Testing

Bug Life Cycle in Software Testing: Every Status, Who Owns It and Common Mistakes

The bug life cycle explained: every defect status from New to Closed, who owns each step, rejected, deferred and reopened…

Read article →
Test Automation

Property-Based Testing in Python: Hypothesis Finds the Bugs Your Examples Miss

Property-based testing explained with Hypothesis tests we ran: properties vs examples, strategies, shrinking, the real bugs it found in our…

Read article →
Databases & SQL

SQL Pagination Explained: OFFSET vs Keyset, With a Million-Row Test

SQL pagination explained with a million-row test we ran: LIMIT and OFFSET, why deep pages get slow, keyset (cursor) pagination,…

Read article →
Databases & SQL

SQL EXISTS vs IN: Differences, the NOT IN NULL Trap and Performance

SQL EXISTS vs IN explained with queries we ran: how each works, why NOT IN returns no rows when a…

Read article →
Software Development

Python Logging Explained: Levels, Handlers, Formatters and Mistakes to Avoid

Python logging explained with code we ran: levels, basicConfig quirks, named loggers, handlers and formatters, logger.exception, lazy formatting, duplicate logs…

Read article →
Software Development

JavaScript Map vs Object: Key Differences, Pitfalls and a Benchmark

JavaScript Map vs Object explained with code we ran in Node.js: key types, ordering, the prototype trap, JSON, size, WeakMap…

Read article →
Software Development

Python Type Hints Explained: Annotations, mypy and the Errors It Catches

Python type hints explained with a real mypy run: basic annotations, Optional and unions, TypedDict, Literal, Protocol, generics, Callable, strict…

Read article →
Artificial Intelligence

LLM Tokens Explained: How AI Models Read Text, Count Costs and Fill Context Windows

LLM tokens explained with a tokenizer we ran: how text is split, why counts differ by language and tokenizer, context…

Read article →
DevOps

Semantic Versioning Explained: MAJOR.MINOR.PATCH, Ranges and Release Rules

Semantic versioning explained with code we ran: MAJOR.MINOR.PATCH rules, pre-releases, correct version sorting, caret and tilde ranges, 0.x versions, lockfiles…

Read article →
DevOps

Git Reset vs Revert: How to Undo Commits Safely, With a Tested Example

Git reset vs revert explained with a repository we built: soft, mixed and hard reset, revert commits, reverting merges, recovering…

Read article →
Software Testing

The Test Pyramid Explained: Layers, Ratios and When to Break the Shape

The test pyramid explained: unit, integration and end-to-end layers, sensible ratios, the ice cream cone anti-pattern, the testing trophy and…

Read article →
Test Automation

pytest Fixtures Explained: Scope, yield Teardown, conftest.py and Factories

pytest fixtures explained with a suite we ran: how fixtures work, scopes, yield teardown, conftest.py, factory fixtures, parametrized fixtures, built-ins…

Read article →
Databases & SQL

SQL Upsert Explained: INSERT ON CONFLICT, MERGE and the REPLACE Trap

SQL upsert explained with queries we ran: INSERT ON CONFLICT DO UPDATE and DO NOTHING, the excluded row, conditional updates,…

Read article →
Databases & SQL

SQL LIKE Operator Explained: Wildcards, Case Sensitivity and Performance

The SQL LIKE operator explained with queries we ran: % and _ wildcards, escaping, NOT LIKE and NULL, case sensitivity…

Read article →
Artificial Intelligence

AI Hallucinations Explained: Why Language Models Make Things Up and How to Reduce It

AI hallucinations explained: what they are, why language models produce them, common types, how to reduce them with grounding and…

Read article →
Test Automation

Mocking in Python: unittest.mock, patch and the Mistakes That Make Tests Lie

Mocking in Python with unittest.mock, tested: Mock and return_value, side_effect, patching where a name is used, spec and autospec, mocking…

Read article →
Software Testing

Code Coverage Explained: Line vs Branch Coverage and What 100% Really Means

Code coverage explained with a real coverage.py run: line, branch and condition coverage, reading a report, why 100% coverage can…

Read article →
Artificial Intelligence

What Are AI Agents? How They Work, With a Simple Agent Loop You Can Run

What are AI agents? A plain guide to the agent loop, tools, memory and guardrails, with a small Python loop…

Read article →
Test Automation

TDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each

TDD vs BDD explained with one discount feature built both ways: red-green-refactor with code we ran, Given-When-Then scenarios, tools, trade-offs…

Read article →
Software Testing

User Acceptance Testing (UAT): Process, Checklist and Real Examples

User acceptance testing explained: who runs it, how it differs from QA, a seven-step process, a UAT test case template,…

Read article →
Artificial Intelligence

What Are Embeddings? Vectors and Cosine Similarity Explained Simply

What are embeddings? A plain-language guide to vectors, cosine similarity and semantic search, with a small example we ran, common…

Read article →
Test Automation

Flaky Tests: What Causes Them and How to Fix Them for Good

Flaky tests explained with demos we ran: the main causes, how to confirm and diagnose one, proper fixes for timing,…

Read article →
Software Testing

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

Load testing vs stress testing explained: what each proves, spike and soak tests, the metrics that matter, why averages mislead,…

Read article →