Files
fn_registry/dev/issues/completed/0039-glirel-relation-extractor.md

92 lines
3.3 KiB
Markdown

---
id: "0039"
title: "GLiREL relation extractor (zero-shot relations → triplets)"
status: completado
type: feature
domain: []
scope: multi-app
priority: media
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0039 — GLiREL relation extractor (zero-shot relations → triplets)
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0039 |
| **Estado** | pendiente |
| **Prioridad** | media |
| **Tipo** | feature — Python (`python/functions/datascience/`) |
## Dependencias
Bloquea-por: **0038** (las entidades vienen del extractor GLiNER o del LLM). Recomendado leer `extract_relations_llm_py_datascience` y `relation_candidate_py_datascience` para mantener contrato.
**Desbloquea:** 0040 (pipeline hibrido), extraccion de triplets `(sujeto, predicado, objeto)` masiva sin LLM.
---
## Objetivo
Wrapper sobre GLiREL (`jackboyla/glirel-large-v0`) que, dadas entidades ya extraidas y un set de tipos de relacion, devuelve `list[RelationCandidate]` con el mismo contrato que `extract_relations_llm`. Output natural en triplets `(from, relation_type, to)` reusables por `deduplicate_relations`, `merge_graphs` y `ops_to_sigma_json`.
## Funciones a crear
| Function ID | Rol |
|---|---|
| `glirel_load_model_py_datascience` | Carga + cachea el modelo. `purity: impure` |
| `extract_relations_glirel_py_datascience` | Extractor de relaciones. `purity: impure` |
## Contrato
```python
def glirel_load_model(
model_name: str = "jackboyla/glirel-large-v0",
device: str = "auto",
) -> "GLiREL": ...
def extract_relations_glirel(
text: str,
entities: list[EntityCandidate], # del paso anterior (GLiNER o LLM)
relation_types: list[str], # ej: ["works_for","owns","communicated_with"]
model: "GLiREL",
threshold: float = 0.5,
max_pairs: int = 0, # 0 = todas las parejas
) -> list[RelationCandidate]
```
GLiREL necesita los spans de las entidades (`attributes["start"]`/`end`), por eso 0038 los expone. Si la entidad viene del LLM y no tiene offsets, se buscan con `text.find(name)` como fallback (con warning).
## Pureza
Ambas `purity: impure`. `error_type: error_go_core`.
## Deliverables
- `python/functions/datascience/glirel_load_model.py` + `.md`
- `python/functions/datascience/extract_relations_glirel.py` + `.md`
- Tests con 2-3 textos donde las relaciones son explicitas (`X trabaja en Y`, `A llamo a B`), schema de 3-5 relation types.
- `pyproject.toml`: añadir `glirel` al extra `nlp`.
- Documentar limitacion: GLiREL es bueno para relaciones explicitas en el texto, malo para razonamiento implicito. Para esos casos seguir usando LLM (eso es lo que orquesta 0040).
## Validacion
```bash
./fn run extract_relations_glirel_py_datascience
```
Comparar precision/recall vs `extract_relations_llm` sobre el mismo corpus + entidades. Registrar en el `.md`.
## Notas
- Triplets: el output es `RelationCandidate(from_name, to_name, relation_type, ...)`. `from_name`/`to_name` deben coincidir con entidades del input — si no, descartar (igual que la version LLM).
- Si una `relation_type` no aparece en el output, no es un error — solo significa que GLiREL no encontro evidencia.
- `deduplicate_relations_py_datascience` ya soporta este formato sin cambios.
- Ver issue 0040 para combinar GLiREL con LLM-fallback en el mismo pipeline.