# Loop Mechanics

Course: [AI Agents](https://www.devtutorials.ai/learn/ai-agents) · Section: Agent Loops
Source: https://www.devtutorials.ai/learn/ai-agents/loops/loop-mechanics

> What happens inside one turn of the agent loop: input assembly, inference, tool execution and observation capture, plus iteration tracking, action evaluation and the three runtime logs.

Think about a cook following a recipe during a busy dinner service.

For every step, the cook follows the same process. They read the order and check what has already been completed. They decide what to do next, perform the action, then look at the result and make a quick note.

They keep repeating this process until the dish is ready. The steps are the same in every turn, even though the situation changes over time.

An agent loop works in exactly the same way. Each turn, called an **iteration**, follows the same sequence of steps.

**The main idea:** In each iteration, the agent prepares the input, asks the model what to do, performs the action, and saves the result. The next iteration starts with a little more history than before.

## A Real-World Example

When the agent fixes the Brightgrid row count bug, it usually runs about five iterations. During iteration 3, the following happens:

1. The application collects the goal and the results from iterations 1 and 2.
2. It sends that information to the model.
3. The model replies: "write_file web/format.js".
4. The application runs the tool and saves the result: "Saved web/format.js".

Iteration 4 then starts with that saved result included in the history.

**In simple words:** One iteration means "collect, ask, do, save". The loop repeats this process until the goal is reached.

## What You Will Learn

In this lesson, you will learn:

1. The four parts of an iteration: input assembly, inference, tool execution, and observation capture.
2. How the loop keeps track of iterations and updates its state.
3. How to determine whether an action made progress.
4. The three runtime logs: execution history, observation history, and decision records.

The **Trace** tab in the **Agent Lab** now includes an input viewer. Select any step to see the exact messages the brain received during that iteration. Notice how the input grows as more history is added with each step.

## Execution Cycle

The **execution cycle** is the set of four parts that happen during a single iteration.

### Input Assembly

**Input assembly** means building the messages that will be sent to the model. These messages usually include:

- The system instructions and tool list.
- The goal.
- All previous actions and their results.

The model does not remember anything between calls. Because of this, the application must send the complete history every time. That is why the input becomes larger with each iteration.

```js runnable title="The input grows every iteration"
const system = "You are a coding agent. Tools: run_tests, read_file, write_file.";
const goal = "Make every test pass.";
const history = [
  { action: "run_tests", result: "1 passed, 2 failed" },
  { action: "read_file web/format.js", result: "return n + \" row\";" },
  { action: "write_file web/format.js", result: "Saved web/format.js" },
];

const tokens = (text) => Math.ceil(text.length / 4);

for (let i = 0; i <= history.length; i++) {
  const messages = [system, "Goal: " + goal];
  for (const h of history.slice(0, i)) messages.push(h.action, "Result: " + h.result);
  console.log("Iteration " + (i + 1) + ": " + messages.length + " messages, about " + tokens(messages.join("\n")) + " tokens");
}
```

Expected output:

```text
Iteration 1: 2 messages, about 23 tokens
Iteration 2: 4 messages, about 33 tokens
Iteration 3: 6 messages, about 45 tokens
Iteration 4: 8 messages, about 59 tokens
```

Every iteration adds two messages: the action and its result. On long tasks, this growth becomes important. Lesson 13 explains how to keep the history small.

### Inference

**Inference** means asking the model what to do next. The model reads the assembled input and returns a response that describes the next action.

The response is only text. The application must convert that text into a real action. If the response is invalid or incorrectly formatted, the application must detect the problem.

```js runnable title="Turn the model's reply into an action"
function readAction(reply) {
  const start = reply.indexOf("{");
  const end = reply.lastIndexOf("}");
  if (start < 0 || end < start) return { error: "no JSON in the reply" };
  try {
    return JSON.parse(reply.slice(start, end + 1));
  } catch {
    return { error: "the JSON could not be read" };
  }
}

const replies = [
  'Sure! {"tool": "run_tests", "args": {}}',
  '{"tool": "read_file", "args": {"path": "web/format.js"',
  "I think the bug is in the format file.",
];

for (const reply of replies) console.log(JSON.stringify(readAction(reply)));
```

Expected output:

```text
{"tool":"run_tests","args":{}}
{"error":"no JSON in the reply"}
{"error":"no JSON in the reply"}
```

The first reply contains extra text, but the JSON inside it is valid. The second reply is incomplete because the closing brace is missing.

The third reply does not contain any JSON at all. The application detects both problems before running anything.

### Tool Execution

**Tool execution** means the application runs the tool requested by the model. Before running the tool, the application verifies the tool name and checks the inputs, just as you learned in Lesson 3.

### Observation Capture

**Observation capture** means saving the tool's result in a format that the model can use in the next iteration.

Two rules help create good observations:

- **Keep it short.** Very large results make the input grow quickly, so long outputs should be shortened to a safe size.
- **Mark errors clearly.** Start failures with `"ERROR:"` so the model can easily recognize them.

```js runnable title="Capture a clean observation"
function capture(result, maxChars) {
  if (!result.ok) return "ERROR: " + result.text;
  if (result.text.length > maxChars) return result.text.slice(0, maxChars) + "... (" + (result.text.length - maxChars) + " more characters)";
  return result.text;
}

console.log(capture({ ok: true, text: "3 passed, 0 failed" }, 40));
console.log(capture({ ok: false, text: "No file named web/fromat.js" }, 40));
console.log(capture({ ok: true, text: "x".repeat(100) }, 40));
```

Expected output:

```text
3 passed, 0 failed
ERROR: No file named web/fromat.js
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx... (60 more characters)
```

The long result is shortened, and the note shows how much content was removed. The error begins with "ERROR:", making it easy for the next thought to recognize and respond to the failure.

Now run an agent and open the **Trace** tab. Select step 1 and then step 5 in the input viewer.

Step 1's input contains only the instructions and the goal. Step 5's input contains the instructions, the goal, and all previous actions and results. This shows how input assembly grows from one iteration to the next.

**Easy Way to Remember:** Every iteration follows **collect, ask, do, save**. Collect the input, ask the model, run the tool, and save the observation.

## Loop Progression

### Iteration Tracking

**Iteration tracking** means counting the turns in the loop. The count is used for the step limit and for logs. It also helps communicate progress, such as "the agent is on step 4 of a maximum of 8 steps."

### State Updates

A **state update** changes what the agent knows after each iteration. For example: "tests run: yes" or "failing: 2".

The state should only be updated from real observations. Never update the state based on what the model *thinks* or *expects* happened.

### Action Evaluation

**Action evaluation** means asking: "Did that action move us closer to the goal?" A simple way to measure this is to compare a value before and after the action, such as the number of failing tests.

```js runnable title="Track iterations, update the state, evaluate each action"
const state = { iteration: 0, failing: 2 };
const results = [2, 2, 1, 1, 0];

for (const failingNow of results) {
  state.iteration++;
  const change = state.failing - failingNow;
  const verdict = change > 0 ? "progress" : failingNow === 0 ? "done" : "no change";
  state.failing = failingNow;
  console.log("Iteration " + state.iteration + ": failing " + failingNow + " (" + verdict + ")");
}
```

Expected output:

```text
Iteration 1: failing 2 (no change)
Iteration 2: failing 2 (no change)
Iteration 3: failing 1 (progress)
Iteration 4: failing 1 (no change)
Iteration 5: failing 0 (progress)
```

Not every iteration makes progress. Reading files does not reduce the number of failures, and that is fine.

However, if many iterations in a row produce no progress, it can be a warning sign. Lesson 10 turns this idea into a rule.

## Runtime Logs

A **runtime log** is a record that the agent writes while it is running. Good agents keep three types of logs.

### Execution History

The **execution history** records what the agent did. It usually includes the step number, the tool that was used, and whether the action succeeded.

### Observation History

The **observation history** records the results returned by each tool.

### Decision Records

The **decision records** record why the agent chose each action. This is the agent's thought or reasoning for that step.

These three logs answer three important questions:

- What did the agent do?
- What did the agent see?
- Why did the agent do it?

```js runnable title="Three logs answer three questions"
const executionLog = [];
const observationLog = [];
const decisionLog = [];

function record(step, tool, ok, observation, thought) {
  executionLog.push({ step, tool, ok });
  observationLog.push({ step, observation });
  decisionLog.push({ step, thought });
}

record(1, "run_tests", true, "1 passed, 2 failed", "I need to find what is broken.");
record(2, "read_file", true, "return n + \" row\";", "The failure points to web/format.js.");
record(3, "write_file", false, "ERROR: BLOCKED", "I fix the word.");

const failed = executionLog.find((e) => !e.ok);
console.log("What failed: step " + failed.step + ", " + failed.tool);
console.log("What it saw: " + observationLog.find((o) => o.step === failed.step).observation);
console.log("Why it tried: " + decisionLog.find((d) => d.step === failed.step).thought);
```

Expected output:

```text
What failed: step 3, write_file
What it saw: ERROR: BLOCKED
Why it tried: I fix the word.
```

With all three logs, a person can understand the cause of a failure in seconds. The **Trace** tab in the Agent Lab displays all three logs together in a single table.

Never log secrets, passwords, or private customer information. Logs are often viewed by many people and stored for a long time. Remove or mask sensitive values before writing them to a log.

## Common Mistakes

| Mistake | Wrong Idea | Correct Idea | What Goes Wrong |
|---|---|---|---|
| Sending only the most recent step | "The model remembers everything else." | Send the full history in every iteration. | The agent forgets information learned earlier. |
| Running an invalid reply | "The model always returns valid JSON." | Parse and validate the reply before using it. | The application crashes or performs the wrong action. |
| Huge observations | "More details are always better." | Trim long results to a safe size. | The input becomes too large and more expensive to process. |
| Hidden errors | "The model will notice the problem." | Start failures with `"ERROR:"`. | The agent behaves as if the action succeeded. |
| No decision log | "The tool log is enough." | Record the reasoning behind every step. | Nobody can understand why the agent made a decision. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- Each iteration follows the same pattern: **collect, ask, do, save**.
- The input grows with every iteration because the complete history is sent to the model each time.
- Always validate the model's reply before running any action.
- Keep observations short and make errors easy to identify by clearly marking them.
- Maintain three types of logs: what was executed, what observations were returned, and why each decision was made.

Next: **Loop Control and Governance**, how to decide when the loop should stop, and how to keep it inside a budget.
