PATCHBOOK SERIES Part of the Patchbook Series

Chapter 2: An agent is a for loop

Every conversational AI, coding assistant, and autonomous agent you have ever used shares the exact same core architecture.

Underneath the user interface, ChatGPT, Claude Code, GitHub Copilot, and custom enterprise agents are all driven by a standard while loop.

In its purest form, an agent looks like this:

let chatHistory = [];

while (true) {
    const userInput = await getUserInput();
    chatHistory.push(userInput);

    const chatbotResponse = await sendChatRequestToLlm(chatHistory);
    chatHistory.push(chatbotResponse);

    displayToUser(chatbotResponse);
}

The agent loop: an unspooling chatHistory ribbon circulating through the four-step loop

The four-step heartbeat

Every turn of the loop follows the same four-step cycle:

  1. Capture Input: Wait for input from the outside world.
  2. Append to History: Record the new message at the end of the chatHistory array.
  3. Dispatch to the LLM: Send the accumulated transcript over the wire to the model.
  4. Record the Output: Append the model's response to chatHistory and take action.

Let us explore each of these four steps in depth.


Step 1: Capture Input (The Intake Gate)

The first step of every iteration is acquiring fresh information from outside the loop.

const userInput = await getUserInput();

Step 1: The user types a message at a terminal keyboard and hands the prompt chit to the developer

In a simple web chatbot, this intake gate pauses execution until a human finishes typing a prompt and hits Enter.

In an autonomous coding agent, however, the input does not always come from a human keyboard. The intake gate receives data from diverse external sources:

Regardless of where the raw input originates, the intake stage normalizes the data into a standard message object:

const userInput = {
    role: "user",
    content: "Please fix the failing unit test in auth.spec.ts"
};

Step 2: Append to History (The Scribe's Scroll)

Once input is captured, the host program immediately appends it to an in-memory array known as chatHistory.

chatHistory.push(userInput);

Step 2: The developer and the loop pamphlet stitch the new message chit onto the growing chatHistory scroll

This step sounds trivial, but it represents the single most important architectural responsibility of your application: state accumulation.

The chatHistory array is an append-only chronological ledger of everything that has occurred in the conversation so far:

[
    { role: "system", content: "You are an expert TypeScript engineer." },
    { role: "user", content: "Fix the failing test." },
    { role: "assistant", content: "I will read the test file first." },
    { role: "user", content: "FAIL: Expected 200 OK, received 401 Unauthorized" }
]

Every message retains its exact position in the sequence. Order matters: the LLM reads messages sequentially from beginning to end to reconstruct the context of the session.


Step 3: Dispatch to the LLM (The Outbound Flight)

With the new message recorded, the host program serializes the entire chatHistory array into a JSON payload and transmits it over HTTP POST to the remote model provider.

const chatbotResponse = await sendChatRequestToLlm(chatHistory);

Step 3: The aviator pigeon carries the chatHistory floppy disk across the room to the glowing LLM cube

This step highlights a fundamental reality of modern AI: large language models are completely stateless.

When you send a request to OpenAI, Anthropic, or a local Ollama instance:

If you only sent the single latest user input, the model would receive the prompt with zero context and give an incoherent response.

Because the model retains nothing, your host application acts as the external memory bank. You send the entire accumulated history on every turn. The model processes the full transcript from start to finish, generates the next token sequence, and immediately forgets everything the moment the HTTP response stream completes.


Step 4: Record Output and Decide Next Action (The Receipt & Turnstile)

When the remote model finishes generating its answer, the host program receives the completion payload.

chatHistory.push(chatbotResponse);
displayToUser(chatbotResponse);

Step 4: The LLM cube prints the response chit, the loop pamphlet appends it to the scroll, and the developer evaluates the next branch

This final step closes the loop through two distinct actions:

  1. Record the Assistant Turn: The model's output is wrapped into a message object with role "assistant" and pushed onto chatHistory. This guarantees that on subsequent turns, the model will see its own prior statements.
  2. Evaluate the Turnstile: The host checks what kind of response was returned.

In a basic chatbot, the text is rendered to the user's screen, and the loop returns to Step 1 to await the next human message.

In an agentic system, however, the response might be a tool call rather than a human-facing message. If the model returns a request to execute a bash command or read a file, the host skips waiting for the human: it executes the requested tool, wraps the tool's output into a new message, appends it to chatHistory, and immediately loops back to Step 3.


From simple chatbot to autonomous coding agent

You might wonder how a simple four-step loop turns into an autonomous harness like Claude Code or Devin.

The answer is tool execution chaining inside the loop:

  1. User Turn: The user asks the agent to fix a bug.
  2. Append & Dispatch: Prompt is appended to chatHistory and sent to the LLM.
  3. Tool Call: Instead of plain prose, the model returns a structured tool call requesting to read auth.ts.
  4. Tool Execution: The host loop intercepts the tool call, reads the file from disk, and appends the file contents to chatHistory.
  5. Immediate Re-dispatch: The loop cycles back without human intervention, sending the updated history with the file contents back to the LLM.
  6. Action Output: The LLM analyzes the code, decides on a patch, and returns a tool call to write the fix to disk.
  7. Resolution: The host writes the file, runs the test suite, records the passing result in chatHistory, and asks the LLM for a final summary.
  8. Final Display: The model emits plain text confirming the fix, which is displayed to the user.

The model is still just taking text in and producing text out. The while loop is the engine that provides statefulness, momentum, and agency.

In the next chapter, we will examine the exact structure of chat messages and construct our first live API request.