How to Build an LLM Agent Loop (Decide, Act, Observe)

An LLM agent is a loop: call the model, run any tool it requests, feed the result back as an Observation, and repeat until the model signals a FINAL answer. A single LLM call is not an agent - the loop is.

AI Engineeraiagentstool-calling

What makes it an agent

A one-shot LLM call sends a question and returns a reply. An agent adds a control loop: the model can emit an action (e.g. ACTION: calculator | 12*8), your code runs the tool, and the result goes back into the message history so the model can reason over the real output. This is the ReAct pattern (Reason + Act).

Without the loop, the model returns ACTION: calculator | 12*8 as the "answer" - the tool never runs and the model is just guessing.

The loop in Python

import re, requests

LLM_URL = "http://llm-service:8080/v1/chat/completions"

SYSTEM = (
    "You are an agent. To use a tool, reply exactly 'ACTION: calculator | <expr>'. "
    "When you have the answer, reply exactly 'FINAL: <answer>'."
)

def calculator(expr):
    return eval(expr, {"__builtins__": {}}, {})

TOOLS = {"calculator": calculator}

def call_llm(messages):
    r = requests.post(LLM_URL, json={"model": "gpt-4o-mini",
                                      "messages": messages, "temperature": 0},
                      timeout=60)
    r.raise_for_status()
    return r.json()["choices"][0]["message"]["content"].strip()

def run(question, max_steps=5):
    messages = [
        {"role": "system", "content": SYSTEM},
        {"role": "user",   "content": question},
    ]
    steps = []
    for _ in range(max_steps):
        reply = call_llm(messages)
        if reply.startswith("FINAL:"):
            return {"answer": reply[len("FINAL:"):].strip(), "steps": steps}
        m = re.match(r"ACTION:\s*(\w+)\s*\|\s*(.+)", reply)
        if not m:
            return {"answer": reply, "steps": steps}
        tool, tool_input = m.group(1), m.group(2).strip()
        result = TOOLS[tool](tool_input)
        steps.append({"tool": tool, "input": tool_input, "observation": result})
        messages.append({"role": "assistant", "content": reply})
        messages.append({"role": "user",      "content": f"Observation: {result}"})
    return {"answer": "(stopped: max steps reached)", "steps": steps}

Key design decisions

Relation to frameworks

LangGraph, the OpenAI Agents SDK, and CrewAI all implement this same decide-act-observe loop under the hood, with added features: parallel tool calls, structured tool-call APIs (no regex needed), retry logic, and tracing. Writing the raw loop first makes the framework's abstractions obvious rather than magical.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What is a ReAct agent loop?

ReAct (Reason + Act) is a pattern where an LLM alternates between reasoning about a task and calling a tool. The loop runs the tool, feeds the result back as an Observation, and lets the model decide whether to call another tool or return a FINAL answer.

Why do I need a loop - can't the LLM just use a tool in one call?

The LLM can only emit text - it can't execute code itself. The loop is what turns that text (e.g. 'ACTION: calculator | 12*8') into a real tool call and returns the result so the model can reason over actual output, not its own guess.

How do I add more tools to an LLM agent?

Add an entry to the TOOLS dict mapping the tool name to a callable, and describe its syntax in the system prompt. The loop dispatches by name, so no other code changes are needed.

Keep learning

Restore a Broken LLM API IntegrationAI/ML projectHarden an LLM Pipeline Against API FailuresAI/ML projectParse JSON From an LLM (Strip Markdown Fences)AI/ML projectAI/ML roadmapStep by step to hiredAI/ML interview questionsSTAR answersAll AI/ML projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →