Building a Good Vertical Agent
The thesis
A good agent is a faithful compression of its task distribution.
The mechanics are commodity: an agent is a while-loop around a model that calls tools until the task is done. Anyone can build one. What separates a good agent from a toy is not cleverness — it is understanding your domain's task distribution and doing tedious, careful work in the few places that matter.
With the model fixed, accuracy is a function of context quality. Bloated context buries the signal; missing context forces guessing. Both cost accuracy, and accuracy is what you sell — the payoff is non-linear (a task that scores 99% is worth far more than one that scores 95%).
Users do not bring a uniform distribution of problems. They bring a long tail:
- Bread-and-butter — the bulk of every session.
- Crucial-but-occasional — a handful of times per session.
- The long tail — each request rare, but there are many, and each still must work.
Your job is to place each capability at the tier that minimizes total cost across that distribution. That is the whole craft.
The memory hierarchy: L1 / L2 / L3
Model context is like a CPU's memory: the fast store next to the processor is tiny, so you tier it. Keep the hot set in the fast tier; reach into slow tiers only for rare stuff. A "cache miss" is the cost you pay to fetch what wasn't resident.
Build the agent's context the same way. Every capability trades compression (cheap to keep resident, but costs prompt tokens on every task) against discovery (free until needed, but costs tool calls to find). Place each capability where total cost is lowest.
L1 — always resident (the ~80%)
Lives in the system prompt on every call. The bread-and-butter operations on the steep part of the frequency curve.
- Make them brutally token-efficient and fast.
- Make them report consequences of what they did.
- Spend disproportionate, "absurd" effort here — the agent pays this cost on every task.
Because L1 is resident forever, fight hardest to keep it tight. Expect a few hundred lines: core operations, the tool contract, the key types, and the execution/safety guidelines.
L2 — curated specs, on demand (the ~15%)
Important-but-occasional capabilities. Documenting them in L1 would bloat every task that never uses them, so fetch them in one discovery step (one cache miss).
- Write curated, gotcha-aware specs in plain English, fetched on demand.
- Encode the canonical recipe and the constraints — the correct sequence and the footguns someone already paid to learn — not just the signatures.
- Costs zero tokens until a task needs it.
Resident cost is thin: an allowlist (~50 lines) of the blessed capabilities and pointers saying the specs exist and when to reach for them. The spec contents stay out of the prompt until pulled in.
L3 — the raw substrate + a skill that maps it (the long tail)
The one obscure thing you never wrapped and never wrote a spec for. You cannot anticipate it, but the agent must still reach it or it hits a wall.
- Keep the complete raw reference on disk (it can be huge — machine-generated, unusable as prompt context).
- Ship a short skill (~100 lines) that teaches the agent to mine it: the structure,
and the exact search/
greprecipes for each kind of question. - It does not have to be ergonomic. It must be reachable, complete, and findable in a bounded number of steps.
Resident cost is ~5 lines: the skill's name and description, plus a pointer. The raw reference never touches the prompt.
The escape-hatch rule: the agent should never be stuck. Miss in L1, drop to L2, and if the curated spec is silent, descend into the raw tome and still return an answer in a sane number of calls. If the wrapped API can't do it, use the raw API — don't compromise.
Supporting principle: one tool, not thirty
Model accuracy degrades as you add tools — more schema in the prompt, more surface to confuse, more ways to pick wrong, especially when tools overlap. Popular agents disagree 4x on tool count, which is a tell that no principle is agreed on.
Prefer a single execute_code tool: the model writes code, the code calls your
functions, the functions touch the domain. This collapses every decision into one ("write
code") and lets the model compose capabilities with a full programming language instead of
stitching rigid tool calls. It also makes all three tiers reachable from one place — L1/L2/L3
are just which functions the model knows it can call, and how much work it did to find them.
Two L1 techniques worth stealing
Reading is an act of compression. A read wrapper should shrink the payload without losing information:
- Formula/value aliasing — normalize repeated formulas to a canonical form, count patterns, and collapse frequent ones to a short alias plus one legend line.
- Free context — attach nearby row labels and header rows so bare numbers have meaning the model never has to guess.
- Style compression — fold uniform formatting into a line or two instead of per-cell.
Writing should report what changed and what looks wrong. After a batch write, hand back a structured diff that is:
- Grouped and sampled, not dumped — group changes, print a deterministic sample with "… and N more" tallies, so hundreds of writes become a handful while totals stay exact.
- Categorized and triaged — clean changes in one bucket; anything suspicious (invalid results, hardcoded values, implausible magnitudes) pulled into a "needs review" section, with the worst flagged MUST FIX so errors don't hide in a wall of green.
The recipe, ported to any domain
The compression in your prompts and specs is an encoding of your users' task distribution — which you understand better than anyone. Answer three questions:
- What do you wrap into L1? The bread-and-butter operations on the steep part of the curve. Make them token-efficient, fast, and consequence-reporting. Spend the most effort here.
- What do you defer to L2? The important-but-occasional capabilities. Write curated, gotcha-aware English specs reachable in one step. Encode the canonical recipe and the constraints, not just the signatures.
- What is your L3 escape hatch? The raw, complete substrate plus a skill that teaches the agent to mine it. It must be reachable, complete, and findable in bounded steps.
Get the three placements right and the agent is fast on the common case, capable on the occasional one, and never truly stuck on the rare one — while context stays small enough that the model stays sharp.
The hierarchy drifts with model strength
What counts as L1 is not fixed. Stronger models absorb larger L2 specs in one shot and reason over more raw L3 detail. As models improve, yesterday's L3 becomes tomorrow's L2, and yesterday's L2 collapses into L1 — the tiers slide down a level.
But the hierarchy never disappears. Context will always be smaller than everything you could put in it, and noise always costs accuracy. Bigger context windows tempt you to paste in more; the better instinct is the one CPUs settled on decades ago: summaries in cache, details on demand, the raw substrate as the last resort. Build the agent's context like a memory hierarchy, and accuracy follows.
Source: Peter Wang (@BrainsAndTennis), "Building a Good Vertical Agent," Jun 2026.