Chapter 4: The first way to act
Chapter 3 ended with the knowledge in the loop and the hands missing. The loop can read your world: it quotes your policies, cites your code, summarizes your spreadsheets. It cannot touch any of them. Words are the model's one exit, and words cannot open a file. This chapter gives the loop its first pair of hands, and the trick is almost disappointing: the model could always act. It was waiting for someone to take its words literally.
One small task carries the whole chapter: a failing test. You paste it into a chat and ask for the fix. The reply comes back as a beautiful plan: open this file, change this line, run the suite. Then the model stops and waits, and you are the one who reaches for the keyboard. The plan is right. The instructions are precise. Doing them was never its part to play. By the end of this chapter, the model will not write you the plan. It will run it.
The homemade actor
The first way to act is a hack, and it predates every official feature for it. You add one instruction to the system prompt: when you want to run a command, reply with JSON, like {"run": "pytest"}. That is the whole change. Ask for the failing test fix, and the model does what it always does: it writes the plan, and at the bottom rides the JSON.
The new behavior lives on your side of the API. Your code, the host, reads every reply before displaying it, hunting for the JSON. When it finds one, it does not display. It runs the command, pastes the output into the array as the next message, and calls the model again. Now the loop acts on the test: the model says what to do, the host does it, and the model reads what happened before writing its next word.
It works, and it shipped products. The insight underneath it is small enough to miss: the model never needed a body. It needed a butler, and the butler was forty lines of your code.
Then the seams show. The model wraps its JSON in a polite sentence, or a code fence, or both. It invents fields the format never had. The longer the conversation runs, the more the format drifts, because the model is a writer, and writers improvise. Your parser is string surgery against a poet. The fix is not a better parser. It is a contract, and the contract has a name.
The tool contract
The vendors watched a thousand teams build that hack, then standardized it. The feature is called tool calling, and it moves the whole arrangement into the API. Before the conversation starts, you declare your tools: each one a name, a plain-language description, and a schema for its arguments, types and all. The model is trained on that contract, and when it wants to act, it emits a tool request: a structured field, separate from the prose, naming the tool and carrying a JSON object of arguments.
The host's job barely changed. Receive the request, execute the tool, return the result. The difference is what returns: the result comes home as a content block, appended to the array, exactly like the screenshot in chapter 2 and the PDF in chapter 3. This is the door those chapters kept mentioning. Tool results use it every time.
Readers of the first book met the four-step heartbeat: collect the input, call the model, append the reply, show it. The handshake threads a second beat inside the first. When the model requests a tool, the host executes and calls the model again before anything reaches your screen. Your one sentence about a failing test can hide a dozen heartbeats, which is why a coding agent seems to think for so long: it is not thinking. It is running errands.
The contract has two signatures. The model promises to request only declared tools, with arguments that match the schema. The host promises to execute and to return every result as a block, so the model always learns what its request did. Keep both promises and the loop can act all day. And notice what the contract reveals: the model never runs anything. It requests. The host runs. The agency was never inside the model. It lives in the deal between two programs.
Contracts break quietly, though. The model hands over a path that does not exist, or a number where the schema asked for a string. The schema drifts from the code: someone renames a function and the description still promises the old one. The fix is the same on both sides, and streaming taught it in chapter 2: validate before acting. The host checks every request against the schema, rejects what does not match, and returns the rejection as a block, so the model can correct itself and try again.
Try it
- Watch the second cycle. Ask your coding agent to fix a small bug, then read the transcript instead of the answer. Count the round trips inside your one instruction: a file read, a command, an edit, another test run. The heartbeat was beating out of sight the whole time.
- Watch discipline decay. Tell a plain chat model to answer every request as JSON only, in a shape you specify, then ask it four or five questions in a row. Sooner or later it wraps the JSON in prose, or invents a field. That drift is why the homemade actor needed a contract.
- Read the array. In the coding agent transcript, notice the rhythm: the agent requests, a tool answers, the agent continues. Those are messages landing in the array, one block at a time.
The problem with this
Look at the diagram again. The machine has two cycles now, and the model can act through both of them. But look at the new block inside the host. It is a single tool. The machine can act, and nothing has decided what it should be able to do.
That decision is the whole game. A read tool makes the agent a researcher. A write tool makes it an editor. A shell makes it an operator. The character of an agent is not in the model; it is in its toolbox. The next chapter builds one.