# One call, ten agents, and the answer moves

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/jev-engineering-swarms
> date: 2026-09-22
> tags: explainer, agents, llm, architecture, calibration
The framing going around is tidy. An agent reaches a fork: a state, several
possible actions, one route that has to win. Sending that fork to a frontier
model costs seconds and cents, so make decisions their own engineering layer.
The loop becomes `LLM → reason`, `Jev → decide`, `tools → execute`,
`state → update`, `Jev → decide again`. Once decisions are a layer you can
benchmark them, batch them, and verify them on their own terms.

I think that is basically right. I also think the number stapled to it —
*"up to 193x faster and 444x cheaper in our tests"* — is doing the argument
active harm, and this site has already done the arithmetic, so I am not going to
do it twice.

<Callout type="note">
[The middle tier is not in the middle](/articles/three-tiers) measured TypeSafe's
"193.6x faster, 444.6x cheaper" homepage claim against a matched head-to-head
somebody else published. The cost multiplier broadly survived: **336x per call**
measured against 444.6x claimed. The speed multiplier did not: **4.57x per
call**, off by a factor of 42. Inverted, a 193.6x ratio needs the LLM side to
spend 136.9 seconds on every call — which is a model writing a long reasoning
trace, not a model answering a bounded question. That is settled here. This
piece asks a different question: what is left of the *architecture* once the
number is corrected, and what the swarm variant adds that the single-agent
version does not.
</Callout>

Two things come out of that, and the second is the one nobody states. The
batching is real and measured: ten questions against one shared arena cost about
the time of one. And **the batch is not answer-preserving** — the same rows
measured grouped and alone move `jev_score` by about 0.29 rubric levels on a 0–3
scale, while `jev_choice` agrees 269 times out of 270, because argmax survives a
shift that a probability-weighted mean does not. Both sections are below; if you
only read one, read the second.

## The layering argument does not need the number it is quoting

Take the corrected pair and put it back into the pitch. A decision layer that is
**4.57x faster and 336x cheaper per call** than a frontier model on the same
bounded question is a good reason to build one. It is a *better* reason than
193x, because 4.57x is a number a reader can imagine surviving their own
workload, and 336x is large enough that nothing about the case rests on the
speed figure at all.

The inflated multiplier buys nothing the argument wanted. Notice what it would
take to be true: a baseline spending over two minutes per decision. Quote it and
you have conceded, in the same breath, that your comparison ran against a system
doing the exact thing your architecture says to stop doing. The honest
single-digit latency number and the honest three-figure cost number point at the
same design; the three-figure latency number points at a straw man. Every reader
who checks it — and this ecosystem has repeatedly checked, in public, within
days — will discount the whole post, including the parts that hold.

So the layering claim survives the correction. It survives it more comfortably
without the number.

## What the layer still cannot do, and what that costs you

The part of the pitch I would push back on is not the multiplier but the
silence. Three limits are now documented well enough that shipping without them
in view is a choice.

**It cannot write a string.** The type signature is `state → one of k known
options`, with a probability attached. That is the whole product. Any deployment
that needs an argument value, a message body, or a file path needs a second
model beside the decision layer. WindTunnel's board is the clean instance:
[Jev picks the tool, Mercury 2.5 writes the arguments](/articles/webmcp-windtunnel),
49 of 49 tasks solved, and the cheap fast model that writes the strings is 78%
of the bill. Two models, one of which is the one the pitch says you were
replacing.

**It cannot plan.** A layer that answers a bounded question about the current
state has no horizon. Every roster entry in
[the nine-day ecosystem census](/articles/jev-ecosystem) rebuilt the option menu
from scratch on every step, because that is the only shape the layer supports.
The Jev Engineering guide states it as principle three — *"rebuild the options at
every step"* — which is a sensible rule and also an admission: the plan is the
loop your code writes, not something the model holds.

**It cannot compare two options to each other.** This is the sharp one.
[Jev scores 0 of 100 on relational choice](/articles/jev-scores-zero) — questions
where the content of one option decides another — on a suite where the same
model scores 100% on rule judgment and 98% on ARC-Challenge. Not low. Zero,
across a hundred attempts, far below guessing. It is the signature of a
structural constraint: the options never share a context. That last reading is
now contested — [permuting a 77-option list moves the hosted model's
answers](/articles/jev-is-not-deterministic) on 12 of 100 items, which a strict
per-option scorer cannot do at all — but the measured 0 of 100 stands whichever
way that resolves, and so does its consequence for a swarm.

That last one is where the swarm variant gets interesting, because a swarm is
nothing but options that need to be compared.

## The swarm variant's real argument is the batch

Here is the definition that is actually circulating, and it is a precise one:

> A decision is `(state, typed question, options) → a probability distribution
> over exactly those options`. A swarm is a set of agents that all decide against
> one shared state in one batched call.

Credit where it is due: that second sentence is a genuinely new claim, and it is
not a restatement of the single-agent pitch. The single-agent framing sells
latency and price per decision. The swarm framing sells something structural —
the state is the expensive half of a decision request, and N agents looking at
one world means N questions against *one copy* of it.

<BatchArithmetic />

[aeluyo8-blip/jev-swarm](https://github.com/aeluyo8-blip/jev-swarm) is the
cleanest instance I found and it publishes its own counters. Ten cooperative
snakes share one 30&times;30 arena; every tick sends a single request carrying
all ten snakes' questions; the model returns a full distribution per snake. Its
README states the batching result plainly and modestly:

> **Parallelism is real.** A 10-question request costs ~0.37 s p50 / ~1.24 s p95
> — the model answers ten judgments in roughly the time of one.

The repository's own framing of what it is for is better than most launch posts
manage, and it is the right question to have asked:

> How much intelligence does one small model contribute to a multi-agent system
> — and how much comes from the code around it?

<Figure
  src="/articles/jev-engineering-swarms/fig1.png"
  alt="Screenshot of the Jev Swarm web app in Jev Joint mode. A dark 30x30 grid holds four coloured snakes and a scattering of food pellets. A right-hand panel of counters reads: mode Jev Joint, alive 4/10, food 8, latency 409 ms, P50/P95 412/495 ms, questions per call 4, tokens in/out 2610/167, raw conflicts 24, collision deaths 2, override count 14, override rate 2.2%, deadline timeouts 0.8%, expired responses 0, fallback ticks 1, tick 176. Below it, this tick's decisions: S1 straight 98%, S6 straight 62%, S7 straight 56%, S10 straight 80%."
  caption="A live tick, with the numbers the project chose to put on screen. Four surviving snakes, four questions in one request, 2,610 input tokens against one shared state, and an override rate of 2.2% — the diagnostic the README calls its own Go/No-Go gate. The UI is Chinese by default; there is an EN toggle top right (aeluyo8-blip/jev-swarm, docs/img/hero.png)."
/>

## Intentions stay private, so the negotiation happens in code

Now read the swarm's own design rule, because it is the relational-choice result
restated by someone who has clearly never seen it:

> Positions are public physics (each snake sees its own body and every other
> snake's head); **intentions stay private** — no question may see another
> question's answer.

That is not a privacy preference. It is the constraint. Ten agents batched into
one request do not become a team; they become ten isolated questions that happen
to share an envelope. Two snakes can each be confidently right about their own
best move and jointly wrong, and no amount of batching fixes it, because the
thing batching shares is the *state*, not the *answers*.

<JointResolver />

So the swarm builds the relation the only place it can: in deterministic code.
The Joint Action Resolver enumerates joint actions, drops the ones that violate
a hard constraint, and keeps the one maximising the sum of log probabilities.
The README is exact about the cost of that — 3<sup>10</sup> enumeration with
pruning is about 0.5 ms, and beam search for twenty agents about 22 ms — which
is cheap, and is also the whole point: **the coordination is a 0.5 ms program,
not a model call.**

This is the same shape [the three-tier piece](/articles/three-tiers) found in
Stanley and in an xArm7 harness: deterministic code builds the relation, then
hands it down as an independent question. A swarm is that pattern with N
questions instead of one. The layer got wider; it did not get smarter.

The project knows this, which is the best thing about it. Its headline
diagnostic is the **override rate** — how often the resolver rewrites what the
model picked — and the README names it as the Go/No-Go gate: *"if the resolver
rewrites most decisions, the code is playing the game — not the model."* In the
published run it is **2.2%**: fourteen overrides, which at that rate implies
roughly 640 individual decisions across the run's 176 ticks. That is a real
result in the model's favour, published by someone who set up the measurement so
it could have gone the other way.

<Figure
  src="/articles/jev-engineering-swarms/fig2.png"
  alt="Screenshot of the Decision Lens panel for snake S6. Probability bars read left 6.0%, straight 73.0%, right 21.0%. Below: model top-1 (JEV TOP-1) straight; actually executed (RESOLVER) straight; override reason, a dash; confidence 0.600. A feature table lists per-action values for legal, food distance (5, 3, 3), reachable cells (793, 793, 793), head distance (33, 33, 31), dead-end risk (low, low, low), conflict candidates (0, 0, 0) and wall distance (0, 1, 2). Footer: tick 434, latency 413 ms."
  caption="One agent's decision, opened up. Every row of the feature table was computed by code before the call — BFS food distance, flood-fill reachability, dead-end risk — and the model's contribution is the three bars at the top. Note also that confidence (0.600) and the winning probability (73.0%) are separate numbers (aeluyo8-blip/jev-swarm, docs/img/decision-lens.png)."
/>

That feature table is the honest accounting of a swarm decision. Food distance,
reachable cells, head distance, dead-end risk, conflict candidates, wall
distance: all of it computed deterministically, per action, before anything is
asked. The model picks among three options that code has already described in
full. That is the architecture working exactly as advertised, and it is also why
the parameter count of the decision model is not the interesting number in the
system.

## The batch is not answer-preserving

Here is the part the swarm framing does not mention, and it comes with
measurements.

`colliber/duckdb-jev` exposes Jev as DuckDB scalar functions, and someone
opened [issue #4](https://github.com/colliber/duckdb-jev/issues/4) with a title
that is the whole finding: *"A shared state changes the answers: propose
`jev_*_each` and `jev_*_all` instead of silent batching."* The measurements:

| readout | setup | result |
|---|---|---|
| `jev_choice` | 30 support tickets, grouped 5 / 10 / 30 rows per state | **269 of 270** answers agree with the one-row-per-state baseline; mean confidence moves from 0.951 alone to 0.939–0.970 grouped |
| `jev_score` | six tickets alone, then grouped with mild companions and with severe ones | a systematic shift of about **&minus;0.29 rubric levels on a 0–3 scale**, roughly a tenth of the range, with all six rows moving the same way |

Same API, same call, two different answers about whether batching is safe.

<SharedStateShift />

The mechanism is the readout. `Choice` reports which option won; a distribution
that shifts without reordering is invisible to it. `Score` reports where the
probability mass sits across an ordered rubric, so the identical shift lands
directly in the output. Batching is answer-preserving for the readout that
throws information away and not for the one that keeps it — which means the
cheap sanity check ("we batched and the answers matched") is run on precisely
the readout that cannot detect the problem.

And it is worse for a swarm than for a SQL function, because the swarm has no
un-batched baseline to compare against. `duckdb-jev` could run each row alone
and diff. A tick that asks ten questions of one arena has no single-agent
control unless you build one, and the efficiency argument is the reason nobody
does.

The issue's proposed fix is the right one and it is an API fix, not a model fix:
stop batching silently. Name the three shapes separately — `jev_choice` for one
row and one state, `jev_choice_each` for many rows sharing a state with an
answer per row, `jev_choice_all` for many rows sharing a state with one answer
about the group — so that grouping is something the caller wrote down rather
than something the runtime did for speed.

## The other swarm, and what a closed PR is worth

The second place this framing shows up with code behind it is
[acyclic-labs/sdk#110](https://github.com/acyclic-labs/sdk/pull/110), which adds
`acyclic-jev` and an arena that judges competing forks of a repository: render
each fork's diff into one shared state, ask which fork wins. Its demo is a
three-model race on one feature task, and the numbers are the good kind —
specific, cheap to falsify, and unflattering to somebody:

> DeepSeek V4 Flash won the verdict at 0.92 with tests passing for \$0.003;
> Sonnet broke the tests for \$0.12. Judge cost \$0.00009.

A judge that costs 0.075% of the cheapest contestant is the decision-layer
argument in one line, and it is the same argument this site has now watched
fifteen-odd projects make. But the PR carries ten blocking review findings —
symlink following that leaks host files to an external endpoint, failed workers
promoting broken edits, cleanup failures leaving stale fork mounts — and it was
closed without addressing them. The decision layer was the easy part. The thing
around it, which had to mount untrusted repository forks and run them, was not.
That asymmetry is the recurring lesson of this whole family: moving the decision
down a tier moves the hard work into the code you now have to write, and that
code is where the security review lands.

## What I would actually take from the swarm framing

Three things, in order of how confident I am.

**Batch against one state when the state is expensive. That is the real win, and
it is structural rather than incidental.** A 30&times;30 arena with per-action
features for ten snakes is a couple of thousand tokens; each additional question
is around a hundred. The saving is not "the model is fast," it is "you stopped
re-sending the world." Ten questions against one arena run in about the time of
one. The guide's own worked example puts a 13-question batch at roughly an order
of magnitude on both axes — though it states that pair twice on the same page,
once as "10x faster and 12.2x cheaper" and once as "11.5 times cheaper and 9.6
times faster," which is a small thing and also exactly the kind of small thing
that makes a reader check the big ones.

**Then measure whether the batch changed the answers, on the readout that can
show it.** Group a held-out set, run it ungrouped, and diff the `Score` values,
not the `Choice` labels. If you only have `Choice`, diff the probability vectors
rather than the argmax. A tenth of a rubric level is invisible until it sits
under a threshold, and every one of these systems is a threshold on a number.

**Do not call what the resolver does "collective intelligence."** The batching
is an efficiency property of the request. The coordination is a program you
wrote, and in the one published run it rewrote the model 2.2% of the time, which
is the number that makes the model look good and is also the number that only
exists because somebody instrumented the override path. Ship the override rate.
It is the only honest way to say which half of the system is playing the game.

<ChangeMyMind>

<Falsifier claim="Batching many agents' questions against one shared state changes the answers, measurably.">
The whole section rests on one repository's issue thread — 30 tickets for
`jev_choice`, six for `jev_score` — and I did not run it myself. A larger
replication that groups and ungroups a few hundred rows and finds the `Score`
delta indistinguishable from run-to-run noise would kill it. So would a
demonstration that the &minus;0.29 is an artefact of the companion rows chosen
("mild" and "severe" are the issue's own words, and both directions were tested,
but the sample is six).
</Falsifier>

<Falsifier claim="A swarm's coordination lives in deterministic code, not in the decision layer, because questions cannot read one another's answers.">
Falsified by a batched request in which one agent's answer demonstrably depends
on another agent's answer within the same call — not on the shared state, on the
answer. Run jev-swarm with the resolver off and the arena seeded so two snakes
contend for one cell, and check whether the two distributions ever anti-correlate
in a way independent scoring cannot produce. If they do, "intentions stay
private" is a convention the harness imposes rather than a property of the
model, and the relational-choice result needs a different explanation.
</Falsifier>

<Falsifier claim="The 2.2% override rate means the model, not the resolver, is playing the game.">
It is one screenshot of one seeded run at tick 176 with four of ten snakes alive,
and a low override rate is also what you would see if the resolver's constraints
rarely bind — which four survivors on a 900-cell board would produce. The
diagnostic I actually want is the override rate as a function of agent count and
density, from the repository's own scaling benchmark (1/2/5/10/20). If it climbs
steeply with N, the headline number is measuring an empty board.
</Falsifier>

<Falsifier claim="The corrected multipliers still support the layering argument.">
This falls if the 4.57x latency figure is transport rather than model. Both arms
of that trial were remote endpoints over OpenRouter, so both carry a network
round trip, and the decision side's 0.707 s is almost certainly dominated by it.
A like-for-like local comparison — same bounded question, both models served on
the same machine — could move the ratio in either direction. If it lands near
1x, the case for a separate decision layer becomes a pure cost argument and the
loop diagram is over-specified.
</Falsifier>

<Falsifier claim="The decision layer cannot write a string, plan, or compare options, so every deployment needs code or a second model around it.">
One counterexample retires it: a shipped System One deployment that emits a
free-form argument value, or holds a multi-step plan across turns, or answers a
question whose options refer to each other, using the decision model alone. The
nearest attempt I know of is contrastive-pair training for the third of those.
If it lands and matches on latency and cost, the layer is a small reasoner and
this article's middle section is wrong about the type signature.
</Falsifier>

</ChangeMyMind>

---

*Sources: the swarm definition and the seven working principles are quoted from
[madewithjev.com/what-is-jev-engineering](https://madewithjev.com/what-is-jev-engineering).
Measurements and design rules attributed to jev-swarm are from that repository's
own README and the two screenshots reproduced above; the counters in Figure 1
are the running app's, not mine. The grouped-vs-solo numbers are from
`colliber/duckdb-jev` issue #4 as reported there. The corrected 4.57x / 336x pair
is from [The middle tier is not in the middle](/articles/three-tiers), which
recomputed it from a published xArm7 seed-0 trial; I have not re-derived it here.
The batching, resolver and shifted-distribution interactives are my own
illustrations of the mechanism — the resolver's two-agent case is drawn to make
the rule visible and is not a recorded tick.*
