You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Epic: Stabilize per-turn extraction token demand at quality parity
Ultimate goal: Per-turn extraction token demand must STABILIZE as a session grows to 300+ turns (NOT grow unbounded) WHILE preserving near-ideal semantic extraction quality.
This is the permanent tracker for the per-turn extraction token-stabilization work over the coming weeks. All findings below were established from a 344-turn run (eval-qwen36-344t-full), re-derived from code + logs by the token-economist and quality-analyst specialists.
Predecessor / lineage: #385 (Context budget for relationship mapper and entity detail). Related: #447 (strategic-redirection epic), #460 and #468 (mistargeted compression -- see Decision below).
Root cause: the runaway
Per-turn total input tokens grow LINEARLY UNBOUNDED: approximately 15182 + 88.6 * turn_index, with NO plateau (~16K at turn 30 -> ~42K at turn 300 -> ~46K at turn 344).
There is NO rolling transcript/history window -- each LLM phase uses only the current turn's text. History is NOT the driver.
DRIVER = the entity_detail phase (64% of load, ~9.8K -> ~27.8K/turn):
Detail-call count rises 3.25 -> 5.93/turn toward the cap _MAX_DETAIL_ENTITIES_PER_TURN=6 (tools/semantic_extraction.py ~L1161).
The PC (player character) prior-state is UNCAPPED -- _format_prior_entity_context (~L1500-1610) caps non-PC relationships at _SCENE_MAX_RELATIONSHIPS=8, but the player keeps ALL relationships (109 at turn 344, 100% active, 0 ever resolved), and the PC appears in nearly every turn.
~15-17K tokens/turn of PURE REPETITION -- the system template + full turn text is re-sent across ~8.9 LLM calls/turn at scale.
This epic CONTINUES #385, which capped NON-PC entity detail + the relationship mapper but LEFT THE PC PATH UNCAPPED. That gap is the dominant remaining driver.
A deep data-driven design report is being authored before implementation; it will refine L1/L2 scoping. Each lever is evaluated independently.
L1 -- Type-tiered PC relationship cap (SHIP-FIRST, low risk). Keep permanent bonds uncapped (kinship / adversarial / mentorship / political / captor-debt); recency + mention-filter the volatile spatial/social tail (~cap 10); force-keep anything mentioned this turn. PC web 109 -> ~30-40. MUST tier by relationship TYPE (0 rels ever resolve, so recency/status is an unreliable freshness signal). Needs a thin callback-retention A/B.
L2 -- Batch entity_detail calls to kill the ~15K/turn template + turn-text REPETITION (BIGGEST token win). Risk: cross-entity attribute bleed -> keep PC + new + high-confidence SOLO, batch only the low-salience existing tail 2-3/call with hard-delimited per-entity blocks. NEEDS an A/B before trust.
L3 -- Do NOT lower the detail cap (rejected as a token lever). It only bites on busy high-info turns where dropping a detail pass is most costly; 525 updates were already dropped at cap=6. Instead FIX entity SELECTION: prioritize by state-change-this-turn + mention-salience + staleness, not discovery-time confidence.
Deep data-driven design report (per-phase token breakdown + per-lever target mapping + honest expected-return estimates) -- being authored.
Add a token-budget / lever-analysis visualization panel to the Extraction KPI Dashboard (private repo saas/orchestrator/dashboard.py) as a permanent reference of the initial ESTIMATES to compare against later MEASURED impact.
Fix the KPI dashboard baseline: the headline baseline is the 100-turn slice (eval-qwen36-100t-temp0-clean), which UNDERSTATES the runaway (only visible at scale). Promote the 344-turn full run (eval-qwen36-344t-full) as the meaningful baseline + add a token-growth-trend KPI.
Implement levers L1 + L4, then L2 (each with the standard A/B per docs/ab-test-standard.md), measuring actual token reduction vs the dashboard estimates, at quality parity (entity-set retention, no new duplicates / phantoms).
Success criteria
Per-turn token demand flattens (asymptotically bounded, not linearly growing) across a 300+ turn session, with extraction quality at parity (no entity drops, no new duplicates, no attribute corruption) vs the 344-turn baseline (eval-qwen36-344t-full).
Notes
Sub-issues are intentionally NOT created yet; the design report will refine L1 / L2 scoping first. This epic holds the checklist.
Lessons learned + current state (consolidated) -- updated 2026-06-11
Authored by @pm after the A/B campaign on eval-qwen36-344t-full. This section is the durable record. Future analyses MUST build on it, not re-derive from the older design docs. Grounded on origin/mainf85b665 + measured logs. Replaces the lever-by-lever checklist above as the current-truth status.
COST FACT. GPU wall-clock is 86.8% DECODE / 13.2% prefill; llama.cpp single-slot prefix-cache already reuses duplicated input. So input-token reduction (option a / L2) gives ~0-2% real speedup -- NOT an A/B-cost lever. What cuts A/B cost = fewer GENERATING calls = lever (b). (b) is the DUAL lever: it bounds the curve AND cheapens every future A/B.
METHODOLOGY. The A/B is non-deterministic at 344-turn depth (noise floor ~5-13 tok/call). Use feat: paired multi-run A/B scoring on the entity_detail token metric (#487) #488 paired multi-run (>=3/variant) + per-turn scoring. Every future lever must pass TWO pre-checks BEFORE GPU spend: (i) projected effect EXCEEDS the noise floor, and (ii) it actually FLATTENS the curve (late-window slope -> 0), not merely beats the baseline by X%.
TWO pre-checks before ANY GPU spend: (i) effect > feat: paired multi-run A/B scoring on the entity_detail token metric (#487) #488 noise floor, (ii) late-window slope -> 0 (the asymptotic-bound test, distinct from beating the baseline). A positive-slope result fails the bounding claim regardless of intercept improvement.
Epic: Stabilize per-turn extraction token demand at quality parity
Ultimate goal: Per-turn extraction token demand must STABILIZE as a session grows to 300+ turns (NOT grow unbounded) WHILE preserving near-ideal semantic extraction quality.
This is the permanent tracker for the per-turn extraction token-stabilization work over the coming weeks. All findings below were established from a 344-turn run (
eval-qwen36-344t-full), re-derived from code + logs by the token-economist and quality-analyst specialists.Predecessor / lineage: #385 (Context budget for relationship mapper and entity detail). Related: #447 (strategic-redirection epic), #460 and #468 (mistargeted compression -- see Decision below).
Root cause: the runaway
15182 + 88.6 * turn_index, with NO plateau (~16K at turn 30 -> ~42K at turn 300 -> ~46K at turn 344)._MAX_DETAIL_ENTITIES_PER_TURN=6(tools/semantic_extraction.py ~L1161)._format_prior_entity_context(~L1500-1610) caps non-PC relationships at_SCENE_MAX_RELATIONSHIPS=8, but the player keeps ALL relationships (109 at turn 344, 100% active, 0 ever resolved), and the PC appears in nearly every turn.0.2 * ctxbudget).This epic CONTINUES #385, which capped NON-PC entity detail + the relationship mapper but LEFT THE PC PATH UNCAPPED. That gap is the dominant remaining driver.
Decision on #460 / PR #468
The "adaptive pressure-gated context compression" (#460 / PR #468) is MISTARGETED:
Resolution:
Levers (checklist, ranked by quality-safety)
A deep data-driven design report is being authored before implementation; it will refine L1/L2 scoping. Each lever is evaluated independently.
Deliverables
Success criteria
Per-turn token demand flattens (asymptotically bounded, not linearly growing) across a 300+ turn session, with extraction quality at parity (no entity drops, no new duplicates, no attribute corruption) vs the 344-turn baseline (
eval-qwen36-344t-full).Notes
Lessons learned + current state (consolidated) -- updated 2026-06-11
Authored by @pm after the A/B campaign on
eval-qwen36-344t-full. This section is the durable record. Future analyses MUST build on it, not re-derive from the older design docs. Grounded onorigin/mainf85b665+ measured logs. Replaces the lever-by-lever checklist above as the current-truth status.The 7 consolidated facts (verified)
max_snapshots=3cap ALREADY bounded -> ~0 token lever. Its A/B was measurement-blind (it scored uncompacted raw the feature never touched, fix: entity_detail prompt-token instrumentation is blind to compaction (raw_tokens captured post-compression) #484). PR feat: A0 periodic checkpoint-compaction digest body (#482) #483 closed unmerged; branchfeat/issue-482-a0-checkpoint-compactionPRESERVED (substrate for lever c-ii / feat: checkpoint context reset/rebuild with identity-anchor floor (epic #477, lever c-ii) #496).conf>=0.8tiering left only0.01 entities/turn batchable (91.5% of entities sit at conf0.9). No quality harm, no token win at the shipped config. N=3 paired verdict NOT SEPARABLE from noise. It is a throughput/wall-clock enabler, not a per-turn bounding lever.Lever scoreboard
The bounding path = (b) + (c). Compaction (A0/L2/A1a) is closed as a bounding strategy. Dependency order: #494 (S0 shadow -> S1 gate) FIRST; #495 parallel-safe with #494's S1; #496 LAST (highest coreference risk). Design:
docs/design-bounded-context-relevance-reset.md.STANDING RULE (applies to every agent, every future analysis)
origin/main+ measured logs, NOT design docs.git fetchfirst. Treat docs: per-turn token-stabilization design report #478/docs: bounded per-turn context architecture (A1+A2) design report #479 (and the A1a feat: identity-anchor floor + checkpoint digest body (A1a) -- bound catalog-coupled context growth #492 framing) as SUPERSEDED bydocs/design-bounded-context-relevance-reset.mdwherever they conflict. The specific stale premise is the pre-L1 "uncapped PC web".