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