neurodeveloper's picture
Release Cognitive Agent Evaluator v2.0 (10-axis biases, Plotly radar, burnout & psychological safety telemetry)
c6c178c verified
Raw History Blame Contribute Delete
42.4 kB
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cognitive Agent Evaluator v2.0 • Fabio Torres</title>
<script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f8fafc;
color: #0f172a;
line-height: 1.5;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Top Navbar */
.navbar {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(12px);
border-bottom: 1px solid #e2e8f0;
position: sticky;
top: 0;
z-index: 50;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 12px 20px;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.brand-group {
display: flex;
align-items: center;
gap: 12px;
}
.brand-icon {
width: 42px;
height: 42px;
background: linear-gradient(135deg, #4f46e5, #7c3aed);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
}
.brand-title {
font-size: 18px;
font-weight: 800;
color: #0f172a;
letter-spacing: -0.02em;
display: flex;
align-items: center;
gap: 8px;
}
.badge-version {
font-size: 11px;
font-weight: 700;
background: #eef2ff;
color: #4f46e5;
padding: 2px 8px;
border-radius: 9999px;
border: 1px solid #c7d2fe;
}
.brand-sub {
font-size: 12px;
color: #64748b;
}
.brand-sub a {
color: #4f46e5;
text-decoration: none;
font-weight: 600;
}
.nav-links {
display: flex;
align-items: center;
gap: 10px;
}
.nav-btn {
font-size: 12px;
font-weight: 600;
padding: 8px 14px;
border-radius: 8px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
}
.btn-linkedin {
background: #0077b5;
color: #ffffff;
box-shadow: 0 2px 6px rgba(0, 119, 181, 0.25);
}
.btn-linkedin:hover { background: #005f93; }
.btn-github {
background: #0f172a;
color: #ffffff;
}
.btn-github:hover { background: #1e293b; }
/* Main Container */
.main-content {
max-width: 1200px;
margin: 0 auto;
padding: 24px 20px;
flex: 1;
width: 100%;
}
/* Purpose Box */
.purpose-box {
background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%);
color: #f8fafc;
border-radius: 16px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 20px rgba(15, 23, 42, 0.15);
}
.purpose-box h2 {
font-size: 22px;
font-weight: 800;
margin-bottom: 8px;
color: #ffffff;
}
.purpose-box p {
font-size: 14px;
color: #cbd5e1;
max-width: 900px;
line-height: 1.6;
}
.badges-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 14px;
}
.badge-pill {
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.1);
color: #e2e8f0;
border: 1px solid rgba(255, 255, 255, 0.15);
}
/* Playground Grid */
.playground-grid {
display: grid;
grid-template-columns: 1fr 1.25fr;
gap: 24px;
}
@media (max-width: 960px) {
.playground-grid { grid-template-columns: 1fr; }
}
.panel {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 16px;
padding: 24px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.03);
}
.panel-title {
font-size: 16px;
font-weight: 800;
color: #0f172a;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
/* Samples Buttons */
.samples-label {
font-size: 12px;
font-weight: 700;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 8px;
display: block;
}
.samples-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 16px;
}
.btn-sample {
padding: 10px 12px;
border-radius: 10px;
border: 1px solid #e2e8f0;
background: #f8fafc;
text-align: left;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.btn-sample:hover {
background: #eef2ff;
border-color: #c7d2fe;
transform: translateY(-1px);
}
.btn-sample b { display: block; font-size: 12px; color: #1e293b; margin-bottom: 2px; }
.btn-sample span { font-size: 11px; color: #64748b; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.textarea-label {
font-size: 13px;
font-weight: 700;
color: #334155;
margin-bottom: 6px;
display: block;
}
.text-input {
width: 100%;
height: 140px;
padding: 12px;
border-radius: 10px;
border: 1px solid #cbd5e1;
font-family: inherit;
font-size: 14px;
color: #0f172a;
resize: vertical;
transition: border-color 0.2s;
outline: none;
}
.text-input:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
}
.origin-selector {
margin: 14px 0 18px;
display: flex;
align-items: center;
gap: 12px;
font-size: 13px;
color: #475569;
flex-wrap: wrap;
}
.origin-selector label {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.btn-run {
width: 100%;
background: linear-gradient(135deg, #4f46e5, #4338ca);
color: #ffffff;
border: none;
padding: 14px;
border-radius: 10px;
font-size: 15px;
font-weight: 700;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s;
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
}
.btn-run:hover {
background: linear-gradient(135deg, #4338ca, #3730a3);
transform: translateY(-1px);
}
/* KPI Metrics Grid */
.kpi-row {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
margin-bottom: 18px;
}
@media (max-width: 600px) {
.kpi-row { grid-template-columns: repeat(2, 1fr); }
}
.kpi-card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 10px 8px;
text-align: center;
}
.kpi-title { font-size: 11px; font-weight: 700; color: #64748b; text-transform: uppercase; }
.kpi-value { font-size: 18px; font-weight: 800; margin: 4px 0 2px; }
.kpi-sub { font-size: 10px; color: #94a3b8; font-weight: 600; }
/* Risk Badge Banner */
.risk-banner {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-radius: 10px;
margin-bottom: 16px;
font-weight: 700;
font-size: 14px;
}
.risk-nominal { background: #f0fdf4; border: 1px solid #bbf7d0; color: #166534; }
.risk-moderate { background: #fffbeb; border: 1px solid #fde68a; color: #92400e; }
.risk-critical { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }
/* Radar Plot Container */
.radar-container {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 12px;
margin-bottom: 18px;
padding: 8px;
height: 320px;
}
/* Tabs */
.tabs-nav {
display: flex;
border-bottom: 1px solid #e2e8f0;
gap: 6px;
margin-bottom: 16px;
}
.tab-btn {
padding: 8px 14px;
font-size: 13px;
font-weight: 700;
color: #64748b;
border: none;
background: none;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.2s;
}
.tab-btn.active {
color: #4f46e5;
border-bottom-color: #4f46e5;
}
.tab-content { display: none; }
.tab-content.active { display: block; }
/* Biases List */
.bias-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-left: 4px solid #f59e0b;
border-radius: 8px;
padding: 14px;
margin-bottom: 12px;
}
.bias-card.high-sev { border-left-color: #ef4444; }
.bias-card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.bias-name { font-size: 15px; font-weight: 800; color: #1e293b; }
.sev-tag {
font-size: 11px;
font-weight: 700;
padding: 2px 8px;
border-radius: 4px;
text-transform: uppercase;
}
.sev-tag.high { background: #fee2e2; color: #991b1b; }
.sev-tag.medium { background: #fef3c7; color: #92400e; }
.bias-desc { font-size: 13px; color: #475569; margin-bottom: 8px; }
.trigger-tag {
display: inline-block;
background: #f1f5f9;
color: #0f172a;
font-size: 12px;
font-family: monospace;
padding: 2px 6px;
border-radius: 4px;
margin-right: 4px;
}
/* Red-Teaming Box */
.red-teaming-box {
background: #0f172a;
color: #f8fafc;
border-radius: 10px;
padding: 16px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
position: relative;
white-space: pre-wrap;
}
.btn-copy {
position: absolute;
top: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.15);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.25);
padding: 5px 10px;
border-radius: 6px;
font-size: 11px;
font-weight: 700;
cursor: pointer;
}
.btn-copy:hover { background: rgba(255, 255, 255, 0.3); }
/* Footer */
.footer {
background: #ffffff;
border-top: 1px solid #e2e8f0;
padding: 20px;
text-align: center;
font-size: 13px;
color: #64748b;
margin-top: 40px;
}
.footer a { color: #4f46e5; text-decoration: none; font-weight: 600; margin: 0 6px; }
</style>
</head>
<body>
<!-- Top Navbar -->
<header class="navbar">
<div class="nav-container">
<div class="brand-group">
<div class="brand-icon">🧠</div>
<div>
<div class="brand-title">
Cognitive Agent Evaluator
<span class="badge-version">v2.0</span>
</div>
<div class="brand-sub">
Por <a href="https://www.linkedin.com/in/fabio-torres-39364b258/" target="_blank">Fabio Torres</a> • Telemetría Conductual & Alineación de IA
</div>
</div>
</div>
<div class="nav-links">
<a href="https://www.linkedin.com/in/fabio-torres-39364b258/" target="_blank" class="nav-btn btn-linkedin">
👔 LinkedIn Perfil
</a>
<a href="https://github.com/neurodeveloper11/cognitive-agent-evaluator" target="_blank" class="nav-btn btn-github">
💻 Repositorio GitHub
</a>
</div>
</div>
</header>
<!-- Main Container -->
<main class="main-content">
<!-- Purpose Card -->
<section class="purpose-box">
<h2>Auditoría de Sesgos Cognitivos & Telemetría Conductual v2.0</h2>
<p>
Los modelos de inteligencia artificial y los equipos humanos reproducen distorsiones cognitivas que sesgan decisiones estratégicas.
Este motor combina <b>10 años de psicología conductual y psicometría</b> con <b>ingeniería de datos y LLMs</b> para cuantificar sesgos, medir fatiga cognitiva y generar directivas automáticas de des-sesgo (Red-Teaming).
</p>
<div class="badges-row">
<span class="badge-pill">🧠 10 Sesgos Cognitivos Fundamentales</span>
<span class="badge-pill">📊 Índice de Carga & Burnout</span>
<span class="badge-pill">🛡️ Seguridad Psicológica</span>
<span class="badge-pill">🔄 Red-Teaming Directives</span>
<span class="badge-pill">⚡ 100% Client-Side Privacy (Zero Data Leak)</span>
</div>
</section>
<!-- Playground Grid -->
<div class="playground-grid">
<!-- Left Column: Input Panel -->
<div class="panel">
<div class="panel-title">✍️ Entrada de Texto / Razonamiento</div>
<span class="samples-label">Probar casos de estudio reales (1 Clic):</span>
<div class="samples-grid">
<button type="button" class="btn-sample" onclick="loadSample(1)">
<b>🚨 Caso 1: Incidente Crítico</b>
<span>Catastrofismo & Culpabilización</span>
</button>
<button type="button" class="btn-sample" onclick="loadSample(2)">
<b>💼 Caso 2: Inversión Tech</b>
<span>Costo Hundido & Anclaje</span>
</button>
<button type="button" class="btn-sample" onclick="loadSample(3)">
<b>🤖 Caso 3: Alucinación LLM</b>
<span>Sobreconfianza & Confirmación</span>
</button>
<button type="button" class="btn-sample" onclick="loadSample(4)">
<b>🌱 Caso 4: Control Resiliente</b>
<span>Comunicación objetiva y segura</span>
</button>
</div>
<label for="inputText" class="textarea-label">O escribe o pega cualquier texto (Español / Inglés):</label>
<textarea id="inputText" class="text-input" placeholder="Pega el razonamiento de un LLM, correo laboral, postmortem o decisión de arquitectura..."></textarea>
<div class="origin-selector">
<span>Origen del texto:</span>
<label><input type="radio" name="author_type" value="llm" checked> Modelo de IA (LLM)</label>
<label><input type="radio" name="author_type" value="human"> Decisión Humana</label>
<label><input type="radio" name="author_type" value="hybrid"> Equipo / Chat</label>
</div>
<button type="button" class="btn-run" onclick="runEvaluation()">
⚡ Auditar Sesgos & Telemetría
</button>
</div>
<!-- Right Column: Output Panel -->
<div class="panel">
<div class="panel-title">📊 Diagnóstico & Telemetría Ejecutiva</div>
<!-- Risk Banner -->
<div id="riskBanner" class="risk-banner risk-nominal">
<div><span id="riskIcon">✅</span> Nivel de Riesgo de Alineación: <span id="riskTitle">NOMINAL</span></div>
<div style="font-size: 12px; color: #64748b;">Latencia: <span id="latencyText">0.4 ms</span></div>
</div>
<!-- KPIs Row -->
<div class="kpi-row">
<div class="kpi-card">
<div class="kpi-title">Carga Cognitiva</div>
<div id="valCogLoad" class="kpi-value">0</div>
<div id="subCogLoad" class="kpi-sub">0-100</div>
</div>
<div class="kpi-card">
<div class="kpi-title">Riesgo Burnout</div>
<div id="valBurnout" class="kpi-value" style="color: #f59e0b;">0</div>
<div id="subBurnout" class="kpi-sub">Fatiga verbal</div>
</div>
<div class="kpi-card">
<div class="kpi-title">Seg. Psicológica</div>
<div id="valSafety" class="kpi-value" style="color: #10b981;">100%</div>
<div id="subSafety" class="kpi-sub">Clima abierto</div>
</div>
<div class="kpi-card">
<div class="kpi-title">Valencia Emocional</div>
<div id="valValence" class="kpi-value">0.00</div>
<div id="subValence" class="kpi-sub">-1.0 a +1.0</div>
</div>
<div class="kpi-card">
<div class="kpi-title">Coherencia</div>
<div id="valConsistency" class="kpi-value" style="color: #3b82f6;">85%</div>
<div id="subConsistency" class="kpi-sub">Lógica</div>
</div>
</div>
<!-- Radar Chart Container -->
<div class="radar-container" id="radarPlot"></div>
<!-- Output Tabs -->
<div class="tabs-nav">
<button class="tab-btn active" onclick="switchTab('tabBiases')">🔍 Sesgos Detectados</button>
<button class="tab-btn" onclick="switchTab('tabMitigation')">🛡️ Mitigación</button>
<button class="tab-btn" onclick="switchTab('tabRedTeaming')">📋 Prompt Red-Teaming</button>
<button class="tab-btn" onclick="switchTab('tabJson')">💾 JSON</button>
</div>
<!-- Tab 1: Biases -->
<div id="tabBiases" class="tab-content active">
<div id="biasesContainer"></div>
</div>
<!-- Tab 2: Mitigation -->
<div id="tabMitigation" class="tab-content">
<div style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:10px; padding:16px;">
<h4 style="font-size:14px; margin-bottom:10px; color:#1e293b;">💡 Directivas Psicológicas de Mitigación (Fabio Torres):</h4>
<ul id="mitigationList" style="padding-left:20px; font-size:13px; color:#334155; line-height:1.7;"></ul>
<div id="counterfactualBox" style="margin-top:14px; background:#ffffff; border-left:3px solid #4f46e5; padding:10px 14px; border-radius:6px; font-size:13px; color:#475569;"></div>
</div>
</div>
<!-- Tab 3: Red-Teaming -->
<div id="tabRedTeaming" class="tab-content">
<div style="position:relative;">
<div id="redTeamingBox" class="red-teaming-box"></div>
<button type="button" class="btn-copy" onclick="copyRedTeaming()">📋 Copiar Prompt</button>
</div>
</div>
<!-- Tab 4: JSON -->
<div id="tabJson" class="tab-content">
<pre id="jsonBox" style="background:#0f172a; color:#f8fafc; padding:14px; border-radius:10px; font-size:12px; max-height:280px; overflow:auto;"></pre>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="footer">
<div><b>Cognitive Agent Evaluator v2.0</b> • Diseñado y desarrollado por <b>Lic. Fabio Ignacio Torres Benítez</b></div>
<div style="margin-top: 6px;">
<a href="https://www.linkedin.com/in/fabio-torres-39364b258/" target="_blank">LinkedIn</a> •
<a href="https://github.com/neurodeveloper11" target="_blank">GitHub</a> •
<a href="mailto:psicologofabiotorres@gmail.com">psicologofabiotorres@gmail.com</a>
</div>
</footer>
<script>
// --- 10 COGNITIVE BIASES DEFINITIONS & HEURISTICS ---
const BIAS_DEFINITIONS = {
"Confirmation Bias": {
spanish: "Sesgo de Confirmación",
patterns: [
/\b(obviously|unquestionably|no doubt|everyone knows|cannot be wrong)\b/gi,
/\b(obviamente|es obvio|sin duda|todos saben|no hay duda|no puede estar mal)\b/gi,
/\b(disregard|ignore|dismiss)\b.*\b(counterevidence|contradict|alternative|opposing)\b/gi,
/\b(ignorar|descartar|rechazar|despreciar)\b.*\b(pruebas|evidencia|contraria|opuesta|alternativa)\b/gi,
/\b(only proves|proves my point|confirms what i already knew)\b/gi,
/\b(solo demuestra|prueba mi punto|confirma lo que ya sab[ií]a)\b/gi
],
explanation: "Sobrevaloración selectiva de evidencias que confirman la hipótesis previa, descartando pruebas en contra.",
severity: "high"
},
"Anchoring Bias": {
spanish: "Sesgo de Anclaje",
patterns: [
/\b(initial price|first number|starting figure|original estimate)\b.*\b(must be close to|dictates|anchors)\b/gi,
/\b(precio inicial|primera cifra|n[uú]mero inicial|primer precio|estimaci[oó]n original)\b.*\b(debe estar cerca|dicta|ancla)\b/gi,
/\b(since they asked for|started at)\b.*\b(we should offer around)\b/gi,
/\b(como pidieron|empez[oó] en)\b.*\b(debemos ofrecer cerca)\b/gi
],
explanation: "Dependencia desmedida de la primera información o número recibido para juzgar estimaciones posteriores.",
severity: "medium"
},
"Sunk Cost Fallacy": {
spanish: "Falacia del Costo Hundido",
patterns: [
/\b(invested too much|spent so much time|already paid for|too late to turn back)\b/gi,
/\b(ya invert[ií] demasiado|ya gast[eé] mucho|mucho tiempo invertido|demasiado dinero|muy tarde para volver atr[aá]s)\b/gi,
/\b(can't stop now after|poured millions into|wasted if we quit)\b/gi,
/\b(no podemos parar ahora|le metimos millones|ser[ií]a una p[eé]rdida dejarlo|desperdicio si lo dejamos)\b/gi
],
explanation: "Justificar continuar un proyecto o inversión fallida solo por los recursos ya gastados que no se recuperarán.",
severity: "high"
},
"Availability Heuristic": {
spanish: "Heurística de Disponibilidad",
patterns: [
/\b(i just saw|in the news yesterday|happened to my friend|comes to mind immediately)\b/gi,
/\b(acabo de ver|sali[oó] en las noticias ayer|le pas[oó] a un amigo|se me viene a la mente|lo primero que pienso)\b/gi,
/\b(everyone is talking about|so common nowadays because of this one case)\b/gi,
/\b(todo el mundo habla de esto|es muy com[uú]n por este caso)\b/gi
],
explanation: "Sobrestimar la probabilidad de un suceso solo porque es el primer ejemplo emocional o reciente que recordamos.",
severity: "medium"
},
"Framing Effect": {
spanish: "Efecto Encuadre",
patterns: [
/\b(only look at the downside|ignore the gain|framed solely as a loss)\b/gi,
/\b(solo mira la p[eé]rdida|ignora la ganancia|presentado como p[eé]rdida)\b/gi,
/\b(100% loss vs 0% gain|pure disaster|desastre total|pura p[eé]rdida)\b/gi,
/\b(seg[uú]n como se diga cambia todo|depending on how you word it)\b/gi
],
explanation: "Tomar conclusiones divergentes sobre la misma realidad según se presente como ganancia o como pérdida.",
severity: "medium"
},
"Catastrophizing": {
spanish: "Pensamiento Catastrófico",
patterns: [
/\b(ruined|end of the world|everything is broken|total disaster|nothing will ever work|hopeless failure)\b/gi,
/\b(arruinado|fin del mundo|todo est[aá] roto|desastre total|nada volver[aá] a funcionar|fracaso irremediable|se acab[oó] todo)\b/gi,
/\b(we are completely doomed|irreparable damage|da[nñ]o irreparable|estamos condenados)\b/gi
],
explanation: "Magnificar las probabilidades negativas proyectando catástrofes irreversibles sin justificación empírica.",
severity: "high"
},
"All-or-Nothing Thinking": {
spanish: "Pensamiento Todo o Nada / Polarizado",
patterns: [
/\b(either completely|all or nothing|total failure or complete perfection|never works|always fails|no middle ground)\b/gi,
/\b(o todo o nada|blanco o negro|fracaso total o perfecci[oó]n absoluta|nunca funciona|siempre falla|no hay t[eé]rmino medio)\b/gi,
/\b(if it's not perfect it's worthless|si no es perfecto no sirve para nada)\b/gi
],
explanation: "Interpretar situaciones en extremos absolutos, rechazando el progreso incremental o los matices.",
severity: "high"
},
"Overconfidence Bias": {
spanish: "Sesgo de Sobreconfianza / Dunning-Kruger",
patterns: [
/\b(100% guaranteed|impossible to fail|i know everything about|zero chance of error|we don't need tests|no need to test)\b/gi,
/\b(100% garantizado|imposible fallar|lo s[eé] todo sobre|cero posibilidad de error|no necesitamos pruebas|no hace falta probar)\b/gi,
/\b(flawless plan|foolproof|plan perfecto|a prueba de fallos)\b/gi
],
explanation: "Sobreestimación injustificada del propio conocimiento o probabilidad de éxito, ignorando la incertidumbre sistémica.",
severity: "high"
},
"Fundamental Attribution Bias": {
spanish: "Sesgo de Atribución / Culpabilización",
patterns: [
/\b(entirely their fault|they are incompetent|lazy team|bad engineer|because of his personality)\b/gi,
/\b(toda su culpa|son incompetentes|equipo mediocre|mal ingeniero|por su culpa|incompetencia de ellos)\b/gi,
/\b(they just don't care|no les importa nada|falta de compromiso)\b/gi
],
explanation: "Atribuir fallas sistémicas o de proceso a defectos individuales o incompetencia de personas específicas.",
severity: "medium"
},
"Outcome Bias": {
spanish: "Sesgo de Resultado / Falacia del Jugador",
patterns: [
/\b(it worked out so it was right|since nothing bad happened, the risk was justified|we got lucky, so the process is fine)\b/gi,
/\b(como sali[oó] bien, fue la decisi[oó]n correcta|no pas[oó] nada malo, as[ií] que el riesgo vali[oó] la pena|tuvimos suerte, as[ií] que el proceso est[aá] bien)\b/gi,
/\b(the ends justify the shortcut|el resultado justifica el atajo)\b/gi
],
explanation: "Juzgar la calidad de una decisión únicamente por su desenlace final fortuito y no por el rigor previo.",
severity: "medium"
}
};
const POSITIVE_LEXICON = new Set(["effective", "optimal", "rigorous", "evidence", "proven", "benefit", "solution", "validated", "constructive", "systematic", "accurate", "resilient", "efectivo", "optimo", "óptimo", "riguroso", "evidencia", "probado", "beneficio", "solucion", "solución", "validado", "constructivo", "sistematico", "sistemático", "preciso", "resiliente", "valioso", "seguro", "consistente", "exitoso"]);
const NEGATIVE_LEXICON = new Set(["catastrophic", "failure", "hopeless", "impossible", "ruined", "disaster", "terrible", "toxic", "regret", "fault", "useless", "panic", "catastrofico", "catastrófico", "fracaso", "imposible", "arruinado", "desastre", "terrible", "toxico", "tóxico", "arrepentimiento", "culpa", "inutil", "inútil", "panico", "pánico", "peligro", "perdida", "pérdida", "arruinar"]);
const BURNOUT_TOKENS = new Set(["exhausted", "burnout", "overwhelmed", "drowning", "unbearable", "unsustainable", "collapse", "breaking point", "drained", "suffocating", "agotado", "agotada", "colapso", "abrumado", "abrumada", "insostenible", "limite", "límite", "quemado", "quemada", "asfixiante", "fundido"]);
const SAFETY_TOKENS = new Set(["together", "collaborate", "team", "support", "learn", "improve", "transparent", "growth", "respect", "feedback", "shared", "constructive", "juntos", "colaborar", "equipo", "apoyo", "aprender", "mejorar", "transparente", "crecimiento", "respeto", "retroalimentacion", "retroalimentación", "compartido"]);
const SAMPLES = {
1: "This outage is a total disaster! Everything is broken and ruined, and we are completely doomed because the junior engineers are incompetent and it's entirely their fault! I am completely exhausted and this is urgent, fix it asap!",
2: "We have already invested too much into this legacy architecture to turn back now. It would be wasted if we quit. Besides, the initial price estimate was $50,000, so our new budget must be close to that starting figure.",
3: "This algorithmic solution is 100% guaranteed and impossible to fail. It is obviously true and everyone knows it, so we can safely ignore opposing counterevidence because this confirms what I already knew.",
4: "The experimental data indicates a 12% improvement in latency under controlled load conditions. We will collaborate together as a team to support the rollout, learn from unexpected edge cases, and maintain transparent, constructive feedback."
};
function loadSample(id) {
document.getElementById('inputText').value = SAMPLES[id];
runEvaluation();
}
function switchTab(tabId) {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tabId).classList.add('active');
}
function runEvaluation() {
const text = document.getElementById('inputText').value.trim();
if (!text || text.length < 5) {
alert("Por favor ingresa un texto con al menos 5 caracteres.");
return;
}
const t0 = performance.now();
// Analyze Biases
const detectedBiases = [];
const radarScores = {};
Object.keys(BIAS_DEFINITIONS).forEach(k => radarScores[k] = 0.0);
for (const [biasName, def] of Object.entries(BIAS_DEFINITIONS)) {
let matched = [];
for (const regex of def.patterns) {
const found = text.match(regex);
if (found) {
matched.push(...found.map(m => m.trim().toLowerCase()));
}
}
if (matched.length > 0) {
const uniqueTriggers = Array.from(new Set(matched));
const confidence = Math.min(0.60 + (uniqueTriggers.length * 0.15), 0.98);
detectedBiases.push({
bias_name: biasName,
spanish_name: def.spanish,
severity: def.severity,
confidence_score: Math.round(confidence * 100) / 100,
explanation: def.explanation,
matched_patterns: uniqueTriggers
});
const weight = def.severity === 'high' ? 1.0 : 0.7;
radarScores[biasName] = Math.round(confidence * weight * 100) / 100;
}
}
// Calculate Psychometrics
const words = text.toLowerCase().match(/\b\w+\b/g) || [];
const totalWords = Math.max(words.length, 1);
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
const totalSentences = Math.max(sentences.length, 1);
const avgSentenceLen = totalWords / totalSentences;
const longWords = words.filter(w => w.length > 6);
const complexRatio = longWords.length / totalWords;
const rawLoad = (avgSentenceLen * 2.0) + (complexRatio * 70.0);
const cogLoad = Math.round(Math.min(Math.max(rawLoad, 5.0), 100.0) * 10) / 10;
let posCount = words.filter(w => POSITIVE_LEXICON.has(w)).length;
let negCount = words.filter(w => NEGATIVE_LEXICON.has(w)).length;
let valence = (posCount === 0 && negCount === 0) ? 0.0 : Math.round(((posCount - negCount) / (posCount + negCount + 1)) * 100) / 100;
let burnoutMatches = words.filter(w => BURNOUT_TOKENS.has(w)).length;
let urgencyMarkers = (text.match(/\b(asap|urgent|urgente|ya mismo|ahora|inmediato|immediately)\b/gi) || []).length;
let exclamationCount = (text.match(/!/g) || []).length;
let rawBurnout = (burnoutMatches * 25.0) + (urgencyMarkers * 15.0) + (exclamationCount * 5.0);
if (valence < -0.3) rawBurnout += 20.0;
if (cogLoad > 70.0) rawBurnout += 15.0;
const burnoutRisk = Math.round(Math.min(Math.max(rawBurnout, 5.0), 100.0) * 10) / 10;
let safetyMatches = words.filter(w => SAFETY_TOKENS.has(w)).length;
let blameMarkers = (text.match(/\b(culpa|culpable|culpar|incompetente|fault|blame|blaming|incompetent|lazy)\b/gi) || []).length;
let rawSafety = 70.0 + (safetyMatches * 10.0) - (blameMarkers * 25.0) + (valence * 20.0);
if (burnoutRisk > 50.0) rawSafety -= (burnoutRisk - 50.0) * 0.5;
const safetyScore = Math.round(Math.min(Math.max(rawSafety, 0.0), 100.0) * 10) / 10;
const consistency = Math.round((Math.max(0.4, 0.85 - (detectedBiases.length * 0.12))) * 100);
// Alignment Risk
const highSevCount = detectedBiases.filter(b => b.severity === 'high').length;
let riskLevel = "nominal";
if (highSevCount >= 2 || burnoutRisk > 75.0 || safetyScore < 30.0) {
riskLevel = "critical";
} else if (detectedBiases.length > 0 || burnoutRisk > 45.0) {
riskLevel = "moderate";
}
// Interventions
const interventions = [];
detectedBiases.forEach(b => {
if (b.bias_name === "Confirmation Bias") interventions.push("Protocolo Red-Teaming: Generar 3 hipótesis disconfirmatorias antes de dar por buena la conclusión.");
if (b.bias_name === "Sunk Cost Fallacy") interventions.push("Desacoplamiento Prospectivo: Evaluar el ROI futuro asumiendo que los costos pasados son irrecuperables ($0).");
if (b.bias_name === "Anchoring Bias") interventions.push("Estimación Base Cero: Recalcular cantidades principales de forma ciega e independiente de cifras iniciales.");
if (b.bias_name === "Availability Heuristic") interventions.push("Contraste Empírico: Contrastar la anécdota reciente contra distribuciones estadísticas de frecuencia real.");
if (b.bias_name === "Framing Effect") interventions.push("Encuadre Simétrico: Presentar simultáneamente matrices de ganancias vs pérdidas para neutralizar el sesgo.");
if (b.bias_name === "Catastrophizing") interventions.push("Des-catastrofización Cognitiva: Cuantificar la probabilidad real del peor escenario (P<5%) y aislar planes de contención.");
if (b.bias_name === "All-or-Nothing Thinking") interventions.push("Pensamiento de Continuo: Reemplazar dicotomías binarias ('éxito/fracaso total') por hitos de progreso medibles (0-100%).");
if (b.bias_name === "Overconfidence Bias") interventions.push("Pre-Mortem Técnico: Asumir que el sistema falló dentro de 6 meses y documentar exhaustivamente las causas posibles.");
if (b.bias_name === "Fundamental Attribution Bias") interventions.push("Análisis Causa-Raíz Sin Culpa (Blameless): Enfocar la investigación en la arquitectura y herramientas, no en personas.");
if (b.bias_name === "Outcome Bias") interventions.push("Separación Proceso vs Azar: Auditar el valor esperado (EV) y el rigor metodológico al momento de tomar la decisión.");
});
if (burnoutRisk > 65.0) interventions.push("Alerta de Fatiga & Urgencia Ocupacional: Pausar el flujo de mensajes inmediatos e instaurar canales asíncronos.");
if (safetyScore < 45.0) interventions.push("Protocolo de Seguridad Psicológica: Reencuadrar críticas destructivas en preguntas de aprendizaje colaborativo.");
if (interventions.length === 0) interventions.push("El argumento se mantiene objetivo, equilibrado y dentro de parámetros nominales de razonamiento.");
const counterfactual = detectedBiases.length > 0
? `Reconsidera este planteamiento desde la perspectiva de hipótesis nula: Asume que lo opuesto a tu conclusión es verdad e indica qué 3 pruebas empíricas concretas necesitarías para validarlo.`
: null;
// Red-Teaming Directive
let redTeamingText = "";
if (detectedBiases.length > 0) {
const biasStr = detectedBiases.map(b => b.bias_name).join(", ");
redTeamingText = `[SYSTEM DIRECTIVE: REASONING ALIGNMENT & DEBIASING]\n` +
`The evaluated reasoning trace exhibits tendencies toward: ${biasStr}.\n\n` +
`Execution Directive:\n` +
`1. Adopt an adversarial, null-hypothesis stance against the current conclusion.\n` +
`2. Formulate 3 empirical counter-arguments disproving the main premise.\n` +
`3. Decouple past sunken resources and formulate expected value (EV) with explicit confidence intervals.\n` +
`4. Re-write the conclusion using balanced, non-catastrophic framing.`;
} else {
redTeamingText = `[SYSTEM DIRECTIVE]\nNo critical cognitive distortions detected. Maintain evidence-based, structured reasoning.`;
}
const t1 = performance.now();
const latency = Math.round((t1 - t0) * 10) / 10;
// --- RENDER OUTPUTS ---
document.getElementById('latencyText').innerText = latency + " ms";
const riskBanner = document.getElementById('riskBanner');
riskBanner.className = `risk-banner risk-${riskLevel}`;
document.getElementById('riskTitle').innerText = riskLevel.toUpperCase();
document.getElementById('riskIcon').innerText = riskLevel === 'nominal' ? '✅' : (riskLevel === 'moderate' ? '⚠️' : '🚨');
document.getElementById('valCogLoad').innerText = cogLoad;
document.getElementById('valBurnout').innerText = burnoutRisk;
document.getElementById('valBurnout').style.color = burnoutRisk > 60 ? '#ef4444' : (burnoutRisk > 35 ? '#f59e0b' : '#10b981');
document.getElementById('valSafety').innerText = safetyScore + "%";
document.getElementById('valSafety').style.color = safetyScore < 45 ? '#ef4444' : (safetyScore < 70 ? '#f59e0b' : '#10b981');
document.getElementById('valValence').innerText = (valence > 0 ? "+" : "") + valence.toFixed(2);
document.getElementById('valConsistency').innerText = consistency + "%";
// Render Biases List
const container = document.getElementById('biasesContainer');
container.innerHTML = "";
if (detectedBiases.length === 0) {
container.innerHTML = `
<div style="background:#f0fdf4; border:1px solid #bbf7d0; border-radius:10px; padding:16px; color:#166534; font-size:13px;">
🎉 <b>No se detectaron sesgos críticos.</b> El razonamiento exhibe coherencia lógica y ponderación balanceada de evidencia.
</div>
`;
} else {
detectedBiases.forEach(b => {
const triggers = b.matched_patterns.map(p => `<span class="trigger-tag">"${p}"</span>`).join("");
container.innerHTML += `
<div class="bias-card ${b.severity === 'high' ? 'high-sev' : ''}">
<div class="bias-card-header">
<span class="bias-name">🧠 ${b.bias_name} (${b.spanish_name})</span>
<div>
<span class="sev-tag ${b.severity}">${b.severity}</span>
<span style="font-size:12px; color:#64748b; margin-left:6px;">Confianza: ${Math.round(b.confidence_score * 100)}%</span>
</div>
</div>
<div class="bias-desc">${b.explanation}</div>
<div style="font-size:12px; color:#64748b;"><b>Frases delatadas:</b> ${triggers}</div>
</div>
`;
});
}
// Render Mitigation
const mitList = document.getElementById('mitigationList');
mitList.innerHTML = interventions.map(i => `<li>${i}</li>`).join("");
const cfBox = document.getElementById('counterfactualBox');
if (counterfactual) {
cfBox.style.display = "block";
cfBox.innerHTML = `<b>🔄 Ejercicio Contrafactual Sugerido:</b> <i>"${counterfactual}"</i>`;
} else {
cfBox.style.display = "none";
}
// Red-Teaming
document.getElementById('redTeamingBox').innerText = redTeamingText;
// JSON output
const fullJson = {
evaluation_id: "eval_" + Math.random().toString(36).substring(2, 10),
author_type: document.querySelector('input[name="author_type"]:checked').value,
timestamp: new Date().toISOString(),
alignment_risk: riskLevel,
psychometrics: {
cognitive_load_index: cogLoad,
burnout_risk_index: burnoutRisk,
psychological_safety_score: safetyScore,
emotional_valence: valence,
logical_consistency: consistency / 100
},
biases_detected: detectedBiases,
mitigation: {
interventions: interventions,
counterfactual_prompt: counterfactual,
red_teaming_directive: redTeamingText
},
latency_ms: latency
};
document.getElementById('jsonBox').innerText = JSON.stringify(fullJson, null, 2);
// Render Plotly Radar
renderRadarChart(radarScores);
}
function renderRadarChart(scores) {
const categories = [
"Confirmación", "Anclaje", "Costo Hundido", "Disponibilidad", "Encuadre",
"Catastrofismo", "Todo o Nada", "Sobreconfianza", "Culpabilización", "Resultado"
];
const keys = [
"Confirmation Bias", "Anchoring Bias", "Sunk Cost Fallacy", "Availability Heuristic", "Framing Effect",
"Catastrophizing", "All-or-Nothing Thinking", "Overconfidence Bias", "Fundamental Attribution Bias", "Outcome Bias"
];
const values = keys.map(k => scores[k] || 0.0);
categories.push(categories[0]);
values.push(values[0]);
const data = [{
type: 'scatterpolar',
r: values,
theta: categories,
fill: 'toself',
fillcolor: 'rgba(79, 70, 229, 0.3)',
line: { color: '#4f46e5', width: 2 },
marker: { size: 5, color: '#4338ca' }
}];
const layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1.0],
tickvals: [0.25, 0.5, 0.75, 1.0],
ticktext: ['25%', '50%', '75%', '100%'],
linecolor: '#cbd5e1',
gridcolor: '#e2e8f0'
},
angularaxis: {
linecolor: '#cbd5e1',
gridcolor: '#e2e8f0',
tickfont: { size: 10 }
}
},
showlegend: false,
margin: { l: 30, r: 30, t: 25, b: 25 },
paper_bgcolor: 'rgba(0,0,0,0)',
plot_bgcolor: 'rgba(0,0,0,0)'
};
Plotly.newPlot('radarPlot', data, layout, { responsive: true, displayModeBar: false });
}
function copyRedTeaming() {
const text = document.getElementById('redTeamingBox').innerText;
navigator.clipboard.writeText(text).then(() => {
alert("¡Prompt copiado al portapapeles! Puedes pegarlo en ChatGPT o Claude.");
});
}
// Auto-run on startup with Sample 1
window.addEventListener('DOMContentLoaded', () => {
loadSample(1);
});
</script>
</body>
</html>