📝 NATURAL_RECORDER_MANUAL.mdv4.6.2 · 2026-10-28

natural_recorder.py — manual

Version 1.0.0 — 2026-10-27 — Andrew.human & Claude.ai

Written for humans and for other AIs/machines. Everything a reader needs to run, check or extend the tool is here.

1. Purpose

The natural-run recorder (build step 2 of the Influence experiments). It drives LLM1 (the 4B on port 8080) one token at a time, directly, with nothing injected, and saves the raw top-50 candidates at every step. It is Experiment 000's recorder and the standard that Experiments 001 and 002 are compared against.

Two things make the record exact:

It does not use the proxy, so no memory facts are injected (Experiment 000 needs the model "au naturel").

2. Requirements and install

3. Commands

probe — run this first, once

natural_recorder.py probe

Short checks of the server, then a verdict. It checks that /tokenize and /detokenize work and agree, that candidate lists carry token ids (the recorder cannot drive a build that does not), how many candidates come back with top-k 40 and with raw settings, and whether the prompt cache is used on the second identical request (from cache should equal the prompt length).

The count with top-k 40 answers the open item in checkpoints 3 to 5: 40 returned = the probabilities come after the sampler's cuts; 50 returned = before. The recorder always asks for raw settings, so it works either way; the answer matters for Experiment 001's blend.

Exit code 0 when no problem was found, 1 otherwise.

run — record replies

natural_recorder.py run --file queries.txt
natural_recorder.py run --text "Why is serotonin syndrome so dangerous with MDMA?" --repeat 20
natural_recorder.py run --file queries.txt --mode sample --temperature 0.7 --seed 100 --repeat 20
Option Default Meaning
--text QUERY A query. Repeat for several.
--file PATH Queries, one per line; - is standard input.
--mode greedy|sample greedy greedy always takes the top candidate. sample draws from the top-N list at --temperature, using a seed.
--temperature 1.0 For --mode sample only; must be above 0.
--seed 1 First seed. Repeat r uses seed + r − 1.
--repeat N 1 Record each query N times.
--max-steps 300 Most tokens per reply.
--ctx 8192 The server's context size; a reply stops with context before it would overflow.
--system-file PATH none Optional system prompt text.
--label Free text stored with the run.
--url http://127.0.0.1:8080 (or env RECORDER_URL) Where the server is.
--n-probs 50 Candidates per step.
--timeout 600 Seconds per request.
--out $MEMDIR/natural_runs Output folder (MEMDIR default /root/memstore).

Exit code 0 when every reply finished, 1 when any stopped on an error, 2 on a usage error.

4. The prompt

The prompt is written by the recorder, not by the server's template, so it is the same every time and is saved with the result:

<|im_start|>system\n{system}<|im_end|>\n          (only with --system-file)
<|im_start|>user\n{query}<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n

This is Qwen3's format with thinking off, which is what Qwen34B.sh sets. It is turned into token ids by the server's /tokenize with special tags parsed. Template name in the settings: qwen3-nothink-v1. Another model family needs another template.

5. What it saves

Under $MEMDIR/natural_runs/<run>/ (run = UTC date-time; a second run in the same second gets -2, never overwrites). Files and folders are mode 600 and 700.

File Holds
q001_r01.json One per query and repeat: the query, the prompt text and hash, the reply text and hash, reply token ids, why it stopped (eos, max_steps, context, error), seed, mean probability and log-probability of the chosen tokens (the "fluency" figure), share of steps where the top candidate was taken, word-start steps, seconds, settings.
q001_r01.steps.jsonl One line per step (below).
pairs.jsonl Each query once, then each finished reply, as {"kind", "text", "label"} lines. It is not added to anything: feed it to hotwords.py add --jsonl yourself, after turning off hotwords_store_stage.py and choosing what to keep.
run.json Settings, counts, and one line per reply (stem, stop reason, tokens, reply hash, seconds).

A step line:

{"step": 7, "word_start": true, "chosen_id": 1234, "chosen_rank": 1, "chosen_p1": 0.4123,
"norm_total": 0.9871, "cands": [[id, "token", p_raw, p1], ... 50 entries, hottest first]}

p_raw is the probability as the server reported it; p1 is it renormalised over the 50 (what Experiment 001's blend uses); norm_total is the sum of the 50 raw values (how much of the whole distribution the 50 cover). word_start is true on step 0 and when the chosen piece begins with a space or line break (influence is applied only at word starts).

6. Using it for Experiment 000

The measures (exact-match count, first divergence step, word-level differences, movement of the top 50 at the same step) are not computed by 1.0.0. They can be read from the saved files; a compare command is the natural next addition.

7. Known limits in 1.0.0

8. Version history

Version Date Change
1.0.0 2026-10-27 First release: probe and run; driver-side choice (greedy or seeded sampling over the top 50), token ids fed back, raw top-50 recorded at every step with the chosen rank and word-start flag; fixed Qwen3 non-thinking prompt; per-reply summaries, pairs.jsonl and a run manifest under natural_runs/.