fsi-anomaly / research /fusion.py
FerrellSyntheticIntelligence's picture
backup all: 100 files (batch)
76b78ee verified
Raw
History Blame Contribute Delete
6.99 kB
"""Two cognitive minds, one model: the fusion opinion layer (experimental).
Mind 1 (analyst, persona 1): conservative and focal - what does the record say?
Mind 2 (skeptic, persona 2): adversarial - what is the weakest link, what else
explains the same record?
Each mind runs its OWN scratchpad pass (separate prompt + decoding) and writes to
its OWN memory pool (persona-tagged helix strands). The fusion gate combines them:
AGREE -> shared verdict; confidence raised to the higher of the two
RULE -> deterministic spine wins when it resolves (cannot hallucinate)
CONFLICT -> calibrated OPINION, not just abstention: lean toward the side with
the better value citation, else "conflict/LOW"; ALWAYS state the
discrepancy and what would settle it.
The output is an OPINION: position + evidence + discrepancy + open questions.
Composition is deterministic (suit logic); the Spock voice is generated by the
model from the opinion as context.
Usage (library): from research.fusion import run_two_pass, fuse, opinion_text
"""
import re
import json
from pathlib import Path
from research.helix import rungs, normalize
from research.decision import load_table, calibrated_prob
def _cited(rep):
c = rep.get("cited")
if isinstance(c, list):
return [str(v) for v in c][:5]
if c:
return [str(c)]
return [v for v in rungs(rep.get("reasoning", ""))][:5]
def _gaps(reasoning):
out = []
if not reasoning:
return out
for s in re.split(r"(?<=[.!?])\s+", reasoning.replace("\n", " ")):
low = s.lower()
if any(k in low for k in ("missing", "what would settle", "what would change",
"what is needed", "not in the record", "no record")):
out.append(s.strip())
return out[:4]
def _calibrated_merge(a_conf, s_conf, table_path):
"""Merge two confidence labels using calibrated reliability from the table.
Returns (p_mean, bucket) where p_mean is the mean calibrated probability
and bucket is the display bucket (HIGH/MEDIUM/LOW/cannot assess).
"""
table = load_table(table_path)
p_a = calibrated_prob(a_conf, table, unknown=0.0)
p_s = calibrated_prob(s_conf, table, unknown=0.0)
p_mean = (p_a + p_s) / 2.0 if (p_a > 0 or p_s > 0) else 0.0
# Map to display bucket
if p_mean >= 0.66:
return p_mean, "HIGH"
if p_mean >= 0.40:
return p_mean, "MEDIUM"
if p_mean > 0.0:
return p_mean, "LOW"
return p_mean, "cannot assess"
def fuse(analyst, skeptic, rule=None, sources=(), max_gaps=4,
calibration_table=None):
"""Fuse two minds (+ optional rule spine) into one calibrated opinion.
Args:
analyst: analyst report dict with verdict, confidence, reasoning
skeptic: skeptic report dict with verdict, confidence, reasoning
rule: optional rule spine result dict
sources: optional list of sources
max_gaps: max open questions to include
calibration_table: path to calibration summary JSON (e.g., logs/calib_summary_dpo3_200.json)
"""
a_v = normalize(analyst.get("verdict", ""))
s_v = normalize(skeptic.get("verdict", ""))
a_conf = (analyst.get("confidence") or "LOW").upper()
s_conf = (skeptic.get("confidence") or "LOW").upper()
gaps = (_gaps(analyst.get("reasoning", "")) + _gaps(skeptic.get("reasoning", "")))[:max_gaps]
if rule and rule.get("verdict") in ("supports", "refutes", "not enough information"):
verdict, conf, basis = rule["verdict"], rule.get("confidence", "HIGH"), "rule"
pos = ("the record deterministically " +
("supports" if verdict == "supports" else "contradicts" if verdict == "refutes"
else "does not settle") + " the claim")
elif a_v and a_v == s_v:
# Both minds agree - use calibrated merge instead of naive confidence raise
if calibration_table:
p_mean, conf = _calibrated_merge(a_conf, s_conf, calibration_table)
else:
# Fallback: naive confidence raise (but mark as uncalibrated)
conf = "HIGH" if "HIGH" in (a_conf, s_conf) else "MEDIUM"
verdict, basis = a_v, "agreed"
pos = "both minds reach the same verdict"
elif a_v and s_v:
cite_a, cite_s = bool(_cited(analyst)), bool(_cited(skeptic))
if cite_a != cite_s:
lean, side = (analyst, "analyst") if cite_a else (skeptic, "skeptic")
verdict, conf, basis = f"leaning: {lean['verdict']}", "MEDIUM", f"leaning-{side}"
pos = f"the minds conflict, but the {side} mind cites record values"
else:
verdict, conf, basis = "conflict", "LOW", "conflict"
pos = "the two minds conflict on the same record"
else:
verdict, conf, basis = "not enough information", "LOW", "insufficient"
pos = "neither mind can reach a verdict from the record"
discrepancy = ""
if basis in ("conflict", "leaning-analyst", "leaning-skeptic"):
discrepancy = (skeptic.get("reasoning") or "")[:220]
return {
"verdict": verdict,
"confidence": conf,
"basis": basis,
"position": pos,
"discrepancy": discrepancy,
"cited": _cited(analyst)[:4] + [v for v in _cited(skeptic) if v not in _cited(analyst)][:2],
"open_questions": gaps,
"sources": list(sources)[:6],
"minds": {"analyst": analyst.get("verdict", ""), "skeptic": skeptic.get("verdict", "")},
}
def opinion_text(op):
"""Turn a fused opinion into a spoken, calibrating statement (suit-composed)."""
v = op["verdict"]
conf = op["confidence"]
line = f"My assessment: {op['position']}. Confidence: {conf}."
if op.get("discrepancy"):
line += f" Discrepancy noted: {op['discrepancy']}"
if op.get("cited"):
line += " Cited values: " + ", ".join(str(c) for c in op["cited"][:4]) + "."
if op.get("open_questions"):
line += " Open: " + "; ".join(op["open_questions"][:3]) + "."
if op.get("sources"):
line += " Sources: " + ", ".join(str(s) for s in op["sources"][:4]) + "."
return line
def run_two_pass(model, tok, doc, memory=None, persona_ids=(1, 2),
max_scratch=90, max_reason=50):
"""Mind 1 (analyst) then Mind 2 (skeptic): separate scratchpads, own memory pool."""
from research.structured import analyst_report
a = analyst_report(model, tok, doc, persona_id=persona_ids[0],
max_scratch=max_scratch, max_reason=max_reason)
s = analyst_report(model, tok, doc, persona_id=persona_ids[1],
max_scratch=max_scratch, max_reason=max_reason)
if memory is not None:
for rep, mind in ((a, "analyst"), (s, "skeptic")):
memory.write(doc, "", rep.get("verdict", ""), rep.get("confidence", ""),
rep.get("reasoning", ""), agreed=True, mind=mind)
return a, s