Skip to content

nhra_gt.agents.evidence_logic

Evidence Analysis and Evaluation Logic.

Extracts structured insights from raw qualitative evidence for model grounding.

Classes

EvidenceAnalyzer

Analyzes evidence using dual confidence scoring (Positive, Negative, Uncertainty).

Source code in src/nhra_gt/agents/evidence_logic.py
class EvidenceAnalyzer:
    """Analyzes evidence using dual confidence scoring (Positive, Negative, Uncertainty)."""

    def analyze(self, evidence_list: list[Evidence]) -> ConstraintEvidence:
        """
        Aggregates multiple pieces of evidence into a single ConstraintEvidence object.
        Uses Bayesian-like updates for confidence.
        """
        if not evidence_list:
            return ConstraintEvidence("N/A", "No evidence", 0.0, "index", 0.0, 0.0, 1.0)

        # Weighted aggregate
        pos_scores = []
        neg_scores = []
        weights = []

        for ev in evidence_list:
            weight = ev.strength * ev.type.reliability_score()
            weights.append(weight)
            if ev.sentiment >= 0:
                pos_scores.append(ev.sentiment * weight)
                neg_scores.append(0.0)
            else:
                pos_scores.append(0.0)
                neg_scores.append(abs(ev.sentiment) * weight)

        total_weight = sum(weights) or 1.0
        conf_pos = sum(pos_scores) / total_weight
        conf_neg = sum(neg_scores) / total_weight

        # Uncertainty is high if weights are low or evidence is conflicting
        conflict = 1.0 - abs(conf_pos - conf_neg) if (conf_pos > 0 and conf_neg > 0) else 0.0
        uncertainty = max(0.0, 1.0 - (total_weight / len(evidence_list)) + 0.5 * conflict)
        uncertainty = min(1.0, uncertainty)

        return ConstraintEvidence(
            source="Aggregated",
            citation=f"N={len(evidence_list)} sources",
            value=1.0,  # Resulting 'multiplier' or index
            unit="index",
            confidence_positive=float(conf_pos),
            confidence_negative=float(conf_neg),
            uncertainty=float(uncertainty),
        )

Functions

analyze(evidence_list)

Aggregates multiple pieces of evidence into a single ConstraintEvidence object. Uses Bayesian-like updates for confidence.

Source code in src/nhra_gt/agents/evidence_logic.py
def analyze(self, evidence_list: list[Evidence]) -> ConstraintEvidence:
    """
    Aggregates multiple pieces of evidence into a single ConstraintEvidence object.
    Uses Bayesian-like updates for confidence.
    """
    if not evidence_list:
        return ConstraintEvidence("N/A", "No evidence", 0.0, "index", 0.0, 0.0, 1.0)

    # Weighted aggregate
    pos_scores = []
    neg_scores = []
    weights = []

    for ev in evidence_list:
        weight = ev.strength * ev.type.reliability_score()
        weights.append(weight)
        if ev.sentiment >= 0:
            pos_scores.append(ev.sentiment * weight)
            neg_scores.append(0.0)
        else:
            pos_scores.append(0.0)
            neg_scores.append(abs(ev.sentiment) * weight)

    total_weight = sum(weights) or 1.0
    conf_pos = sum(pos_scores) / total_weight
    conf_neg = sum(neg_scores) / total_weight

    # Uncertainty is high if weights are low or evidence is conflicting
    conflict = 1.0 - abs(conf_pos - conf_neg) if (conf_pos > 0 and conf_neg > 0) else 0.0
    uncertainty = max(0.0, 1.0 - (total_weight / len(evidence_list)) + 0.5 * conflict)
    uncertainty = min(1.0, uncertainty)

    return ConstraintEvidence(
        source="Aggregated",
        citation=f"N={len(evidence_list)} sources",
        value=1.0,  # Resulting 'multiplier' or index
        unit="index",
        confidence_positive=float(conf_pos),
        confidence_negative=float(conf_neg),
        uncertainty=float(uncertainty),
    )

EvidenceEvaluator

Evaluates evidence quality and relevance (extracts from raw search results).

Source code in src/nhra_gt/agents/evidence_logic.py
class EvidenceEvaluator:
    """Evaluates evidence quality and relevance (extracts from raw search results)."""

    def extract_from_raw(self, search_results: list[dict[str, Any]]) -> list[Evidence]:
        """
        Mock extraction of structured evidence from raw JSON search results.
        In production, this would call an LLM to parse the snippets.
        """
        evidence = []
        for res in search_results:
            snippet = res.get("snippet", "").lower()

            # Simple heuristic parser
            sentiment = 0.5
            if "crisis" in snippet or "failure" in snippet or "overwhelmed" in snippet:
                sentiment = -0.8
            elif "stable" in snippet or "improving" in snippet or "efficient" in snippet:
                sentiment = 0.8

            e_type = EvidenceType.ANECDOTAL
            if "report" in snippet or "data" in snippet:
                e_type = EvidenceType.EMPIRICAL
            elif "study" in snippet or "theory" in snippet:
                e_type = EvidenceType.THEORETICAL

            evidence.append(
                Evidence(
                    source=res.get("title", "Unknown"),
                    content=res.get("snippet", ""),
                    type=e_type,
                    strength=0.7,
                    sentiment=sentiment,
                )
            )
        return evidence

Functions

extract_from_raw(search_results)

Mock extraction of structured evidence from raw JSON search results. In production, this would call an LLM to parse the snippets.

Source code in src/nhra_gt/agents/evidence_logic.py
def extract_from_raw(self, search_results: list[dict[str, Any]]) -> list[Evidence]:
    """
    Mock extraction of structured evidence from raw JSON search results.
    In production, this would call an LLM to parse the snippets.
    """
    evidence = []
    for res in search_results:
        snippet = res.get("snippet", "").lower()

        # Simple heuristic parser
        sentiment = 0.5
        if "crisis" in snippet or "failure" in snippet or "overwhelmed" in snippet:
            sentiment = -0.8
        elif "stable" in snippet or "improving" in snippet or "efficient" in snippet:
            sentiment = 0.8

        e_type = EvidenceType.ANECDOTAL
        if "report" in snippet or "data" in snippet:
            e_type = EvidenceType.EMPIRICAL
        elif "study" in snippet or "theory" in snippet:
            e_type = EvidenceType.THEORETICAL

        evidence.append(
            Evidence(
                source=res.get("title", "Unknown"),
                content=res.get("snippet", ""),
                type=e_type,
                strength=0.7,
                sentiment=sentiment,
            )
        )
    return evidence

EvidenceRequirements

Defines evidence requirements for different constraint types.

Source code in src/nhra_gt/agents/evidence_logic.py
class EvidenceRequirements:
    """Defines evidence requirements for different constraint types."""

    @staticmethod
    def get_requirements(constraint_type: str) -> dict[str, Any]:
        reqs = {
            "capacity": {
                "min_sources": 3,
                "preferred_type": EvidenceType.EMPIRICAL,
                "freshness_years": 2,
            },
            "efficiency": {
                "min_sources": 2,
                "preferred_type": EvidenceType.THEORETICAL,
                "freshness_years": 5,
            },
        }
        return reqs.get(
            constraint_type,
            {"min_sources": 1, "preferred_type": EvidenceType.ANECDOTAL, "freshness_years": 10},
        )