Files
fn_registry/dev/issues/completed/0038-gliner-entity-extractor.md

98 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: "0038"
title: "GLiNER entity extractor (zero-shot NER)"
status: completado
type: feature
domain: []
scope: multi-app
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0038 — GLiNER entity extractor (zero-shot NER)
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0038 |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature — Python (`python/functions/datascience/`) |
## Dependencias
Recomendado leer `extract_entities_llm_py_datascience`, `entity_candidate_py_datascience`, `build_entity_schema_prompt_py_datascience`. El contrato debe ser **drop-in** con la version LLM.
**Desbloquea:** 0039 (GLiREL), 0040 (pipeline hibrido). Permite extraccion masiva sin coste por token.
---
## Objetivo
Wrapper sobre GLiNER (`urchade/gliner_multi-v2.1` por defecto, multilingue ES/EN) que extrae entidades zero-shot a partir del mismo `entity_schema: list[dict]` que ya consume `extract_entities_llm`. Misma firma, mismo retorno (`list[EntityCandidate]`), 50200x mas rapido y sin coste por token.
## Funciones a crear
| Function ID | Rol |
|---|---|
| `gliner_load_model_py_datascience` | Carga + cachea el modelo. `purity: impure`, `kind: function` |
| `extract_entities_gliner_py_datascience` | Extractor por chunk. `purity: impure`, mismo contrato que la version LLM |
## Contrato
```python
# gliner_load_model.py
def gliner_load_model(
model_name: str = "urchade/gliner_multi-v2.1",
device: str = "auto", # "auto" | "cpu" | "cuda" | "cuda:0"
) -> "GLiNER": ...
# extract_entities_gliner.py
def extract_entities_gliner(
text: str,
entity_schema: list[dict], # mismo formato que extract_entities_llm
model: "GLiNER", # inyectado, cargado una sola vez
threshold: float = 0.5,
flat_ner: bool = True, # False = nested entities
) -> list[EntityCandidate]
```
`entity_schema` se traduce internamente: cada `{"name": "person", "label": "Person", ...}` se convierte al label que GLiNER espera. `EntityCandidate` se rellena con:
- `name` = span text
- `type_ref` = entity name del schema
- `type_label` = label legible del schema
- `confidence` = score de GLiNER
- `attributes["start"]`, `attributes["end"]` = offsets en el chunk
- `source_chunk_indices` lo setea el caller (igual que en la version LLM)
## Pureza
`gliner_load_model`: impure (lee disco/red la primera vez, descarga ~200 MB de HF).
`extract_entities_gliner`: impure (modelo es estado externo) — `error_type: error_go_core` siguiendo la regla del registry.
## Deliverables
- `python/functions/datascience/gliner_load_model.py` + `.md`
- `python/functions/datascience/extract_entities_gliner.py` + `.md`
- Tests en `python/functions/datascience/tests/test_extract_entities_gliner.py` con un corpus minimo (3-5 textos cortos ES/EN, schema con 4-5 tipos).
- `pyproject.toml`: añadir `gliner>=0.2.13` como dep opcional bajo `[project.optional-dependencies]` `nlp = ["gliner", ...]`. NO añadir a deps principales para no inflar `.venv` de quien no use NER.
- Documentar en el `.md`: `pip install gliner` (o `uv pip install gliner`), tamaño del modelo, latencia esperada CPU vs GPU.
## Validacion
1. `./fn run extract_entities_gliner_py_datascience` corre los tests.
2. Bench manual: medir tokens/seg en un texto de 10 KB con 8 labels, CPU y GPU si hay.
3. Compararlo con `extract_entities_llm` sobre el mismo corpus pequeño y registrar precision/recall en el `.md`.
## Notas
- Mantener el contrato **identico** al de la version LLM permite usar `deduplicate_entities`, `merge_entity_attributes` y todo el resto del pipeline sin cambios.
- GLiNER es malo con IoCs tecnicos (IPs, hashes, wallets) — eso lo cubre 0037. Documentar la limitacion.
- El modelo se carga UNA vez por proceso: el caller lo inyecta. No cargarlo dentro de `extract_entities_gliner` — penalty fatal en batch.
- Si la GPU no esta disponible, `device="auto"` debe caer a CPU sin error.