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

103 lines
4.5 KiB
Markdown

---
id: 0041
title: Split confidence_threshold en entity_threshold + relation_threshold
status: pending
priority: medium
created: 2026-05-04
parent: 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:
```python
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:
```yaml
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.