Skip to content

feat(recall): add error-signature bypass and domain/type filters - #279

Open
m0Nst3r873 wants to merge 2 commits into
Tencent:mainfrom
m0Nst3r873:feature/recall-signature-filters
Open

feat(recall): add error-signature bypass and domain/type filters#279
m0Nst3r873 wants to merge 2 commits into
Tencent:mainfrom
m0Nst3r873:feature/recall-signature-filters

Conversation

@m0Nst3r873

Copy link
Copy Markdown
Collaborator

Stacked on #278. This branch is cut from feature/recall-idf-domain-isolation, so the diff shown here includes #278's two commits. Review #278 first; only the second commit (0ed1e81) belongs to this PR. GitHub can't target a base branch that doesn't exist upstream, hence main. Once #278 merges, this diff reduces to just its own commit.

Motivation came from the data, not from a hunch

I profiled the real team knowledge base (HyperAI/teamai, 83 learnings) before writing anything:

tag docs share
troubleshooting 42 51%
sglang 36 43%
k8s 23 28%
deployment 19 23%

That 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 → filenames map 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:

RuntimeError: shape '[2045, -1, 64]' is invalid for input of size 8388608

collapses to the same signature as upstream sglang issue #30037's '[2048, -1, 64]' ... 8368128:

runtimeerror: shape '[<n>, -<n>, <n>]' is invalid for input of size <n>

Extraction over the 83 real documents yields 13 signatures, including the team's own:

 1. runtimeerror: assertion error (<path>:<n>):
 2. valueerror: weight output_partition_size = <n> is not divisible by weight quantization block_n = <n>
 3. keyerror: 'glm_moe_dsa'
 5. nameerror: name 'deep_gemm' is not defined
 7. outofmemoryerror: cuda out of memory. tried to allocate <n> mib.
12. indexerror: start out of range (expected [-<n>,<n>], got <n>)

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

  • Hash detection requires at least one letter. The obvious [0-9a-f]{7,40} reads 8388608 as a hash, and the two shape errors then never merge. Pure decimals must fall through to <N>.
  • Quoted identifiers stay verbatim. An early draft replaced '...' wholesale, collapsing KeyError: 'glm_moe_dsa' and 'glm_moe_v2' — distinct root causes. Numbers inside quotes are still normalized, so shape-error merging is unaffected.
  • Paths need ≥3 segments and absorb a relative leading segment. A 2-segment rule mangled URLs into https:/<path> and split prose asymmetrically (a/b survived while c/d/e was 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.tokens or the df table. This matters because #278 exists to fix IDF drift; a bypass that fed the corpus would reintroduce it.

A test asserts no placeholder reaches df or tokens. Note what it does not assert: whole-map df equality. An error line's ordinary words (divisible, runtimeerror, 2045) legitimately enter df, because raw body text is tokenized for BM25 as it always was. The invariant is placeholder absence, not df identity — an earlier draft asserted df['timeout'] === 1, which cannot observe the regression it claimed to guard (timeout isn't a signature token, so it stays 1 even under total leakage).

Precedence by sorting, not by score

SIGNATURE_MATCH_SCORE = 20 was initially meant to outrank scored hits "by construction." It cannot. Learnings scores are unbounded TF-IDF sums:

N single token hitting title+tag+body +votes
25 21.4 26.4
83 (this corpus) ~29.5
1000 43.3 48.3

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 / --type

Comma-separated filters over knowledge domain and type, giving callers an escape hatch to narrow retrieval:

teamai recall "重试逻辑" --domain technical
teamai recall "部署失败" --domain ops
teamai recall "xxx" --type learnings,docs

Applied before sorting and before the --check verdict, so --check --domain ops reflects 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.warn convention (as --depth does), not commander's .choices().

Test plan

  • tsc --noEmit clean
  • Full suite 1966/1966, 148 files
  • error-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 assertions
  • recall-filters.test.tsparseFilterValues edge cases (empty, casing, partial-invalid, all-invalid, duplicates) and matchesFilters (missing domain → neutral)
  • Sort-partition test: a BM25 hit scoring above 20 still ranks below a signature hit
  • End-to-end against the real 83-doc library: 13 signatures extracted, zero placeholder leakage into df
  • --check stdout format byte-identical

Index version 6→7 so existing indexes rebuild and pick up signatures.

Known limitations

  • Only 11 of 83 docs contain a parseable 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.
  • Signature extraction reads the truncated bodyExcerpt, so errors past 2000 chars are missed (measured cost above).
  • The 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

m0Nst3r873 and others added 2 commits August 4, 2026 17:22
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant