How the engine actually thinks. If you've read the README, this is the deeper model behind it.
Everything the engine does is a transformation: some input becomes some output. The only thing that varies is the kind of processing in the middle:
| kind | how | cost & certainty | use it for |
|---|---|---|---|
| deterministic | ordinary Clojure | exact, cheap, certain | IO, parsing, regex, counting, sorting, grouping, joining, shape checks, composing values |
| probabilistic | lm / map-lm (a leaf) | a model judging one bounded input | semantic judgment over already-bounded material |
| recursive | rlm / map-rlm (a child) | a fresh run of the whole loop | a sub-problem that needs its own inspect/search/judge loop |
The whole skill of using the engine — and the whole skill the model is prompted toward — is choosing the cheapest sufficient kind of processing for each transformation, splitting where the surface is large or uncertain and collapsing back the moment a sub-problem is bounded enough to solve directly.
```clojure blocks (no
provider "tool calls" — the host only evaluates text fences).(FINAL value).Two rules that matter:
(FINAL value) returns a value to whoever called
this session (a parent rlm, or you).FINAL work you haven't seen. If you need a result before deciding, bind it
with def and inspect it next step; don't call FINAL in the same batch as work
whose result you haven't observed.REPL vars are durable working memory for the whole session — across steps and across
turns. The observation text is a lossy projection; the real values live in your vars.
The model is prompted to bind large values with def and inspect projections (counts,
keys, samples) rather than dumping them. There is no magic context variable: if
the model wants state, it defs it.
(FINAL value) ; end this turn, return value to the caller
(lm input query [mode]) ; one bounded input → one model judgment
(map-lm inputs query [mode]) ; lm over ≤50 inputs, in parallel, order preserved
(rlm task) ; run the whole loop on a sub-problem; returns its FINAL
(map-rlm tasks [shared]) ; rlm over ≤50 independent sub-problems, in parallel
(attach-rlm path task [opts]) ; resume a prior session as a child, then run task
mode is :string (default) or :edn (the model returns parseable EDN).map-lm / map-rlm are real parallel fanout, capped at 50 inputs per call by
default. For more, chunk in Clojure and compose the chunk results yourself.attach-rlm restores a completed prior session's last snapshot as a child, then runs
task against that restored state — reach for it only when a prior session already
holds state you need.A leaf (lm/map-lm) is the non-recursive base case: a pure function whose body
happens to be a model. It reads one already-bounded input and returns one answer. It
does not loop, inspect, or decompose.
A child (rlm/map-rlm) is a full recursion of the engine: it gets a task and runs
its own evaluate-observe-until-FINAL loop, with its own REPL, its own leaves, and its
own children. A child is the root of its own subtree.
The mistake to avoid: a
map-rlmwhere every child does the same single bounded read is really amap-lm. Reserve children for sub-problems that need their own loop.
Children inherit none of the parent's vars, helpers, or working directory. A good child
task states the material (or handles to it), the boundary, the question, the missingness
rules, and the exact FINAL shape wanted back.
These are a few lines of Clojure over the surface; the model composes and reuses them
(often as defn helpers in its own session):
map-lm per-item labels, then a deterministic reduce in
Clojure. The model labels; Clojure counts. (Never ask a leaf for an exact count.)map-rlm.For counting, ranking, set membership, or exact extraction, the model is steered to keep
an auditable ledger (parser checks, per-item labels, frequency maps, the selected
answer, uncertain items) and compute the aggregate deterministically in Clojure from
the model-produced vector — then run a consistency check before FINAL. Leaves judge;
Clojure tallies. This is how the engine keeps probabilistic processing from corrupting
exact results.
Because a FINAL value is a claim, the engine ships a way to audit claims against the
source they cite. A claim typically looks like {:description "…" :evidence "path: a quote or code snippet"}. Verification has two layers (fractal verify):
--root). Verdicts: supported / partial / unsupported /
file-missing. This catches a citation to a file the model never read, or invented
symbols, instantly and for free. It cannot judge meaning.--deep. Hand the claims back to the engine as a fresh task:
"read the cited code and decide whether it genuinely supports the claim; use a child
or leaves, your call; be adversarial and try to refute." The engine reads the real
source and returns :supported / :refuted per claim. This catches the confabulation
the grep waves through — a claim can cite entirely real symbols and still misdescribe
what the code does.The two are complementary on purpose. Grep answers "are the cited symbols real?" with
certainty and no cost; the judge answers "does the code mean what's claimed?" with the
engine's own recursive reading. Run the floor always; escalate to --deep for
load-bearing claims you're about to act on.
Every level of leaves and children spends real calls. The model is prompted to split only at genuine uncertainty, collapse to the cheapest sufficient processing the instant a sub-problem is bounded, and never re-verify what is already certain. Scale the decomposition to the question: a quick lookup wants one or two moves; a thorough audit justifies wide fanout, panels, and adversarial checks. Over-investigation is a failure mode, not thoroughness.
There is no engine-level budget or timeout governor yet — see the cost/leashing warnings in the README.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |