]> git.ipfire.org Git - thirdparty/rspamd.git/commit
[Fix] neural: resilient ANN reuse across symbol-list drift
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 23 May 2026 10:34:17 +0000 (11:34 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 23 May 2026 10:34:17 +0000 (11:34 +0100)
commited97ec8a7be8649bf87ab73287e6847e787cd5cc
tree6990b4ae5136a20b083a05a775d330a58cb38ba4
parent1323fc5fbfeb74754e2f625f51f37462c0fbdba3
[Fix] neural: resilient ANN reuse across symbol-list drift

Two follow-up fixes that complete the "neural keeps working when symbols
change" story started by the disable_symbols_input digest stability
commit. Both motivated by inspecting the actual vbspam Redis state on
sp-collector, which showed multiple coexisting profiles per rule and an
orphaned training set (~100 spam / 15 ham) under a stale digest.

is_profile_compatible (pure-symbols mode)

The 30% Levenshtein-drift cap rejected the prior profile on every modest
config change (new RBL, multimap addition, SA-style rule loaded via
multimap regexp_rules). When rejected, set.training_profile stayed nil,
inference went dark, and training samples had nowhere to accumulate
until a brand-new ANN trained from scratch -- weeks under realistic
class imbalance. Raise the cap to 50%, with a comment pointing at the
result_to_vector path (it builds vectors from profile.symbols, NOT
set.symbols, so loading the older profile keeps the trained weights
correctly indexed against the features that produced them).

maybe_carryover_ann (hybrid providers + symbols)

The carryover copied an ANN blob from an old key (trained against
profile.symbols A) into a fresh key whose profile entry carries
set.symbols (current = B). load_new_ann later writes
set.ann.symbols = profile.symbols, so at inference the copied weights
got applied to indices that no longer correspond to the symbols they
were trained on -- silent garbage output. Guard the carryover with
rule.disable_symbols_input: only then does the symbol portion not
contribute to the input vector, and copied weights remain meaningful.
For hybrid mode without disable_symbols_input the existing
is_profile_compatible path already keeps inference alive via the prior
profile entry (whose own symbol list keeps weights aligned), so
skipping carryover is the correct behaviour, not a regression.

Combined with the earlier digest-stability commit, the failure
modes the user kept hitting in production -- disable_symbols_input
digest rotation, pure-symbols cap too tight, hybrid carryover
misindexing -- are all addressed.
lualib/plugins/neural.lua
src/plugins/lua/neural.lua