EmpryoEmpryo.beta
← Blog
engineeringAugust 2, 2026 · 8 min read

I made my agent's prompt smarter before the expensive model ever read it

My first prompt compiler wrote hypotheses and dropped the fix rate from 3/3 to 1/3. The version that only points at files scored 3/3 at 11% less spend. Same model, same bug — the difference was deleting every field where a guess could live.

PProxySoul

My prompt compiler made my agent worse.

Not slower. Not pricier. Worse at fixing the bug. The plain agent solved it 3 times out of 3. With my helpful pre-pass attached, 1 out of 3. One of the failing runs wrote an acceptance criterion instructing the agent to *preserve* the bug it was sent to fix — and the agent, trusting its briefing, complied. It executed beautifully against a wrong spec.

That result killed a feature I'd already built. Then it told me exactly what to build instead, and the rebuild scored 3/3 at 11% less spend.

Full disclosure as always: I build Empryo. Alone, nights and weekends, no funding. Every number below comes from a harness in the repo (scripts/marionette-bench.ts), run against isolated Anthropic workspaces, with the spend ledger reconciled against the billing API to 0.2%.

The thing I was trying to shave

Most coding agents open a repo blind and grep their way to the file. Empryo doesn't. It boots with the Genome — the codebase parsed into a ranked dependency graph, every file, symbol, and import edge, LSP navigation on top — and puts that map in every prompt. That's most of why it lands cheaper and faster than agents that explore from scratch. The agent never burns a turn asking where auth lives.

But a map of the *repo* is not a map of your *request*.

The agent still opens every turn resolving one human sentence into the four files it actually touches. With the Genome that's a short walk instead of an expedition — and it still runs on the frontier model, at frontier prices, on every single prompt. On a vague sentence in a big repo it's ten steps before the first real edit.

So: move that walk to a model that costs a fortieth as much, and run it *before* the expensive one wakes up.

That's the Marionette. A cheap model intercepts your prompt, walks the same graph the main agent trusts, and hands it your exact words plus a map of where this specific sentence lands. The Genome says what the repo is. The marionette says which rooms you just pointed at.

Real capture: the compile runs, the map assembles under the prompt, the agent starts on it.

What the agent actually receives

You type:

when we close the terminal on desktop, if it was open it stays on bottom bar until i click it then it disappears from bottom bar

The agent gets that sentence verbatim — it is never rewritten, never "improved," never summarized. Underneath it rides the map:

  • asks — each distinct behaviour, quoting your words. That sentence contains two. An early version caught one and fixed half the bug.
  • lociTerminalPanel.tsx:630-642 · closeTab · owns close cleanup, ranked primary and adjacent. Rooms, not summaries.
  • strands — the wiring: closeTab never touches minimized state; the chip mirror recomputes off tabs. Every edge names the tool that found it (navigate, genome_impact), so no line of the map is a guess.
  • memories — past decisions by id, from the project memory store.
  • unmapped — what the survey didn't reach, said plainly.
The map as it lands, expanded — asks, loci with line ranges, strands, memories.
The map as it lands, expanded — asks, loci with line ranges, strands, memories.

Read that list again and notice what isn't in it. No cause. No proposed fix. No acceptance criteria. That absence is the entire feature, and it cost me a benchmark pass to learn.

The version that thought

My first marionette formed hypotheses. It proposed fix shapes. It wrote acceptance criteria. On paper that's strictly more value per compile — you're paying for a model, why not let it reason?

The bench said otherwise. One real desktop bug, an opus-class main agent, five isolated workspaces, three runs per arm, cache-cold via per-run nonces so no arm could ride another's cache.

bench pass 2 · same bug, same main model
──────────────────────────────────────────
control (no pre-pass)      3/3 fixed · $1.49
diagnosing marionette      1/3 fixed · $1.37

Cheaper, and it broke the agent.

The mechanism is worth naming because it generalizes past my feature: a cheap model's guesses anchor an expensive model's judgment. The main agent had every tool it needed to discover the truth. It didn't go looking, because it arrived believing it already knew. I paid frontier prices for a model that had been talked out of thinking.

No prompt tweak fixes that. The model was doing exactly what my schema invited it to do. So I deleted the invitation — every field a hypothesis could live in, gone. What's left is mechanical: this file, these lines, this caller, this memory, this thing I didn't check.

bench pass 3 · locator rewrite
──────────────────────────────────────────
control                    3/3 fixed · $1.49 · 18 steps
locator marionette         3/3 fixed · $1.32 · 14 steps

Quality parity with the control, judged blind on the diffs. Four fewer main-agent steps. 11% less spend. The pre-pass itself: $0.09–0.13 per compile.

Where it doesn't win: on a bug the frontier model already solves alone, the marionette matches it slightly cheaper — it doesn't beat it. Wall time goes *up*, because the compile runs first and you watch it. The value concentrates exactly where cold starts hurt: vague prompts, large repos, sentences with two asks hiding in them, and cheap main-agent models that can't afford twenty exploration steps.

"Isn't that just an explore agent?"

Reasonable, and I tested it. Empryo already has an explore lane — a read-only agent the main agent can dispatch mid-turn — so the bench ran "force an explore agent before touching anything" as its own arm. It did well: a sonnet scout went 3/3 at ~$1.30, a haiku scout 2/3 at $1.13, the cheapest arm in the pass.

So the marionette isn't explore with a costume. It's the same instinct moved to a different point in the turn, and the point is what matters:

  • Guaranteed, not chosen. The main agent decides whether to dispatch explore. On exactly the vague prompts that most need a survey, it often decides no. The pre-pass always runs.
  • Before the turn, not inside it. An explore report lands as tool output mid-conversation, costing steps, plumbing, and context position. The map arrives attached to your prompt at step zero, at the top of a cache-friendly prefix.
  • A schema, not prose. The explore agent writes a report. The marionette fills fields — and there is no field for a hypothesis. That constraint *is* the 1/3 → 3/3 story.
  • Lanes an agent can't price-match. Two of the four lanes skip the tool loop entirely: the index answers in 0.2 seconds, the fast lane in about twenty.
  • Memory, and a surface you can see. The pre-pass searches your project memories, and the map renders as expandable UI under your prompt instead of vanishing into the transcript.

Explore is still right mid-turn, when the agent discovers it needs eyes on a subsystem. The marionette owns the cold start.

The measurement that changed the default

Four lanes, measured on 11 real requests against this repo, three trials each. Gold recall = did it find the files the fix actually touched.

lanegold recallprecisionmedian latencytokens in
none (index only)83%20%0.2s0
fast83%45%~22s7.4k
assist72%48%~60s~20k
deep (full survey)~2min~390k (mostly cached)

Look at the top row. Pure retrieval — no model at all — matched the tool loop on recall, in 200 milliseconds, for zero tokens. The index already knows where requests land, because symbols, dependency edges, and co-change history encode most of the answer. The loop was rediscovering, expensively, what was already on disk.

It only lost on *precision*: a raw candidate list is noisy. So the default lane, fast, spends one cheap model pass turning those candidates into a brief — recall of the index, precision of a model, ~22 seconds. The full survey agent still exists, still carries the same intelligence belt as the main agent, and is no longer what you get by default.

Retrieval first, reasoning second. That ordering is the whole design.

Using it

/marionette fix the race in tab restore   # compile one prompt
/marionette on | off | status             # always-on
/router                                   # pick lane + model

Headless: --marionette, --marionette-mode <none|fast|assist|deep>. Unset, the model inherits the cheap spark slot. A failed compile always degrades to your raw prompt — the pre-pass can never kill a turn.

Same feature in the terminal — the map builds in the transcript, then folds to one line:

The TUI lane. Same compile, same map, drawn in text.

The part worth stealing

If you're building anything that briefs a bigger model, the finding transfers: the value is in the pointing, not the thinking. Let the cheap model tell the expensive one *where to look*, never *what to conclude*. Give it a schema with nowhere to put an opinion, because if you leave the field there it will fill it, and the model downstream will believe it.

Numbers from bench passes 2–3 and a 16-run lane-tuning series. Harness and raw results are in the repo.