agents · Level 3
The loop harness: how AI agents actually run
The scaffolding that turns one model call into a working agent: context, tools, permissions, budgets, stop conditions, logs and tests.

Your next step
Start this course online
Register your start, then work through the lessons and exercises at your own pace. You can download the PDF separately. Where a scored exam is available, you will find it after the lessons.
Register or resume your course
Start with the essentials
The short answer
A loop harness, also called an agent harness, is the program that runs a language model in a loop. On each turn it assembles the context, calls the model, parses any tool request, runs the tool if the rules allow, feeds the result back and repeats until a stop condition is met. The harness, not the model, enforces permissions, budgets, logging and safety.
What you will learn
- You will be able to explain what a loop harness is and how it differs from the model inside it.
- You will be able to read a minimal agent loop and say what each part does.
- You will know how to set permissions, sandboxes, budgets and stop conditions for an agent.
- You will understand why logging, tamper-evident audit trails and testing matter, and how to start both.
- You will be able to recognise prompt injection and name the guardrails that limit its damage.
- You will have a complete harness design on paper for a real task of your own.
Who it is for
Builders and technically curious readers who want to understand or design an AI agent. You should be able to read a short Python example, but you do not need to write any code to do the exercises.
Before you start
- Comfort with prompts, tokens and context windows (the What is AI workbook covers them). Having read AI agents explained helps but is not essential.
Learn in your browser · Chapter 01 of 06
What a harness is, and why it matters
On its own, a language model can only produce text in response to its input. It cannot open a file, search the web or send an email. Something has to sit around it and do those things on its behalf. That something is the harness.
A loop harness (also called an agent harness) is the program that runs a model in a loop. It is ordinary software, written by people, wrapped around a model. Together they make an agent: a system that pursues a goal over many steps by choosing an action, seeing what happened and choosing again.
In plain words, the harness builds what the model sees and calls it. If the model asks for a tool, the harness checks the request, runs the tool and adds the result to the conversation. Then it goes round again until a stop condition is met.
Scroll sideways to see every column.
| Job | Done by |
|---|---|
| Decide what to do next, and write the tool arguments | The model |
| Check that a request is allowed and well formed | The harness |
| Actually run the tool | The harness |
| Decide what the model sees next turn | The harness |
| Count steps, tokens, time and money | The harness |
| Decide when to stop, and record what happened | The harness |
Two agents on the same model can behave very differently: one has sensible limits and a good log, the other runs up a bill or changes the wrong file. Published research has explored interleaving a model's reasoning with actions in the outside world (see Sources).
Learn in your browser · Chapter 02 of 06
One turn, step by step
Read the loop first as a story with six beats, then as code.
- Assemble. Build the full prompt: system prompt, tool definitions, the task and the conversation so far.
- Call. Send it to the model. The reply is either plain text or one or more tool requests.
- Parse. Read the reply. A tool request names a tool and gives its arguments, usually as structured data.
- Check and run. Confirm the tool exists, the arguments are valid and the action is allowed. Ask a human if the rules say so. Then run it.
- Feed back. Add the tool's result, or its error, to the conversation as a new message.
- Repeat or stop. Check the budgets and stop conditions. If none has triggered, go round again.
Here is a minimal loop in illustrative Python. It is vendor-neutral: client.chat stands for whichever model access you use.
# Illustrative pseudocode: every helper is a placeholder you would write.
import time
def run_agent(task, client, tools, limits):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": task},
]
steps, tokens_used = 0, 0
started = time.monotonic()
recent = []
while True:
# 1. Check budgets BEFORE spending anything.
if steps >= limits.max_steps:
return finish("step_limit", messages)
if tokens_used >= limits.max_tokens:
return finish("token_limit", messages)
if time.monotonic() - started >= limits.max_seconds:
return finish("time_limit", messages)
# 2. Ask the model what to do next.
reply = client.chat(messages=messages, tools=describe(tools))
steps += 1
tokens_used += reply.usage_tokens
messages.append(reply.as_message())
audit_log("model_reply", steps, reply)
# 3. No tool request: the model believes it is done.
if not reply.tool_calls:
return finish("final_answer", messages, reply.text)
# 4. Run each requested tool through the safety checks.
for call in reply.tool_calls:
signature = (call.name, str(call.arguments))
recent.append(signature)
if recent[-3:].count(signature) == 3:
return finish("stuck_repeating", messages)
result = run_tool_safely(call, tools)
audit_log("tool_result", steps, call, result)
messages.append(tool_message(call.id, result))
# 5. Keep the context inside its limit.
messages = compact_if_needed(messages, limits.max_context_tokens)def run_tool_safely(call, tools):
tool = tools.get(call.name)
if tool is None:
return "error: no such tool"
problem = check_arguments(call.arguments, tool.schema)
if problem:
return "error: bad arguments: " + problem
if tool.needs_approval and not ask_human(call):
return "error: a human declined this action"
try:
return tool.run(call.arguments, timeout_seconds=tool.timeout)
except Exception as exc:
return "error: " + str(exc)[:300] # short; never put secrets in errorsNotice what the model never does: it never runs anything and never sets its own limits. A failing tool does not crash the loop, because run_tool_safely turns problems into short error text the model can react to. And every planned exit goes through finish, which records why the run ended.
Task: What do the three invoices in the folder "march" come to?
Turn 1 model -> tool call: list_files(folder="march")
harness -> ["inv1.txt", "inv2.txt", "inv3.txt"]
Turn 2 model -> tool call: read_file(path="march/inv1.txt")
harness -> "Total due: 120.00 GBP"
Turn 3 model -> tool call: read_file(path="march/inv2.txt")
harness -> "Total due: 75.50 GBP"
Turn 4 model -> tool call: read_file(path="march/inv3.txt")
harness -> "Total due: 42.25 GBP"
Turn 5 model -> text: "The invoices come to 237.75 GBP."
(no tool call, so the loop ends)Try it yourself · Activity 01
10 minBe the harness
The transcript above is one complete run of a small agent. Read it as the person who built the harness.
- On Turn 2, mark where each of the six beats happens.
- Write the stop condition that ended the run, and the budget that would have stopped it if the model had kept calling tools.
- The model added up the three numbers itself. Name one extra tool that would let the harness do the arithmetic instead.
Which of the six beats was hardest to point to? That is the part of the loop to reread.
Learn in your browser · Chapter 03 of 06
What the model sees: prompt, tools and memory
The model is stateless. Each call starts from nothing, so on every turn the harness must rebuild everything the model needs to know. Getting that assembly right is most of the craft.
The system prompt is the standing set of instructions at the start of every call. A good one states the role, the goal, the rules, when to ask for help and when to stop. Keep it short, specific and testable. If you cannot imagine a test that would show a rule was broken, the rule is too vague.
Tool definitions tell the model what it may ask for. Each has a name, a plain description of what it does and when to use it, and a schema of its arguments. The model knows a tool only through this description, so a vague one produces wrong calls. Prefer a few narrow tools such as search_orders and read_file over one broad tool such as run_command that can do anything.
Context assembly builds the prompt for each call from the system prompt, the tool definitions, the task, the conversation so far and any saved notes. Memory comes in two kinds. Short-term memory is the conversation held in the context window. Long-term memory is anything saved outside it, such as notes in a file, and loaded back only when relevant. The model remembers nothing by itself. The harness decides what is saved and reloaded.
Every turn adds to the conversation, and every call pays again, in cost and delay, for all of it. Sooner or later the history nears the context limit. Compaction is how the harness copes: it replaces older turns with a short summary and keeps recent turns word for word.
- Pin the system prompt, the task and any hard constraints so they are never summarised away.
- Trim bulky tool results. Keep the useful part, or save the full output to a file and give the model only a reference.
- Test after compaction. A summary can silently drop a detail or a rule, so try tasks that depend on early information.
Learn in your browser · Chapter 04 of 06
Control: permissions, sandboxes, budgets and stops
A harness earns its keep by saying no. These controls decide what an agent may do, how much it may spend and when it must stop.
Permissions. Give the agent the least power it needs (the principle of least privilege) and sort every tool by the damage it could do.
Scroll sideways to see every column.
| Kind of action | Example | Sensible default |
|---|---|---|
| Read only | Look up a record | Allow automatically |
| Reversible change | Edit a draft inside the sandbox | Allow, and log it |
| Hard to undo | Delete data, overwrite a shared file | Ask a human first |
| Leaves the building | Send an email, publish, pay an invoice | Ask a human, showing the exact content |
| Not needed | Anything the task does not require | Do not provide the tool |
A good approval request shows the exact action in plain words, such as the recipient and full text of the email, not just 'Allow?'. Save approvals for actions that matter. If people must approve everything, they soon click yes without reading.
Sandboxing. Run tools that touch files, code or the network inside a boundary: a limited folder, no network unless needed, no secrets in the environment, capped memory and time, and a disposable environment. If the model writes code, it runs inside the sandbox. Never pass model output to eval.
Budgets. Set four kinds of limit and check them before every call, not after.
Scroll sideways to see every column.
| Budget | Catches | Example failure |
|---|---|---|
| Steps | Endless loops | The same search retried again and again |
| Tokens | Ballooning context | A huge document resent on every turn |
| Time | Stalls | A web request that never returns |
| Money | Everything else | Many runs started at once |
When a budget runs out, stop and report what was done and what is left. Do not carry on quietly.
Stop conditions. A run should end for a named reason: a final answer, a passed check (such as a test now succeeding), a spent budget, an agent stuck repeating itself, a person pressing stop, an unrecoverable error, or an attempt to break policy. Always return the reason.
Errors and retries. Send tool errors back to the model as short text so it can adapt. Retry only failures likely to be temporary, such as a timeout or a rate limit, waiting a little longer each time with a small random variation (backoff with jitter) and giving up after a few attempts. Never retry permanent errors, and never blindly retry an action that changes the world, such as taking a payment. Use an idempotency key, or check first whether it already happened.
Try it yourself · Activity 02
15 minSet the limits
Imagine an agent that reads a shared support inbox and drafts replies for a person to send.
- List the tools it needs and mark each one read only, reversible, hard to undo or leaves the building.
- Decide which need human approval and write the exact wording the approver would see.
- Choose starting limits for steps, time and money. These are guesses to tune later, not rules.
For each limit, what is the worst thing that could happen if you set it ten times too high?
Learn in your browser · Chapter 05 of 06
Trust: logs, tests and prompt injection
You will not watch every step. Logs, tests and defences against hostile text are how you keep trust in a system that acts on its own.
Logging and the audit trail. A log is how you find out afterwards what an agent did and why. For every step record a run identifier, a timestamp, the prompt version, each model reply, each tool request with its arguments, each approval or refusal, each result, token counts and the stop reason. Redact secrets and personal data before writing.
A log you can rely on needs tamper-evidence. In a tamper-evident log, each entry carries a fingerprint (a hash) worked out from its own content plus the previous entry's fingerprint. Change or delete an old entry and every later fingerprint stops matching, so the change is detectable. This does not stop anyone editing the file. It makes an edit visible, provided you keep the latest fingerprint somewhere the editor cannot reach.
import hashlib, json
def fingerprint(previous, event):
body = json.dumps(event, sort_keys=True)
return hashlib.sha256((previous + body).encode("utf-8")).hexdigest()
def append_entry(log, event):
previous = log[-1]["hash"] if log else "start"
log.append({"event": event, "hash": fingerprint(previous, event)})
def chain_is_intact(log, kept_hash):
# kept_hash: the latest fingerprint, stored where the editor cannot reach
previous = "start"
for entry in log:
if entry["hash"] != fingerprint(previous, entry["event"]):
return False
previous = entry["hash"]
return previous == kept_hashEvaluation and testing. Agents vary from run to run, so test them like a process, not a single function.
- Write scenarios that check the end result (a file changed, a record created), not exact wording. Run each several times and record how often it passes, its steps and its cost.
- Include scenarios that go wrong on purpose: a failing tool, a tool returning nonsense, hostile text.
- Test the harness with a fake model that returns scripted replies. It is fast, free and repeatable, and it checks that budgets, stops and approvals work.
- Re-run everything whenever you change the prompt, a tool or the model.
Prompt injection. A model reads instructions and data through the same channel: text. So when it reads a web page, email or tool result that contains instructions, it may follow them as if they came from you. That is prompt injection. It is direct when a user types it and indirect when it hides in content the agent fetches while working.
The UK National Cyber Security Centre advises that prompt injection is a risk to reduce by design rather than one that can be fully removed, and OWASP lists it first among its top ten risks for language model applications (see Sources). So build in layers.
- Treat everything the agent reads from outside as untrusted data, and label it so in the prompt.
- Use least privilege, so that a hijacked agent can do little.
- Be wary of any agent that holds private data, reads untrusted content and can send information out. Split those powers across agents, or add approvals.
- Check tool arguments in code with allow-lists, such as permitted recipients or web domains.
- Log everything, so an attack can be seen and studied afterwards.
Try it yourself · Activity 03
15 minAttack your own plan
Take the support inbox agent from the last exercise and try to break it on paper.
- Write three hostile emails, each hiding a different instruction: reveal private data, message a stranger, delete something.
- For each, name the control in your design that stops it: a permission, an allow-list, an approval or the sandbox.
- Where no control stops it, write the control you will add.
Which attack were you least able to stop, and what does that say about where your design is weakest?
Learn in your browser · Chapter 06 of 06
Design a harness on paper
The best way to make the parts stick is to design a whole harness for a real task before writing any code.
Pick a real task from your own life or work: triaging a shared inbox, updating a stock sheet from supplier emails, or researching a topic and producing a briefing with sources. Then work through this sheet.
HARNESS DESIGN SHEET
1. Task, and the test for "done and correct"
2. Tools: name, purpose, read only or not
3. Permissions: free, or needs a human
4. Sandbox: what the agent can reach
5. Context plan: pinned, trimmed, saved
6. Budgets: steps, tokens, time, money
7. Stop conditions and error rules
8. Log: recorded, redacted, fingerprint kept where
9. Tests: five scenarios, two that go wrong
10. Injection: where untrusted text entersTry it yourself · Activity 04
30 minYour harness design sheet
Design a harness on paper for one real task, using the ten lines of the sheet.
- Fill in every line of the sheet in writing, starting with how you would check the task was done correctly.
- Draw the loop as a box diagram, with checks placed before the model call and before each tool.
- Trace one hostile piece of text through your design and mark where it is stopped.
Which line was hardest to fill in? Is that because you do not yet understand the task, or because the design has a gap?
Keep your sheet: when you build, it becomes your specification, test plan and safety case. To go further, read AI agents explained, RAG and knowledge bases, and Reasoning models.
Keep learning
The complete workbook
This workbook opens up the part of an AI agent that most tutorials skip: the harness around the model. You will follow one full turn of the loop, read a minimal loop in Python, and meet every component that helps make an agent safe and dependable, from permissions and budgets to audit logs and tests. It ends with a paper design exercise for a real task.
- 01What a harness is, and why it mattersRead here · Reading
On its own, a language model can only produce text in response to its input. It cannot open a file, search the web or send an email. Something has to sit around it and do those things on its behalf. That something is the harness.
- 02One turn, step by stepRead here · 1 exercise
Read the loop first as a story with six beats, then as code.
- 03What the model sees: prompt, tools and memoryRead here · Reading
The model is stateless. Each call starts from nothing, so on every turn the harness must rebuild everything the model needs to know. Getting that assembly right is most of the craft.
- 04Control: permissions, sandboxes, budgets and stopsRead here · 1 exercise
A harness earns its keep by saying no. These controls decide what an agent may do, how much it may spend and when it must stop.
- 05Trust: logs, tests and prompt injectionRead here · 1 exercise
You will not watch every step. Logs, tests and defences against hostile text are how you keep trust in a system that acts on its own.
- 06Design a harness on paperRead here · 1 exercise
The best way to make the parts stick is to design a whole harness for a real task before writing any code.
Also inside: a 9-point checklist, a glossary of 12 terms and 10 questions and answers to test yourself. 4 hands-on exercises, each with a worked answer at the back where the workbook gives one.
The PDF is free. We ask for your name and email before downloading. Read the privacy notice at the download form. Free to download and use for personal learning, study groups and inside your own team. Please do not resell the workbooks or republish them as your own. Link people to trust-agent.ai instead.
Get your free PDF workbook
Enter your name and email, then download the complete workbook. This form also works without JavaScript.
Test yourself
Questions and answers
What is a loop harness?
It is the program that runs a language model in a loop. It builds the context, calls the model, runs any tool the model asks for, feeds the result back and repeats until a stop condition is met. The harness also enforces permissions, budgets and logging.
Is the harness the same thing as the model?
No. The model turns text into text. The harness is ordinary software around it that supplies context, runs tools, applies limits and keeps records. Two agents on the same model can behave very differently because their harnesses differ.
What ends the loop?
A named stop condition. Usually the model replies with an answer and no tool request. Other causes are a passed success check, a spent budget, a repeating pattern, a human pressing stop, an unrecoverable error or a policy breach. Record the reason every time.
Why does an agent need budgets?
Because a loop can run without end. Limits on steps, tokens, time and money stop a confused agent from looping, growing its context or spending without control. Check them before each call and stop with a report when one runs out.
Which actions should need human approval?
Anything hard to undo or that leaves your control, such as deleting data, sending messages, publishing or paying. Show the approver the exact action. Do not ask for approval on trivial steps, or people will stop reading the requests.
What is prompt injection?
It is hostile or unintended instructions hidden in text the model reads, such as a web page or email. Because instructions and data share one channel, the model may obey them. Reduce the risk with least privilege, approvals, allow-lists and treating outside text as untrusted.
What is a tamper-evident log?
A log where each entry includes a fingerprint made from its content and the previous fingerprint. Editing or deleting an old entry breaks every later fingerprint, so the change can be detected. It does not prevent editing, and the latest fingerprint should be stored separately.
How do I test an agent when its answers vary?
Write scenarios with checks on the end result rather than exact wording, and run each several times. Include failure cases and hostile text. Test the harness with a fake model that returns scripted replies, and re-run everything after any change.
Why compact the context instead of sending the whole history?
The history grows every turn, and each call pays for all of it again in cost and delay. It also eventually exceeds the context window. Compaction keeps recent turns and a short summary, while pinning the instructions and the task.
Do I need a framework to build a harness?
No. The core loop is short, as the example in this workbook shows. A framework can help with plumbing, but you should still understand and own the permissions, budgets, stop conditions and logs, because the agent's safety depends on them.
Write as you go
My notes
Use this scratchpad for your own notes, working or answers as you read. Notes are saved in this browser on this device. Downloading makes a local copy; it does not send your notes to Mickai.
Consolidate your learning
Review your work
Work through the exercises and questions, then compare your reasoning with the worked answers. A scored exam and verifiable certificate are not yet available for this course.
Review the questionsLearn the language
Key terms
- Agent harness
- The program that runs a model in a loop, supplying context and tools and enforcing limits. Also called a loop harness.
- Tool call
- A structured request from the model to run a named tool with given arguments. It is only a request until the harness runs it.
- Context assembly
- Building the full prompt for each model call from the system prompt, tool definitions, task, history and any saved notes.
- Compaction
- Replacing older parts of the conversation with a shorter summary so the context stays within its limit.
- Sandbox
- A restricted environment in which tools run, limiting what they can read, change or reach.
- Least privilege
- Giving a system only the access it needs for its task and nothing more.
6 of the workbook's 12 terms. The complete glossary is in the workbook.
Follow the evidence
Sources and checks
Facts last checked: .
These workbooks use AI assistance. See how the workbooks are made.
- OWASP Top 10 for LLM Applications (includes prompt injection)OWASP Gen AI Security Project
- Prompt injection is not SQL injection (it may be worse)National Cyber Security Centre (NCSC)
- ReAct: Synergizing Reasoning and Acting in Language Models (Yao et al., 2022)arXiv
Created by Mickarle Wagstaff-Irons - Micky Irons with the Mickai team. Published by Mickai LTD. Last updated 25 September 2026.
NextKeep going
Where to go next
Recommended for you
What is an AI agent? Agentic AI explained
A plain-English guide to AI agents: how they differ from chatbots and workflows, the parts and the loop, levels of autonomy, the main risks and how to keep a human in control.
Recommended for you
RAG and knowledge bases, step by step
Build a knowledge base a model can answer from: collect, clean, chunk, embed, retrieve, cite and evaluate, with security basics and a hands-on exercise using five short documents.
Recommended for you
Reasoning models and reasoning engines
What reasoning models are, when extra thinking pays off, how to control it, why a reasoning trace is not proof, and how classical reasoning engines differ.