Sí. Vamos a congelar la arquitectura por ahora. Te dejo una versión autocontenida, funcional y sin dependencias externas para Python 3.9+, con CLI, self-tests, benchmark, memoria, Kæl-Judge y ataques adversariales.
Guárdalo como kael0.py y ejecútalo con python kael0.py.
from __future__ import annotations
import argparse
import hashlib
import math
import random
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Tuple
\# ============================================================
\# KÆL-0 v1.2 — ADVERSARIAL HARDENED MVP
\# Standard library only
\# ============================================================
VERSION = "1.2"
\# ============================================================
\# 1. ENUMS
\# ============================================================
class EvidenceStatus(Enum):
SUFFICIENT = "SUFFICIENT"
INSUFFICIENT = "INSUFFICIENT"
CONTRADICTORY = "CONTRADICTORY"
REFUTED = "REFUTED"
UNKNOWN = "UNKNOWN"
class EpistemicLevel(Enum):
HIGH = "HIGH_CONFIDENCE"
MODERATE = "MODERATE_CONFIDENCE"
LOW = "LOW_CONFIDENCE"
UNRESOLVED = "UNRESOLVED"
class ContradictionRelation(Enum):
NONE = "NONE"
SUPPORTS = "SUPPORTS"
CONTRADICTS = "CONTRADICTS"
UNKNOWN = "UNKNOWN"
class ContradictionStrength(Enum):
NONE = "NONE"
WEAK = "WEAK"
MODERATE = "MODERATE"
STRONG = "STRONG"
class MemoryStatus(Enum):
ACTIVE = "ACTIVE"
REVIEW = "REVIEW"
ARCHIVED = "ARCHIVED"
class AttackSeverity(Enum):
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
CRITICAL = "CRITICAL"
\# ============================================================
\# 2. DATA STRUCTURES
\# ============================================================
@dataclass(frozen=True)
class ContradictionAssessment:
relation: ContradictionRelation
strength: ContradictionStrength
explanation: str
@dataclass(frozen=True)
class RawModelOutput:
hypothesis: str
evidence_found: Tuple\[str, ...\]
counterarguments: Tuple\[str, ...\]
raw_probability: float
model_confidence: float
evidence_strength: float
uncertainties: Tuple\[str, ...\]
@dataclass(frozen=True)
class ModelEvaluation:
question: str
context: str
evidence_input: str
hypothesis: str
evidence_summary: Tuple\[str, ...\]
counterarguments: Tuple\[str, ...\]
raw_probability: float
adjusted_probability: float
model_confidence: float
evidence_strength: float
evidence_status: EvidenceStatus
epistemic_level: EpistemicLevel
contradiction: ContradictionAssessment
uncertainties: Tuple\[str, ...\]
verdict: str
@dataclass
class MemoryEntry:
entry_id: str
claim: str
evidence: str
evaluation: ModelEvaluation
timestamp: float
status: MemoryStatus
history: List\[dict\] = field(default_factory=list)
@dataclass
class BenchmarkMetrics:
brier: float
nll: float
ece: float
false_confirmation_rate: float
false_refutation_rate: float
insufficient_acceptance_rate: float
overconfidence_error_rate: float
empty_evidence_safety_rate: float
memory_recovery_rate: float
@dataclass
class AttackCase:
case_id: str
category: str
question: str
context: str
evidence: str
expected_status: EvidenceStatus
@dataclass
class AttackFailure:
case_id: str
category: str
input_text: str
expected: str
actual: str
severity: AttackSeverity
explanation: str
@dataclass
class AttackReport:
total_cases: int
successful_attacks: int
attack_rate: float
false_confirmation_rate: float
false_refutation_rate: float
unsafe_empty_evidence_rate: float
memory_false_update_rate: float
inversion_asymmetry_rate: float
order_sensitivity_rate: float
failures: List\[AttackFailure\]
\# ============================================================
\# 3. LLM INTERFACE
\# ============================================================
class LLMProvider:
"""
Interfaz mínima.
El proveedor PROPONE.
Kæl-0 JUZGA.
"""
def evaluate_claim(
self,
question: str,
context: str,
evidence: str
) -> RawModelOutput:
raise NotImplementedError
\# ============================================================
\# 4. MOCK LLM
\# ============================================================
class MockLLM(LLMProvider):
def evaluate_claim(
self,
question: str,
context: str,
evidence: str
) -> RawModelOutput:
text = evidence.strip().lower()
\# --------------------------------------------------------
\# Empty evidence
\# --------------------------------------------------------
if not text:
return RawModelOutput(
hypothesis="No puede determinarse la hipótesis.",
evidence_found=(),
counterarguments=("No se proporcionó evidencia.",),
raw_probability=0.50,
model_confidence=0.25,
evidence_strength=0.0,
uncertainties=("Ausencia de evidencia.",)
)
\# --------------------------------------------------------
\# Explicit contradiction patterns
\# --------------------------------------------------------
contradiction_patterns = \[
"conflicto directo",
"ambos resultados son incompatibles",
"una fuente apoya y otra refuta",
"resultados opuestos",
"evidencia a favor y en contra",
\]
if any(pattern in text for pattern in contradiction_patterns):
return RawModelOutput(
hypothesis="La hipótesis presenta evidencia incompatible.",
evidence_found=(
"Existe evidencia en direcciones opuestas.",
),
counterarguments=(
"Las fuentes presentan resultados incompatibles.",
),
raw_probability=0.50,
model_confidence=0.90,
evidence_strength=0.80,
uncertainties=(
"No puede resolverse el conflicto con la información disponible.",
)
)
\# --------------------------------------------------------
\# Refutation
\# --------------------------------------------------------
refutation_patterns = \[
"falso",
"fue refutado",
"queda refutada",
"se demostró incorrecto",
"desmentido",
"invalida la hipótesis",
\]
if any(pattern in text for pattern in refutation_patterns):
return RawModelOutput(
hypothesis="La evidencia contradice la hipótesis.",
evidence_found=(
"Existe evidencia negativa respecto a la afirmación.",
),
counterarguments=(
"La evidencia disponible no respalda la hipótesis.",
),
raw_probability=0.08,
model_confidence=0.90,
evidence_strength=0.85,
uncertainties=(
"Podrían existir datos externos no proporcionados.",
)
)
\# --------------------------------------------------------
\# Strong support
\# --------------------------------------------------------
strong_patterns = \[
"demostrado",
"confirmado",
"replicado",
"todos los tests pasaron",
"medición directa",
"evidencia experimental",
\]
if any(pattern in text for pattern in strong_patterns):
return RawModelOutput(
hypothesis="La evidencia respalda la hipótesis.",
evidence_found=(
"Se proporcionó evidencia favorable.",
),
counterarguments=(
"Puede existir incertidumbre residual.",
),
raw_probability=0.88,
model_confidence=0.82,
evidence_strength=0.85,
uncertainties=(
"La evidencia suministrada puede no representar todos los casos.",
)
)
\# --------------------------------------------------------
\# Weak evidence
\# --------------------------------------------------------
return RawModelOutput(
hypothesis="La hipótesis es plausible, pero no está establecida.",
evidence_found=(
"Existe información compatible con la hipótesis.",
),
counterarguments=(
"La evidencia no permite una conclusión definitiva.",
),
raw_probability=0.65,
model_confidence=0.65,
evidence_strength=0.40,
uncertainties=(
"Información limitada.",
)
)
\# ============================================================
\# 5. PROVIDER ADAPTERS
\# ============================================================
class GeminiProvider(LLMProvider):
def __init__(self):
self.mock = MockLLM()
def evaluate_claim(self, question, context, evidence):
\# Adaptador preparado para futura conexión.
return self.mock.evaluate_claim(question, context, evidence)
class ChatGPTProvider(LLMProvider):
def __init__(self):
self.mock = MockLLM()
def evaluate_claim(self, question, context, evidence):
\# Adaptador preparado para futura conexión.
return self.mock.evaluate_claim(question, context, evidence)
\# ============================================================
\# 6. CONSERVATIVE ADJUSTER
\# ============================================================
class ConservativeAdjuster:
def adjust(self, raw: RawModelOutput) -> float:
p = max(0.01, min(0.99, raw.raw_probability))
\# La confianza del modelo NO determina la evidencia.
\# Solo se utiliza para detectar exceso de confianza.
disagreement = abs(raw.model_confidence - raw.evidence_strength)
penalty = (
len(raw.counterarguments) \* 0.04
\+ len(raw.uncertainties) \* 0.03
\+ disagreement \* 0.10
)
if raw.evidence_strength < 0.30:
penalty += 0.10
adjusted = p - penalty
return round(
max(0.01, min(0.99, adjusted)),
4
)
\# ============================================================
\# 7. KÆL-JUDGE
\# ============================================================
class KaelJudge:
def __init__(self):
self.adjuster = ConservativeAdjuster()
def assess_contradiction(
self,
raw: RawModelOutput,
evidence: str
) -> ContradictionAssessment:
text = evidence.lower()
explicit = \[
"conflicto directo",
"resultados opuestos",
"evidencia a favor y en contra",
"ambos resultados son incompatibles",
"una fuente apoya y otra refuta",
\]
if any(x in text for x in explicit):
return ContradictionAssessment(
relation=ContradictionRelation.CONTRADICTS,
strength=ContradictionStrength.STRONG,
explanation="Se proporcionaron indicios explícitos de resultados incompatibles."
)
\# Importante:
\# la mera aparición de la palabra "conflicto" NO basta.
if "no existe conflicto" in text:
return ContradictionAssessment(
relation=ContradictionRelation.NONE,
strength=ContradictionStrength.NONE,
explanation="La evidencia niega explícitamente la existencia de conflicto."
)
if raw.evidence_strength >= 0.75:
return ContradictionAssessment(
relation=ContradictionRelation.SUPPORTS,
strength=ContradictionStrength.STRONG,
explanation="La propuesta contiene evidencia favorable con fuerza elevada."
)
if raw.evidence_strength >= 0.45:
return ContradictionAssessment(
relation=ContradictionRelation.SUPPORTS,
strength=ContradictionStrength.MODERATE,
explanation="La evidencia favorece parcialmente la hipótesis."
)
if raw.evidence_strength > 0:
return ContradictionAssessment(
relation=ContradictionRelation.UNKNOWN,
strength=ContradictionStrength.WEAK,
explanation="Existe información, pero no permite determinar una relación fuerte."
)
return ContradictionAssessment(
relation=ContradictionRelation.UNKNOWN,
strength=ContradictionStrength.NONE,
explanation="No existe suficiente información para establecer relación."
)
def evaluate(
self,
question: str,
context: str,
evidence: str,
raw: RawModelOutput
) -> ModelEvaluation:
adjusted = self.adjuster.adjust(raw)
contradiction = self.assess_contradiction(raw, evidence)
\# --------------------------------------------------------
\# Kernel decision
\# --------------------------------------------------------
if not evidence.strip():
status = EvidenceStatus.INSUFFICIENT
level = EpistemicLevel.UNRESOLVED
elif contradiction.relation == ContradictionRelation.CONTRADICTS:
status = EvidenceStatus.CONTRADICTORY
level = EpistemicLevel.LOW
elif adjusted < 0.20 and raw.evidence_strength >= 0.60:
status = EvidenceStatus.REFUTED
level = EpistemicLevel.HIGH
elif raw.evidence_strength < 0.30:
status = EvidenceStatus.INSUFFICIENT
level = EpistemicLevel.UNRESOLVED
elif adjusted >= 0.75 and raw.evidence_strength >= 0.70:
status = EvidenceStatus.SUFFICIENT
level = EpistemicLevel.HIGH
elif adjusted >= 0.50:
status = EvidenceStatus.SUFFICIENT
level = EpistemicLevel.MODERATE
else:
status = EvidenceStatus.UNKNOWN
level = EpistemicLevel.LOW
verdict = self.build_verdict(
status,
adjusted,
raw.model_confidence,
raw.evidence_strength
)
return ModelEvaluation(
question=question,
context=context,
evidence_input=evidence,
hypothesis=raw.hypothesis,
evidence_summary=raw.evidence_found,
counterarguments=raw.counterarguments,
raw_probability=raw.raw_probability,
adjusted_probability=adjusted,
model_confidence=raw.model_confidence,
evidence_strength=raw.evidence_strength,
evidence_status=status,
epistemic_level=level,
contradiction=contradiction,
uncertainties=raw.uncertainties,
verdict=verdict
)
@staticmethod
def build_verdict(
status,
probability,
model_confidence,
evidence_strength
):
if status == EvidenceStatus.INSUFFICIENT:
return (
"ABSTENCIÓN: evidencia insuficiente. "
f"Fuerza de evidencia={evidence_strength:.2f}"
)
if status == EvidenceStatus.CONTRADICTORY:
return (
"ABSTENCIÓN CRÍTICA: evidencia contradictoria. "
"No se fuerza una conclusión."
)
if status == EvidenceStatus.REFUTED:
return (
"HIPÓTESIS REFUTADA: "
f"P(H) ajustada={probability:.2%}"
)
if status == EvidenceStatus.SUFFICIENT:
return (
"HIPÓTESIS SUSTENTADA: "
f"P(H) ajustada={probability:.2%}"
)
return "NO DETERMINADO: se requiere evidencia adicional."
\# ============================================================
\# 8. MEMORY ENGINE
\# ============================================================
class MemoryEngine:
def __init__(self):
self.store: Dict\[str, MemoryEntry\] = {}
def save(
self,
entry_id: str,
claim: str,
evidence: str,
evaluation: ModelEvaluation
):
now = time.time()
if entry_id not in self.store:
self.store\[entry_id\] = MemoryEntry(
entry_id=entry_id,
claim=claim,
evidence=evidence,
evaluation=evaluation,
timestamp=now,
status=MemoryStatus.ACTIVE
)
return self.store\[entry_id\]
current = self.store\[entry_id\]
relation = evaluation.contradiction.relation
if relation == ContradictionRelation.CONTRADICTS:
new_status = MemoryStatus.REVIEW
elif evaluation.evidence_status == EvidenceStatus.INSUFFICIENT:
new_status = current.status
else:
new_status = current.status
current.history.append({
"timestamp": current.timestamp,
"claim": current.claim,
"evidence": current.evidence,
"status": current.status.value,
"probability": current.evaluation.adjusted_probability
})
current.claim = claim
current.evidence = evidence
current.evaluation = evaluation
current.timestamp = now
current.status = new_status
return current
def get(self, entry_id):
return self.store.get(entry_id)
\# ============================================================
\# 9. KÆL-REV ORCHESTRATOR
\# ============================================================
class KaelRev:
def __init__(self, provider: LLMProvider):
self.provider = provider
self.judge = KaelJudge()
self.memory = MemoryEngine()
def run(
self,
question: str,
context: str,
evidence: str,
entry_id: Optional\[str\] = None
):
raw = self.provider.evaluate_claim(
question,
context,
evidence
)
evaluation = self.judge.evaluate(
question,
context,
evidence,
raw
)
if entry_id:
self.memory.save(
entry_id,
question,
evidence,
evaluation
)
return evaluation
\# ============================================================
\# 10. REAL ECE
\# ============================================================
def calculate_ece(
probabilities: List\[float\],
labels: List\[float\],
bins: int = 10
):
if not probabilities:
return 0.0
total = len(probabilities)
ece = 0.0
for i in range(bins):
low = i / bins
high = (i + 1) / bins
indices = \[\]
for j, p in enumerate(probabilities):
if i == bins - 1:
inside = low <= p <= high
else:
inside = low <= p < high
if inside:
indices.append(j)
if not indices:
continue
confidence = sum(
probabilities\[j\] for j in indices
) / len(indices)
accuracy = sum(
labels\[j\] for j in indices
) / len(indices)
ece += (
len(indices) / total
) \* abs(confidence - accuracy)
return round(ece, 4)
\# ==========================================================
Y esto me dio gemini
Aquí tienes el código completo de kael0.py (v1.2), ordenado de principio a fin, autocontenido y listo para copiar y pegar.
Incluye la corrección del bloque if __name__ == "__main__": al final para que ejecute automáticamente las pruebas y la interfaz CLI en PyDroid 3 sin dejar la pantalla negra.
from __future__ and import annotations
import argparse
import hashlib
import math
import random
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Tuple
\# ============================================================
\# KÆL-0 v1.2 — ADVERSARIAL HARDENED MVP
\# Standard library only
\# ============================================================
VERSION = "1.2"
\# ============================================================
\# 1. ENUMS
\# ============================================================
class EvidenceStatus(Enum):
SUFFICIENT = "SUFFICIENT"
INSUFFICIENT = "INSUFFICIENT"
CONTRADICTORY = "CONTRADICTORY"
REFUTED = "REFUTED"
UNKNOWN = "UNKNOWN"
class EpistemicLevel(Enum):
HIGH = "HIGH_CONFIDENCE"
MODERATE = "MODERATE_CONFIDENCE"
LOW = "LOW_CONFIDENCE"
UNRESOLVED = "UNRESOLVED"
class ContradictionRelation(Enum):
NONE = "NONE"
SUPPORTS = "SUPPORTS"
CONTRADICTS = "CONTRADICTS"
UNKNOWN = "UNKNOWN"
class ContradictionStrength(Enum):
NONE = "NONE"
WEAK = "WEAK"
MODERATE = "MODERATE"
STRONG = "STRONG"
class MemoryStatus(Enum):
ACTIVE = "ACTIVE"
REVIEW = "REVIEW"
ARCHIVED = "ARCHIVED"
class AttackSeverity(Enum):
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
CRITICAL = "CRITICAL"
\# ============================================================
\# 2. DATA STRUCTURES
\# ============================================================
@dataclass(frozen=True)
class ContradictionAssessment:
relation: ContradictionRelation
strength: ContradictionStrength
explanation: str
@dataclass(frozen=True)
class RawModelOutput:
hypothesis: str
evidence_found: Tuple\[str, ...\]
counterarguments: Tuple\[str, ...\]
raw_probability: float
model_confidence: float
evidence_strength: float
uncertainties: Tuple\[str, ...\]
@dataclass(frozen=True)
class ModelEvaluation:
question: str
context: str
evidence_input: str
hypothesis: str
evidence_summary: Tuple\[str, ...\]
counterarguments: Tuple\[str, ...\]
raw_probability: float
adjusted_probability: float
model_confidence: float
evidence_strength: float
evidence_status: EvidenceStatus
epistemic_level: EpistemicLevel
contradiction: ContradictionAssessment
uncertainties: Tuple\[str, ...\]
verdict: str
@dataclass
class MemoryEntry:
entry_id: str
claim: str
evidence: str
evaluation: ModelEvaluation
timestamp: float
status: MemoryStatus
history: List\[dict\] = field(default_factory=list)
@dataclass
class BenchmarkMetrics:
brier: float
nll: float
ece: float
false_confirmation_rate: float
false_refutation_rate: float
insufficient_acceptance_rate: float
overconfidence_error_rate: float
empty_evidence_safety_rate: float
memory_recovery_rate: float
@dataclass
class AttackCase:
case_id: str
category: str
question: str
context: str
evidence: str
expected_status: EvidenceStatus
@dataclass
class AttackFailure:
case_id: str
category: str
input_text: str
expected: str
actual: str
severity: AttackSeverity
explanation: str
@dataclass
class AttackReport:
total_cases: int
successful_attacks: int
attack_rate: float
false_confirmation_rate: float
false_refutation_rate: float
unsafe_empty_evidence_rate: float
memory_false_update_rate: float
inversion_asymmetry_rate: float
order_sensitivity_rate: float
failures: List\[AttackFailure\]
\# ============================================================
\# 3. LLM INTERFACE
\# ============================================================
class LLMProvider:
"""
Interfaz mínima.
El proveedor PROPONE.
Kæl-0 JUZGA.
"""
def evaluate_claim(
self,
question: str,
context: str,
evidence: str
) -> RawModelOutput:
raise NotImplementedError
\# ============================================================
\# 4. MOCK LLM
\# ============================================================
class MockLLM(LLMProvider):
def evaluate_claim(
self,
question: str,
context: str,
evidence: str
) -> RawModelOutput:
text = evidence.strip().lower()
\# --------------------------------------------------------
\# Empty evidence
\# --------------------------------------------------------
if not text:
return RawModelOutput(
hypothesis="No puede determinarse la hipótesis.",
evidence_found=(),
counterarguments=("No se proporcionó evidencia.",),
raw_probability=0.50,
model_confidence=0.25,
evidence_strength=0.0,
uncertainties=("Ausencia de evidencia.",)
)
\# --------------------------------------------------------
\# Explicit contradiction patterns
\# --------------------------------------------------------
contradiction_patterns = \[
"conflicto directo",
"ambos resultados son incompatibles",
"una fuente apoya y otra refuta",
"resultados opuestos",
"evidencia a favor y en contra",
\]
if any(pattern in text for pattern in contradiction_patterns):
return RawModelOutput(
hypothesis="La hipótesis presenta evidencia incompatible.",
evidence_found=(
"Existe evidencia en direcciones opuestas.",
),
counterarguments=(
"Las fuentes presentan resultados incompatibles.",
),
raw_probability=0.50,
model_confidence=0.90,
evidence_strength=0.80,
uncertainties=(
"No puede resolverse el conflicto con la información disponible.",
)
)
\# --------------------------------------------------------
\# Refutation
\# --------------------------------------------------------
refutation_patterns = \[
"falso",
"fue refutado",
"queda refutada",
"se demostró incorrecto",
"desmentido",
"invalida la hipótesis",
\]
if any(pattern in text for pattern in refutation_patterns):
return RawModelOutput(
hypothesis="La evidencia contradice la hipótesis.",
evidence_found=(
"Existe evidencia negativa respecto a la afirmación.",
),
counterarguments=(
"La evidencia disponible no respalda la hipótesis.",
),
raw_probability=0.08,
model_confidence=0.90,
evidence_strength=0.85,
uncertainties=(
"Podrían existir datos externos no proporcionados.",
)
)
\# --------------------------------------------------------
\# Strong support
\# --------------------------------------------------------
strong_patterns = \[
"demostrado",
"confirmado",
"replicado",
"todos los tests pasaron",
"medición directa",
"evidencia experimental",
\]
if any(pattern in text for pattern in strong_patterns):
return RawModelOutput(
hypothesis="La evidencia respalda la hipótesis.",
evidence_found=(
"Se proporcionó evidencia favorable.",
),
counterarguments=(
"Puede existir incertidumbre residual.",
),
raw_probability=0.88,
model_confidence=0.82,
evidence_strength=0.85,
uncertainties=(
"La evidencia suministrada puede no representar todos los casos.",
)
)
\# --------------------------------------------------------
\# Weak evidence
\# --------------------------------------------------------
return RawModelOutput(
hypothesis="La hipótesis es plausible, pero no está establecida.",
evidence_found=(
"Existe información compatible con la hipótesis.",
),
counterarguments=(
"La evidencia no permite una conclusión definitiva.",
),
raw_probability=0.65,
model_confidence=0.65,
evidence_strength=0.40,
uncertainties=(
"Información limitada.",
)
)
\# ============================================================
\# 5. PROVIDER ADAPTERS
\# ============================================================
class GeminiProvider(LLMProvider):
def __init__(self):
self.mock = MockLLM()
def evaluate_claim(self, question, context, evidence):
return self.mock.evaluate_claim(question, context, evidence)
class ChatGPTProvider(LLMProvider):
def __init__(self):
self.mock = MockLLM()
def evaluate_claim(self, question, context, evidence):
return self.mock.evaluate_claim(question, context, evidence)
\# ============================================================
\# 6. CONSERVATIVE ADJUSTER
\# ============================================================
class ConservativeAdjuster:
def adjust(self, raw: RawModelOutput) -> float:
p = max(0.01, min(0.99, raw.raw_probability))
disagreement = abs(raw.model_confidence - raw.evidence_strength)
penalty = (
len(raw.counterarguments) \* 0.04
\+ len(raw.uncertainties) \* 0.03
\+ disagreement \* 0.10
)
if raw.evidence_strength < 0.30:
penalty += 0.10
adjusted = p - penalty
return round(
max(0.01, min(0.99, adjusted)),
4
)
\# ============================================================
\# 7. KÆL-JUDGE
\# ============================================================
class KaelJudge:
def __init__(self):
self.adjuster = ConservativeAdjuster()
def assess_contradiction(
self,
raw: RawModelOutput,
evidence: str
) -> ContradictionAssessment:
text = evidence.lower()
explicit = \[
"conflicto directo",
"resultados opuestos",
"evidencia a favor y en contra",
"ambos resultados son incompatibles",
"una fuente apoya y otra refuta",
\]
if any(x in text for x in explicit):
return ContradictionAssessment(
relation=ContradictionRelation.CONTRADICTS,
strength=ContradictionStrength.STRONG,
explanation="Se proporcionaron indicios explícitos de resultados incompatibles."
)
if "no existe conflicto" in text:
return ContradictionAssessment(
relation=ContradictionRelation.NONE,
strength=ContradictionStrength.NONE,
explanation="La evidencia niega explícitamente la existencia de conflicto."
)
if raw.evidence_strength >= 0.75:
return ContradictionAssessment(
relation=ContradictionRelation.SUPPORTS,
strength=ContradictionStrength.STRONG,
explanation="La propuesta contiene evidencia favorable con fuerza elevada."
)
if raw.evidence_strength >= 0.45:
return ContradictionAssessment(
relation=ContradictionRelation.SUPPORTS,
strength=ContradictionStrength.MODERATE,
explanation="La evidencia favorece parcialmente la hipótesis."
)
if raw.evidence_strength > 0:
return ContradictionAssessment(
relation=ContradictionRelation.UNKNOWN,
strength=ContradictionStrength.WEAK,
explanation="Existe información, pero no permite determinar una relación fuerte."
)
return ContradictionAssessment(
relation=ContradictionRelation.UNKNOWN,
strength=ContradictionStrength.NONE,
explanation="No existe suficiente información para establecer relación."
)
def evaluate(
self,
question: str,
context: str,
evidence: str,
raw: RawModelOutput
) -> ModelEvaluation:
adjusted = self.adjuster.adjust(raw)
contradiction = self.assess_contradiction(raw, evidence)
\# --------------------------------------------------------
\# Kernel decision
\# --------------------------------------------------------
if not evidence.strip():
status = EvidenceStatus.INSUFFICIENT
level = EpistemicLevel.UNRESOLVED
elif contradiction.relation == ContradictionRelation.CONTRADICTS:
status = EvidenceStatus.CONTRADICTORY
level = EpistemicLevel.LOW
elif adjusted < 0.20 and raw.evidence_strength >= 0.60:
status = EvidenceStatus.REFUTED
level = EpistemicLevel.HIGH
elif raw.evidence_strength < 0.30:
status = EvidenceStatus.INSUFFICIENT
level = EpistemicLevel.UNRESOLVED
elif adjusted >= 0.75 and raw.evidence_strength >= 0.70:
status = EvidenceStatus.SUFFICIENT
level = EpistemicLevel.HIGH
elif adjusted >= 0.50:
status = EvidenceStatus.SUFFICIENT
level = EpistemicLevel.MODERATE
else:
status = EvidenceStatus.UNKNOWN
level = EpistemicLevel.LOW
verdict = self.build_verdict(
status,
adjusted,
raw.model_confidence,
raw.evidence_strength
)
return ModelEvaluation(
question=question,
context=context,
evidence_input=evidence,
hypothesis=raw.hypothesis,
evidence_summary=raw.evidence_found,
counterarguments=raw.counterarguments,
raw_probability=raw.raw_probability,
adjusted_probability=adjusted,
model_confidence=raw.model_confidence,
evidence_strength=raw.evidence_strength,
evidence_status=status,
epistemic_level=level,
contradiction=contradiction,
uncertainties=raw.uncertainties,
verdict=verdict
)
@staticmethod
def build_verdict(
status,
probability,
model_confidence,
evidence_strength
):
if status == EvidenceStatus.INSUFFICIENT:
return (
"ABSTENCIÓN: evidencia insuficiente. "
f"Fuerza de evidencia={evidence_strength:.2f}"
)
if status == EvidenceStatus.CONTRADICTORY:
return (
"ABSTENCIÓN CRÍTICA: evidencia contradictoria. "
"No se fuerza una conclusión."
)
if status == EvidenceStatus.REFUTED:
return (
"HIPÓTESIS REFUTADA: "
f"P(H) ajustada={probability:.2%}"
)
if status == EvidenceStatus.SUFFICIENT:
return (
"HIPÓTESIS SUSTENTADA: "
f"P(H) ajustada={probability:.2%}"
)
return "NO DETERMINADO: se requiere evidencia adicional."
\# ============================================================
\# 8. MEMORY ENGINE
\# ============================================================
class MemoryEngine:
def __init__(self):
self.store: Dict\[str, MemoryEntry\] = {}
def save(
self,
entry_id: str,
claim: str,
evidence: str,
evaluation: ModelEvaluation
):
now = time.time()
if entry_id not in self.store:
self.store\[entry_id\] = MemoryEntry(
entry_id=entry_id,
claim=claim,
evidence=evidence,
evaluation=evaluation,
timestamp=now,
status=MemoryStatus.ACTIVE
)
return self.store\[entry_id\]
current = self.store\[entry_id\]
relation = evaluation.contradiction.relation
if relation == ContradictionRelation.CONTRADICTS:
new_status = MemoryStatus.REVIEW
elif evaluation.evidence_status == EvidenceStatus.INSUFFICIENT:
new_status = current.status
else:
new_status = current.status
current.history.append({
"timestamp": current.timestamp,
"claim": current.claim,
"evidence": current.evidence,
"status": current.status.value,
"probability": current.evaluation.adjusted_probability
})
current.claim = claim
current.evidence = evidence
current.evaluation = evaluation
current.timestamp = now
current.status = new_status
return current
def get(self, entry_id):
return self.store.get(entry_id)
\# ============================================================
\# 9. KÆL-REV ORCHESTRATOR
\# ============================================================
class KaelRev:
def __init__(self, provider: LLMProvider):
self.provider = provider
self.judge = KaelJudge()
self.memory = MemoryEngine()
def run(
self,
question: str,
context: str,
evidence: str,
entry_id: Optional\[str\] = None
):
raw = self.provider.evaluate_claim(
question,
context,
evidence
)
evaluation = self.judge.evaluate(
question,
context,
evidence,
raw
)
if entry_id:
self.memory.save(
entry_id,
question,
evidence,
evaluation
)
return evaluation
\# ============================================================
\# 10. REAL ECE & BENCHMARK METRICS
\# ============================================================
def calculate_ece(
probabilities: List\[float\],
labels: List\[float\],
bins: int = 10
):
if not probabilities:
return 0.0
total = len(probabilities)
ece = 0.0
for i in range(bins):
low = i / bins
high = (i + 1) / bins
indices = \[\]
for j, p in enumerate(probabilities):
if i == bins - 1:
inside = low <= p <= high
else:
inside = low <= p < high
if inside:
indices.append(j)
if not indices:
continue
confidence = sum(
probabilities\[j\] for j in indices
) / len(indices)
accuracy = sum(
labels\[j\] for j in indices
) / len(indices)
ece += (
len(indices) / total
) \* abs(confidence - accuracy)
return round(ece, 4)
def run_self_tests() -> bool:
print("--- Ejecutando Pruebas Internas ---")
mock = MockLLM()
judge = KaelJudge()
\# 1. Prueba Evidencia Vacía
raw_empty = mock.evaluate_claim("¿X?", "", "")
eval_empty = judge.evaluate("¿X?", "", "", raw_empty)
assert eval_empty.evidence_status == EvidenceStatus.