feat(recall): add error-signature bypass and domain/type filters - #279
Open
m0Nst3r873 wants to merge 2 commits into
Open
feat(recall): add error-signature bypass and domain/type filters#279m0Nst3r873 wants to merge 2 commits into
m0Nst3r873 wants to merge 2 commits into
Conversation
Two defects in recall's relevance gating, both measured rather than inferred, plus a regression baseline for a third that is deferred. 1. --check compared a hardcoded absolute score (4.0) against results drawn from two incompatible scales. Learnings scores are unbounded TF-IDF sums whose IDF numerator is the total entry count, so they drift with corpus size: adding 12 unrelated documents moved one entry from 14.0 to 30.6 (+119%). Codebase scores are log-compressed into [0,10] and do not drift. Verdicts are now taken per source -- codebase keeps the absolute threshold, learnings normalizes against the IDF baseline of a single-occurrence token. The relative cutoff alone regressed cold starts: at N<=5 a lone tag match scored 1.7-3.6 and would newly pass where 4.0 rejected it, so LEARNINGS_ABSOLUTE_FLOOR keeps the stricter behavior until the corpus is large enough (N>=7) for the ratio to exceed it. 2. inferQueryDomain matched only ASCII tag entries, so every Chinese query scored zero and fell back to 'neutral' -- the technical/ops/ support rows of DOMAIN_WEIGHT were unreachable for CJK users. The three vocabularies now carry Chinese entries. Only 2-char words are added: the tokenizer emits bigrams, making single chars ambiguous and 3+ char words unreachable by construction. Also adds a regression baseline for cross-domain IDF pollution. The invariant "adding entries of one domain must not change scores in another" does not hold today; fixing it requires partitioning IDF per domain, which bumps SEARCH_INDEX_VERSION and forces a full rebuild. That work is deferred, so the case is marked it.fails() -- CI stays green, and if the assertion ever starts passing, it.fails() reports it and the case can be promoted to a plain it(). Measurements show the pollution shifts absolute scores (shared tokens -33%, domain-exclusive tokens +139%) but preserves same-domain ordering, since IDF scales all candidates for a given query alike. That is why the threshold fix above is the higher-value half of the pair. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two additions aimed at troubleshooting recall, motivated by the shape of a real knowledge base: of 83 learnings in HyperAI/teamai, 51% carry the `troubleshooting` tag. That library is a failure-case archive, and the natural lookup key for a failure case is the error text itself — which is where BM25 over CJK bigrams is weakest, because the signal lives in the structure of the message while its numbers differ on every occurrence. 1. Error-signature index. Error lines are normalized into signatures by replacing volatile parts (hex, hashes, URLs, paths, versions, numbers) with placeholders, then stored as a signature→filenames map consulted before scoring. An exact hit is strong evidence by definition, so it needs no score. Verified against real data: the crash investigated in this session, `shape '[2045, -1, 64]' ... size 8388608`, collapses to the same signature as upstream sglang issue #30037's `'[2048, -1, 64]' ... 8368128`. Extraction over the 83 real documents yields 13 signatures, among them the team's own `output_partition_size = <n> is not divisible by ... block_n = <n>` — the same class of tensor-partitioning bug. Two normalization decisions came from testing on that data. Hash detection requires at least one letter, or `8388608` is read as a hash and the two shape errors never merge. Quoted identifiers are kept verbatim, since `KeyError: 'glm_moe_dsa'` and `'glm_moe_v2'` are distinct root causes that must not collapse. Signatures live in their own top-level map and never enter entry.tokens or the df table, so the bypass adds no IDF pressure. A test asserts no placeholder ever reaches df or tokens. Precedence is enforced by sorting on the signature flag rather than by score. A fixed constant cannot win on magnitude: learnings scores are unbounded TF-IDF sums, and a single token matching title+tag+body already reaches ~21 at N=25 and ~29 on this 83-document corpus. Ranking by score would have silently inverted the feature's premise as the corpus grew. SIGNATURE_MATCH_SCORE remains, for display only. 2. `recall --domain` / `--type`. Comma-separated filters over knowledge domain and type, applied before sorting and before the --check verdict so `--check --domain ops` reflects the narrowed set. Wholly invalid values warn and disable the filter rather than aborting: one typo should not silently return nothing. Index version 6→7 so existing indexes rebuild and pick up signatures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Motivation came from the data, not from a hunch
I profiled the real team knowledge base (
HyperAI/teamai, 83 learnings) before writing anything:troubleshootingsglangk8sdeploymentThat library is a failure-case archive. And the natural lookup key for a failure case is the error text — which is exactly where BM25-over-CJK-bigrams is weakest: the signal lives in the structure of the message, while the numbers differ on every occurrence.
This profiling also killed a third change I had planned (per-domain subscription filtering for learnings). With 83 docs overwhelmingly tagged ops/k8s/deployment across a single product line, there is no meaningful subset to isolate — every member works on the same thing. Dropped rather than built on a false premise.
1. Error-signature bypass
Error lines are normalized — hex, hashes, URLs, paths, versions, numbers → placeholders — and stored as a
signature → filenamesmap consulted before scoring. An exact hit is strong evidence by definition, so it carries no score.Verified on real data. The crash I was investigating when this idea surfaced:
collapses to the same signature as upstream sglang issue #30037's
'[2048, -1, 64]' ... 8368128:Extraction over the 83 real documents yields 13 signatures, including the team's own:
Signature #2 is the same class of bug as the one I was chasing — tensor partitioning that doesn't divide evenly. Had this existed, my first query would have surfaced it instead of a source-reading detour.
Three normalization decisions that testing forced
[0-9a-f]{7,40}reads8388608as a hash, and the two shape errors then never merge. Pure decimals must fall through to<N>.'...'wholesale, collapsingKeyError: 'glm_moe_dsa'and'glm_moe_v2'— distinct root causes. Numbers inside quotes are still normalized, so shape-error merging is unaffected.https:/<path>and split prose asymmetrically (a/bsurvived whilec/d/ewas eaten mid-token). Two docs describing one error would then yield different signatures.Isolation from IDF — the hard constraint
Signatures live in their own top-level map and never enter
entry.tokensor thedftable. This matters because #278 exists to fix IDF drift; a bypass that fed the corpus would reintroduce it.A test asserts no placeholder reaches
dfortokens. Note what it does not assert: whole-mapdfequality. An error line's ordinary words (divisible,runtimeerror,2045) legitimately enterdf, because raw body text is tokenized for BM25 as it always was. The invariant is placeholder absence, not df identity — an earlier draft asserteddf['timeout'] === 1, which cannot observe the regression it claimed to guard (timeoutisn't a signature token, so it stays 1 even under total leakage).Precedence by sorting, not by score
SIGNATURE_MATCH_SCORE = 20was initially meant to outrank scored hits "by construction." It cannot. Learnings scores are unbounded TF-IDF sums:Already beaten at N=25 — so on the team's own library, exact error matches would have ranked below ordinary keyword hits, silently inverting the feature's premise as the corpus grew. Precedence is now enforced by sorting on the signature flag first; the constant remains for display only.
2.
recall --domain/--typeComma-separated filters over knowledge domain and type, giving callers an escape hatch to narrow retrieval:
Applied before sorting and before the
--checkverdict, so--check --domain opsreflects the narrowed set. Signature hits are subject to these filters too — no back door.Wholly invalid values warn and disable the filter rather than aborting: one typo should not silently return nothing. Validation follows the existing hand-rolled
Set+log.warnconvention (as--depthdoes), not commander's.choices().Test plan
tsc --noEmitcleanerror-signature.test.ts— both normalization regressions (8388608→<N>;'glm_moe_dsa'≠'glm_moe_v2'), the core merge case, URL/path symmetry, idempotency,EXTRACT_LIMIT, and the df/tokens isolation assertionsrecall-filters.test.ts—parseFilterValuesedge cases (empty, casing, partial-invalid, all-invalid, duplicates) andmatchesFilters(missing domain →neutral)df--checkstdout format byte-identicalIndex version 6→7 so existing indexes rebuild and pick up signatures.
Known limitations
XxxError:line — most troubleshooting write-ups describe failures in Chinese prose ("707 错误", "创建卡 Creating"). Extraction stays deliberately conservative; loosening it would mint noisy signatures and dilute the exactness that makes the bypass worth having. Of those 11, 9 have their error within the 2000-char body excerpt, so truncation costs 2 docs — not worth changing the excerpt logic for.bodyExcerpt, so errors past 2000 chars are missed (measured cost above).TODO(cross-scale)from fix(recall): stabilize relevance threshold and CJK domain inference #278 still stands: learnings and codebase scores remain directly compared in the sort. The signature channel now sidesteps it, but the underlying mismatch is untouched.🤖 Generated with Claude Code