rgcm / concept_memory /concept_bank.py
dewmal's picture
Upload Concept Memory base model
0768e31 verified
Raw History Blame Contribute Delete
3.37 kB
"""Concept Bank vote blending shared by the playground and benchmark."""
from __future__ import annotations
from typing import Any
CONCEPT_BANK_POLICY_VERSION = "agreement-gate-v2"
def _resolve_outcome(value: Any, outcomes: list[dict[str, str]]) -> int | None:
target = str(value).strip().casefold()
for index, outcome in enumerate(outcomes):
if target in {outcome["id"].casefold(), outcome["description"].casefold()}:
return index
aliases = {"true": "yes", "false": "no", "1": "yes", "0": "no"}
target = aliases.get(target, target)
for index, outcome in enumerate(outcomes):
if target in {outcome["id"].casefold(), outcome["description"].casefold()}:
return index
return None
def apply_concept_votes(
raw_probabilities: list[float],
signals: list[dict[str, Any]],
concepts: list[dict[str, Any]],
outcomes: list[dict[str, str]],
) -> tuple[list[float], list[dict[str, Any]]]:
"""Use linked bank votes only when they agree with the raw decision.
Relevance is a relative ranking, not a calibrated measure of evidence. Until
bank reliability is measured, a conflicting vote must not override the model.
"""
votes = [0.0] * len(outcomes)
contributions = []
for signal, concept in zip(signals, concepts):
relevance = float(signal["probability"])
supports = concept.get("supports", []) or []
contradicts = concept.get("contradicts", []) or []
if isinstance(supports, str):
supports = [supports]
if isinstance(contradicts, str):
contradicts = [contradicts]
supported = sorted(
{i for value in supports if (i := _resolve_outcome(value, outcomes)) is not None}
)
opposed = sorted(
{i for value in contradicts if (i := _resolve_outcome(value, outcomes)) is not None}
)
concept_votes = [0.0] * len(outcomes)
if supported:
for i in supported:
concept_votes[i] += relevance / len(supported)
if opposed:
alternatives = [i for i in range(len(outcomes)) if i not in opposed]
if alternatives:
for i in alternatives:
concept_votes[i] += relevance / len(alternatives)
for i, amount in enumerate(concept_votes):
votes[i] += amount
contributions.append(
{
"description": signal["description"],
"probability": relevance,
"votes": [
{"description": outcomes[i]["description"], "weight": amount}
for i, amount in enumerate(concept_votes)
if amount > 0
],
}
)
total = sum(votes)
if total <= 0:
return raw_probabilities, []
bank_probabilities = [value / total for value in votes]
raw_winner = max(range(len(raw_probabilities)), key=raw_probabilities.__getitem__)
bank_winner = max(range(len(bank_probabilities)), key=bank_probabilities.__getitem__)
if bank_winner != raw_winner:
return raw_probabilities, contributions
adjusted = [0.2 * raw + 0.8 * bank for raw, bank in zip(raw_probabilities, bank_probabilities)]
adjusted_total = sum(adjusted)
return [value / adjusted_total for value in adjusted], contributions