86 lines
2.6 KiB
Python
86 lines
2.6 KiB
Python
"""Meta-reasoning: challenge assumptions, detect contradictions, revisit nodes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from fusionagi.schemas.atomic import AtomicSemanticUnit, AtomicUnitType
|
|
from fusionagi.reasoning.tot import ThoughtNode, expand_node
|
|
from fusionagi._logger import logger
|
|
|
|
|
|
def challenge_assumptions(
|
|
units: list[AtomicSemanticUnit],
|
|
current_conclusion: str,
|
|
) -> list[str]:
|
|
"""
|
|
Identify and flag assumptions in units that support the conclusion.
|
|
"""
|
|
flagged: list[str] = []
|
|
conclusion_lower = current_conclusion.lower()
|
|
|
|
for u in units:
|
|
if u.type == AtomicUnitType.ASSUMPTION:
|
|
flagged.append(u.content)
|
|
elif "assume" in u.content.lower() or "assumption" in u.content.lower():
|
|
flagged.append(u.content)
|
|
elif u.type == AtomicUnitType.CONSTRAINT:
|
|
if any(w in conclusion_lower for w in ["must", "should", "require"]):
|
|
flagged.append(f"Constraint may be assumed: {u.content[:100]}")
|
|
|
|
logger.debug("Assumptions flagged", extra={"count": len(flagged)})
|
|
return flagged
|
|
|
|
|
|
def detect_contradictions(
|
|
units: list[AtomicSemanticUnit],
|
|
) -> list[tuple[str, str]]:
|
|
"""
|
|
Find conflicting units (heuristic: negation mismatch, same subject).
|
|
"""
|
|
neg_words = {"not", "no", "never", "none", "cannot", "shouldn't", "won't", "don't", "doesn't"}
|
|
pairs: list[tuple[str, str]] = []
|
|
|
|
for i, a in enumerate(units):
|
|
wa = set(a.content.lower().split())
|
|
for b in units[i + 1:]:
|
|
wb = set(b.content.lower().split())
|
|
a_neg = bool(wa & neg_words)
|
|
b_neg = bool(wb & neg_words)
|
|
if a_neg != b_neg:
|
|
overlap = len(wa & wb) / max(len(wa), 1)
|
|
if overlap > 0.2:
|
|
pairs.append((a.unit_id, b.unit_id))
|
|
|
|
logger.debug("Contradictions detected", extra={"count": len(pairs)})
|
|
return pairs
|
|
|
|
|
|
def revisit_node(
|
|
tree: ThoughtNode | None,
|
|
node_id: str,
|
|
new_evidence: str,
|
|
) -> ThoughtNode | None:
|
|
"""
|
|
Re-expand a node when new evidence arrives.
|
|
Creates a new child with the new evidence.
|
|
"""
|
|
if tree is None:
|
|
return None
|
|
|
|
def find_node(n: ThoughtNode) -> ThoughtNode | None:
|
|
if n.node_id == node_id:
|
|
return n
|
|
for c in n.children:
|
|
found = find_node(c)
|
|
if found:
|
|
return found
|
|
return None
|
|
|
|
node = find_node(tree)
|
|
if not node:
|
|
return tree
|
|
child = expand_node(node, new_evidence)
|
|
child.metadata["revisit_evidence"] = new_evidence[:200]
|
|
return tree
|