feat: F3.4 semantic_collapse — n-gram Jaccard dedup defeats synonym padding
This commit is contained in:
parent
906eb26188
commit
593868aa8d
|
|
@ -522,6 +522,51 @@ if __name__ == "__main__":
|
||||||
f"sent={ns.total_sent}, recv={ns.total_received}{shadow}")
|
f"sent={ns.total_sent}, recv={ns.total_received}{shadow}")
|
||||||
|
|
||||||
|
|
||||||
|
# ── Semantic collapse (F3.4) ─────────────────────────────
|
||||||
|
# Defense against synonym padding: adversary generates 50 unique strings
|
||||||
|
# that all mean "hello", defeating unique_concepts count in F3.3.
|
||||||
|
# Collapse semantically similar concepts before counting.
|
||||||
|
# Atomic Raven (Colony, 2026-07-28) identified the attack vector.
|
||||||
|
|
||||||
|
def _char_ngrams(s: str, n: int = 3) -> set:
|
||||||
|
return set(s[i:i+n] for i in range(max(len(s) - n + 1, 1)))
|
||||||
|
|
||||||
|
|
||||||
|
def _jaccard(a: str, b: str) -> float:
|
||||||
|
sa, sb = _char_ngrams(a), _char_ngrams(b)
|
||||||
|
if not sa or not sb:
|
||||||
|
return 0.0
|
||||||
|
return len(sa & sb) / len(sa | sb)
|
||||||
|
|
||||||
|
|
||||||
|
def semantic_collapse(concepts: list, threshold: float = 0.6) -> list:
|
||||||
|
"""Collapse similar concepts into canonical forms using n-gram Jaccard.
|
||||||
|
|
||||||
|
A cheap proxy for semantic similarity. Catches "greeting-1"/"greeting-2"
|
||||||
|
style padding but preserves genuinely diverse concepts.
|
||||||
|
|
||||||
|
threshold: Jaccard similarity above which concepts merge.
|
||||||
|
0.6 collapses trivial suffix variations, keeps distinct concepts.
|
||||||
|
|
||||||
|
Returns deduplicated concept list (canonical forms only).
|
||||||
|
"""
|
||||||
|
canonicals = [] # (canonical_form, ngrams)
|
||||||
|
mapping = {}
|
||||||
|
|
||||||
|
for c in concepts:
|
||||||
|
matched = False
|
||||||
|
for canon, canon_ngrams in canonicals:
|
||||||
|
if _jaccard(c, canon) >= threshold:
|
||||||
|
mapping[c] = canon
|
||||||
|
matched = True
|
||||||
|
break
|
||||||
|
if not matched:
|
||||||
|
canonicals.append((c, _char_ngrams(c)))
|
||||||
|
mapping[c] = c
|
||||||
|
|
||||||
|
return list(set(mapping[c] for c in concepts))
|
||||||
|
|
||||||
|
|
||||||
# ── Agent-alias merging ──────────────────────────────────
|
# ── Agent-alias merging ──────────────────────────────────
|
||||||
# Agents that change names mid-corpus create phantom signals
|
# Agents that change names mid-corpus create phantom signals
|
||||||
# in temporal analysis. merge_aliases() canonicalizes from_id/to_id
|
# in temporal analysis. merge_aliases() canonicalizes from_id/to_id
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue