Anatomy of an Agent
Every agent you will ever build or debug has the same six parts. Name them once, and every future bug report turns into a question about which part is wrong.
Agent bugs arrive as vague complaints. "It watered the wrong zone." "It ran for nine minutes and gave up." "It said it was done but nothing happened."
None of those are debuggable as stated. What makes them debuggable is knowing that every agent, from the twelve-line version in lesson 1 to whatever your company runs next year, is assembled from the same six parts. A bug is always in one of them, and usually you can tell which from the complaint alone.
Here they are. The rest of this course is, honestly, a tour of these six.
The six parts:
- Instructions: the standing text that tells the model what job it is doing, what it must not do, and how it knows it is finished
- Model: the decider. Which one you pick is a cost, latency, and reliability decision, and it is a decision you will revisit
- Tools: each one is a schema the model reads plus an executor you wrote. Together they are the complete set of things this agent can physically do
- Loop: the code that calls the model, dispatches tool calls, appends results, and goes round again
- Stop condition: every way the run can end. A final answer, a turn cap, a cost cap, a no-progress detector, a fatal error
- Output contract: the typed thing the run hands back to whatever called it
The grower reports: "It logged a work order for zone 4 three times, with identical text, in the same run." Given the six parts, which two are the first places to look?
- AThe loop and the tools: either the result was never appended so the model repeated itself, or create_work_order is not idempotent (calling it twice files two orders) and silently accepted duplicates
- BThe model and the instructions: a stronger model with a longer prompt would not repeat itself
- CThe stop condition and the output contract: the run should have ended after the first work order
- DThe instructions and the model: work-order wording is a style problem
Here is the GH-North agent written out as a config, so you can see all of it at once. Real frameworks scatter these across decorators and yaml, which is exactly why people lose track of them.
{
"name": "gh-north-morning-run",
"instructions": "You operate greenhouse GH-North, zones 1-6. Read moisture before irrigating any zone. Never irrigate a zone whose sensor reports an error; open a work order instead. Report what you did when finished.",
"model": "a mid-tier general model",
"tools": ["read_moisture", "read_forecast", "run_irrigation", "log_action", "create_work_order"],
"loop": {"dispatch": "sequential", "append_results": true},
"stop": {"max_turns": 6, "max_usd": 0.40, "on_no_progress": "halt"}
}Five of the six parts are here and reasonably well specified. Read it again with the list from two steps ago in hand, and find the one that is missing.
A downstream service is going to consume this agent's result and decide whether to page the on-duty grower. Which of the six parts does this config leave undefined?
{
"name": "gh-north-morning-run",
"instructions": "You operate greenhouse GH-North, zones 1-6. ... Report what you did when finished.",
"model": "a mid-tier general model",
"tools": ["read_moisture", "read_forecast", "run_irrigation", "log_action", "create_work_order"],
"loop": {"dispatch": "sequential", "append_results": true},
"stop": {"max_turns": 6, "max_usd": 0.40, "on_no_progress": "halt"}
}- AThe output contract: nothing here says what shape the run returns, so the caller gets whatever prose the final message happens to contain
- BThe stop condition: max_turns and max_usd are budgets, not stop conditions
- CThe loop: sequential dispatch is a performance setting, so the loop itself is unspecified
- DThe tools: open_vent is absent, so the tool set is incomplete
The output contract is the part people skip, so it is worth a minute.
The model's last message is prose. Prose is fine for a human reading a report, and useless to the code that called the agent, which wants to know: did it succeed, which zones were touched, is anything still outstanding, does a person need to be paged?
Two ways to close that gap, both of which you will meet again later in the course:
A terminator tool. Give the agent asubmit_report tool with a strict schema and instruct it that a run ends by calling it. Now the run's result is a validated object, and "the model called submit_report" is a much crisper stop condition than "the model stopped calling tools".
A structured final turn. Let the loop end normally, then make one more request with tools disabled and a required output shape.
Either way, the point is the same: the run has a return type. An agent whose result is "whatever it felt like writing" cannot be composed with anything, and composition is the whole reason you automated it.
Complete the list of the six parts: instructions, model, tools, loop, stop condition, and output ____. (One word, exactly as this lesson names it.)
instructions, model, tools, loop, stop condition, output ____- Every agent has six parts: instructions, model, tools, loop, stop condition, output contract.
- Each has a signature failure. "Wrong job, done well" is instructions. "Repeated identical calls" is the loop or the tools. "Ends with nothing" is the stop condition.
- Writing them out as one config is worth doing, because frameworks scatter them and you lose track of which one you never specified.
- The output contract is the most commonly skipped part, and its absence is what makes an agent impossible to compose with other software.
- Reaching for a bigger model is the reflex to unlearn: most agent bugs are in the five parts you wrote.
Next: the question that decides whether you should build any of this. Does the next step depend on what you find?