---
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.
