Labs / Lab 06

Build a tiny agent loop

Make the think → act → observe cycle visible before a real model starts driving it.

What you'll build

A minimal agent loop that decides, acts, and stops.

By the end of this lab you will have a tiny runtime that keeps state, asks decide_next_step() what to do, runs a tool, records the result, and then asks again. There is no real LLM here on purpose. The control flow is the lesson.

This is the smallest version of an agent that still feels like an agent. It does not just call one function and return. It inspects what it knows, chooses an action, observes the result, and decides whether it has enough to answer.

Run it

cd ai_ecosystem_labs
python3 06-agent-loop/agent_loop.py
Starting here? Quick setup
git clone https://github.com/BanditF/ai_ecosystem_labs
cd ai_ecosystem_labs
python3 06-agent-loop/agent_loop.py

Requires Python 3.8+. No additional packages needed for this lab.

Time guide. Setup: ~2 min. Working through it: 20–35 min if you want to really trace the loop state by state.

Why this piece exists

Agents are not magic. They are loops with state and stopping rules.

A lot of agent talk makes the model sound like the whole system. It is not. The model is usually just the decision-maker inside a larger loop. Something still has to hold state, dispatch tool calls, collect results, and decide when the run is over.

A fair real-world analog is a person doing lightweight research. They look at the question, decide to search once, read what came back, and then either search again or answer. Frameworks like LangGraph make that loop explicit, but the core idea is already here in about 40 lines.

The code

agent_loop.py

Walk through it

Four things worth noticing.

decide_next_step() is the brain

This function inspects the current state and returns a structured decision. In this lab it is plain Python. In a real agent, this is roughly the spot where an LLM call would sit. The important part is not the intelligence source. It is the contract: look at state, then return the next action.

The loop runs until done

There is no fixed number of steps. The runtime keeps asking for the next move until the decision comes back as {"type": "done"}. That is one of the main differences between a single tool call and an agent loop. The agent decides when it has enough.

State accumulates as the run continues

Each tool result gets appended to state["steps"]. That means the next decision can read the full history, not just the most recent request. Even in this tiny example, the final answer depends on a prior tool result stored in state.

Tool calls are just data

The planner returns a dictionary like {"type": "tool_call", "tool": "term_count", "arguments": {...}}. The runtime then dispatches based on that data. This is the same basic shape used by real agent frameworks and tool-calling APIs. The tool request is not special code. It is a structured instruction.

Expected output

What a successful run looks like.

Your file paths may appear as absolute paths depending on where you run the script — that's expected.

Readable trace:

decision 1:
  type: tool_call
  tool: term_count
  arguments: {term: "agent", files: [...]}
  reason: Need evidence before answering.

result 1:
  ok: true
  total: 3

decision 2:
  type: done
  answer: Found 3 mentions of 'agent' across sample docs.

Actual JSON printed by the script:

{
  "goal": "Find where the docs discuss agents.",
  "term": "agent",
  "steps": [
    {
      "decision": {
        "type": "tool_call",
        "tool": "term_count",
        "arguments": {
          "term": "agent",
          "files": [
            "labs/sample_docs/agents.txt",
            "labs/sample_docs/protocols.txt",
            "labs/sample_docs/memory.txt"
          ]
        },
        "reason": "Need evidence before answering."
      },
      "result": {
        "ok": true,
        "items": [
          {
            "file": "labs/sample_docs/agents.txt",
            "count": 2
          },
          {
            "file": "labs/sample_docs/protocols.txt",
            "count": 0
          },
          {
            "file": "labs/sample_docs/memory.txt",
            "count": 1
          }
        ],
        "summary": {
          "total": 3
        }
      }
    }
  ],
  "final": "Found 3 mentions of 'agent' across sample docs."
}

The key thing to notice is the shape: a decision, a tool result recorded in state, and then a final answer once the loop has enough evidence.

Try this

Three things to try before moving on.

  1. Change the search term. Update the initial term in state to a different word and re-run the script. The agent still decides to call the tool, gets a different count, and reaches the same done condition.
  2. Make it take two tool steps. Modify decide_next_step() so it calls the tool twice with different terms before deciding it is done. You have just turned a one-step agent into multi-step reasoning.
  3. Add a step limit. Put a max_steps guard in the loop that stops after 5 iterations. Real agent frameworks usually call this a step limit, budget, or safety cap.

Concepts behind this

For the bigger picture on what agents are and why this loop is the core of the idea, read Agents.

To see how production systems wrap loops like this with extra runtime behavior, read Lab 05: hooks.