Simplicity the Saviour
technical field note · by Nihal Rahman · updated
Here's how we made an AI Agent faster and more capable by making it smaller.
Simplicity is not the opposite of power. In complex AI products, simplicity is often what makes power usable. Over the last several months, our codebase went through repeated simplification waves:
- tool surfaces were narrowed
- runtime architecture was split by ownership
- browser automation became primitive first
- context was cut into on-demand surfaces
The main ideology behind this shift was to give the agent fewer things to guess.
The strongest version of simplicity is not "do less." It is "make the system legible enough that you can safely do more."
That became obvious while building an AI desktop agent. The product itself is ambitious: it needs to understand a user's context, operate across apps, use tools, browse the web, remember useful facts, ask for permission before sensitive actions, and keep working over long tasks. The natural failure mode for a product like that is not a missing feature. It is too many half-clear boundaries stacked on top of each other.
Tool Simplification: Stop Giving The Model Everything
AI agents fail differently from normal software. If you give a normal program more functions, nothing happens until you call them. If you give a model too many tools, every turn becomes a search problem inside the model's head.
Earlier versions of the system exposed broad tool surfaces too eagerly. That made the agent more "capable" on paper, but also more expensive to prompt, harder to debug, and easier to derail. A model could see local tools, web tools, app integrations, browser tools, media tools, memory tools, task UI tools, and more.
The simplification was to make tool access progressive:
- Keep a small core set available by default.
- Register heavier capability groups as discoverable rather than active.
- Let the model search for tools when the task actually needs them.
- Reveal exact tools dynamically instead of front-loading every schema.
- Keep permission metadata and approval categories attached to tool definitions.
This is the difference between a toolbox and a warehouse.
The model should not need to walk through the warehouse on every turn. It should start with the tools that match ordinary work, then ask for the aisle it needs.
Context Simplification: Smaller First, Deeper On Demand
The same pattern showed up in context.
Large context windows make it easy to get lazy. If a model supports a lot of tokens, the obvious move is to stuff in memory, tools, history, app state, browser state, and instructions upfront. The problem is that a bigger prompt is not automatically a better prompt. It can be slower, noisier, more expensive, and harder for smaller or cheaper models to use reliably.
An easy way to evaluate your architecture is to test it with the weakest model you can reasonably use. If the system stays legible there, stronger models usually have a much easier time with it too.
Browser Simplification: Primitives Before Scripts
Browser automation is where abstraction debt shows up immediately.
One version of browser control gave the model a powerful script-like interface. Stronger models can sometimes use that well, but smaller models often drift: they write invalid JavaScript, assume page globals exist, open duplicate tabs, forget async steps, or stop after describing the next action instead of doing it.
The browser simplification was to stop asking the model to invent browser programs for normal browser work.
The simpler interface became primitive-first:
- navigate
- snapshot
- click
- type
- scroll
- press a key
- select an option
- use a bounded escape hatch only when primitives cannot handle the page
Every normal action returns a fresh page state. Element references come from snapshots. Screenshots are opt-in. Text and element counts are capped. Tabs are summarized compactly. The system tracks the preferred tab for a run so the model does not accidentally act on the wrong browser state.
The Real Standard In Tech
A lot of solid engineering systems converge on some version of this. They may describe it differently, but the pattern is familiar:
- Small, owned services instead of ambiguous mega-services.
- Narrow interfaces instead of omnipotent APIs.
- Progressive disclosure instead of dumping every option upfront.
- Explicit contracts instead of string parsing and convention.
- Observability around transitions, not just final states.
- Deleting dead paths instead of preserving them as "just in case" compatibility.
In AI systems, this matters even more because the model is part of the runtime. Every extra tool, stale instruction, duplicated memory path, or unclear owner becomes something the model can trip over.
That is also why I keep coming back to explicit tools and permission boundaries in projects like furnace, and to observable transitions in flowlens. The implementation changes, but the principle stays the same: fewer guesses, clearer state.
›sources (2)
- Context management — Anthropic
- Effective harnesses for long-running agents — Anthropic
disagree?
write what you wanna fight about and i'll read it.