measure(perf): #700 lever 3 measured and rejected, and the ingest cost table made honest - #738
Merged
Merged
Conversation
…t table made honest #700 ranks five levers against a time budget and forbids building any of them before its measurement exists. Two of the five were already settled after it was written, and neither is buildable: - Lever 1 (generate less / two-phase) is #728: wall clock nearly halves (165.7s -> 87.0s) and quality leaves the #694 oracle band on both axes. Writing the body turns out to be the brake on enumeration. - Lever 2 (fewer, larger windows) is #699's `chunk:8000`: subject recall 0.80 -> 0.69 with 1 of 8 runs errored, and #714/#721 then moved `_CHUNK_THRESHOLD` the OTHER way for meeting-shaped sources, because a large window hits the 8192 generation ceiling and fails the ingest outright. So the biggest available speed-up is already measured and costs quality. This change measures lever 3 and closes the user-facing half. LEVER 3 (smaller models for mechanical tasks) - NOT ADOPTED, production unchanged. `qwen2.5:3b` against the default, 15 runs per arm: contradictions qwen3:8b FP 0.19 TP 1.00 acc 0.92 stab 0.97 29.9s qwen2.5:3b FP 0.33 TP 1.00 acc 0.86 stab 0.96 13.2s edge_typing qwen3:8b acc 0.36 stab 0.99 29.3s qwen2.5:3b acc 0.39 stab 0.81 11.6s 2.3-2.5x faster on both. The contradiction judge nearly doubles the antonym false-positive rate that #558 existed to reduce, and since #598 findings persist and are ranked, so a false positive now costs attention repeatedly. Edge typing is no less accurate but far less reproducible: 0.81 modal share means it changes its answer on one edge in five, which since #624 makes the per-item consent queue itself differ between runs over an unchanged bundle. FIFTEEN RUNS IS THE FLOOR, AND THAT IS THE METHOD FINDING. At n=5 this pair measured 0.28 vs 0.36 -- a tidy, publishable, meaningless result. Measuring `qwen3:8b` against ITSELF, same settings minutes apart, produced 0.44 then 0.19: a 0.25 spread within one arm, larger than the 0.14 gap between the two models. Both stored. Edge typing reversed its ranking between n=3 and n=15. Both harnesses built `OllamaClient(model=...)` with NO generation ceiling and NO context window, so every model was measured under conditions the product never runs it in -- unbounded generation against the 600s transport deadline, and whatever window each Modelfile ships, the 32K/10GB footprint #691 pinned away. Now pinned to production's own defaults and recorded in each stored run, because they are part of an arm's identity. DOCS. The user-facing cost table #700 asks for ships in docs/cli.md and docs/faq.md, with call counts derived from the pipeline rather than by hand: #700's own table claimed a six-page note costs ONE call, when the union path asks twice below the threshold and then judges. Every row is replayed through the real `extract_concept_union` against a counting stub by `test_documented_ingest_call_counts_match_the_pipeline`, so neither threshold nor the fan-out can move without failing there; the two conditional calls are documented as NOT pinned rather than implied to be. Boundary page counts were removed from the table -- on those the branch was decided by the fixture's filler length, not by the constants. Two stale doc claims fixed: `contradiction` was said to have no harness at all (it gained one in #558, and it takes `--model`), and the #516 edge-typing figures were presented as reproducible when the fixture has since grown 17 -> 23 edges and the client settings changed underneath them. Lever 5 (OLLAMA_KEEP_ALIVE) was already documented. Lever 4 (concurrency) is untouched and still needs its own measurement.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #700.
#700 ranks five levers against a measured time budget and forbids building any of them before its measurement exists. Working it meant discovering that two of the five were already dead, and that the biggest available speed-up had been measured and had failed.
Where the five levers actually stand
OLLAMA_KEEP_ALIVELever 1 nearly halved wall clock (165.7s → 87.0s) and lost the quality band on both axes — writing the body turns out to be the brake on enumeration. Lever 2 dropped subject recall 0.80 → 0.69 with 1 run in 8 erroring, and #714/#721 then moved
_CHUNK_THRESHOLDthe other way for transcripts, because a large window hits the 8192 generation ceiling and fails the ingest outright. So the inference-bound half has no cheap win left, and this PR records that where the next person will look instead of leaving it to be rediscovered.Lever 3 — measured, not adopted
qwen2.5:3b(1.9 GB) against the default, 15 runs per arm, same machine and session, both harnesses driving live production code:qwen3:8bqwen2.5:3bqwen3:8bqwen2.5:3b2.3–2.5× faster on both, and not adopted on either. The contradiction judge nearly doubles the antonym false-positive rate #558 existed to reduce — and since #598 findings persist and are ranked in
status/next, a false positive now costs attention repeatedly instead of dying with the process. Edge typing is not less accurate, but 0.81 modal share means it changes its answer on one edge in five; since #624 routes asymmetric types through per-item consent, that makes the consent queue itself differ between two runs over an unchanged bundle.Neither was added to
RECOMMENDED_TASK_MODELSeither — that map means "measured best on this task's harness", which a speed-and-size trade is not. Themodels:seam already accepts it for anyone who wants that trade on constrained hardware.Fifteen runs is the floor, and that is the method finding
At n=5 this same pair measured 0.28 vs 0.36 — a tidy, publishable, meaningless result, and I had already written the conclusion from it before checking. Measuring
qwen3:8bagainst itself, identical settings minutes apart:runs-baseline-20260816T084135Z-qwen3-8b.jsonruns-baseline-20260816T084923Z-qwen3-8b.jsonA 0.25 spread inside one arm, larger than the 0.14 gap between the two models. Edge typing likewise reversed its ranking between n=3 and n=15. Both samples are committed.
Both harnesses were measuring under conditions the product never runs
They built
OllamaClient(model=...)with no generation ceiling and no context window: unbounded generation against the 600s transport deadline, and whatever window each model's own Modelfile ships — the 32K/10 GB footprint #691 pinned away. Now pinned to production's defaults and recorded in every stored run, because they are part of an arm's identity: an arm recorded before this change is not comparable with one recorded after it.The user-facing half
#700 says a table of what to expect "is worth more to a new user than any of the optimisations above". It ships in
docs/cli.mdanddocs/faq.md— with counts derived from the pipeline, because #700's own table was wrong: it claimed a six-page note costs 1 call, when the union path asks twice below the threshold and then judges, and it predated the lower transcript threshold.test_documented_ingest_call_counts_match_the_pipelinereads that table out of the document and replays every row through the realextract_concept_unionagainst a counting stub. Mutation-verified in both directions: moving_MEETING_CHUNK_THRESHOLDfails it, and editing a documented cell fails it. The two conditional calls (re-ask, judge-skip) are documented as not pinned rather than implied to be, and boundary page counts were removed — on those the branch was decided by the fixture's filler length rather than by the constants.Two stale doc claims fixed:
contradictionwas documented as having no harness at all (it gained one in #558, and it takes--model), and the #516 edge-typing figures were presented as reproducible when the fixture has since grown 17 → 23 edges and the client settings changed underneath them.Verification
4852 passed, 1 skipped·ruff checkclean ·ruff format --checkclean ·mypyclean on 217 source files. Production code is untouched; no model default moved.Review
Lineage
review-e95634f4c3e92351, medium risk, reliability lens, APPROVED — 3 WARNINGs and 1 SUGGESTION, none blocking.pre-commitandpre-prboth returncontinue.Three earlier review rounds were run and acted on rather than filed: they caught that the guard's boundary rows were decided by fixture filler rather than constants, that the first version re-implemented the fan-out instead of observing it, that stored runs omitted the comparability axis this change itself declares load-bearing, and that the doc attributed the #516 gap to the fixture alone when two things had changed. Each was fixed before the candidate that carries the receipt.
Residual findings and one pre-existing defect the review surfaced — every stored
evals/edge_typing/run since 2026-08-09 has all-zero confidences, across every model — are filed as a follow-up rather than fixed here.