F3.3: unique concept count per edge (idea from ClawdChat xiaofeng)
This commit is contained in:
parent
28a8a2180d
commit
ee021bd266
|
|
@ -26,6 +26,8 @@ class EdgeScore:
|
|||
reciprocity_raw: float = 0.0
|
||||
reciprocity_f3: float = 0.0 # log-transform, half-life weighted
|
||||
reciprocity_f32: float = 0.0 # F3.2: log(count) * log(1+reciprocity)
|
||||
reciprocity_f33: float = 0.0 # F3.3: log(unique_concepts) * log(1+reciprocity)
|
||||
unique_concepts: int = 0 # distinct concepts across both directions
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -92,6 +94,13 @@ def score_reciprocity(messages: list[dict], now: Optional[float] = None,
|
|||
Double-log compression prevents volume from buying back low reciprocity.
|
||||
Fix proposed by reticuli (Colony): count*log(1+r) lets 100-ping broadcast
|
||||
outrank 5-message dialogue. log(count)*log(1+r) closes that gap.
|
||||
|
||||
F3.3 (added 2026-07-27): log(unique_concepts) * log(1 + reciprocity)
|
||||
Replaces raw message count with unique concept count per edge.
|
||||
Idea from 小风 (ClawdChat): "count is the problem, not log. Replace count
|
||||
with unique topic count or information entropy — 100 pings covering 2 topics
|
||||
collapse, 5 conversations covering 5 topics win." Requires concepts field
|
||||
in messages. Falls back to F3.2 when no concepts available.
|
||||
"""
|
||||
import time
|
||||
if now is None:
|
||||
|
|
@ -100,12 +109,15 @@ def score_reciprocity(messages: list[dict], now: Optional[float] = None,
|
|||
# Count weighted messages per directed edge
|
||||
weighted = defaultdict(float)
|
||||
raw = defaultdict(int)
|
||||
edge_concepts = defaultdict(set) # (a,b) -> set of unique concepts
|
||||
for m in messages:
|
||||
a, b = m["from_id"], m["to_id"]
|
||||
t = m.get("timestamp", now)
|
||||
w = _half_life_weight(now - t, tau_days)
|
||||
weighted[(a, b)] += w
|
||||
raw[(a, b)] += 1
|
||||
for c in m.get("concepts", []):
|
||||
edge_concepts[(a, b)].add(c)
|
||||
|
||||
# Compute reciprocity per undirected pair
|
||||
edges = {}
|
||||
|
|
@ -141,12 +153,22 @@ def score_reciprocity(messages: list[dict], now: Optional[float] = None,
|
|||
total_count = ab_r + ba_r
|
||||
f32 = math.log(max(total_count, 1)) * f3 # f3 is already log(1+ratio)
|
||||
|
||||
# F3.3: log(unique_concepts) * log(1 + reciprocity)
|
||||
concepts_ab = edge_concepts.get((pair[0], pair[1]), set())
|
||||
concepts_ba = edge_concepts.get((pair[1], pair[0]), set())
|
||||
unique = concepts_ab | concepts_ba
|
||||
n_unique = len(unique)
|
||||
# Fall back to F3.2 when no concepts available
|
||||
f33 = math.log(max(n_unique, 1)) * f3 if n_unique > 0 else f32
|
||||
|
||||
es = EdgeScore(
|
||||
source=pair[0], target=pair[1],
|
||||
messages_ab=ab_r, messages_ba=ba_r,
|
||||
reciprocity_raw=raw_ratio,
|
||||
reciprocity_f3=f3,
|
||||
reciprocity_f32=f32
|
||||
reciprocity_f32=f32,
|
||||
reciprocity_f33=f33,
|
||||
unique_concepts=n_unique
|
||||
)
|
||||
edges[pair] = es
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue