Writing a book to decide what to build
A language model is a stateless function from text to text. Everything that looks like intelligence around it is code somebody wrote. I wrote a book to find out where each piece goes.
Motivation
A language model is a function that takes text and returns text. It holds nothing between calls: no record of your last message, no sense of who you are, no way to affect anything outside the string it hands back. Send it the same input twice under the same settings and it draws from the same distribution both times.
Almost everything people find remarkable about an agent lives outside that function.
- It remembers your name because some code put your name in the prompt.
- It looks something up because some code parsed a piece of its output, matched it against a list of permitted functions, called one, and pasted the result into the next prompt.
- It works overnight because a scheduler is calling it on a timer and rebuilding its instructions from a database each time.
In each case the model did what it always does, which is read text and write text.
The gap between what a system appears to do and what its code does is the subject, and the practical version is: when you want to make an agent smarter, where does the improvement go? Prompt, storage, or scheduling? Without a map of the parts every design decision feels arbitrary, and you invent the same pattern twice under two names or put a feature where it does not belong.
The book as a spike
Building an agent runtime raises one question before any other: what belongs inside it, and what belongs to whoever calls it. I could not settle it, and writing a book about agentic systems instead is a way of answering it, because the book itself is a meta-spike.
A spike is code you write to answer a question and then throw away: not to keep, but to find out whether something is possible, or how hard, before committing to a design. I was doing it with prose. Each section needed working examples to be honest, and building those examples would tell me what the runtime had to support.
Deriving the ten elements
The opening question: what does a developer need in order to design intelligence rather than plumbing? The resolution at which intelligence design happens is not streaming, retrieval pipelines, caching, or chunking. It is how information flows, how context gets loaded and transformed, how things are put into memory and taken back out, and how an agent makes a decision and routes text to effects. Intelligence design is code design.
The premise everything rests on: you have a stateless text-to-text function and you wish to give it some intelligent behavior, so you break human-level language down into mechanism, then describe what each mechanism is and the code structures that enable it.
Deriving in the wrong direction produces a catalogue instead of a diagnostic:
- By technique, a chapter each on retrieval, function calling, planning, produces a catalogue, and you cannot look up a symptom in a catalogue.
- By behavior produces a diagnostic. "The agent forgot what I told it" is an ordinary sentence about an experience, and it traces to exactly two places in the code: what got assembled into the prompt for that call, or what got stored and retrieved beforehand.
The reference table is that translation, done once for every behavior people describe:
| "It seems to..." | Actually is... |
|---|---|
| Remember what I said | Conversation history array in prompt |
| Have long-term memory | Database plus retrieval into context |
| Do things in the world | Structured output, parser, function dispatch |
| Think step by step | Multiple calls with state passed between |
| Plan before acting | plan = llm(task), then for step in plan: execute(step) |
| Check its own work | Generate, separate verify call, conditional retry |
| Have multiple experts | Different system prompts routed by classifier |
| Work while I sleep | Cron job triggers agent |
| Learn from experience | Outcomes extracted, stored, retrieved into future contexts |
Nothing in the right column is exotic, which is the point of putting it next to a left column where everything sounds like a mind.
Ten elements come out of that, each named for the behavior its code produces rather than for the code itself. The third column is the one that bears on the runtime question: where in a codebase the capability lives.
| # | Element | What it is | Where capability lives |
|---|---|---|---|
| 1 | Context | Information available to the model for a single call | Token budget and context construction |
| 2 | Memory | External storage for selective retrieval into context | Storage structures and retrieval mechanisms |
| 3 | Agency | Translation layer from text to effects | Execution boundary and policy enforcement |
| 4 | Reasoning | Grammar of call composition, meaning chaining, looping and branching | Call structure and interstitial computation |
| 5 | Coordination | Communication and sequencing between reasoning structures | Execution flow and data flow |
| 6 | Artifacts | Shared persistent state for coordination | Typed objects, operations and lifecycle |
| 7 | Autonomy | What triggers execution and who owns the main loop | Trigger infrastructure and context reconstruction |
| 8 | Evaluation | Determining whether the system succeeded | Quality signals and measurement functions |
| 9 | Feedback | Gradient signals that steer behavior | Signal sources and injection points |
| 10 | Learning | Feedback that persists to change future behavior | Learnable parameters and extraction pipeline |
Every row names a place in a repository where a change would go, which is what stops the list being a taxonomy.
Try it against a real complaint: a research agent keeps going off and reading things nobody asked about. One sentence of ordinary annoyance, four candidate diagnoses in four different files:
- Context, if the task description going into each call is vague enough that wandering is a reasonable reading of it
- Reasoning, if the loop has no step that asks whether the current sub-question still serves the original one
- Agency, if the search tool is registered with no scoping parameter, so the model has no way to express a narrow search even when it wants one
- Evaluation, if nothing measures whether a retrieved document was used, so the system has no signal that wandering is bad and you are relying on the model to know
Four different afternoons of work in four different files, three of them wasted if you pick wrong. The list does not say which of the four it is. It says there are four, which is enough to stop a week going into whichever one is nearest to hand, usually the prompt.
Three of the ten are the same move at different scopes, and noticing that is when the list stopped feeling arbitrary:
- Memory is externalized context: storage for one agent across time.
- Artifacts are externalized coordination: shared state across several agents.
- Learning is externalized feedback: a signal stored so it can steer a task that has not happened yet.
Each is something that only existed inside one call, written down so it can outlast one call. That is the pattern the whole design space is made of.
It is also what the book was a spike for. Asked as a list of features, what belongs in the runtime has no answer; asked once every capability has a location, it becomes tractable, because then the question is which locations the runtime owns and which it leaves to the caller.
The execution boundary
The diagrams follow one rule, which turned out to be more useful than any single picture: find the visual punchline, and the theme is locating the chain of causality that creates what you experience. A normal diagram shows how tool calls work. This one shows a text zone, then your code translating, then an effect zone.
Draw the vertical line where the translation happens:
- Left. The model has produced a string, and that string is inert. It can say
delete_all_recordsas easily as it can say hello, and neither one does anything. - Right. Effects happen: a file is written, an email goes out, a row disappears.
- The line. Code you wrote, doing two jobs at once. It parses the string into a call, and it decides whether that call is allowed.
So capability is a property of your registry, not of the model. If the model emits a request to delete everything and your dispatch table has no entry for it, nothing happens, because there is no path from that string to that effect. No filter caught it and no good behaviour prevented it. Most of the anxiety about what an agent might do is a question about what you wired up.
Context reconstruction
The identity diagram took the most redrawing. Continuity is an illusion: agent identity and behavior are determined by context reconstruction on each call, and the unified identity you experience is captured in persisted state rather than in the model.
Every turn, your system assembles a fresh context out of stored pieces and sends it. The model acts as though it has whatever history you included, because that history is text in front of it. Nothing carried over. The sense of talking to a continuous someone is produced fresh, each time, by an assembly step you control.
A class of bug reclassifies itself:
- The agent that forgot a fact did not forget it, because forgetting requires having held it. The fact was not in the context for that call.
- The agent that contradicted itself was not being inconsistent, because consistency requires a self to be consistent with. Two different contexts were assembled and each was reasoned over correctly.
The model behaved the same way it always does. The assembly was different.
It is testable, which is what makes it a claim rather than a metaphor. Keep the history and swap the model underneath and the character mostly holds, because the character was in the transcript. Keep the model and change what you inject and the character changes.
The Ralph loop counterexample
One result cuts against the framework. The Ralph loop, in its plainest form, is running the same coding agent on the same task over and over rather than building elaborate scaffolding around it, and it outperforms much more sophisticated designs.
The easy reading is that simple beats complex, which divides the wrong axis. Running the same task many times and keeping what passes is a search, and search converts compute into quality wherever checking an answer is cheaper than producing one. The repetition is a design rather than the absence of one, and the loop wins by spending compute in a shape the sophisticated designs do not.
The prediction that follows, untested: a Ralph loop with better context construction, better error feedback signals, and better learning should work as well or better.
- If true, the loop is not a counterexample to the framework at all, it is an unusually clean instance of a few elements doing all the work.
- If false, repetition is doing a job the elements do not describe, and the framework has a hole in it.
Renaming to harness engineering
The field converged on a different name for the same thing. A LangChain post on harness engineering is what made me look, and the term had been circulating before that in work coming out of HumanLayer. What I had been calling agentic system design was what people were calling harness engineering, so the map moved under the field's term. The structure did not change and the vocabulary did.
Giving up your own name for a thing is almost never a loss, because a vocabulary exists to be understood in, not to be owned. A framework with a private name has to teach the name before it can teach anything, and every reader who already has a word has to translate. Adopting the common term deletes that work.
What I keep is smaller than the book. Every intelligent-seeming behavior around a stateless text-to-text function is a loop, a query, a scheduler, or a policy check that somebody wrote, and once you can name which one you are looking at, an agent stops being a personality you negotiate with and becomes a program you can inspect.