docs(issues): cerrar 0040 — hybrid extraction pipeline
Mueve el issue a completed/ y actualiza el indice.
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user