What Makes Something an Agent
An agent is an LLM run in a loop with tools. The model proposes actions; the harness decides what actually happens.
What you will be able to do
- Explain the agent loop: reason, call a tool, observe, repeat
- Separate what the model owns from what the harness owns
- Recognise when a fixed workflow beats an agent
A chatbot answers once and stops. An agent keeps going: it decides on an action, looks at the result, then decides the next action — until the task is done or it is stopped. The difference is not the model. The model can be exactly the same. The difference is the loop around it.
The loop#
| Step | Who does it | What happens |
|---|---|---|
| 1. Context | Harness | Assembles the instructions, the history and the list of available tools |
| 2. Decision | Model | Answers directly, or requests a tool call with specific arguments |
| 3. Execution | Harness | Validates the request, runs the tool, captures the result or the error |
| 4. Observation | Harness | Appends the tool result to the context, then goes back to step 2 |
| 5. Stop | Harness | When the model gives a final answer — or a step, time or cost limit is hit |
What the harness owns#
The simplest loop, no framework#
MAX_STEPS = 10 def run_agent(task, tools, llm): messages = [{"role": "user", "content": task}] for step in range(MAX_STEPS): # stop condition reply = llm(messages, tools=tools.schemas()) messages.append(reply.as_message()) if not reply.tool_calls: # final answer return reply.text for call in reply.tool_calls: try: tools.validate(call) # schema + permissions result = tools.run(call) except Exception as err: result = f"error: {err}" # hand it back to the model messages.append(tool_result(call.id, result)) return "Stopped: step limit reached."That is the whole core — about twenty lines. Agent frameworks add convenience, but every line above is still in there. If you can write this loop yourself, you can debug any framework.
When you do not need an agent#
Agents pay for flexibility with cost, latency and unpredictability. If you know the steps in advance — extract, classify, summarise, store — a fixed workflow of one or a few LLM calls is almost always cheaper, faster and easier to test. An agent earns its keep when the number and order of steps genuinely depend on what is found along the way.
The model requests the tool call delete_file(path="/data/report.csv"). Who carries out the deletion?
Every incoming invoice must be extracted into the same five fields and stored. What is the best design?