Spaces:
Running
Running
File size: 6,122 Bytes
70e641d 1e215c9 bf9f7d1 1e215c9 70e641d bf9f7d1 1e215c9 bf9f7d1 1e215c9 70e641d 1e215c9 70e641d bf9f7d1 1e215c9 70e641d bf9f7d1 1e215c9 bf9f7d1 1e215c9 bf9f7d1 1e215c9 bf9f7d1 1e215c9 bf9f7d1 1e215c9 bf9f7d1 1e215c9 bf9f7d1 1e215c9 70e641d 1e215c9 70e641d 1e215c9 70e641d 1e215c9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | # Evaluación del asistente clínico
Suite de evals rigurosa para un asistente de diagnóstico veterinario: mide precisión,
groundedness y **seguridad**, y bloquea despliegues ante regresiones.
Principio de diseño: **toda la evaluación tiene que poder correr sin una clave de pago.**
Una puerta de calidad que depende de saldo se apaga el día que se acaba, y se apaga justo
cuando más falta hace. Por eso los jueces LLM usan el CLI de Claude Code (la sesión que ya
tienes) o un modelo local en Ollama; la ruta por SDK con `ANTHROPIC_API_KEY` queda como
opción explícita.
## Capas
1. **Regresión del motor determinista** (`frontend/tests`, Vitest) — fija el comportamiento
de `analisis.ts`. Es la red de seguridad de la migración.
2. **Evals clínicas** (`run_evals.py`) — comprobaciones deterministas sobre la salida del
modelo: recall de diferenciales, cobertura de hallazgos, acierto de derivación, idioma
y **violaciones de seguridad** (tolerancia cero). Puerta de CI. La cobertura se mide
sobre el campo estructurado o, si el backend devuelve prosa, sobre las menciones en el
texto (marcado `~` en la salida): una métrica que la ruta de producción no puede pasar
por construcción no es una puerta.
3. **Juez clínico LLM** (`judge/clinical_judge.py`) — rúbrica de corrección, hedging,
seguridad y completitud, servida por el **CLI de Claude Code** o por un modelo **local en
Ollama**, sin clave de API en ninguno de los dos casos. Atrapa lo que ninguna comparación
de strings ve: razonamiento incorrecto con las palabras clave correctas, sobreconfianza,
consejo peligroso.
4. **Groundedness con Ragas** (`run_ragas.py`) — faithfulness y precisión/recall del
contexto sobre el índice RAG real, con el mismo juez local.
5. **Eval de recuperación** (`run_retrieval_eval.py`) — aísla la recuperación de la
generación para hacer A/B de configs (embeddings × idioma de consulta).
6. **promptfoo** (`promptfooconfig.yaml`) — regresión declarativa del prompt (idioma, sin
tokens de control, rúbricas).
## Ejecutar
```bash
# Tubería sin modelo (valida la mecánica y los umbrales)
make evals # split dev, sólo casos validados
make evals-test # split reservado
# Con el modelo real (genera interpretaciones vía backend)
cd backend && uv run python ../evals/run_evals.py --modelo medgemma
# Con salidas precomputadas, eligiendo juez
cd backend && uv run python ../evals/run_evals.py --predicciones preds.jsonl --juez cli
cd backend && uv run python ../evals/run_evals.py --predicciones preds.jsonl --juez ollama
# Groundedness (requiere índice RAG y Ollama)
make ragas ARGS="--predicciones preds.jsonl"
# A/B de recuperación
make retrieval-eval
# promptfoo
cd evals && npx promptfoo@latest eval
```
## Los jueces
Tres transportes tras la misma rúbrica y el mismo esquema de salida. `--juez auto` (por
defecto) prueba en este orden y usa el primero disponible:
| `--juez` | Qué necesita | Coste | Notas |
|---|---|---|---|
| `cli` | El CLI `claude` con sesión iniciada | Límites de uso de tu suscripción | Mejor juicio. No existe en CI |
| `ollama` | Ollama con el modelo descargado | Ninguno | Reproducible (temp. 0). Mantiene viva la rúbrica en CI |
| `claude` | `ANTHROPIC_API_KEY` | Saldo de API | Explícito, para auditorías |
```bash
claude --version # juez CLI: basta con tener sesión iniciada
ollama pull qwen2.5:7b # juez local por defecto
```
| Variable | Por defecto | Para qué |
|---|---|---|
| `MORPHOS_JUEZ_CLI_MODELO` | `sonnet` | Modelo del juez CLI (`opus` para auditar) |
| `MORPHOS_JUEZ_MODELO` | `qwen2.5:7b` | Modelo del juez local |
| `MORPHOS_JUEZ_BASE_URL` | `http://localhost:11434` | Dónde está Ollama |
### El juez CLI
`judge/claude_cli.py` invoca `claude -p` con la rúbrica como system prompt y
`--output-format json`, sin MCP, sin sesión persistida y con un solo turno. Resuelve lo que
bloqueaba esta capa: **no hace falta clave de API**, basta la sesión que ya tienes. A cambio
no está en un runner de CI y no expone temperatura, así que es menos reproducible que la
ruta Ollama.
### Elegir modelo de juez
- **No uses el modelo bajo evaluación.** Un modelo que se juzga a sí mismo se puntúa alto
por sesgo de auto-preferencia y deja de detectar sus propias regresiones.
- **Los umbrales dependen del juez.** `UMBRALES_JUEZ` está calibrado contra el juez local
pequeño. Sobre las mismas salidas simuladas (deliberadamente pobres), qwen2.5:7b puntuó
`hedging_apropiado` en 1.0 y el juez CLI en 0.4: el pequeño aprueba lo que el grande
suspende. Si cambias de juez, recalibra los umbrales en vez de asumir que la escala es la
misma.
Con `--simular` el juez se omite a propósito: puntuaría el simulador, no el modelo. Con
`--juez-informativo` corre pero no bloquea la puerta. Los jueces remotos se paralelizan
(4 casos a la vez); el local va en serie porque competiría consigo mismo por la GPU.
## Umbrales (puerta de CI)
Definidos en `run_evals.py` → `UMBRALES` (deterministas) y `UMBRALES_JUEZ` (rúbrica). Salida
con código ≠0 si alguna métrica cae por debajo o si hay cualquier violación de seguridad,
venga de la comprobación determinista o del juez. Ver `.github/workflows/evals.yml`.
## Disciplina del dataset
Dos reglas, ambas aplicadas por el runner y no sólo documentadas:
- **Split reservado.** `--split dev` (por defecto) es el conjunto sobre el que se itera;
`--split test` sólo se mira en agregado y antes de desplegar. Mirar los fallos caso a caso
del split reservado para afinar un prompt lo convierte en otro split de desarrollo.
- **Sin firma veterinaria no es oro.** Los casos con `validado: false` no cuentan para la
puerta (salvo `--incluir-pendientes`): no pueden aprobar ni bloquear un despliegue.
```bash
make revision # hoja de revisión
python evals/revision.py --validar imha-canino --revisor "Dra. Pérez"
python evals/revision.py --estado
```
Esquema de los casos y estado de validación: `dataset/README.md`.
|