Merge branch 'online' of github.com:BRUNIX-AI/assistance-engine into mrh-online-dev
This commit is contained in:
commit
c6b57849cd
|
|
@ -0,0 +1,341 @@
|
||||||
|
# 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 |
|
||||||
Loading…
Reference in New Issue