Files
graph_explorer/issues/0041-split-confidence-thresholds.md
T
egutierrez 5e6023f639 docs: issues 0041 (split thresholds) + 0042 (GLiNER2), supersedes mREBEL
Cierra el ciclo del analysis gliner_glirel_tuning: documenta en app.md el
pipeline NER+RE disponible en el registry y abre los dos issues que faltan
para cablearlo en extract_graph_hybrid + panel paste_extract. Archiva el
0042 original (mREBEL) tras la decision a favor de GLiNER2 (Apache 2.0,
joint NER+RE, 20-30x mas rapido en CPU).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 10:07:43 +02:00

4.5 KiB

id, title, status, priority, created, parent
id title status priority created parent
0041 Split confidence_threshold en entity_threshold + relation_threshold pending medium 2026-05-04 0013

Objetivo

extract_graph_hybrid y el panel paste_extract actualmente comparten un solo confidence_threshold. El analisis empirico (projects/osint_graph/analysis/gliner_glirel_tuning/) demuestra que las dos capas tienen distribuciones de score radicalmente distintas:

  • GLiNER emite scores 0.92-0.99 en narrativa, 0.5-0.95 en OSINT. Threshold sano: 0.50-0.70.
  • GLiREL emite scores 0.05-0.34. Threshold sano: 0.10-0.20.
  • mREBEL (issue 0042) emite confianzas implicitas via num_beams — distinto modelo, distinto rango.

Un solo threshold global o filtra todas las relaciones (t=0.6 mata GLiREL) o satura el grafo de entidades dudosas (t=0.15 deja pasar entidades borderline).

Cambios

En python/functions/pipelines/extract_graph_hybrid.py

Aceptar dos thresholds opcionales con back-compat:

def extract_graph_hybrid(
    chunks, entity_schema, relation_types,
    gliner_model, glirel_model,
    llm_chat_json=None,
    confidence_threshold=0.6,        # legacy, valor por defecto si los dos siguientes no se pasan
    entity_threshold=None,           # nuevo — gobierna GLiNER + LLM-fallback de entidades
    relation_threshold=None,         # nuevo — gobierna GLiREL + LLM-fallback de relaciones
):
    if entity_threshold is None:
        entity_threshold = confidence_threshold
    if relation_threshold is None:
        relation_threshold = confidence_threshold
    ...

Pasar cada threshold a su capa respectiva (GLiNER predict_entities(threshold=entity_threshold), GLiREL predict_relations(threshold=relation_threshold)).

En enrichers/paste_extract/run.py

Aceptar entity_threshold y relation_threshold en params. Mantener confidence_threshold por compatibilidad. Defaults sugeridos en el manifest:

params:
  text: { type: string, required: true }
  use_hybrid: { type: bool, default: true }
  entity_threshold: { type: float, default: 0.50 }
  relation_threshold: { type: float, default: 0.15 }

En extract_panel.cpp / extract_panel.h

Reemplazar el slider unico por dos sliders verticales (mas el toggle use_hybrid):

┌─ Extract config ─────────────────────────────┐
│ ☑ Use hybrid (GLiNER + GLiREL)               │
│                                              │
│ Entity threshold       0.50  [====      ]    │
│ Relation threshold     0.15  [==        ]    │
└──────────────────────────────────────────────┘

Defaults: entity_threshold=0.50, relation_threshold=0.15.

Tests

tests/test_paste_extract.py:

  • Test que pasar solo confidence_threshold mantiene comportamiento legacy.
  • Test que pasar entity_threshold=0.5, relation_threshold=0.1 aplica thresholds distintos a cada capa.
  • Test que la UI envia los dos parametros correctamente al subprocess.

Definicion de hecho

  • Pego un texto y veo entidades con confianza 0.5+ Y relaciones con confianza 0.15+ por defecto.
  • Mover el slider de relaciones a 0.05 me muestra mas relaciones (potencialmente espurias) sin afectar las entidades.
  • Mover el slider de entidades a 0.9 reduce las entidades sin tocar las relaciones.
  • El test legacy (confidence_threshold solo) sigue pasando.

Datos que respaldan los defaults

Notebook 01_gliner_glirel_tuning.ipynb, tabla "GLiNER thresholds" (corpus es_corporate, en_corporate, es_journalism). Notebook 02_e2e_spanish_graph.ipynb, comparacion recall (t=0.15) vs precision (t=0.30) — ambos modos producen grafos validos por separado, no hay un punto medio compartido bueno.

Out of scope

  • Optimizacion automatica de thresholds (issue futuro).
  • Threshold por tipo de entidad (person mas estricto que location) — issue futuro.
  • Calibracion automatica con feedback del usuario (al hacer Apply, ajustar thresholds aprendiendo).

Reversibilidad

Si los nuevos defaults producen mas ruido del esperado, basta con cambiarlos en el manifest. El esquema de dos thresholds es estrictamente mas expresivo que el legacy.

Relacionado

  • Issue 0042 — sustituir GLiREL por mREBEL. Si 0042 va primero, este issue cambia: relation_threshold se vuelve menos relevante (mREBEL no usa threshold continuo del mismo modo) pero entity_threshold sigue siendo necesario.