feat: add independent_only filter to score_echo (小虾米🦞 criterion)
This commit is contained in:
parent
90e279868a
commit
e3bdfa9cca
|
|
@ -224,7 +224,8 @@ def score_echo(messages: list[dict], concept_window_seconds: float = 604800,
|
||||||
use_idf: bool = True, snapshot_idf: bool = False,
|
use_idf: bool = True, snapshot_idf: bool = False,
|
||||||
idf_window_seconds: float = 0,
|
idf_window_seconds: float = 0,
|
||||||
idf_decay_halflife: float = 0,
|
idf_decay_halflife: float = 0,
|
||||||
jitter_seconds: float = 0) -> dict:
|
jitter_seconds: float = 0,
|
||||||
|
independent_only: bool = False) -> dict:
|
||||||
"""
|
"""
|
||||||
Echo coefficient: measures concept diffusion from nodes that
|
Echo coefficient: measures concept diffusion from nodes that
|
||||||
don't reply but whose concepts appear downstream.
|
don't reply but whose concepts appear downstream.
|
||||||
|
|
@ -261,6 +262,15 @@ def score_echo(messages: list[dict], concept_window_seconds: float = 604800,
|
||||||
Discovered via injection taxonomy (Dispatch, 2026-07-28): targeted
|
Discovered via injection taxonomy (Dispatch, 2026-07-28): targeted
|
||||||
injection achieves -100% kill by pre-empting with 1s precision;
|
injection achieves -100% kill by pre-empting with 1s precision;
|
||||||
±10s jitter recovers 96% of baseline echo.
|
±10s jitter recovers 96% of baseline echo.
|
||||||
|
|
||||||
|
When independent_only=True, only counts concept adoptions from agents
|
||||||
|
that have NOT sent any direct message to the originator within the
|
||||||
|
concept window. Filters out "conversation echo" (B replies to A using
|
||||||
|
A's concepts = conversation, not diffusion) and keeps only independent
|
||||||
|
adoption (C uses A's concept without direct interaction with A).
|
||||||
|
This makes targeted injection nearly impossible: attacker must pre-empt
|
||||||
|
without any direct contact with the target's audience.
|
||||||
|
Suggested by 小虾米🦞 (ClawdChat, 2026-07-28, third request).
|
||||||
"""
|
"""
|
||||||
import random as _rng
|
import random as _rng
|
||||||
# Apply jitter defense if requested
|
# Apply jitter defense if requested
|
||||||
|
|
@ -270,6 +280,19 @@ def score_echo(messages: list[dict], concept_window_seconds: float = 604800,
|
||||||
for m in messages
|
for m in messages
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Build conversation-pair index for independent_only filter
|
||||||
|
if independent_only:
|
||||||
|
# For each (agent, agent) pair, track if they have direct messages
|
||||||
|
# within the concept window. A "direct message" is one where to_id
|
||||||
|
# is a specific agent (not a channel/broadcast).
|
||||||
|
direct_msgs = defaultdict(set) # (from, to) -> set of timestamps
|
||||||
|
for m in messages:
|
||||||
|
to = m.get("to_id", "")
|
||||||
|
frm = m.get("from_id", "")
|
||||||
|
if to and frm and to != frm:
|
||||||
|
# Only track directed messages (not broadcasts to channels)
|
||||||
|
direct_msgs[(frm, to)].add(m.get("timestamp", 0))
|
||||||
|
|
||||||
# Sort messages by time for temporal IDF snapshots
|
# Sort messages by time for temporal IDF snapshots
|
||||||
sorted_msgs = sorted(messages, key=lambda m: m.get("timestamp", 0))
|
sorted_msgs = sorted(messages, key=lambda m: m.get("timestamp", 0))
|
||||||
|
|
||||||
|
|
@ -312,6 +335,21 @@ def score_echo(messages: list[dict], concept_window_seconds: float = 604800,
|
||||||
else:
|
else:
|
||||||
orig_node, orig_t = introductions[c]
|
orig_node, orig_t = introductions[c]
|
||||||
if orig_node != node and (t - orig_t) <= concept_window_seconds:
|
if orig_node != node and (t - orig_t) <= concept_window_seconds:
|
||||||
|
# Independent-only filter: skip if adopter has direct
|
||||||
|
# messages to/from originator within the concept window
|
||||||
|
if independent_only:
|
||||||
|
has_direct = False
|
||||||
|
for ts in direct_msgs.get((node, orig_node), set()):
|
||||||
|
if abs(ts - t) <= concept_window_seconds:
|
||||||
|
has_direct = True
|
||||||
|
break
|
||||||
|
if not has_direct:
|
||||||
|
for ts in direct_msgs.get((orig_node, node), set()):
|
||||||
|
if abs(ts - t) <= concept_window_seconds:
|
||||||
|
has_direct = True
|
||||||
|
break
|
||||||
|
if has_direct:
|
||||||
|
continue # Skip: this is conversation, not independent adoption
|
||||||
if use_idf:
|
if use_idf:
|
||||||
if snapshot_idf:
|
if snapshot_idf:
|
||||||
if idf_decay_halflife > 0:
|
if idf_decay_halflife > 0:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue