2026-09-19 — nef-silo: mejora 4 (búsqueda híbrida) aplicada y desplegada

Repo: ~/nef/nef-silo (commits bf1e422, ae52ea5, más fix de orden).

Qué se hizo

Mejora 4 — búsqueda híbrida en silo_engine.py:

  • Embeddings sin dependencias (no hay torch en el Linode): hashing trick sobre char 3-grams + palabras, 256 dims con signo por hash, L2 normalizado (_embed). Multilingüe (CJK incluido), determinista, rápido.
  • Columna items.emb (BLOB) + _backfill_emb (en buscar y en capturar).
  • buscar con consulta: canal léxico BM25 (FTS5) + canal semántico (coseno query↔titular, top 200 recientes, umbral 0.05) → fusión RRF k=60.
  • Rerank barato: desempate por capturado (rerank neural diferido; no hay modelo en el server). --solo-lexica para comparar.

Error 6 — silo.jsonl: _compactar_jsonl() al final de cada capturar: dedup por url, descarta >30 días, tope 20k líneas, escritura atómica. Verificado: 2012 → 1351 líneas, 0 dups.

Fix de proceso — pull.sh: ahora solo baja data/. La versión anterior rsyncaba el código del server y revirtió edits locales sin commitear (pasó en esta sesión; re-apliqué a mano). Regla: pull = datos, deploy = código.

Verificación (Linode, 21:5x)

  • “ataque cibernético a infraestructura”: léxica 0 → híbrida 5 (recupera “ataque a balazos”, “military strike”).
  • “inteligencia artificial”: léxica 1 → híbrida 5, respeta filtros tema/lang.
  • 1338/1350 items con embedding; FTSMATCH con sintaxis rara no revienta.
  • estado.json ahora incluye jsonl_vivos.

Pendiente que sigue (del plan)

  • Mejora 5: clustering incremental (fingerprint + LSH + assign-or-open).
  • Mejora 6: calidad de silos (c-TF-IDF; KMeans vs HDBSCAN por corpus).
  • Mejora 7: robustez/observabilidad (topes por fuente, alertas, —dry-run).
  • Rerank neural solo si algún día hay GPU/modelo en el server.

2026-09-21 — Mejora 5 aplicada: clustering incremental de eventos

Commit 5d1cd8d, desplegado y verificado en Linode.

Diseño (versión mínima real)

  • Tabla silos (tema, abierto, ultimo, n) + items.silo_id.
  • Assign-or-open en cada capturar: si un item ~similar (MinHash, Jaccard ≥ 0.6) y ~coetáneo (Publicado dentro de 48h, fallback capturado) ya tiene silo → se une; si no, silo nuevo.
  • Filtro temporal con publicado: separa columnas semanales/diarias que se capturan juntas (Khrys’presso, Pluralistic quedaron separadas).
  • Digestos excluidos (早报|晚报|日报|早知道|快讯|点氪|newsletter|roundup...): no anclan ni se unen a eventos — era la fuente principal de over-merge.
  • reparar (union-find): reconstruye la ventana de 7 días por componentes conexas; reutiliza silo mayoritario existente. 2192 items en 0.46 s.
  • silos (cmd): lista eventos con n≥2 (default).

Tuning con datos reales

  • 0.35/0.45: over-merge zh (tokens genéricos “Pro” juntaban 中兴 M3 Pro, Geekbench M6 Pro, 小米 18 Pro). 0.6 = limpio (39 eventos multi en 2192).
  • Env para ajustar: SILO_EVENTO_UMBRAL, SILO_EVENTO_DIAS, SILO_EVENTO_VENTANA_H.
  • Residuo conocido: pares zh que comparten solo “2026” aún se juntan a veces. Pertenece a mejora 6 (c-TF-IDF / calidad de clusters).

Lección de depuración

Test fallaba con “todos los items comparten silo_id”: mi helper abría conexión y hacía close() sin commit() → SQLite rollback → todos los INSERT devolvían rowid 1. El código de producción usa una conexión y commitea: antes de culpar al código, verificar cómo cierras la conexión en el test.

Estado de producción

Corrida 02:26 UTC: +29 nuevos, 2221/2221 items con silo_id, 0 errores, jsonl_vivos visible en estado.json.

Siguiente: mejora 6 (calidad de silos: c-TF-IDF para etiquetar eventos; KMeans vs HDBSCAN por tipo de corpus).