diff --git a/bench/README.md b/bench/README.md index ef1ec34..0d67ac3 100644 --- a/bench/README.md +++ b/bench/README.md @@ -60,6 +60,52 @@ a320-bench score runs/ --json > scores.json # ScoreCards + aggregates for plots a320-bench score runs/elec-apu-gen-fault/one.jsonl --json --detail ``` +## Experiment runbook (needs provider API keys) + +The baselines and ablations of #20 are a loop over `run` (records) and `score` +(measures) — no bespoke code. The protocol (models, N per cell, ablation axes, +statistical power) is in [`docs/fase5-metrica.md`](../docs/fase5-metrica.md); +here are the exact commands the matrix executes. + +Keys live in the environment, never in a file or a trajectory (the adapter +records the model and sampling, never the credential): + +```powershell +$env:ANTHROPIC_API_KEY = "..." # Claude via litellm +$env:GEMINI_API_KEY = "AQ..." # Gemini via Vertex express (AQ.* keys) +``` + +One cell — a model against every scenario, N runs each, into a run tree: + +```powershell +$SCEN = Get-ChildItem scenarios -Recurse -Filter *.yaml | + Where-Object { $_.Directory.Name -ne "schema" } +foreach ($s in $SCEN) { + a320-bench run --scenario $s.FullName ` + --model anthropic/claude-opus-4-8 --runs 10 --out runs/baseline +} +# Gemini needs its Vertex express api_base passed through --sampling: +foreach ($s in $SCEN) { + a320-bench run --scenario $s.FullName --model gemini/gemini-2.5-flash --runs 10 ` + --out runs/baseline ` + --sampling '{"api_base": "https://aiplatform.googleapis.com/v1beta1/publishers/google"}' +} +``` + +Score the whole tree — the table separates by `(scenario, model)`, the JSON +feeds the plots: + +```powershell +a320-bench score runs/baseline # human table + aggregates +a320-bench score runs/baseline --json > baseline.json +``` + +An ablation is the same loop with one lever changed (a scenario variant with a +different `instructions_profile`, a `--sampling` temperature, a tool-surface +profile) into a separate `--out runs/ablation-`, scored the same way. +`score` needs no binding and no network, so the whole measurement half runs on +a reviewer's machine with `pip install -e bench/` alone. + ## Tests ```powershell diff --git a/docs/fase5-metrica.md b/docs/fase5-metrica.md index 383eeaa..a22aafc 100644 --- a/docs/fase5-metrica.md +++ b/docs/fase5-metrica.md @@ -107,3 +107,28 @@ Los procedimientos anormales del QRH/FCOM A320 son públicos y plausiblemente es 1. **Qué mide entonces el benchmark**: *ejecución en bucle cerrado bajo observación*, no recall — leer la ECAM real, secuenciar con un reloj que solo avanza vía `advance`, interpretar el reset fallido (la caution vuelve), y sortear trampas del entorno (la supresión de la caution bajo EXT PWR). **Evidencia empírica ya en mano**: en los smokes, dos modelos que claramente *conocían* el procedimiento (volaron el bloque de reset completo y en orden) **omitieron igualmente la acción de aislamiento** — conocer ≠ cumplir, que es precisamente el hueco que la métrica separa (coverage 0.75 con order 1.0). El recall no explica esa omisión; la ejecución bajo observación, sí. 2. **Canarios naturales**: las divergencias documentadas FBW-vs-real (los `source.notes` de cada escenario, la distinción `EcamSource` de D-014) hacen de detector — un agente que ejecute el QRH verbatim donde el modelo diverge delata recall sobre observación; se reporta cualitativamente. 3. **Mitigación futura** (no de esta etapa): el eje de ablación "QRH access vs not" ya previsto en #20, y el reporte por componentes en vez de por pass-rate (el vector ya lo impone). + +## Protocolo experimental (bloqueado por claves de API) + +Esta sección fija *cómo* se correrán los baselines y las ablations cuando haya claves de proveedor. **No hay código nuevo que escribir**: `a320-bench run` graba trayectorias y `a320-bench score` las puntúa; la matriz es un bucle sobre esos dos comandos (el runbook exacto está en `bench/README.md`). Nada de esto corre en CI ni toca la red hasta ese momento. + +### Modelos (≥2 baselines) + +El paper exige al menos dos modelos (CLAUDE.md, #20). Los dos ya validados end-to-end el 2026-07-24 son la base: **Claude** (`anthropic/claude-*` vía litellm, o la suscripción vía `a320-bench serve` + `claude -p` para desarrollo — no para el baseline formal, porque el system prompt de Claude Code es un confound) y **Gemini** (`gemini/gemini-2.5-flash` vía litellm contra el endpoint express de Vertex). Cada modelo se identifica en el `meta.adapter` de cada trayectoria, así que la agregación por `(scenario, model)` los separa sola. + +### Runs por escenario (potencia estadística) + +Los LLM no son deterministas, así que un solo run por celda no distingue modelos. Punto de partida: **N = 10 runs por (escenario × modelo × ablación)**, con la desviación reportada por `aggregate` (`score_std`, `pass_rate`). N=10 es el mínimo para una media estable con la varianza que se observa en tareas de agente; se sube a 20–30 en las celdas donde dos modelos queden dentro de una desviación. El escenario también aporta varianza (el azar del vendor, D-022), que las ventanas de tolerancia de los predicados absorben — la varianza dominante es el muestreo del modelo. Con 4 escenarios (`elec-apu-gen-fault`, `elec-eng1-gen-fault`, `hyd-eng2-pump-overheat`, `hyd-blue-epump-overheat`) × 2 modelos × N=10 = 80 runs por ablación; asequible. + +### Ejes de ablación (palancas ya existentes, cero código) + +Cada eje es un parámetro que ya expone el harness; se varía uno a la vez desde el baseline: + +- **Instrucciones del sistema** (`instructions_profile` del escenario / `INSTRUCTIONS_PROFILES` del servidor, D-023/D-017): el prompt del agente es "prompt engineering, no documentación" y el eje de ablación que D-017 anunciaba. Variar la riqueza de las reglas de pulgar del avión mide cuánto del cumplimiento viene del prompt vs del modelo. +- **Sampling** (`--sampling` de `a320-bench run`, reenviado verbatim a `litellm.completion` y grabado en `meta`): temperatura 0 vs por defecto mide la contribución del muestreo a la varianza intra-modelo. +- **Superficie de tools** (perfil del servidor): el perfil `benchmark` ya retira `inject_failure`/`clear_failure`; una variante que exponga u oculte tools de descubrimiento (`snapshot`, `list_*`) mide cuánto depende el diagnóstico de la exploración libre. +- **QRH access** (mitigación de contaminación): dar vs no dar el texto del procedimiento en el prompt, para separar recall de ejecución bajo observación. + +### Qué se reporta + +Por el compromiso de forma (D-026), el paper reporta el **vector agregado**, no solo el escalar: media±desv del escalar, y de `coverage`/`order`/`end_state` por separado, más `pass_rate`, `dangerous_rate` y las tasas de infraestructura (`provider_error_rate`, `invalid_rate`). El escalar rankea; el vector explica *por qué* un modelo rankea donde rankea — y es donde vive el hallazgo de los smokes (dos modelos con `end_state` alto pero `coverage` 0.75). Un punto de referencia humano/experto, si es factible, da techo al escalar (un score sin techo es difícil de interpretar); queda como deseable, no como bloqueante.