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:
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:
name, y hay un solo SKILL.md.description existe y no pasa de 1.024 caracteres..md de más de 100 líneas tiene índice; ninguno pasa de 2.500 palabras.User Input, Outline, STOP conditions y Quick Guidelines con 10 puntos como máximo.
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.
k-skill, con todas las reglas del tema 20. Así el agente las aplica al crear o revisar cualquier skill del proyecto.
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
prompt.md: lo que pediría un usuario, sin nombrar la skill: si la nombra, se prueba que obedece, no que se activa. En el frontmatter, cuántas vueltas puede dar el agente (max_turns) y cuántas veces se ejecuta el caso (runs).
--- 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.
fixture.sh: deja los ficheros mínimos de partida, aquí un PedidoServiceImpl.java sin update. Lo enlaza case.yaml:
schema_version: "1.1" name: update-sin-filtrar context: scaffold_script: fixture.sh
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:
regex sobre ficheros largos; llm solo para salidas cortas y con un PASS y un FAIL concretos.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ó.
evals/. Si ve los criterios, aprueba por imitación. Ni el prompt ni fixture.sh apuntan a nada de evals/.
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
--scaffold: ejecuta el fixture.sh de cada caso. Sin él, el caso empieza sin ficheros.--runs 10: veces que se ejecuta cada caso (por defecto, 3; ver la sección siguiente).--judge-model haiku: el modelo de los evaluadores llm.--case update-sin-filtrar: solo ese caso.
Al terminar saca una tabla por caso con su nota y su PASS%, y deja el detalle en evals/results/<fecha>/.
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.
claude plugin eval por defecto.
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 % |
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:
--runs 10 o runs: 10 en el prompt.md.--case:
claude plugin eval .claude/skills/k-servicios --scaffold --runs 30 --case update-sin-filtrar
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 |
|---|---|---|---|
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 |
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? |
|---|---|---|---|---|---|---|