Step 4 · The loop — an agent
What this step adds: the loop. After this file runs, you have built an agent — the rest of the course is about making it trustworthy.
The new idea
Section titled “The new idea”The agent loop: call the model; if it requests a tool, run the tool, append the result to the conversation, and call the model again. Repeat until the model answers in text instead of asking for a tool.
Why a loop and not a fixed sequence? Because you can’t know in advance how many rounds it needs. Maybe one lookup answers the question; maybe the first result raises a second question. The number of iterations is the model’s decision — which is exactly what a while loop is for.
The code
Section titled “The code”Create step4.py:
import ollama
def get_transaction(txn_id): records = {"T-1002": {"amount": 4200, "country": "Nigeria", "card_present": False}} return records[txn_id]
schema = { "type": "function", "function": { "name": "get_transaction", "description": "Look up a transaction's amount, country and card_present flag by its id.", "parameters": { "type": "object", "properties": {"txn_id": {"type": "string"}}, "required": ["txn_id"], }, },}
messages = [{"role": "user", "content": "Is transaction T-1002 suspicious? Explain why."}]
while True: response = ollama.chat(model="qwen2.5:3b", messages=messages, tools=[schema]) messages.append(response.message) if response.message.tool_calls: for call in response.message.tool_calls: result = get_transaction(call.function.arguments["txn_id"]) print("RAN TOOL, got:", result) messages.append({"role": "tool", "content": str(result)}) else: print() print("MODEL:", response.message.content) breakThree lines carry the whole design:
messages.append(response.message)— the model’s own request goes into the history. The model is stateless; this list is its only memory. Skip this line and the tool result arrives as an answer to a question nobody asked.messages.append({"role": "tool", ...})— the result goes back in with the roletool, so the model can tell data it requested from things the user said.break— the exit. No tool calls means the model has what it needs and has answered.
Run it
Section titled “Run it”uv run step4.pyExpect:
RAN TOOL, got: {'amount': 4200, 'country': 'Nigeria', 'card_present': False}
MODEL: Transaction T-1002 appears suspicious: it is a large amount ($4,200),made card-not-present, from Nigeria...Wording varies run to run. What doesn’t vary: the facts in the answer came from your function. In step 1 the model invented them; now it looked them up. Compare the two runs — that difference is the entire value of tools.
Why this is an agent
Section titled “Why this is an agent”Ask the loop a question that doesn’t need the tool and the model answers immediately — one pass, no tool call, straight to break. Ask something needing data and it fetches first. The model is directing its own process: deciding whether to act, acting, reading the result, deciding it’s done. Your code supplies the mechanism; the model supplies the control flow.
That’s the definition worth keeping: an agent is a loop in which the branch is chosen by a model rather than by your code.
What’s still wrong
Section titled “What’s still wrong”Everything this agent concludes is prose. “Appears suspicious” — according to whom, by what rule? If the model phrases it differently tomorrow, has the decision changed? You can’t build a process on paragraphs. Step 5 separates what the model is good at from what should never be left to it.