Files
egutierrez 7cdb8e1eb2 feat(datascience): GLiNER entity extractor (zero-shot NER) drop-in con LLM
Funciones nuevas en python/functions/datascience/:
- gliner_load_model: carga + cachea modelo GLiNER por (name, device).
  device='auto' resuelve a cuda/cpu segun torch.cuda.is_available, sin
  fallar si torch no esta instalado. ImportError claro si falta gliner.
- extract_entities_gliner: contrato drop-in de extract_entities_llm
  (mismo entity_schema, mismo list[EntityCandidate]). El caller inyecta
  el modelo (cargado UNA vez por proceso). Anota offsets start/end en
  attributes para reconciliar con extract_iocs (issue 0040).

Diferencias vs LLM extractor:
- 50-200x mas rapido en GPU, 0 USD/token.
- Malo con IoCs tecnicos (lo cubre 0037).
- Threshold y flat_ner ajustables por dominio.

pyproject.toml: gliner como extra opcional `[nlp]` para no inflar el
.venv de quien no use NER. Instalacion: `uv pip install -e '.[nlp]'`.

Refs #0038 — Desbloquea 0039 (GLiREL) y 0040 (pipeline hibrido).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:41:30 +02:00

4.1 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports params output tested tests test_file_path file_path
extract_entities_gliner function py datascience 1.0.0 impure def extract_entities_gliner(text: str, entity_schema: list[dict], model: Any, threshold: float = 0.5, flat_ner: bool = True) -> list[EntityCandidate] Extrae entidades zero-shot con GLiNER. Drop-in del contrato de extract_entities_llm pero 50-200x mas rapido y sin coste por token. El caller inyecta el modelo cargado con gliner_load_model. Anota offsets start/end en attributes para reconciliar con extract_iocs.
gliner
ner
nlp
entity
extract
zero-shot
osint
graph
datascience
python
gliner_load_model_py_datascience
entity_candidate_py_datascience
false error_go_core
warnings
name desc
text chunk de texto a analizar (parrafo, documento corto, output de OCR)
name desc
entity_schema lista de dicts con 'type_ref' y 'label'. Mismo formato que extract_entities_llm. El 'label' se usa como label de GLiNER.
name desc
model instancia GLiNER cargada con gliner_load_model. Inyectar para evitar penalty de carga en batch.
name desc
threshold score minimo para aceptar una entidad (0.0-1.0). Defecto 0.5 — ajustable segun precision/recall objetivo.
name desc
flat_ner True (defecto) sin entidades anidadas; False permite spans solapados (ej. 'Universidad de Madrid' como ORG y 'Madrid' como LOC en simultaneo)
lista de EntityCandidate con name, type_ref, type_label, confidence y attributes={'start': int, 'end': int} true
Schema basico y modelo stub retorna EntityCandidate con offsets
Threshold filtra spans con score bajo
Schema vacio lanza ValueError
Schema sin label+type_ref validos retorna vacio con warning
Excepcion del modelo se captura y retorna vacio
Label desconocido se descarta
flat_ner se propaga al modelo
python/functions/datascience/tests/test_extract_entities_gliner.py python/functions/datascience/extract_entities_gliner.py

Ejemplo

from python.functions.datascience import (
    gliner_load_model,
    extract_entities_gliner,
)

model = gliner_load_model(device="auto")

schema = [
    {"type_ref": "osint_person_go_cybersecurity", "label": "Person"},
    {"type_ref": "osint_organization_go_cybersecurity", "label": "Organization"},
    {"type_ref": "osint_location_go_cybersecurity", "label": "Location"},
]

text = "Alice Johnson works at OpenAI in San Francisco."
entities = extract_entities_gliner(text, schema, model, threshold=0.4)
# [EntityCandidate(name='Alice Johnson', type_ref='osint_person_go_cybersecurity',
#                  attributes={'start': 0, 'end': 13}, confidence=0.92), ...]

Drop-in con extract_entities_llm

El retorno es identico (list[EntityCandidate]), por lo que se puede sustituir sin tocar el resto del pipeline (deduplicate_entities, merge_entity_attributes, etc). Diferencias:

  • Coste: GLiNER = 0 USD/token. LLM = depende de modelo.
  • Latencia: GLiNER 50-200x mas rapido en GPU.
  • IoCs tecnicos (IPs, hashes, wallets, CVEs): GLiNER es malo — usar extract_iocs_py_cybersecurity para esos. Combinar regex + GLiNER en el pipeline hibrido (issue 0040).
  • Schemas con muchos tipos: GLiNER pierde precision con >20 labels; LLM la mantiene. Para esquemas grandes, dividir en bloques.
  • Razonamiento implicito ("CEO de la empresa"): el LLM lo deduce, GLiNER solo extrae lo explicito.

Notas

  • El modelo se carga UNA vez por proceso. No cargarlo aqui dentro: penalty fatal en batch. Inyeccion explicita por contrato.
  • impure: el modelo es estado externo (memoria, GPU si aplica). error_type: error_go_core segun la regla de pureza del registry.
  • Si flat_ner=False, validar que el caller dedupica/normaliza spans solapados — EntityCandidate.attributes['start'/'end'] permite hacerlo facilmente.
  • Para precision maxima, ajustar threshold por dominio: 0.3-0.4 para recall alto, 0.6-0.8 para precision alta.