Tabla de Contenidos

21. Evaluación de skills

Una skill se prueba como el código: primero que está bien hecha (los checks) y después que funciona (los evals), repitiendo cada prueba varias veces porque el modelo no responde siempre igual. Cómo se construye la skill está en Estructura de un skill.

Más información:


1. Los checks: la forma

Antes de probar si una skill funciona, se comprueba que está bien hecha: una lista de comprobaciones que tiene que pasar entera antes de darla por terminada. Casi todo lo del tema 20 se puede comprobar con un script, sin gastar tokens:

Lo demás (que la description diga el cuándo, que no haya teoría que el modelo ya sabe, que cada ❌ lleve su porqué) lo revisa un modelo o una persona.

Esa lista y su script se guardan en una skill de conocimiento, k-skill, con todas las reglas del tema 20. Así el agente las aplica al crear o revisar cualquier skill del proyecto.

2. evals/: el comportamiento

Una batería de casos que se repite al editar la skill y al cambiar de modelo. Cada caso responde a dos preguntas:

.claude/skills/k-servicios/evals/
├── update-sin-filtrar/          <- un caso, con el nombre de lo que prueba
│   ├── prompt.md                <- lo que escribiría el usuario
│   ├── case.yaml                <- opcional: dice qué script prepara los ficheros
│   ├── fixture.sh               <- opcional: crea los ficheros de partida
│   └── graders/
│       ├── filtra-campos.md     <- un evaluador por fichero
│       └── skill-activada.md
└── results/                     <- las notas de cada evaluación; va en .gitignore

El caso

---
max_turns: 30
runs: 10
---
Añade a PedidoServiceImpl un método update que permita cambiar la dirección y el teléfono del pedido.

schema_version: "1.1"
name: update-sin-filtrar
context:
  scaffold_script: fixture.sh

Los evaluadores

En graders/, un evaluador por fichero, con su type en el frontmatter. Sobre el resultado, con un modelo que hace de juez:

---
type: llm
focus: { source: file, path: PedidoServiceImpl.java }
---
PASS si el update solo deja cambiar la dirección y el teléfono.
FAIL si copia sobre la entidad todo lo que recibe, o si el cliente puede cambiar el estado o el importe.

Y sobre el proceso, con un script:

---
type: tool_used
tool: Skill
input_match: '"skill"\s*:\s*"k-servicios"'
---

Los tipos de evaluador:

Tipo Aprueba cuando Lo aplica
regex Un patrón aparece (o no) en la respuesta o en un fichero Un script
tool_used Se llamó a una herramienta, con min y max veces Un script
tool_order Una herramienta se llamó antes que otra Un script
file_exists Se creó un fichero que cumple el patrón Un script
llm Un juez vota PASS en dos de tres votos Un modelo juez

Cómo se hacen bien:

skill-activada no cuenta en la nota. Sin la skill siempre suspende, así que inflaría la diferencia. Claude Code lo sabe: un tool_used sobre Skill solo dice si se activó.
El modelo evaluado no puede leer evals/. Si ve los criterios, aprueba por imitación. Ni el prompt ni fixture.sh apuntan a nada de evals/.

Ejecutarlos

En Claude Code, con claude plugin eval y la carpeta de la skill. Por defecto ejecuta cada caso con la skill y sin ella, y da la diferencia:

claude plugin eval .claude/skills/k-servicios --scaffold --runs 10 --judge-model haiku

Al terminar saca una tabla por caso con su nota y su PASS%, y deja el detalle en evals/results/<fecha>/.


3. Repetir la prueba: intervalos de confianza

El modelo no responde siempre igual, así que cada caso se ejecuta $n$ veces y lo que se mide es la tasa de aprobado: $k$ aprobados de $n$. Esa tasa es una estimación de la de verdad, y con pocas ejecuciones, muy imprecisa. Lo que dice cuánto te puedes fiar es su intervalo de confianza al 95 %: el rango en el que está la tasa real.

3 de 3 no es un 100 %. Con 3 ejecuciones, todo aprobado solo dice que la tasa real está entre el 44 % y el 100 %: la skill podría fallar más de la mitad de las veces. Y 3 es lo que hace claude plugin eval por defecto.

El intervalo de Wilson

Con $\hat p = k/n$ y $z = 1{,}96$ para el 95 %:

$$\frac{\hat p + \frac{z^2}{2n} \pm z\sqrt{\frac{\hat p(1-\hat p)}{n} + \frac{z^2}{4n^2}}}{1 + \frac{z^2}{n}}$$

No uses la fórmula de clase de estadística, $\hat p \pm z\sqrt{\hat p(1-\hat p)/n}$: con todo aprobado o todo suspendido da un intervalo de anchura cero, justo en los casos más frecuentes en un eval.

Con este script se calcula a partir de lo que da claude plugin eval:

import sys
from math import sqrt

def wilson(k, n, z=1.96):
    p = k / n
    centro = (p + z*z/(2*n)) / (1 + z*z/n)
    margen = z * sqrt(p*(1-p)/n + z*z/(4*n*n)) / (1 + z*z/n)
    return centro - margen, centro + margen

k, n = int(sys.argv[1]), int(sys.argv[2])
bajo, alto = wilson(k, n)
print(f"{k}/{n}: {bajo:.0%} – {alto:.0%}")

python3 intervalo.py 9 10

9/10: 60% – 98%

Lo que da según el número de ejecuciones:

Resultado Intervalo al 95 %
3/3 44 % – 100 %
5/5 57 % – 100 %
10/10 72 % – 100 %
20/20 84 % – 100 %
9/10 60 % – 98 %
5/10 24 % – 76 %

¿Aporta la skill? Que no se solapen

La skill aporta en un caso cuando el intervalo con ella y el intervalo sin ella no se solapan. Si se solapan, la diferencia puede ser suerte:

Ejecuciones Con la skill Sin la skill ¿Aporta?
10 9/10 (60 % – 98 %) 5/10 (24 % – 76 %) No se sabe: se solapan
30 27/30 (74 % – 97 %) 15/30 (33 % – 67 %) Sí

Cuántas veces ejecutar:

claude plugin eval .claude/skills/k-servicios --scaffold --runs 30 --case update-sin-filtrar

Si con 30 ejecuciones los intervalos siguen solapándose, la skill no aporta en ese caso, o aporta tan poco que no compensa los tokens que añade.

Ejercicios

Ejercicio 1

Crea la carpeta evals/ de la skill do-crear-test del ejercicio 3 del tema 20 con tres casos: dos que salgan de fallos que hayas visto al pedir tests sin la skill y uno negativo. Rellena la tabla:

Caso Prompt Evaluador del resultado Evaluador del proceso

Ejercicio 2

Calcula el intervalo de confianza al 95 % de cada resultado y di si la skill aporta en cada caso:

Caso Con la skill Intervalo Sin la skill Intervalo ¿Aporta?
test-con-id 10/10 6/10
test-sin-mock 8/10 7/10
test-negativo 3/3 3/3

Ejercicio 3

Ejecuta los casos del ejercicio 1 con --runs 10. Rellena la tabla y repite con --runs 30 los casos en los que los intervalos se solapen:

Caso Ejecuciones Con la skill Intervalo Sin la skill Intervalo ¿Aporta?