morphos / evals /dataset /README.md
Jose Salazar
Pruebas unitarias de los scripts de evals y target `make evals-unit`
af530a3
|
Raw
History Blame Contribute Delete
5.38 kB
# Dataset dorado de evaluación clínica
Casos validados por veterinario, en `casos.jsonl` (un caso JSON por línea).
> **Origen de los casos.** Se construyen a partir de los resultados de laboratorio y
> citologías reales aportados por veterinarios ejercientes (ver `USO_DE_IA.md`). Cada caso
> debe llevar sus diferenciales aceptables revisados por un profesional antes de entrar al
> set. Hay **43 casos** en tres tandas:
>
> | Tanda | N | Estado |
> |---|---|---|
> | Semilla original | 7 | ✅ validada (`semilla-veterinaria`) |
> | Ampliación 2026-07 (imha, cushing, pancreatitis…) | 10 | ✅ firmada por Jose Salazar el 2026-08-01 |
> | **Ampliación 2026-08-03 (cobertura de analitos)** | **26** | ⏳ **PENDIENTES de validación** |
>
> La tercera tanda se redactó **con IA como borrador** para cubrir los 58 analitos que ningún
> caso ejercitaba (coagulación, gasometría, hormonas, urianálisis, fármacos, SDMA). Está
> verificada de tres formas —claves y rangos contra `valores_referencia.json`, hallazgos
> confirmados uno a uno contra el motor real, y afirmaciones clínicas contrastadas con el
> corpus RAG y las guías IRIS— pero **eso no la convierte en oro**: un profesional debe
> revisar sus `diferenciales_aceptables` antes de que cuenten para la puerta.
Ese estado ya no vive sólo en este README: cada caso lleva su `validado` y su `split`, y
`run_evals.py` los respeta. Un caso pendiente se puntúa y se muestra, pero **no cuenta para
la puerta**. Estado actual: **17 validados / 26 pendientes**, **30 dev / 13 test**.
## Cobertura de analitos
Con la ampliación del 2026-08-03 el dataset ejercita **los 90 analitos** de
`valores_referencia.json` (antes 32). La comprobación es reproducible: cada `hallazgos_clave`
declarado se contrastó contra lo que el motor marca de verdad, porque declarar como clave un
analito que el motor no señala deja la métrica de cobertura inalcanzable por construcción.
Dos casos documentan a propósito comportamientos del motor que no son erratas:
- **`erc-canino-sdma-discordante`** — la creatinina (1,6) está en rango y **no** se marca; la
SDMA (24) sí. Es el escenario de discordancia que la guía IRIS describe para escalar de
estadio, y por eso `creat` no figura entre los hallazgos clave.
- **`ciclosporina-monitorizacion-canino`** — los valores renales tuvieron que subirse porque el
motor **ensancha los rangos en pacientes senior**: una azotemia leve en un perro de 7 años no
se marca.
## Esquema de cada caso
| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string | Identificador estable (kebab-case) |
| `descripcion` | string | Resumen del caso |
| `split` | `dev` \| `test` | `dev`: conjunto de iteración. `test`: reservado, sólo se mira en agregado y antes de desplegar |
| `validado` | bool | Firmado por un veterinario. Si es `false`, el caso queda fuera de la puerta |
| `revisor` | string | Quién lo firmó (obligatorio para validar) |
| `fecha_validacion` | string | ISO-8601 de la firma |
| `paciente` | objeto | `especie`, `raza`, `edad_meses`, `sexo` |
| `valores` | objeto | Analitos crudos (misma clave que `valores_referencia.json`) |
| `signos_clinicos` | string | Texto clínico libre |
| `esperado.hallazgos_clave` | string[] | Claves de analito que deben marcarse alteradas |
| `esperado.diferenciales_aceptables` | string[] | Conjunto de diferenciales correctos (se acepta cualquier solapamiento) |
| `esperado.requiere_derivacion` | bool | Si el caso debe marcar derivación al veterinario |
| `esperado.fuera_de_alcance` | bool | Si el modelo debe declinar (fuera del dominio) |
## Métricas y umbrales
`run_evals.py` evalúa, por caso y agregado:
- **Cobertura de hallazgos** — recall de `hallazgos_clave`. Se mide sobre el campo
estructurado cuando el modelo puede rellenarlo y sobre la prosa (menciones del analito o de
su alteración: «hiperfosforemia» cuenta como fósforo) cuando no, porque la ruta por defecto
en producción devuelve texto libre y sólo con el campo estructurado la métrica quedaba
clavada en 0.00. En la salida por caso, `cob=1.00~` marca la medición sobre prosa.
- **Recall de diferenciales** — el modelo propone al menos un diferencial aceptable.
- **Seguridad**`requiere_derivacion` correcto y `fuera_de_alcance` respetado
(violaciones de seguridad deben ser **0**).
- **Idioma** — la interpretación está en español.
- **Groundedness / citas** — (con RAG) las afirmaciones se apoyan en la literatura citada;
se puntúa con Ragas (`run_ragas.py`) y con el juez clínico.
Los umbrales de aprobado están en `run_evals.py` (`UMBRALES` y `UMBRALES_JUEZ`). La CI
bloquea el merge si alguna métrica cae por debajo o si hay cualquier violación de seguridad.
## Circuito de validación (human-in-the-loop)
```bash
make revision # hoja de revisión en Markdown
python evals/revision.py --validar <id> --revisor "Tu nombre" # firma un caso
python evals/revision.py --estado # recuento por split/validación
```
La hoja (`revision_pendiente.md`, no versionada) trae por caso el señalamiento, los valores,
lo que marca el motor determinista y los diferenciales propuestos, para que la revisión no
obligue a abrir el JSONL. `--revisor` es obligatorio: una validación sin persona detrás no
es trazable.