fix(extraction): keep validation-emptied batches out of the noise bank#959
Draft
gorkem2020 wants to merge 11 commits into
Draft
fix(extraction): keep validation-emptied batches out of the noise bank#959gorkem2020 wants to merge 11 commits into
gorkem2020 wants to merge 11 commits into
Conversation
…l grounding
Extraction has no notion of register: an invented rule stated inside a
game is textually indistinguishable from a durable user preference, so
the admission gate (which scores shape, not reality) happily promotes
in-fiction assertions to profile/preference facts. Add a "grounding"
field ("real" | "constructed") to the extraction output schema so the
extractor can flag content that only holds inside an in-conversation
constructed context (games, roleplay, drafted fiction, hypotheticals,
sample data). A real aside stated in passing during play is still
"real" and keeps its natural category.
This is prompt-only; the enforcement post-filter lands separately.
…e extraction-policy knob Two independent, deterministic filters in extractCandidates()'s validation loop: - Grounding post-filter (companion to the prompt change): a candidate tagged grounding:"constructed" is dropped unless its category is "events", and at most one constructed "events" note survives per extraction. Missing/non-string/unrecognized grounding values fail open to "real" so a model that ignores the field cannot regress extraction. Real-grounded candidates are never affected by this filter, regardless of category. - Scope-glob extraction-policy knob (config only, no host plumbing required): SmartExtractorConfig.extractionPolicy maps a scope or scope-glob to "full" (default, unchanged) | "episodic-only" (only "events"-class candidates survive, independent of grounding) | "none" (extraction skipped entirely, zero LLM calls). Resolved via the new exported resolveExtractionPolicy(); exact-string entries take priority over glob entries. Neither filter touches admission control or speaker attribution.
Covers: constructed game content dropped from profile/preferences/ entities/cases; at most one constructed episodic events note per extraction; a genuine real first-person aside stated during play survives (false-positive guard); a purely factual transcript is unchanged; malformed/missing grounding fails open to "real"; the prompt documents the grounding contract; and the scope-glob policy resolver plus its "none"/"episodic-only"/unmatched behavior through extractAndPersist. Fixtures are synthetic (agent-one/agent-two).
Adds the new suite to package.json's test script and to scripts/ci-test-manifest.mjs under core-regression, alongside the other smart-extractor regression suites.
…icy knob Compiled output for the extraction-prompts.ts grounding tag and the smart-extractor.ts post-filter + scope-glob extraction-policy knob.
The per-item grounding self-tags wobble: the same clean fixture produced all-real tags on one run and correct constructed tags on two others, so a filter that trusts per-item tags alone (failing open to real) lets mislabeled fiction land in durable registers. Counter this at three deterministic layers plus the admission judge: - Extraction prompt v2: the model judges a batch-level conversation_register (real, mixed, fiction) once per extraction, and the grounding rules now state that grounding is judged per item with no expected count per batch, that the one-constructed-events cap is a storage rule rather than a tagging quota, and that items sharing a fictional frame must share a grounding tag. Few-shot examples now show full batches in varied shapes, including an all-constructed mid-game batch and a mixed batch with a real out-of-character aside. - Filter enforcement: register fiction drops all durable candidates regardless of per-item tags and keeps at most one events note; a mixed or missing register runs a batch contradiction check that demotes real-tagged durables whenever a sibling candidate carries an explicit constructed tag; register real keeps current behavior. Missing registers fail toward scrutiny (mixed), never toward open. - Grounding propagation: candidates keep their grounding and register past the filter, into stored metadata (grounding, conversation_register) and into the admission audit record, creating a persisted trail of which stored rows used the constructed allowance. - Admission: constructed candidates targeting durable registers are rejected deterministically before any LLM call; the utility prompt now interpolates grounding and register, names all six memory registers correctly, and instructs near-zero durable scores for fiction-framed content; the type prior for durable categories is capped at the events prior when the batch register is fiction, so the 0.6-weight prior cannot launder fiction into profile or preferences. Legacy payloads without grounding metadata keep their existing behavior throughout. Red-proved: 9 of the new regression tests fail against the previous implementation and pass after it.
…mpound path Complements the existing "missing register, no constructed tags" test: a missing/malformed conversation_register defaults to "mixed", and a genuinely constructed sibling in the same batch is enough to arm the same batch-wide durable wipe a "fiction"/"mixed" register would. No production behavior change, just closes a gap the existing per-register tests didn't individually exercise. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fleet-proven refinement of the grounding model: 'real' includes an assertion ABOUT a fiction/game session (that it happened); 'constructed' is a claim true only WITHIN the fiction and is never stored, in any category or register. The batch register deterministically overrides per-item tag wobble for durable categories, and the admission judge's grounding rule is restated in candidate-content terms (the judge is transcript-free by design).
…atches, log contradiction demotions extractAndPersist fed learnAsNoise(conversationText) whenever extraction returned ok with zero candidates: the model found nothing, strongest noise signal. But a batch can reach zero candidates through validation drops and the batch contradiction demotion, not genuine emptiness. In that shape the model DID find candidates; feeding the conversation to the embedding noise bank teaches that content like this is noise and risks pre-filtering similar real content away from future extractions. Gate noise learning on the RAW model output being empty (rawCandidateCount now rides the ok result), and log the skip at the standard level. Also surface the batch contradiction check at the standard log level (one line with count and register) when it demotes anything: a fully demoted batch was previously indistinguishable from a model that found nothing, without debug logging.
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.
What Problem This Solves
Two side effects of the extraction validation layer:
extractAndPersisttrains the embedding noise bank whenever extraction returns ok with zero candidates. A batch can reach zero candidates through validation drops (constructed grounding, fiction register, the batch contradiction demotion) rather than genuine emptiness. In that shape the model DID emit candidates; feeding the conversation to the noise bank as a noise exemplar teaches "content like this is noise" and risks pre-filtering similar real content away from future extractions. Observed live: a mixed-register conversation containing a real durable fact was fed to the noise bank after the contradiction check demoted the whole batch.The batch contradiction check logs its demotions at debug only, so a fully demoted batch is indistinguishable from "model found nothing" at the standard log level. That ambiguity cost a live investigation.
Why This Change Was Made
extractCandidatesnow reportsrawCandidateCount(items the model emitted before validation) on the ok result. Noise learning fires only when that raw count is zero; validation-emptied batches log a standard-level skip line instead.The demotion policy itself is unchanged; this only fixes its side effects.
User Impact
Deployments stop committing wrong "this is noise" beliefs to the learning store when validation empties a batch, and operators can see demotions without enabling debug logging.
Evidence
Note: this branch is cut from the grounding PR's head (#927) because the demotion machinery lives there; it stays a draft until #927 settles and merges.