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