Spaces:
Running
Running
Suelo de cola garantizado + referencias (BM25 Robertson 1994, IDF Sparck Jones 1972, RRF Cormack 2009)
Browse files- js/acer-core.js +58 -3
js/acer-core.js
CHANGED
|
@@ -88,6 +88,39 @@
|
|
| 88 |
* · Degradación limpia: sin función de embeddings, esto es BM25 solo — que ya
|
| 89 |
* es F1 23,59 frente al 6,07 de la v1. Lo semántico suma, no es requisito.
|
| 90 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
* No usa APIs de Node → cargable en el navegador como módulo ES.
|
| 92 |
*/
|
| 93 |
|
|
@@ -114,7 +147,9 @@ function terms(s) {
|
|
| 114 |
}
|
| 115 |
|
| 116 |
/**
|
| 117 |
-
* Índice BM25
|
|
|
|
|
|
|
| 118 |
* La IDF sale del PROPIO corpus → los tokens ubicuos (`the`, `para`, `const`,
|
| 119 |
* la puntuación de formato) caen a peso ~0 sin que nadie los liste.
|
| 120 |
*/
|
|
@@ -207,6 +242,7 @@ const DEFAULTS = {
|
|
| 207 |
PIN_QUERY_TERMS: true, // lo que la pregunta nombra no se desaloja jamás
|
| 208 |
PIN_MAX: 40,
|
| 209 |
// — presupuesto —
|
|
|
|
| 210 |
HEAD_FRAC: 0.05, // reserva para los PRIMEROS mensajes, sin puntuar
|
| 211 |
// (solo bajo presión — ver autotune)
|
| 212 |
RECENT: 6,
|
|
@@ -352,13 +388,32 @@ function prepare(history, budgetTok, O) {
|
|
| 352 |
// Los turnos recientes se conservan literales, pero SOLO mientras quepan en su
|
| 353 |
// reserva. Un RECENT fijo puede comerse el presupuesto entero él solo (una
|
| 354 |
// página de 100 líneas son ~1,5k tokens) y devolver varias veces lo pedido.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
const reserve = Math.floor(budgetTok * tuned.recentFrac);
|
|
|
|
| 356 |
const recent = []; let used = 0;
|
| 357 |
for (let i = history.length - 1, k = 0; i >= 0 && k < O.RECENT; i--, k++) {
|
| 358 |
const m = clampMsg(history[i], O.MAX_MSG_CHARS);
|
| 359 |
const t = msgTok(m);
|
| 360 |
-
if (used + t
|
| 361 |
-
recent.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
}
|
| 363 |
let old = history.slice(0, history.length - recent.length);
|
| 364 |
if (!old.length || used >= budgetTok) return { done: true, recent, old, used, head: [] };
|
|
|
|
| 88 |
* · Degradación limpia: sin función de embeddings, esto es BM25 solo — que ya
|
| 89 |
* es F1 23,59 frente al 6,07 de la v1. Lo semántico suma, no es requisito.
|
| 90 |
*
|
| 91 |
+
*
|
| 92 |
+
* ─────────────────────────────────────────────────────────────────────────────
|
| 93 |
+
* REFERENCIAS
|
| 94 |
+
*
|
| 95 |
+
* BM25 (Okapi BM25) — Robertson, Walker, Jones, Hancock-Beaulieu & Gatford,
|
| 96 |
+
* «Okapi at TREC-3», TREC-3, 1994. Formulacion moderna y justificacion de
|
| 97 |
+
* k1 / b: Robertson & Zaragoza, «The Probabilistic Relevance Framework:
|
| 98 |
+
* BM25 and Beyond», Foundations and Trends in IR 3(4), 2009.
|
| 99 |
+
* https://doi.org/10.1561/1500000019
|
| 100 |
+
*
|
| 101 |
+
* IDF — Sparck Jones, «A statistical interpretation of term specificity and
|
| 102 |
+
* its application in retrieval», Journal of Documentation 28(1), 1972.
|
| 103 |
+
* El origen de la idea que sostiene todo esto: lo raro informa.
|
| 104 |
+
*
|
| 105 |
+
* RRF (fusion por rangos reciprocos) — Cormack, Clarke & Buettcher,
|
| 106 |
+
* «Reciprocal Rank Fusion outperforms Condorcet and individual Rank
|
| 107 |
+
* Learning Methods», SIGIR 2009. https://doi.org/10.1145/1571941.1572114
|
| 108 |
+
*
|
| 109 |
+
* MMR (relevancia marginal maxima) — Carbonell & Goldstein, «The use of MMR,
|
| 110 |
+
* diversity-based reranking for reordering documents and producing
|
| 111 |
+
* summaries», SIGIR 1998. https://doi.org/10.1145/290941.291025
|
| 112 |
+
*
|
| 113 |
+
* Sumideros de atencion (por que la CABECERA se protege sin puntuar) —
|
| 114 |
+
* Xiao, Tian, Chen, Han & Lewis, «Efficient Streaming Language Models with
|
| 115 |
+
* Attention Sinks», ICLR 2024. https://arxiv.org/abs/2309.17453
|
| 116 |
+
*
|
| 117 |
+
* Contraste con lo que se publica hoy en compresion de KV, donde el
|
| 118 |
+
* solapamiento lexico aparece solo como bandera BINARIA sobre un ranking que
|
| 119 |
+
* sigue siendo de atencion: CodeComp, «Structural KV Cache Compression for
|
| 120 |
+
* Agentic Coding», 2026, §4.4. https://arxiv.org/abs/2604.10235
|
| 121 |
+
* -> nuestro margen medido frente a esa regla binaria: F1 23,59 vs 8,03;
|
| 122 |
+
* y quitar la IDF cuesta -3,25 F1, o sea que la gradacion hace trabajo.
|
| 123 |
+
*
|
| 124 |
* No usa APIs de Node → cargable en el navegador como módulo ES.
|
| 125 |
*/
|
| 126 |
|
|
|
|
| 147 |
}
|
| 148 |
|
| 149 |
/**
|
| 150 |
+
* Índice BM25 (Okapi BM25 — Robertson et al., TREC-3 1994; formulación moderna
|
| 151 |
+
* en Robertson & Zaragoza 2009) sobre un conjunto de documentos: aquí, las
|
| 152 |
+
* líneas del historial.
|
| 153 |
* La IDF sale del PROPIO corpus → los tokens ubicuos (`the`, `para`, `const`,
|
| 154 |
* la puntuación de formato) caen a peso ~0 sin que nadie los liste.
|
| 155 |
*/
|
|
|
|
| 242 |
PIN_QUERY_TERMS: true, // lo que la pregunta nombra no se desaloja jamás
|
| 243 |
PIN_MAX: 40,
|
| 244 |
// — presupuesto —
|
| 245 |
+
TAIL_MIN_FRAC: 0.10, // SUELO garantizado para los ultimos turnos
|
| 246 |
HEAD_FRAC: 0.05, // reserva para los PRIMEROS mensajes, sin puntuar
|
| 247 |
// (solo bajo presión — ver autotune)
|
| 248 |
RECENT: 6,
|
|
|
|
| 388 |
// Los turnos recientes se conservan literales, pero SOLO mientras quepan en su
|
| 389 |
// reserva. Un RECENT fijo puede comerse el presupuesto entero él solo (una
|
| 390 |
// página de 100 líneas son ~1,5k tokens) y devolver varias veces lo pedido.
|
| 391 |
+
// ── RESERVA DE COLA — es un SUELO, no solo un techo ────────────────────────
|
| 392 |
+
// La version anterior paraba en el primer mensaje que no cabia. Eso convierte
|
| 393 |
+
// la reserva en un tope y no en una garantia: medido, con presupuesto 3.000 la
|
| 394 |
+
// cola se quedaba con el 3-5 % en vez del ~38 % reservado, porque UN resultado
|
| 395 |
+
// de herramienta grande en la penultima posicion bloqueaba todo lo anterior.
|
| 396 |
+
// Los ultimos turnos son lo que el modelo necesita si o si para saber donde
|
| 397 |
+
// esta, y eso no puede depender de que el turno de antes fuera voluminoso.
|
| 398 |
+
//
|
| 399 |
+
// Ahora, mientras no se alcance el suelo, el mensaje que no cabe se TRUNCA por
|
| 400 |
+
// el medio (cabeza y cola, que es lo que importa de un resultado) en vez de
|
| 401 |
+
// descartarse entero. Por encima del suelo se vuelve al comportamiento de
|
| 402 |
+
// siempre: se para y el resto compite por relevancia.
|
| 403 |
const reserve = Math.floor(budgetTok * tuned.recentFrac);
|
| 404 |
+
const floorTok = Math.floor(budgetTok * O.TAIL_MIN_FRAC);
|
| 405 |
const recent = []; let used = 0;
|
| 406 |
for (let i = history.length - 1, k = 0; i >= 0 && k < O.RECENT; i--, k++) {
|
| 407 |
const m = clampMsg(history[i], O.MAX_MSG_CHARS);
|
| 408 |
const t = msgTok(m);
|
| 409 |
+
if (used + t <= reserve) { recent.unshift(m); used += t; continue; }
|
| 410 |
+
if (used >= floorTok || recent.length >= 1 && used + t > reserve && used >= floorTok) break;
|
| 411 |
+
// aun por debajo del suelo: cabe recortado, no se tira
|
| 412 |
+
const room = Math.max(40, Math.min(reserve, floorTok) - used - 4);
|
| 413 |
+
if (room < 40) break;
|
| 414 |
+
recent.unshift({ ...m, content: truncateToTokens(m.content, room) });
|
| 415 |
+
used += room + 4;
|
| 416 |
+
if (used >= floorTok) break;
|
| 417 |
}
|
| 418 |
let old = history.slice(0, history.length - recent.length);
|
| 419 |
if (!old.length || used >= budgetTok) return { done: true, recent, old, used, head: [] };
|