Files
fn_registry/dev/issues/completed/0040-hybrid-extraction-pipeline.md
T

104 lines
4.3 KiB
Markdown

---
id: "0040"
title: "Pipeline hibrido extraccion entidades+relaciones (regex + GLiNER/GLiREL + LLM fallback)"
status: completado
type: feature
domain: []
scope: multi-app
priority: media
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0040 — Pipeline hibrido extraccion entidades+relaciones (regex + GLiNER/GLiREL + LLM fallback)
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0040 |
| **Estado** | pendiente |
| **Prioridad** | media |
| **Tipo** | feature — Python pipeline (`python/functions/pipelines/`) |
## Dependencias
Bloquea-por: **0037** (IoC regex), **0038** (GLiNER), **0039** (GLiREL).
**Desbloquea:** flujo OSINT/grafo de produccion con coste predecible. Reusable desde apps en `apps/*/` y desde notebooks en `analysis/*/`.
---
## Objetivo
Pipeline que combina los tres extractores en cascada para obtener triplets `(entidad, relacion, entidad)` masivamente con buen coste/calidad:
1. **Capa regex** (0037) — extrae IoCs tecnicos con precision 100%. Coste 0.
2. **Capa GLiNER** (0038) — extrae entidades semanticas (person, organization, location, event...) zero-shot. Coste bajo.
3. **Capa GLiREL** (0039) — relaciones zero-shot entre las entidades de capa 1+2.
4. **Capa LLM fallback** (existente: `extract_entities_llm` + `extract_relations_llm`) — solo se invoca cuando `confidence < threshold` o sobre los chunks marcados como "complejos".
Output: `(list[EntityCandidate], list[RelationCandidate])` listos para `deduplicate_entities``deduplicate_relations``ops_to_sigma_json` / `ops_to_rdf_triples`.
## Funcion a crear
| Function ID | Rol |
|---|---|
| `extract_graph_hybrid_py_pipelines` | Pipeline orquestador. `kind: pipeline`, `purity: impure`, `uses_functions: [...]` con todos los anteriores |
## Contrato
```python
def extract_graph_hybrid(
chunks: list[str],
entity_schema: list[dict],
relation_types: list[str],
gliner_model, # inyectado
glirel_model, # inyectado
llm_chat_json: Callable | None = None, # opcional; si None, sin fallback LLM
ioc_types: list[str] | None = None, # None = todos
confidence_threshold: float = 0.6, # bajo este umbral se llama LLM
languages: str = "Respond in Spanish.",
) -> tuple[list[EntityCandidate], list[RelationCandidate]]
```
Logica interna por chunk:
1. `extract_iocs(chunk, ioc_types)``EntityCandidate` con `type_ref` tecnico (ip/email/hash/...).
2. `extract_entities_gliner(chunk, entity_schema, gliner_model)` → entidades semanticas.
3. Si hay chunks con < N entidades o confidence baja **y** hay `llm_chat_json``extract_entities_llm` sobre esos chunks; mergear.
4. `extract_relations_glirel(chunk, entities_del_chunk, relation_types, glirel_model)`.
5. Si baja cobertura de relaciones y hay LLM → `extract_relations_llm` sobre el chunk.
6. Devolver listas concatenadas (sin deduplicar — eso lo hace el caller con `deduplicate_*`).
## Pureza
`kind: pipeline``purity: impure` (regla del registry).
`uses_functions`: lista los 5+ extractores invocados. `error_type: error_go_core`.
## Deliverables
- `python/functions/pipelines/extract_graph_hybrid.py` + `.md`
- Test de integracion en `python/functions/pipelines/tests/test_extract_graph_hybrid.py` con un corpus pequeño (2-3 textos OSINT realistas, mock del LLM).
- `.md` documenta: cuando usar fallback LLM, latencia esperada por chunk, recomendacion de batch size.
## Validacion
```bash
./fn run extract_graph_hybrid_py_pipelines
```
Bench end-to-end sobre 100 KB de texto:
- Solo LLM (linea base actual): registrar tiempo y coste estimado.
- Pipeline hibrido: registrar tiempo, coste (solo chunks con fallback) y delta de calidad vs solo-LLM.
Registrar resultados en el `.md` para tener referencia historica.
## Notas
- La deduplicacion fuzzy (Levenshtein + Union-Find) ya esta hecha en `deduplicate_entities` — NO replicar aqui.
- IoCs y entidades semanticas pueden solapar (ej: GLiNER detecta `apple.com` como organization, regex como domain). Resolver dejando ambas con `type_ref` distinto y que `deduplicate_entities` con `same_type_only=True` no las mezcle. Documentar esta decision.
- Pensar en un app `apps/osint_extractor/` que use este pipeline + sigma viz como demo. Fuera de scope de este issue — proponer en proposals despues.