- Move dev/issues/0038-gliner-entity-extractor.md a completed/ - Update README link y estado a completado Closes #0038 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.7 KiB
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]), 50–200x 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
# 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 texttype_ref= entity name del schematype_label= label legible del schemaconfidence= score de GLiNERattributes["start"],attributes["end"]= offsets en el chunksource_chunk_indiceslo 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+.mdpython/functions/datascience/extract_entities_gliner.py+.md- Tests en
python/functions/datascience/tests/test_extract_entities_gliner.pycon un corpus minimo (3-5 textos cortos ES/EN, schema con 4-5 tipos). pyproject.toml: añadirgliner>=0.2.13como dep opcional bajo[project.optional-dependencies]nlp = ["gliner", ...]. NO añadir a deps principales para no inflar.venvde quien no use NER.- Documentar en el
.md:pip install gliner(ouv pip install gliner), tamaño del modelo, latencia esperada CPU vs GPU.
Validacion
./fn run extract_entities_gliner_py_datasciencecorre los tests.- Bench manual: medir tokens/seg en un texto de 10 KB con 8 labels, CPU y GPU si hay.
- Compararlo con
extract_entities_llmsobre 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_attributesy 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.