fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
98 lines
3.9 KiB
Markdown
98 lines
3.9 KiB
Markdown
---
|
||
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]`), 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
|
||
|
||
```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.
|