AI Agents & the Agent Loop
What an AI agent really is: an LLM in a loop with tools, a step limit and state. ReAct, planning, and when not to build one.
What it is
An agent is a program that lets a model decide what to do next, do it through tools, look at the result and decide again, until a goal is reached. Strip away the marketing and that is the whole definition: a loop around function calling with some memory and a stop condition.
The canonical shape is ReAct, short for reason and act. Each iteration the model writes a short thought, picks an action (a tool call), the code executes it, and the observation goes back into the context. The loop ends when the model decides it can answer, or when the step limit says it must.
My voice agent is a small agent by this definition. The model has a search tool and a screen-control tool. A visitor asks about my AWS work, the model calls search_portfolio, reads the chunks, calls show_on_screen to display the experience card, then speaks the answer. Three steps, two tools, one loop.
Why it's needed
A single prompt cannot look things up, cannot check its work and cannot take actions in the world. The moment a task needs 'find out X, then based on X do Y', you need a loop. Agents are how LLMs stop being a text box and become software that gets things done.
The loop is also where most of the engineering risk lives. Each extra step multiplies latency, cost and the chance of a wrong turn. Understanding the loop is what lets you decide when a plain pipeline with fixed steps is better than an agent, which is more often than the hype suggests.
How it works
- Give the model a goal, a system prompt describing its tools and limits, and the conversation so far.
- Call the model with the tool schemas. It either answers or returns one or more tool calls.
- Execute each tool call in your code, validate the arguments, and append the results as tool messages.
- Repeat. Track the step count, elapsed time and tokens used; stop at a hard limit and return a partial result rather than running forever.
- Keep state outside the context where possible: a scratchpad of findings, a list of completed sub-tasks, files on disk. Context is expensive memory.
- For bigger tasks, plan first: ask the model for a numbered list of sub-tasks, then run the loop per sub-task. Planning cuts wandering.
Code example
import json, time
from openai import OpenAI
client = OpenAI()
MAX_STEPS, MAX_SECONDS = 8, 30
def run_agent(goal: str, tools: list, funcs: dict, system: str) -> str:
messages = [{"role": "system", "content": system},
{"role": "user", "content": goal}]
start = time.time()
for step in range(MAX_STEPS):
if time.time() - start > MAX_SECONDS:
return "Stopped: time budget exceeded."
r = client.chat.completions.create(model="gpt-4o-mini",
messages=messages, tools=tools)
msg = r.choices[0].message
if not msg.tool_calls:
return msg.content # agent decided it is done
messages.append(msg)
for call in msg.tool_calls: # act
args = json.loads(call.function.arguments)
try:
result = funcs[call.function.name](**args)
except Exception as e: # observation, not a crash
result = f"Tool error: {e}"
print(f"step {step}: {call.function.name}({args}) -> {str(result)[:60]}")
messages.append({"role": "tool", "tool_call_id": call.id,
"content": str(result)})
return "Stopped: step limit reached. Partial findings above."
Where I used this
My voice agent runs a small agent loop inside Pipecat: the LLM has search_portfolio and show_on_screen tools, calls them as the conversation needs, and a hard session timer plus a cheap model keep every call bounded in time and cost.
Use cases
- Research assistants that search, read and summarise across several sources
- Support agents that look up an order, check policy and issue a refund through APIs
- Voice concierges that answer and control a screen at the same time
- Coding assistants that read files, run tests and edit based on the output
- Data tasks: query a database, inspect the result, refine the query
Common mistakes
- No step limit or time limit. An agent that cannot stop will eventually loop on a failing tool and run up the bill.
- Building an agent for a fixed workflow. If the steps are always the same, write the pipeline in code and call the model only where judgement is needed. It is faster, cheaper and testable.
- Letting tool errors crash the loop. Return the error text as the observation; the model usually recovers.
- Giving the agent twenty tools. Selection accuracy drops; group tools or route to specialised sub-agents with a few each.
- Trusting the final answer without logs. Record every step so you can replay a bad run and see where it turned.