assistance-engine/scripts/pipelines/samples_generator/DOCUMENTATION.md

342 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AVAP MAP-Elites Dataset Pipeline — Documentación
> Scripts para la síntesis de datasets de benchmarks AVAP mediante cobertura de gramática garantizada y priors estadísticos extraídos de código real de producción.
---
## Índice
- [Visión general del sistema](#visión-general-del-sistema)
- [Requisitos e instalación](#requisitos-e-instalación)
- [construct_prior.py](#construct_priorpy)
- [Uso](#uso-construct_prior)
- [Cómo funciona](#cómo-funciona-construct_prior)
- [generate_mbap_v2.py](#generate_mbap_v2py)
- [Uso](#uso-generate_mbap_v2)
- [Cómo funciona](#cómo-funciona-generate_mbap_v2)
- [Flujo de trabajo recomendado](#flujo-de-trabajo-recomendado)
- [Archivos generados](#archivos-generados)
---
## Visión general del sistema
El pipeline consta de dos scripts que trabajan en conjunto:
```
construct_prior.py → construct_map.yaml
↓ ↓
generate_mbap_v2.py ←─────────────────┘
dataset .json + coverage_stats .json
```
1. **`construct_prior.py`** analiza codebases reales en GitHub para extraer con qué frecuencia co-ocurren los 38 comandos AVAP en código de producción real. El resultado es un fichero `construct_map.yaml` con pesos estadísticos.
2. **`generate_mbap_v2.py`** usa esos pesos para dirigir un generador MAP-Elites que llama a la API de Claude, garantizando cobertura uniforme de todas las combinaciones de pares y tríos del DSL AVAP.
---
## Requisitos e instalación
```bash
pip install requests pyyaml anthropic
pip install datasets huggingface_hub # solo si usas --prior-sources huggingface
export ANTHROPIC_API_KEY=sk-ant-...
export GITHUB_TOKEN=ghp_... # opcional pero recomendado
```
El parser AVAP debe estar en ejecución si se desea validación AST real (opcional; si no está disponible, se usa keyword scanning como fallback):
```bash
# El parser debe escuchar en el puerto configurado (por defecto 8080)
# Ejemplo: http://localhost:8080
```
---
## `construct_prior.py`
### Uso (construct_prior)
El script tiene dos modos de operación: **generación** del mapa y **verificación** de uno existente.
#### Generar `construct_map.yaml` (ejecutar una vez, o cuando cambie la gramática AVAP)
```bash
# Ejecución básica (sin token: límite de 10 req/min en GitHub)
python construct_prior.py --generate-map
# Con token de GitHub (recomendado — 30 req/min)
python construct_prior.py --generate-map --github-token ghp_...
# Analizar más ficheros para un prior más rico
python construct_prior.py --generate-map --max-files 200 --github-token ghp_...
# Ruta de salida personalizada
python construct_prior.py --generate-map --output /ruta/construct_map.yaml
```
#### Verificar un `construct_map.yaml` existente
```bash
python construct_prior.py --verify --map construct_map.yaml
```
#### Uso como módulo desde `generate_mbap_v2.py`
```python
from construct_prior import ConstructPrior
# Cargar desde YAML generado
prior = ConstructPrior.from_yaml("construct_map.yaml")
# Consultar el peso de una celda (par o trío de comandos AVAP)
w = prior.cell_weight(frozenset({"try", "ormAccessSelect"}))
# Fallback estático (sin necesidad de YAML)
prior = ConstructPrior.static_fallback()
```
#### Parámetros CLI
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `--generate-map` | flag | — | Activa el modo de extracción desde GitHub |
| `--verify` | flag | — | Carga y muestra estadísticas de un YAML existente |
| `--github-token` | str | `$GITHUB_TOKEN` | Token de acceso personal de GitHub |
| `--max-files` | int | `100` | Número máximo de ficheros a analizar |
| `--output` | str | `construct_map.yaml` | Ruta de salida del YAML |
| `--map` | str | `construct_map.yaml` | Ruta del YAML a verificar (solo con `--verify`) |
---
### Cómo funciona (construct_prior)
El objetivo del script es responder a la pregunta: **¿qué combinaciones de comandos AVAP aparecen juntas con más frecuencia en código API real de producción?** Esa información sirve para sesgar el generador de datasets hacia patrones realistas.
#### 1. Vocabulario AVAP (`AVAP_NODE_NAMES`)
Se define una lista canónica de 38 comandos AVAP organizados por categoría (variables, ORM, HTTP, criptografía, concurrencia, etc.). Esta lista es la única fuente de verdad del módulo y es importada también por `generate_mbap_v2.py`.
#### 2. Tabla de equivalencias de lenguaje (`LANGUAGE_MAPPINGS`)
Cada comando AVAP se mapea a sus equivalentes en Python (detección por AST), Go (keywords) y SQL. Esta tabla, verificada manualmente, define *qué buscar* al escanear los codebases. Por ejemplo:
- `ormAccessSelect` → llamadas como `.fetchall()`, `.query()`, `.filter()`
- `RequestPost``requests.post`, `httpx.post`, `session.post`
- `try` → nodo `ast.Try` en el AST de Python
#### 3. Extracción desde GitHub (`GitHubFetcher`)
Se lanzan 16 queries predefinidas contra la GitHub Code Search API, cada una orientada a un patrón típico de microservicio (ORM + manejo de errores, clientes HTTP, autenticación con crypto, concurrencia async, JSON, fechas, etc.). Los ficheros encontrados se descargan en base64 y se decodifican en memoria.
El fetcher respeta automáticamente el rate limit de la API (10 req/min sin token, 30 req/min con token) con esperas adaptativas entre peticiones.
#### 4. Detección AST de Python (`PythonASTDetector`)
Cada fichero descargado se parsea con el módulo estándar `ast` de Python. El walker del AST recorre todos los nodos y detecta qué comandos AVAP están presentes según su equivalente estructural:
- `ast.Try``"try"` + `"exception"`
- `ast.FunctionDef``"function"` + `"addParam"` (si tiene argumentos)
- `ast.For` / `ast.AsyncFor``"startLoop"`
- `ast.Call` con callee `.fetchall``"ormAccessSelect"`
- etc.
Este enfoque es **AST-level**, no keyword scanning, lo que elimina falsos positivos por nombres de variable, strings o comentarios. Si el fichero tiene errores de sintaxis, cae automáticamente a un keyword fallback.
#### 5. Acumulación de co-ocurrencias (`CooccurrenceExtractor`)
Por cada fichero analizado se obtiene un conjunto de comandos AVAP detectados. Se calculan todas las combinaciones de pares y tríos posibles de ese conjunto y se incrementan sus contadores. Por ejemplo, si un fichero contiene `{try, ormAccessSelect, return}`, se incrementan los contadores de `(try, ormAccessSelect)`, `(try, return)`, `(ormAccessSelect, return)` y el trío `(try, ormAccessSelect, return)`.
#### 6. Normalización y escritura del YAML (`generate_construct_map`)
Los contadores de co-ocurrencia se normalizan a `[0, 1]` dividiéndolos por el máximo observado. El resultado se escribe en `construct_map.yaml` con dos secciones:
- `language_mappings` — la tabla de equivalencias (trazabilidad)
- `pair_weights` / `trio_weights` — pesos empíricos extraídos
#### 7. Propagación de pesos a subsets (`_propagate_subset_weights`)
Los tríos que contienen pares con alto peso heredan el 60% del peso del par más relevante que contienen. Esto garantiza que los tríos formados por pares comunes sean visitados antes que tríos de combinaciones raras.
#### 8. Fallback estático
Si no se dispone de conexión a GitHub o de un YAML previo, `ConstructPrior.static_fallback()` retorna un conjunto de 40+ pesos hard-coded basados en conocimiento experto (por ejemplo, `(try, exception)` = 1.0, `(function, return)` = 0.98).
---
## `generate_mbap_v2.py`
### Uso (generate_mbap_v2)
#### Invocación básica
```bash
# Modo por defecto: MAP-Elites + ConstructPrior (Candidato F)
python generate_mbap_v2.py --lrm avap.md --parser http://localhost:8080
# Generar 5000 ejemplos con celdas de hasta 3 comandos
python generate_mbap_v2.py --lrm avap.md --parser http://localhost:8080 \
--problems 5000 --cell-size 3 --mode map-elites-prior
```
#### Opciones del prior
```bash
# Usar datos reales de GitHub para el prior (AST-level, requiere red)
python generate_mbap_v2.py --lrm avap.md --mode map-elites-prior \
--prior-sources github
# Guardar el prior tras la extracción (para reutilizarlo después)
python generate_mbap_v2.py --lrm avap.md --mode map-elites-prior \
--prior-sources github --prior-save prior_weights.json
# Cargar un prior pre-extraído (sin peticiones a GitHub)
python generate_mbap_v2.py --lrm avap.md --mode map-elites-prior \
--prior-load prior_weights.json
# Usar el prior generado por construct_prior.py
python generate_mbap_v2.py --lrm avap.md --mode map-elites-prior \
--prior-map construct_map.yaml
```
#### Modos de generación
```bash
# Candidato F: MAP-Elites + ConstructPrior (por defecto, recomendado)
python generate_mbap_v2.py --lrm avap.md --mode map-elites-prior
# Candidato E: MAP-Elites con pesos uniformes (baseline sin prior)
python generate_mbap_v2.py --lrm avap.md --mode map-elites
# Candidato A: CW-Reward pool (no implementado en v2, usar generate_mbap.py)
python generate_mbap_v2.py --lrm avap.md --mode reward
```
#### Parámetros CLI completos
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `--lrm` | str | `avap.md` | Ruta al Language Reference Manual de AVAP |
| `--output` | str | `output/mbpp_avap_v2.json` | Ruta del dataset de salida |
| `--problems` | int | `5000` | Número de ejemplos a generar |
| `--parser` | str | `http://localhost:8080` | URL del parser AVAP |
| `--cell-size` | int | `3` | Tamaño máximo de celda: 2=solo pares, 3=pares+tríos |
| `--quality-threshold` | float | `0.80` | Calidad mínima para considerar una celda "buena" |
| `--alpha` | float | `0.30` | Peso de los comandos bonus en la calidad |
| `--beta` | float | `0.20` | Peso de la calidad de los tests en la calidad |
| `--gamma` | float | `0.10` | Peso de la riqueza del código en la calidad |
| `--mode` | choice | `map-elites-prior` | Modo de generación (ver arriba) |
| `--prior-map` | str | `construct_map.yaml` | Ruta al YAML generado por `construct_prior.py` |
| `--prior-epsilon` | float | `0.05` | Peso mínimo para celdas cola (tail cells) |
| `--prior-phase3-threshold` | float | `0.70` | Calidad a partir de la cual se activa la Fase 3 (tail) |
| `--api-key` | str | `$ANTHROPIC_API_KEY` | API key de Anthropic |
---
### Cómo funciona (generate_mbap_v2)
El generador implementa un algoritmo de **Quality-Diversity (QD)** llamado MAP-Elites aplicado a la generación de ejemplos de código AVAP. El objetivo es producir un dataset que cubra de forma garantizada todas las combinaciones de pares y tríos de los 38 comandos del DSL, sin sesgos de distribución.
#### 1. Vocabulario y detección de constructs (`AVAP_NODE_TYPES`, `CellValidator`)
Se define un diccionario que mapea cada comando AVAP a sus patrones de keyword. `CellValidator` detecta qué comandos están presentes en un ejemplo generado usando dos estrategias:
- **Desde el AST del parser AVAP**: se recorre el árbol recursivamente buscando nodos por `type`.
- **Desde el código fuente** (fallback): se buscan los patrones de keyword del diccionario. `if_mode2` se comprueba antes que `if_mode1` para evitar ambigüedad.
Además calcula una **puntuación de calidad** compuesta:
```
quality = fidelity
+ alpha * bonus_constructs_ratio
+ beta * test_quality
+ gamma * code_richness
```
- `fidelity`: fracción de los constructs requeridos por la celda que están presentes (componente principal).
- `bonus_ratio`: constructs adicionales más allá de los requeridos.
- `test_quality`: proporción de tests con patrón `re.match()` y longitud > 10.
- `richness`: número de líneas normalizadas a 30 (proxy de complejidad).
#### 2. Mapa de cobertura MAP-Elites (`CoverageMap`)
Es la estructura central del algoritmo. Mantiene **una celda por cada combinación posible de 2 o 3 comandos AVAP** (con `cell_size=3`: 703 pares + N tríos de un total de 38 comandos). Cada celda almacena el mejor ejemplo encontrado hasta el momento para esa combinación.
Una celda solo se considera válida si el ejemplo que contiene usa **todos** los constructs de su clave. El mapa expone métricas en tiempo real: tasa de llenado, entropía de distribución (Shannon), y celdas de baja calidad.
#### 3. Selector de celdas — Candidato E (`CellSelector`)
Implementa la estrategia de selección sin prior en tres fases:
- **Fase 1**: Celdas vacías (round-robin aleatorio con semilla fija para reproducibilidad).
- **Fase 2**: Celdas con calidad por debajo del umbral.
- **Fase 3**: UCB (Upper Confidence Bound) sobre todas las celdas — equilibrio entre explotar celdas de alta calidad y explorar celdas poco visitadas.
#### 4. Selector con prior — Candidato F (`CellSelectorPrior`)
Extiende `CellSelector` incorporando los pesos de `ConstructPrior`:
- **Fase 1**: Las celdas vacías con peso de prior alto (> 1.5× epsilon) se priorizan mediante muestreo ponderado. Esto hace que el dataset sea útil para RAG desde los primeros ejemplos (los patrones más frecuentes en producción se cubren primero).
- **Fase 2**: Las celdas de baja calidad se seleccionan con UCB multiplicado por el peso del prior.
- **Fase 3**: Una vez que las celdas de prior alto alcanzan el umbral de calidad, se activa la cobertura de las celdas cola (tail cells, prior ≈ epsilon).
#### 5. Construcción del prompt y llamada a la API (`Generator`)
Para cada celda seleccionada se construye un prompt estructurado que incluye el LRM completo de AVAP y una especificación precisa: "genera UN problema de benchmark estilo MBPP que use **exactamente** estos constructs: `{cell}`". El generador nunca pide exploración libre — cada llamada tiene una especificación de cobertura forzada.
La respuesta se parsea esperando JSON con campos `prompt`, `code`, y `tests`.
#### 6. Validación y actualización del mapa
Cada ejemplo generado pasa por `CellValidator`:
1. Se intenta parsear con el parser AVAP (si está disponible).
2. Se detectan los constructs presentes.
3. Se calcula la puntuación de calidad.
4. Si la calidad supera el ejemplo actual de la celda (o la celda está vacía), el mapa se actualiza.
#### 7. Checkpoints y métricas
Cada 100 llamadas a la API se imprime un checkpoint con: tamaño del dataset, tasa de éxito, estado del mapa, entropía de distribución, y divergencia KL entre el dataset y el prior (KL = 0 significa alineación perfecta con patrones de producción).
#### 8. Guardado (`_save`)
Al finalizar se guardan dos ficheros:
- `<output>.json` — el dataset completo.
- `<output>_coverage_stats.json` — estadísticas de cobertura, entropía, frecuencia por nodo, y KL-divergence respecto al prior.
---
## Flujo de trabajo recomendado
```bash
# Paso 1: Generar el prior estadístico (una sola vez, o al actualizar la gramática)
python construct_prior.py --generate-map --github-token $GITHUB_TOKEN --max-files 200
# Paso 2: Verificar el prior generado
python construct_prior.py --verify --map construct_map.yaml
# Paso 3: Generar el dataset
python generate_mbap_v2.py \
--lrm avap.md \
--parser http://localhost:8080 \
--prior-map construct_map.yaml \
--problems 5000 \
--output output/dataset_v2.json \
--mode map-elites-prior
```
---
## Archivos generados
| Fichero | Generado por | Descripción |
|---|---|---|
| `construct_map.yaml` | `construct_prior.py` | Pesos de co-ocurrencia de pares/tríos AVAP extraídos de GitHub |
| `<output>.json` | `generate_mbap_v2.py` | Dataset de ejemplos AVAP estilo MBPP |
| `<output>_coverage_stats.json` | `generate_mbap_v2.py` | Estadísticas de cobertura, entropía y KL-divergence |