agentic-kdd 2.1.10 → 2.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/akdd.js +19 -1
- package/package.json +1 -1
- package/src/init.js +12 -0
- package/templates/.agentic/grafo/grafo.cjs +413 -0
- package/templates/.agentic/grafo/schema.sql +139 -30
package/bin/akdd.js
CHANGED
|
@@ -25,8 +25,12 @@ const HELP = `
|
|
|
25
25
|
akdd graph Sync memory + show graph stats
|
|
26
26
|
akdd sync Sync memory files to SQLite graph
|
|
27
27
|
akdd stats Show graph stats and HIGH rules
|
|
28
|
+
akdd coala Show CoALA memory stats (procedural + episodic + semantic)
|
|
28
29
|
akdd metricas Show agent KPIs (Goal Attainment, Autonomy, etc.)
|
|
29
|
-
akdd
|
|
30
|
+
akdd buscar Hybrid search across all 3 memory layers
|
|
31
|
+
akdd impacto Show impact of touching a module/file
|
|
32
|
+
akdd semantico Semantic search (needs API key)
|
|
33
|
+
akdd decay Apply temporal decay to stale patterns
|
|
30
34
|
akdd dashboard Open visual dashboard in browser
|
|
31
35
|
akdd --version Show version
|
|
32
36
|
akdd --help Show this help
|
|
@@ -71,6 +75,9 @@ switch (command) {
|
|
|
71
75
|
case 'stats':
|
|
72
76
|
runGrafo('stats');
|
|
73
77
|
break;
|
|
78
|
+
case 'coala':
|
|
79
|
+
runGrafo('coala');
|
|
80
|
+
break;
|
|
74
81
|
case 'metricas':
|
|
75
82
|
runGrafo('metricas');
|
|
76
83
|
break;
|
|
@@ -78,6 +85,17 @@ switch (command) {
|
|
|
78
85
|
if (!arg1) { console.log('\n Uso: akdd semantico "tu query"\n'); break; }
|
|
79
86
|
runGrafo('semantico', `"${arg1}"`);
|
|
80
87
|
break;
|
|
88
|
+
case 'buscar':
|
|
89
|
+
if (!arg1) { console.log('\n Uso: akdd buscar "query" [area]\n'); break; }
|
|
90
|
+
runGrafo('buscar', `"${arg1}"${arg2 ? ' ' + arg2 : ''}`);
|
|
91
|
+
break;
|
|
92
|
+
case 'impacto':
|
|
93
|
+
if (!arg1) { console.log('\n Uso: akdd impacto "NombreModulo"\n'); break; }
|
|
94
|
+
runGrafo('impacto', `"${arg1}"`);
|
|
95
|
+
break;
|
|
96
|
+
case 'decay':
|
|
97
|
+
runGrafo('decay');
|
|
98
|
+
break;
|
|
81
99
|
case 'dashboard':
|
|
82
100
|
dashboard();
|
|
83
101
|
break;
|
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -254,6 +254,18 @@ async function init() {
|
|
|
254
254
|
console.log(chalk.gray(' Agentic los usará automáticamente en el siguiente aa:'));
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
// ── CREAR config.md BASE ────────────────────────────────────
|
|
258
|
+
// Necesario para que akdd graph funcione antes de aa: configurar
|
|
259
|
+
const configPath = path.join(projectPath, '.agentic', 'config.md');
|
|
260
|
+
if (!fs.existsSync(configPath)) {
|
|
261
|
+
fs.writeFileSync(configPath, `# Configuración del proyecto
|
|
262
|
+
CONFIGURADO: SI
|
|
263
|
+
Nombre: ${name}
|
|
264
|
+
Stack: ${stack.framework} · ${stack.language} · ${stack.packageManager}
|
|
265
|
+
Estado: Pendiente aa: configurar
|
|
266
|
+
`);
|
|
267
|
+
}
|
|
268
|
+
|
|
257
269
|
// ── RESUMEN FINAL ───────────────────────────────────────────
|
|
258
270
|
console.log('\n' + chalk.bold(' Instalado:'));
|
|
259
271
|
console.log(chalk.gray(' .agentic/agentes/ — pipeline de 9 agentes'));
|
|
@@ -294,6 +294,52 @@ function sincronizar() {
|
|
|
294
294
|
}
|
|
295
295
|
if (db.type === 'sqljs' && db.save) db.save();
|
|
296
296
|
detectarRelaciones(db);
|
|
297
|
+
// Gap CoALA: consolidación episódica automática
|
|
298
|
+
// Episodios "resuelto" sin consolidar → extraer como patrones/errores
|
|
299
|
+
try {
|
|
300
|
+
const episodiosPendientes = db.all(
|
|
301
|
+
`SELECT * FROM episodios WHERE consolidado=0 AND resultado='resuelto'
|
|
302
|
+
AND tipo IN ('fix','error') ORDER BY fecha DESC LIMIT 20`
|
|
303
|
+
);
|
|
304
|
+
episodiosPendientes.forEach(ep => {
|
|
305
|
+
try {
|
|
306
|
+
// Solo consolidar si tiene suficiente info
|
|
307
|
+
if (!ep.descripcion || !ep.accion_tomada) return;
|
|
308
|
+
const titulo = `[EP] ${ep.descripcion.slice(0, 80)}`;
|
|
309
|
+
const ex = db.get('SELECT id FROM nodos WHERE titulo=?', titulo);
|
|
310
|
+
if (!ex) {
|
|
311
|
+
const contenido = `## ${ep.fecha.split('T')[0]} [EP] ${ep.descripcion}
|
|
312
|
+
Área: ${ep.area || 'global'}
|
|
313
|
+
Confianza: BAJA
|
|
314
|
+
Estado: ACTIVO
|
|
315
|
+
Origen: consolidado de episodio
|
|
316
|
+
Fix aplicado: ${ep.accion_tomada}
|
|
317
|
+
Razón: ${ep.razon_resultado || 'ver episodio original'}`;
|
|
318
|
+
db.run(`INSERT INTO nodos (tipo,titulo,contenido,area,confianza,aplicado,util,estado,ultima_validacion,fecha_update)
|
|
319
|
+
VALUES (?,?,?,?,?,?,?,?,datetime('now'),datetime('now'))`,
|
|
320
|
+
'error', titulo, contenido, ep.area || 'global', 'BAJA', 1, 1, 'ACTIVO');
|
|
321
|
+
const nodoId = (db.get('SELECT id FROM nodos WHERE titulo=?', titulo) || {}).id;
|
|
322
|
+
if (nodoId) {
|
|
323
|
+
db.run('UPDATE episodios SET consolidado=1, nodo_generado_id=? WHERE id=?', nodoId, ep.id);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} catch(e) {}
|
|
327
|
+
});
|
|
328
|
+
} catch(e) {}
|
|
329
|
+
// Gap CoALA: decay automático — patrones sin uso pierden relevancia
|
|
330
|
+
try {
|
|
331
|
+
const ahora = Date.now();
|
|
332
|
+
const nodos = db.all("SELECT id, ultimo_acceso, fecha_creacion, aplicado, confianza FROM nodos WHERE estado='ACTIVO'");
|
|
333
|
+
nodos.forEach(n => {
|
|
334
|
+
const base = n.ultimo_acceso || n.fecha_creacion;
|
|
335
|
+
const dias = base ? (ahora - new Date(base).getTime()) / (1000*60*60*24) : 0;
|
|
336
|
+
const tasa = { 'ALTA': 0.003, 'MEDIA': 0.008, 'BAJA': 0.015 }[n.confianza] || 0.01;
|
|
337
|
+
const decay = Math.max(0.1, 1.0 - (dias * tasa));
|
|
338
|
+
let estado = 'ACTIVO';
|
|
339
|
+
if (decay < 0.3 && n.confianza === 'BAJA' && (n.aplicado || 0) === 0) estado = 'OBSOLETO';
|
|
340
|
+
try { db.run('UPDATE nodos SET decay_score=?, estado=? WHERE id=?', decay, estado, n.id); } catch(e) {}
|
|
341
|
+
});
|
|
342
|
+
} catch(e) {}
|
|
297
343
|
// Forzar checkpoint WAL para que los datos queden en la DB principal
|
|
298
344
|
try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch(e) {}
|
|
299
345
|
db.close();
|
|
@@ -828,12 +874,72 @@ Razón: Dependencia confirmada en package.json`;
|
|
|
828
874
|
|
|
829
875
|
// Detectar relaciones entre nodos nuevos
|
|
830
876
|
detectarRelaciones(db);
|
|
877
|
+
|
|
878
|
+
// ── 7. MEMORIA SEMÁNTICA — entidades y relaciones del proyecto ────────────
|
|
879
|
+
// Gap CoALA: analizarProyecto ahora llena entidades y relaciones_semanticas
|
|
880
|
+
const archivosConImports = {};
|
|
881
|
+
function recorrerParaEntidades(dir, nivel) {
|
|
882
|
+
if (nivel > 5) return;
|
|
883
|
+
let items; try { items = fs.readdirSync(dir); } catch(e) { return; }
|
|
884
|
+
for (const item of items) {
|
|
885
|
+
if (ignorar.has(item) || item.startsWith('.')) continue;
|
|
886
|
+
const full = path.join(dir, item);
|
|
887
|
+
let stat; try { stat = fs.statSync(full); } catch(e) { continue; }
|
|
888
|
+
if (stat.isDirectory()) {
|
|
889
|
+
recorrerParaEntidades(full, nivel + 1);
|
|
890
|
+
} else if (stat.isFile() && extsCodigo.has(path.extname(item).toLowerCase())) {
|
|
891
|
+
let content; try { content = fs.readFileSync(full, 'utf8'); } catch(e) { continue; }
|
|
892
|
+
const relPath = path.relative(ROOT, full).replace(/\\/g, '/');
|
|
893
|
+
const nombre = path.basename(item, path.extname(item));
|
|
894
|
+
const area = inferirArea(full);
|
|
895
|
+
// Registrar entidad
|
|
896
|
+
const esTest = /\.test\.|\.spec\./.test(item);
|
|
897
|
+
const esCritico = ['middleware','auth','session','database','config','index','app','main','server'].some(k => nombre.toLowerCase().includes(k));
|
|
898
|
+
try {
|
|
899
|
+
const ex = db.get('SELECT id FROM entidades WHERE nombre=?', nombre);
|
|
900
|
+
if (!ex) {
|
|
901
|
+
db.run(`INSERT INTO entidades (nombre, tipo, descripcion, area, propiedades, critica, fecha_creacion, fecha_update) VALUES (?,?,?,?,?,?,datetime('now'),datetime('now'))`,
|
|
902
|
+
nombre,
|
|
903
|
+
esTest ? 'test' : 'archivo',
|
|
904
|
+
`Archivo: ${relPath}`,
|
|
905
|
+
area,
|
|
906
|
+
JSON.stringify({ ruta: relPath, extension: path.extname(item) }),
|
|
907
|
+
esCritico ? 1 : 0
|
|
908
|
+
);
|
|
909
|
+
nodosCreados++;
|
|
910
|
+
}
|
|
911
|
+
} catch(e) {}
|
|
912
|
+
// Detectar imports locales para relaciones semánticas
|
|
913
|
+
const importLocales = [];
|
|
914
|
+
const rxLocal = /(?:import|require)\s*(?:.*?\s+from\s+)?['"](\.[^'"]+)['"]/g;
|
|
915
|
+
let m; while ((m = rxLocal.exec(content)) !== null) {
|
|
916
|
+
const importado = path.basename(m[1]).replace(/\.[^.]+$/, '') || path.basename(m[1]);
|
|
917
|
+
if (importado && importado !== nombre) importLocales.push(importado);
|
|
918
|
+
}
|
|
919
|
+
archivosConImports[nombre] = importLocales;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
recorrerParaEntidades(ROOT, 0);
|
|
924
|
+
|
|
925
|
+
// Registrar relaciones semánticas basadas en imports
|
|
926
|
+
Object.entries(archivosConImports).forEach(([desde, imports]) => {
|
|
927
|
+
imports.forEach(hacia => {
|
|
928
|
+
try {
|
|
929
|
+
db.run(`INSERT OR IGNORE INTO relaciones_semanticas (desde_entidad, tipo, hacia_entidad, peso) VALUES (?,?,?,?)`,
|
|
930
|
+
desde, 'importa', hacia, 1.0);
|
|
931
|
+
} catch(e) {}
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
|
|
831
935
|
if (db.type === 'sqljs' && db.save) db.save();
|
|
832
936
|
|
|
833
937
|
// Actualizar archivos .md de memoria con lo detectado
|
|
834
938
|
actualizarMemoriaMd(patronesEncontrados, decisionesEncontradas, stackInfo, fecha);
|
|
835
939
|
|
|
836
940
|
const totalNodos = (db.get('SELECT COUNT(*) as n FROM nodos') || {}).n || 0;
|
|
941
|
+
const totalEntidades = (() => { try { return (db.get('SELECT COUNT(*) as n FROM entidades') || {}).n || 0; } catch(e) { return 0; } })();
|
|
942
|
+
const totalRelSem = (() => { try { return (db.get('SELECT COUNT(*) as n FROM relaciones_semanticas') || {}).n || 0; } catch(e) { return 0; } })();
|
|
837
943
|
db.close();
|
|
838
944
|
|
|
839
945
|
// ── OUTPUT ─────────────────────────────────────────────────────────────────
|
|
@@ -848,6 +954,9 @@ Razón: Dependencia confirmada en package.json`;
|
|
|
848
954
|
.forEach(([p,d]) => console.log(` [${d.count}x] ${p}`));
|
|
849
955
|
console.log(`\n Decisiones inferidas: ${Object.keys(decisionesEncontradas).length}`);
|
|
850
956
|
Object.keys(decisionesEncontradas).forEach(d => console.log(` ~ ${d}`));
|
|
957
|
+
console.log(`\n Memoria semántica:`);
|
|
958
|
+
console.log(` Entidades: ${totalEntidades} archivos/módulos mapeados`);
|
|
959
|
+
console.log(` Relaciones: ${totalRelSem} dependencias entre archivos`);
|
|
851
960
|
console.log(`\n Nodos nuevos en grafo: ${nodosCreados} (total: ${totalNodos})`);
|
|
852
961
|
console.log(`\n Dashboard actualizado — corre: node dashboard.cjs\n`);
|
|
853
962
|
}
|
|
@@ -916,3 +1025,307 @@ Razón: Inferido del código — verificar con el equipo\n`;
|
|
|
916
1025
|
}
|
|
917
1026
|
|
|
918
1027
|
module.exports = { sincronizar, consultar, stats, metricas, registrarCiclo, buscarSemantico, snapshotMemoria, analizarProyecto };
|
|
1028
|
+
|
|
1029
|
+
// ─── CoALA v3: MEMORIA EPISÓDICA ──────────────────────────────────────────
|
|
1030
|
+
function registrarEpisodio(datos) {
|
|
1031
|
+
try {
|
|
1032
|
+
const db = initDB();
|
|
1033
|
+
const episodio_id = (crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(36) + Math.random().toString(36).slice(2));
|
|
1034
|
+
db.run(`INSERT INTO episodios
|
|
1035
|
+
(episodio_id, ciclo_id, sesion_id, tipo, descripcion, intento_num,
|
|
1036
|
+
contexto_antes, accion_tomada, resultado, razon_resultado,
|
|
1037
|
+
archivos_tocados, area, modulo, relevancia)
|
|
1038
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
1039
|
+
episodio_id,
|
|
1040
|
+
datos.ciclo_id || null,
|
|
1041
|
+
datos.sesion_id || null,
|
|
1042
|
+
datos.tipo || 'accion',
|
|
1043
|
+
datos.descripcion || '',
|
|
1044
|
+
datos.intento_num || 1,
|
|
1045
|
+
datos.contexto_antes || null,
|
|
1046
|
+
datos.accion_tomada || null,
|
|
1047
|
+
datos.resultado || null,
|
|
1048
|
+
datos.razon_resultado || null,
|
|
1049
|
+
JSON.stringify(datos.archivos_tocados || []),
|
|
1050
|
+
datos.area || 'global',
|
|
1051
|
+
datos.modulo || 'global',
|
|
1052
|
+
datos.relevancia || 1.0
|
|
1053
|
+
);
|
|
1054
|
+
if (db.type === 'sqljs' && db.save) db.save();
|
|
1055
|
+
db.close();
|
|
1056
|
+
return episodio_id;
|
|
1057
|
+
} catch(e) { console.error('Error registrarEpisodio:', e.message); return null; }
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Consolidar episodios → patrones/decisiones (episódica → procedural)
|
|
1061
|
+
// Agentmemory lo hace con barridos horarios; aquí lo hacemos por demanda
|
|
1062
|
+
function consolidarEpisodios(area) {
|
|
1063
|
+
try {
|
|
1064
|
+
const db = initDB();
|
|
1065
|
+
const filtro = area && area !== 'global' ? `AND area='${area}'` : '';
|
|
1066
|
+
const episodios = db.all(
|
|
1067
|
+
`SELECT * FROM episodios WHERE consolidado=0 ${filtro} ORDER BY fecha DESC LIMIT 50`
|
|
1068
|
+
);
|
|
1069
|
+
if (!episodios.length) { db.close(); return { consolidados: 0, episodios: [] }; }
|
|
1070
|
+
// Devolver los episodios sin consolidar para que el agente Memoria los procese
|
|
1071
|
+
// El agente decide cuáles merecen convertirse en patrones/decisiones
|
|
1072
|
+
db.close();
|
|
1073
|
+
return { consolidados: episodios.length, episodios };
|
|
1074
|
+
} catch(e) { return { consolidados: 0, episodios: [] }; }
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// Marcar episodio como consolidado (después de que el agente lo procesó)
|
|
1078
|
+
function marcarEpisodioConsolidado(episodio_id, nodo_id) {
|
|
1079
|
+
try {
|
|
1080
|
+
const db = initDB();
|
|
1081
|
+
db.run('UPDATE episodios SET consolidado=1, nodo_generado_id=? WHERE episodio_id=?',
|
|
1082
|
+
nodo_id || null, episodio_id);
|
|
1083
|
+
if (db.type === 'sqljs' && db.save) db.save();
|
|
1084
|
+
db.close();
|
|
1085
|
+
return true;
|
|
1086
|
+
} catch(e) { return false; }
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// ─── CoALA v3: MEMORIA SEMÁNTICA (grafo de entidades) ────────────────────
|
|
1090
|
+
function registrarEntidad(datos) {
|
|
1091
|
+
try {
|
|
1092
|
+
const db = initDB();
|
|
1093
|
+
const ex = db.get('SELECT id FROM entidades WHERE nombre=?', datos.nombre);
|
|
1094
|
+
if (ex) {
|
|
1095
|
+
db.run(`UPDATE entidades SET tipo=?, descripcion=?, area=?, propiedades=?,
|
|
1096
|
+
modificaciones=modificaciones+1, fecha_update=datetime('now') WHERE nombre=?`,
|
|
1097
|
+
datos.tipo || 'modulo',
|
|
1098
|
+
datos.descripcion || null,
|
|
1099
|
+
datos.area || 'global',
|
|
1100
|
+
JSON.stringify(datos.propiedades || {}),
|
|
1101
|
+
datos.nombre
|
|
1102
|
+
);
|
|
1103
|
+
} else {
|
|
1104
|
+
db.run(`INSERT INTO entidades (nombre, tipo, descripcion, area, propiedades, critica)
|
|
1105
|
+
VALUES (?,?,?,?,?,?)`,
|
|
1106
|
+
datos.nombre,
|
|
1107
|
+
datos.tipo || 'modulo',
|
|
1108
|
+
datos.descripcion || null,
|
|
1109
|
+
datos.area || 'global',
|
|
1110
|
+
JSON.stringify(datos.propiedades || {}),
|
|
1111
|
+
datos.critica ? 1 : 0
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
if (db.type === 'sqljs' && db.save) db.save();
|
|
1115
|
+
db.close();
|
|
1116
|
+
return true;
|
|
1117
|
+
} catch(e) { return false; }
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function registrarRelacionSemantica(desde, tipo, hacia, descripcion, peso) {
|
|
1121
|
+
try {
|
|
1122
|
+
const db = initDB();
|
|
1123
|
+
db.run(`INSERT OR REPLACE INTO relaciones_semanticas
|
|
1124
|
+
(desde_entidad, tipo, hacia_entidad, peso, descripcion)
|
|
1125
|
+
VALUES (?,?,?,?,?)`,
|
|
1126
|
+
desde, tipo, hacia, peso || 1.0, descripcion || null
|
|
1127
|
+
);
|
|
1128
|
+
if (db.type === 'sqljs' && db.save) db.save();
|
|
1129
|
+
db.close();
|
|
1130
|
+
return true;
|
|
1131
|
+
} catch(e) { return false; }
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Consultar impacto de tocar una entidad (qué más puede romperse)
|
|
1135
|
+
function impactoEntidad(nombre) {
|
|
1136
|
+
try {
|
|
1137
|
+
const db = initDB();
|
|
1138
|
+
const entidad = db.get('SELECT * FROM entidades WHERE nombre=?', nombre);
|
|
1139
|
+
if (!entidad) { db.close(); return null; }
|
|
1140
|
+
// Qué depende de esta entidad
|
|
1141
|
+
const dependientes = db.all(
|
|
1142
|
+
`SELECT desde_entidad, tipo, descripcion FROM relaciones_semanticas
|
|
1143
|
+
WHERE hacia_entidad=? ORDER BY tipo`,
|
|
1144
|
+
nombre
|
|
1145
|
+
);
|
|
1146
|
+
// Qué depende esta entidad
|
|
1147
|
+
const dependencias = db.all(
|
|
1148
|
+
`SELECT hacia_entidad, tipo, descripcion FROM relaciones_semanticas
|
|
1149
|
+
WHERE desde_entidad=? ORDER BY tipo`,
|
|
1150
|
+
nombre
|
|
1151
|
+
);
|
|
1152
|
+
// Errores asociados a esta entidad
|
|
1153
|
+
const errores = db.all(
|
|
1154
|
+
`SELECT titulo, confianza FROM nodos
|
|
1155
|
+
WHERE tipo='error' AND (area=? OR contenido LIKE ?) AND estado='ACTIVO' LIMIT 5`,
|
|
1156
|
+
entidad.area, `%${nombre}%`
|
|
1157
|
+
);
|
|
1158
|
+
db.close();
|
|
1159
|
+
return { entidad, dependientes, dependencias, errores };
|
|
1160
|
+
} catch(e) { return null; }
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// ─── CoALA v3: DECAY TEMPORAL ─────────────────────────────────────────────
|
|
1164
|
+
// Los patrones que no se usan pierden relevancia gradualmente
|
|
1165
|
+
// Inspirado en ACT-R activation decay: A(t) = ln(Σ t_i^-d)
|
|
1166
|
+
function aplicarDecay() {
|
|
1167
|
+
try {
|
|
1168
|
+
const db = initDB();
|
|
1169
|
+
const ahora = Date.now();
|
|
1170
|
+
const nodos = db.all("SELECT id, ultimo_acceso, aplicado, confianza FROM nodos WHERE estado='ACTIVO'");
|
|
1171
|
+
|
|
1172
|
+
nodos.forEach(n => {
|
|
1173
|
+
const diasSinUso = (ahora - new Date(n.ultimo_acceso || n.fecha_creacion || ahora).getTime()) / (1000 * 60 * 60 * 24);
|
|
1174
|
+
const baseDecay = { 'ALTA': 0.005, 'MEDIA': 0.01, 'BAJA': 0.02 }[n.confianza] || 0.01;
|
|
1175
|
+
const nuevoDecay = Math.max(0.1, 1.0 - (diasSinUso * baseDecay));
|
|
1176
|
+
|
|
1177
|
+
// Si decay < 0.3 y confianza BAJA → marcar como OBSOLETO
|
|
1178
|
+
let nuevoEstado = 'ACTIVO';
|
|
1179
|
+
if (nuevoDecay < 0.3 && n.confianza === 'BAJA' && n.aplicado === 0) nuevoEstado = 'OBSOLETO';
|
|
1180
|
+
|
|
1181
|
+
try {
|
|
1182
|
+
db.run('UPDATE nodos SET decay_score=?, estado=? WHERE id=?', nuevoDecay, nuevoEstado, n.id);
|
|
1183
|
+
} catch(e) {}
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
if (db.type === 'sqljs' && db.save) db.save();
|
|
1187
|
+
const obsoletos = nodos.filter(n => n.confianza === 'BAJA').length;
|
|
1188
|
+
db.close();
|
|
1189
|
+
return { procesados: nodos.length, obsoletos_potenciales: obsoletos };
|
|
1190
|
+
} catch(e) { return { procesados: 0, error: e.message }; }
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// ─── CoALA v3: RECUPERACIÓN HÍBRIDA ──────────────────────────────────────
|
|
1194
|
+
// Combina: búsqueda por keyword (BM25-like) + filtro por área + ranking por decay
|
|
1195
|
+
// Para búsqueda vectorial real se necesitan embeddings (Voyage AI / local)
|
|
1196
|
+
// Sin embeddings usa keyword scoring (ya mejor que solo SQL)
|
|
1197
|
+
function buscarHibrido(query, area, topK) {
|
|
1198
|
+
topK = topK || 10;
|
|
1199
|
+
try {
|
|
1200
|
+
const db = initDB();
|
|
1201
|
+
const terms = (query || '').toLowerCase().split(/\s+/).filter(t => t.length > 2);
|
|
1202
|
+
|
|
1203
|
+
// Búsqueda en nodos procedurales
|
|
1204
|
+
let sqlNodos = `SELECT *, 'procedural' as memoria_tipo FROM nodos WHERE estado='ACTIVO'`;
|
|
1205
|
+
if (area && area !== 'global') sqlNodos += ` AND (area=? OR area='global')`;
|
|
1206
|
+
const nodosAll = area && area !== 'global'
|
|
1207
|
+
? db.all(sqlNodos, area)
|
|
1208
|
+
: db.all(sqlNodos);
|
|
1209
|
+
|
|
1210
|
+
// Búsqueda en episodios
|
|
1211
|
+
let sqlEpisodios = `SELECT *, 'episodica' as memoria_tipo FROM episodios WHERE relevancia > 0.3`;
|
|
1212
|
+
if (area && area !== 'global') sqlEpisodios += ` AND (area=? OR area='global')`;
|
|
1213
|
+
sqlEpisodios += ' ORDER BY fecha DESC LIMIT 50';
|
|
1214
|
+
const episodiosAll = area && area !== 'global'
|
|
1215
|
+
? db.all(sqlEpisodios, area)
|
|
1216
|
+
: db.all(sqlEpisodios);
|
|
1217
|
+
|
|
1218
|
+
// Búsqueda en entidades semánticas
|
|
1219
|
+
const entidadesAll = db.all('SELECT *, \'semantica\' as memoria_tipo FROM entidades LIMIT 100');
|
|
1220
|
+
|
|
1221
|
+
db.close();
|
|
1222
|
+
|
|
1223
|
+
// Scoring BM25-like por keyword
|
|
1224
|
+
const scoreItem = (item) => {
|
|
1225
|
+
const text = ([item.titulo, item.descripcion, item.contenido, item.accion_tomada, item.razon_resultado, item.nombre]
|
|
1226
|
+
.filter(Boolean).join(' ')).toLowerCase();
|
|
1227
|
+
let score = 0;
|
|
1228
|
+
terms.forEach(term => {
|
|
1229
|
+
const count = (text.match(new RegExp(term, 'g')) || []).length;
|
|
1230
|
+
if (count > 0) score += 1 + Math.log(count); // TF component
|
|
1231
|
+
});
|
|
1232
|
+
// Boost por confianza/relevancia
|
|
1233
|
+
if (item.confianza === 'ALTA') score *= 2.0;
|
|
1234
|
+
else if (item.confianza === 'MEDIA') score *= 1.5;
|
|
1235
|
+
// Boost por decay
|
|
1236
|
+
score *= (item.decay_score || 1.0);
|
|
1237
|
+
// Boost por accesos recientes
|
|
1238
|
+
score += Math.log(1 + (item.accesos_total || item.aplicado || 0)) * 0.3;
|
|
1239
|
+
return score;
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
// Combinar todos los resultados y rankear
|
|
1243
|
+
const todos = [...nodosAll, ...episodiosAll, ...entidadesAll];
|
|
1244
|
+
const scored = todos
|
|
1245
|
+
.map(item => ({ ...item, _score: scoreItem(item) }))
|
|
1246
|
+
.filter(item => item._score > 0)
|
|
1247
|
+
.sort((a, b) => b._score - a._score)
|
|
1248
|
+
.slice(0, topK);
|
|
1249
|
+
|
|
1250
|
+
return {
|
|
1251
|
+
resultados: scored,
|
|
1252
|
+
trace: {
|
|
1253
|
+
query, area, topK,
|
|
1254
|
+
nodos_candidatos: nodosAll.length,
|
|
1255
|
+
episodios_candidatos: episodiosAll.length,
|
|
1256
|
+
entidades_candidatas: entidadesAll.length,
|
|
1257
|
+
resultados_finales: scored.length,
|
|
1258
|
+
metodo: 'keyword_hybrid_rrf'
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
} catch(e) { return { resultados: [], trace: { error: e.message } }; }
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// ─── CoALA v3: STATS EXTENDIDO ────────────────────────────────────────────
|
|
1265
|
+
function statsCoala() {
|
|
1266
|
+
const db = initDB();
|
|
1267
|
+
const proc = (db.get('SELECT COUNT(*) as n FROM nodos WHERE estado=\'ACTIVO\'') || {}).n || 0;
|
|
1268
|
+
const episod = (db.get('SELECT COUNT(*) as n FROM episodios') || {}).n || 0;
|
|
1269
|
+
const sinConsolidar = (db.get('SELECT COUNT(*) as n FROM episodios WHERE consolidado=0') || {}).n || 0;
|
|
1270
|
+
const entidades = (db.get('SELECT COUNT(*) as n FROM entidades') || {}).n || 0;
|
|
1271
|
+
const relSem = (db.get('SELECT COUNT(*) as n FROM relaciones_semanticas') || {}).n || 0;
|
|
1272
|
+
const obsoletos = (db.get('SELECT COUNT(*) as n FROM nodos WHERE estado=\'OBSOLETO\'') || {}).n || 0;
|
|
1273
|
+
db.close();
|
|
1274
|
+
|
|
1275
|
+
console.log('\n MEMORIA CoALA v3 — Agentic KDD\n');
|
|
1276
|
+
console.log(` 🔧 Procedural (patrones/errores/decisiones): ${proc} activos, ${obsoletos} obsoletos`);
|
|
1277
|
+
console.log(` 📖 Episódica (trayectorias): ${episod} total, ${sinConsolidar} sin consolidar`);
|
|
1278
|
+
console.log(` 🧠 Semántica (grafo de entidades): ${entidades} entidades, ${relSem} relaciones`);
|
|
1279
|
+
console.log('');
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// Exportar funciones CoALA nuevas
|
|
1283
|
+
const _originalExports = module.exports || {};
|
|
1284
|
+
module.exports = {
|
|
1285
|
+
..._originalExports,
|
|
1286
|
+
registrarEpisodio,
|
|
1287
|
+
consolidarEpisodios,
|
|
1288
|
+
marcarEpisodioConsolidado,
|
|
1289
|
+
registrarEntidad,
|
|
1290
|
+
registrarRelacionSemantica,
|
|
1291
|
+
impactoEntidad,
|
|
1292
|
+
aplicarDecay,
|
|
1293
|
+
buscarHibrido,
|
|
1294
|
+
statsCoala
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
// Agregar casos al switch CLI
|
|
1298
|
+
const _args = process.argv.slice(2);
|
|
1299
|
+
if (_args[0] === 'episodio') {
|
|
1300
|
+
try {
|
|
1301
|
+
const d = JSON.parse(_args[1] || '{}');
|
|
1302
|
+
const id = registrarEpisodio(d);
|
|
1303
|
+
console.log(id ? `Episodio registrado: ${id}` : 'Error');
|
|
1304
|
+
} catch(e) { console.log('JSON inválido'); }
|
|
1305
|
+
}
|
|
1306
|
+
if (_args[0] === 'consolidar') {
|
|
1307
|
+
const r = consolidarEpisodios(_args[1]);
|
|
1308
|
+
console.log(JSON.stringify(r, null, 2));
|
|
1309
|
+
}
|
|
1310
|
+
if (_args[0] === 'entidad') {
|
|
1311
|
+
try {
|
|
1312
|
+
const d = JSON.parse(_args[1] || '{}');
|
|
1313
|
+
registrarEntidad(d);
|
|
1314
|
+
console.log('Entidad registrada:', d.nombre);
|
|
1315
|
+
} catch(e) { console.log('JSON inválido'); }
|
|
1316
|
+
}
|
|
1317
|
+
if (_args[0] === 'impacto') {
|
|
1318
|
+
const r = impactoEntidad(_args[1]);
|
|
1319
|
+
console.log(JSON.stringify(r, null, 2));
|
|
1320
|
+
}
|
|
1321
|
+
if (_args[0] === 'decay') {
|
|
1322
|
+
const r = aplicarDecay();
|
|
1323
|
+
console.log(`Decay aplicado: ${r.procesados} nodos procesados`);
|
|
1324
|
+
}
|
|
1325
|
+
if (_args[0] === 'buscar') {
|
|
1326
|
+
const r = buscarHibrido(_args[1], _args[2], parseInt(_args[3]) || 10);
|
|
1327
|
+
console.log(JSON.stringify(r, null, 2));
|
|
1328
|
+
}
|
|
1329
|
+
if (_args[0] === 'coala') {
|
|
1330
|
+
statsCoala();
|
|
1331
|
+
}
|
|
@@ -1,27 +1,99 @@
|
|
|
1
|
-
-- Agentic KDD — Schema
|
|
1
|
+
-- Agentic KDD — Schema CoALA v3.0
|
|
2
2
|
-- SQLite — vive en .agentic/memoria.db
|
|
3
|
+
-- Arquitectura de memoria: Working | Episodic | Semantic | Procedural
|
|
4
|
+
-- Inspirado en: CoALA (arXiv:2309.02427), Mem0, agentmemory, MemGPT
|
|
3
5
|
|
|
4
|
-
--
|
|
6
|
+
-- ─── MEMORIA PROCEDURAL (lo que ya tenía KDD) ─────────────────────────────
|
|
7
|
+
-- Patrones, errores, decisiones — reglas y skills del proyecto
|
|
5
8
|
CREATE TABLE IF NOT EXISTS nodos (
|
|
6
9
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
-
tipo TEXT NOT NULL, -- error | patron | decision | modulo |
|
|
10
|
+
tipo TEXT NOT NULL, -- error | patron | decision | modulo | entidad
|
|
8
11
|
titulo TEXT NOT NULL,
|
|
9
12
|
contenido TEXT,
|
|
10
13
|
area TEXT DEFAULT 'global',
|
|
11
|
-
confianza TEXT DEFAULT 'BAJA',
|
|
12
|
-
aplicado INTEGER DEFAULT 0,
|
|
13
|
-
util INTEGER DEFAULT 0,
|
|
14
|
-
estado TEXT DEFAULT 'ACTIVO',
|
|
14
|
+
confianza TEXT DEFAULT 'BAJA', -- BAJA | MEDIA | ALTA
|
|
15
|
+
aplicado INTEGER DEFAULT 0, -- cuántas veces se usó
|
|
16
|
+
util INTEGER DEFAULT 0, -- cuántas veces fue útil
|
|
17
|
+
estado TEXT DEFAULT 'ACTIVO', -- ACTIVO | OBSOLETO | CONSOLIDADO
|
|
18
|
+
-- CoALA: decay temporal
|
|
19
|
+
ultimo_acceso TEXT DEFAULT (datetime('now')),
|
|
20
|
+
accesos_total INTEGER DEFAULT 0,
|
|
21
|
+
decay_score REAL DEFAULT 1.0, -- 1.0=máximo, decae con el tiempo sin uso
|
|
22
|
+
-- Embeddings para búsqueda semántica (JSON array de floats, opcional)
|
|
23
|
+
embedding TEXT,
|
|
24
|
+
embedding_modelo TEXT, -- qué modelo generó el embedding
|
|
15
25
|
ultima_validacion TEXT DEFAULT (datetime('now')),
|
|
16
26
|
fecha_creacion TEXT DEFAULT (datetime('now')),
|
|
17
27
|
fecha_update TEXT DEFAULT (datetime('now'))
|
|
18
28
|
);
|
|
19
29
|
|
|
20
|
-
--
|
|
30
|
+
-- ─── MEMORIA EPISÓDICA ────────────────────────────────────────────────────
|
|
31
|
+
-- Trayectorias completas de lo que se intentó, en qué orden, resultado real
|
|
32
|
+
-- Crítico: NO summarizar al escribir (causa "summarization drift")
|
|
33
|
+
-- Registra la experiencia RAW, la consolidación ocurre después
|
|
34
|
+
CREATE TABLE IF NOT EXISTS episodios (
|
|
35
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
36
|
+
episodio_id TEXT NOT NULL UNIQUE,
|
|
37
|
+
-- Contexto del episodio
|
|
38
|
+
ciclo_id TEXT, -- FK a ciclos (si viene de un ciclo aa:)
|
|
39
|
+
sesion_id TEXT, -- agrupar episodios de una misma sesión de trabajo
|
|
40
|
+
tipo TEXT DEFAULT 'accion', -- accion | decision | error | fix | aprendizaje
|
|
41
|
+
-- Qué pasó exactamente (raw, sin summarizar)
|
|
42
|
+
descripcion TEXT NOT NULL, -- descripción detallada de lo que ocurrió
|
|
43
|
+
intento_num INTEGER DEFAULT 1, -- era el intento #N de resolver esto
|
|
44
|
+
contexto_antes TEXT, -- estado del proyecto antes
|
|
45
|
+
accion_tomada TEXT, -- qué se hizo exactamente
|
|
46
|
+
resultado TEXT, -- qué pasó (éxito | fallo | parcial)
|
|
47
|
+
razon_resultado TEXT, -- por qué pasó lo que pasó
|
|
48
|
+
archivos_tocados TEXT DEFAULT '[]', -- JSON array de archivos modificados
|
|
49
|
+
-- Consolidación a memoria semántica/procedural
|
|
50
|
+
consolidado INTEGER DEFAULT 0, -- 0=raw, 1=ya extrajo patrones
|
|
51
|
+
nodo_generado_id INTEGER, -- FK a nodos si se consolidó en patrón
|
|
52
|
+
-- Metadata
|
|
53
|
+
area TEXT DEFAULT 'global',
|
|
54
|
+
modulo TEXT DEFAULT 'global',
|
|
55
|
+
relevancia REAL DEFAULT 1.0, -- decae con el tiempo
|
|
56
|
+
fecha TEXT DEFAULT (datetime('now')),
|
|
57
|
+
FOREIGN KEY (nodo_generado_id) REFERENCES nodos(id)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
-- ─── MEMORIA SEMÁNTICA ────────────────────────────────────────────────────
|
|
61
|
+
-- Grafo de entidades del proyecto: módulos, APIs, convenciones, dependencias
|
|
62
|
+
-- Extrae el "mapa" del proyecto para que el agente entienda impacto de cambios
|
|
63
|
+
CREATE TABLE IF NOT EXISTS entidades (
|
|
64
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
65
|
+
nombre TEXT NOT NULL UNIQUE,
|
|
66
|
+
tipo TEXT NOT NULL, -- modulo | archivo | funcion | api | tabla | variable | concepto
|
|
67
|
+
descripcion TEXT,
|
|
68
|
+
area TEXT DEFAULT 'global',
|
|
69
|
+
-- Qué sabe el sistema sobre esta entidad
|
|
70
|
+
propiedades TEXT DEFAULT '{}', -- JSON: {ruta, lenguaje, exporta, importa, etc.}
|
|
71
|
+
embedding TEXT, -- para búsqueda semántica
|
|
72
|
+
-- Métricas de actividad
|
|
73
|
+
modificaciones INTEGER DEFAULT 0, -- cuántas veces fue tocada
|
|
74
|
+
errores_asociados INTEGER DEFAULT 0,
|
|
75
|
+
critica INTEGER DEFAULT 0, -- 1 si es una entidad crítica del sistema
|
|
76
|
+
fecha_creacion TEXT DEFAULT (datetime('now')),
|
|
77
|
+
fecha_update TEXT DEFAULT (datetime('now'))
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
-- Relaciones semánticas entre entidades (grafo de conocimiento del proyecto)
|
|
81
|
+
CREATE TABLE IF NOT EXISTS relaciones_semanticas (
|
|
82
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
83
|
+
desde_entidad TEXT NOT NULL, -- nombre de la entidad origen
|
|
84
|
+
tipo TEXT NOT NULL, -- depende_de | importa | usa | extiende | llama | define
|
|
85
|
+
hacia_entidad TEXT NOT NULL, -- nombre de la entidad destino
|
|
86
|
+
peso REAL DEFAULT 1.0, -- fuerza de la relación
|
|
87
|
+
descripcion TEXT, -- por qué existe esta relación
|
|
88
|
+
fecha TEXT DEFAULT (datetime('now')),
|
|
89
|
+
UNIQUE(desde_entidad, tipo, hacia_entidad)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
-- ─── RELACIONES (memoria procedural — ya existía) ─────────────────────────
|
|
21
93
|
CREATE TABLE IF NOT EXISTS relaciones (
|
|
22
94
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23
95
|
desde_id INTEGER NOT NULL,
|
|
24
|
-
tipo TEXT NOT NULL, --
|
|
96
|
+
tipo TEXT NOT NULL, -- resuelto_por | origino | aplica_a | relacionado_con | contradice
|
|
25
97
|
hacia_id INTEGER NOT NULL,
|
|
26
98
|
peso REAL DEFAULT 1.0,
|
|
27
99
|
fecha TEXT DEFAULT (datetime('now')),
|
|
@@ -29,67 +101,104 @@ CREATE TABLE IF NOT EXISTS relaciones (
|
|
|
29
101
|
FOREIGN KEY (hacia_id) REFERENCES nodos(id)
|
|
30
102
|
);
|
|
31
103
|
|
|
32
|
-
--
|
|
104
|
+
-- ─── CICLOS — observabilidad y métricas ───────────────────────────────────
|
|
33
105
|
CREATE TABLE IF NOT EXISTS ciclos (
|
|
34
106
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35
|
-
ciclo_id TEXT NOT NULL UNIQUE,
|
|
107
|
+
ciclo_id TEXT NOT NULL UNIQUE,
|
|
36
108
|
tarea TEXT NOT NULL,
|
|
109
|
+
tipo_tarea TEXT DEFAULT 'feature', -- feature | bugfix | refactor | docs | audit
|
|
37
110
|
modulo TEXT DEFAULT 'global',
|
|
38
111
|
area TEXT DEFAULT 'global',
|
|
39
|
-
estado TEXT DEFAULT 'EN_PROGRESO',
|
|
40
|
-
context_guard TEXT DEFAULT 'OK',
|
|
112
|
+
estado TEXT DEFAULT 'EN_PROGRESO', -- EN_PROGRESO | COMPLETADO | STOP
|
|
113
|
+
context_guard TEXT DEFAULT 'OK',
|
|
41
114
|
fases_total INTEGER DEFAULT 0,
|
|
42
115
|
fases_completadas INTEGER DEFAULT 0,
|
|
43
|
-
patrones_aplicados TEXT DEFAULT '[]',
|
|
44
|
-
errores_evitados TEXT DEFAULT '[]',
|
|
45
|
-
decisiones_usadas TEXT DEFAULT '[]',
|
|
116
|
+
patrones_aplicados TEXT DEFAULT '[]',
|
|
117
|
+
errores_evitados TEXT DEFAULT '[]',
|
|
118
|
+
decisiones_usadas TEXT DEFAULT '[]',
|
|
119
|
+
memory_trace TEXT DEFAULT '[]', -- qué consultó el Analista
|
|
46
120
|
tests_generados INTEGER DEFAULT 0,
|
|
47
121
|
tests_pasando INTEGER DEFAULT 0,
|
|
48
122
|
review_blockers INTEGER DEFAULT 0,
|
|
49
123
|
review_required INTEGER DEFAULT 0,
|
|
50
124
|
stops_count INTEGER DEFAULT 0,
|
|
51
|
-
sync_grafo INTEGER DEFAULT 0,
|
|
125
|
+
sync_grafo INTEGER DEFAULT 0,
|
|
52
126
|
duracion_ms INTEGER DEFAULT 0,
|
|
127
|
+
snapshot_inicio TEXT, -- JSON: estado de memoria al inicio
|
|
128
|
+
snapshot_fin TEXT, -- JSON: estado de memoria al final
|
|
53
129
|
fecha_inicio TEXT DEFAULT (datetime('now')),
|
|
54
130
|
fecha_fin TEXT
|
|
55
131
|
);
|
|
56
132
|
|
|
57
|
-
--
|
|
133
|
+
-- ─── FASES — tracing detallado ────────────────────────────────────────────
|
|
58
134
|
CREATE TABLE IF NOT EXISTS fases (
|
|
59
135
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
60
136
|
ciclo_id TEXT NOT NULL,
|
|
61
137
|
fase_num INTEGER NOT NULL,
|
|
62
138
|
fase_nombre TEXT,
|
|
63
|
-
agente TEXT,
|
|
139
|
+
agente TEXT,
|
|
64
140
|
estado TEXT DEFAULT 'EN_PROGRESO',
|
|
65
|
-
memoria_leida TEXT DEFAULT '[]',
|
|
66
|
-
decision_tomada TEXT,
|
|
141
|
+
memoria_leida TEXT DEFAULT '[]',
|
|
142
|
+
decision_tomada TEXT,
|
|
67
143
|
resultado TEXT,
|
|
68
144
|
intentos INTEGER DEFAULT 1,
|
|
145
|
+
duracion_ms INTEGER DEFAULT 0,
|
|
146
|
+
tokens_aprox INTEGER DEFAULT 0,
|
|
69
147
|
fecha_inicio TEXT DEFAULT (datetime('now')),
|
|
70
148
|
fecha_fin TEXT,
|
|
71
149
|
FOREIGN KEY (ciclo_id) REFERENCES ciclos(ciclo_id)
|
|
72
150
|
);
|
|
73
151
|
|
|
74
|
-
--
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
CREATE
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
152
|
+
-- ─── WORKING MEMORY — contexto activo de la sesión ────────────────────────
|
|
153
|
+
-- Buffer temporal que se vacía al inicio de cada sesión nueva
|
|
154
|
+
-- Equivale al "context window estructurado" de CoALA
|
|
155
|
+
CREATE TABLE IF NOT EXISTS working_memory (
|
|
156
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
157
|
+
sesion_id TEXT NOT NULL,
|
|
158
|
+
tipo TEXT NOT NULL, -- observacion | razonamiento | plan | resultado
|
|
159
|
+
contenido TEXT NOT NULL,
|
|
160
|
+
relevancia REAL DEFAULT 1.0,
|
|
161
|
+
expirado INTEGER DEFAULT 0, -- 1 si ya fue consolidado o expiró
|
|
162
|
+
fecha TEXT DEFAULT (datetime('now'))
|
|
163
|
+
);
|
|
81
164
|
|
|
82
|
-
--
|
|
165
|
+
-- ─── ÍNDICES OPTIMIZADOS ──────────────────────────────────────────────────
|
|
166
|
+
-- Nodos (procedural)
|
|
167
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_nodos_unique ON nodos(tipo, titulo);
|
|
83
168
|
CREATE INDEX IF NOT EXISTS idx_nodos_area_tipo ON nodos(area, tipo);
|
|
84
169
|
CREATE INDEX IF NOT EXISTS idx_nodos_area_confianza ON nodos(area, confianza);
|
|
85
170
|
CREATE INDEX IF NOT EXISTS idx_nodos_tipo_confianza ON nodos(tipo, confianza);
|
|
86
171
|
CREATE INDEX IF NOT EXISTS idx_nodos_tipo_estado ON nodos(tipo, estado);
|
|
87
172
|
CREATE INDEX IF NOT EXISTS idx_nodos_area_tipo_estado ON nodos(area, tipo, estado);
|
|
88
173
|
CREATE INDEX IF NOT EXISTS idx_nodos_confianza_aplicado ON nodos(confianza, aplicado);
|
|
174
|
+
CREATE INDEX IF NOT EXISTS idx_nodos_decay ON nodos(decay_score, confianza);
|
|
175
|
+
|
|
176
|
+
-- Episódica
|
|
177
|
+
CREATE INDEX IF NOT EXISTS idx_episodios_ciclo ON episodios(ciclo_id);
|
|
178
|
+
CREATE INDEX IF NOT EXISTS idx_episodios_sesion ON episodios(sesion_id);
|
|
179
|
+
CREATE INDEX IF NOT EXISTS idx_episodios_tipo ON episodios(tipo);
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_episodios_area ON episodios(area);
|
|
181
|
+
CREATE INDEX IF NOT EXISTS idx_episodios_consolidado ON episodios(consolidado);
|
|
182
|
+
CREATE INDEX IF NOT EXISTS idx_episodios_fecha ON episodios(fecha);
|
|
183
|
+
|
|
184
|
+
-- Semántica
|
|
185
|
+
CREATE INDEX IF NOT EXISTS idx_entidades_tipo ON entidades(tipo);
|
|
186
|
+
CREATE INDEX IF NOT EXISTS idx_entidades_area ON entidades(area);
|
|
187
|
+
CREATE INDEX IF NOT EXISTS idx_rel_semanticas_desde ON relaciones_semanticas(desde_entidad);
|
|
188
|
+
CREATE INDEX IF NOT EXISTS idx_rel_semanticas_hacia ON relaciones_semanticas(hacia_entidad);
|
|
89
189
|
|
|
90
|
-
--
|
|
190
|
+
-- Ciclos y fases
|
|
191
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ciclos_unique ON ciclos(ciclo_id);
|
|
91
192
|
CREATE INDEX IF NOT EXISTS idx_ciclos_estado ON ciclos(estado);
|
|
92
193
|
CREATE INDEX IF NOT EXISTS idx_ciclos_modulo ON ciclos(modulo);
|
|
93
194
|
CREATE INDEX IF NOT EXISTS idx_ciclos_fecha ON ciclos(fecha_inicio);
|
|
94
195
|
CREATE INDEX IF NOT EXISTS idx_fases_ciclo ON fases(ciclo_id);
|
|
95
196
|
CREATE INDEX IF NOT EXISTS idx_fases_agente ON fases(agente);
|
|
197
|
+
|
|
198
|
+
-- Working memory
|
|
199
|
+
CREATE INDEX IF NOT EXISTS idx_working_sesion ON working_memory(sesion_id);
|
|
200
|
+
CREATE INDEX IF NOT EXISTS idx_working_expirado ON working_memory(expirado);
|
|
201
|
+
|
|
202
|
+
-- Relaciones
|
|
203
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_rel_unique ON relaciones(desde_id, tipo, hacia_id);
|
|
204
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_rel_sem_unique ON relaciones_semanticas(desde_entidad, tipo, hacia_entidad);
|