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:
- skill-creator: la skill de Anthropic para crear y evaluar skills.
- A statistical approach to model evaluations: por qué un resultado de eval sin su intervalo de confianza no dice nada.
- Wilson score interval: el intervalo que se usa aquí para la tasa de aprobado.
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:
- La carpeta y el H1 se llaman igual que el
name, y hay un soloSKILL.md. - La
descriptionexiste y no pasa de 1.024 caracteres. - La tabla índice está antes de cualquier otra sección y no hay huérfanos ni rutas a ficheros que no existen.
- Nombres en kebab-case, sin repetir la carpeta y sin carpetas fuera de la tabla.
- Todo
.mdde más de 100 líneas tiene índice; ninguno pasa de 2.500 palabras. - Las de procedimiento tienen
User Input,Outline,STOP conditionsyQuick Guidelinescon 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.
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:
- ¿Se activa? Que se cargue solo dice que el modelo la encontró, no que la siguiera.
- ¿Aporta? El caso se ejecuta con la skill y sin ella, y la diferencia de nota es lo que aporta. Si sin ella también aprueba, sobra para ese caso.
.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
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í unPedidoServiceImpl.javasinupdate. Lo enlazacase.yaml:
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:
- Al menos 3 casos, sacados de fallos reales del modelo sin la skill, y uno negativo: un prompt parecido en el que la skill no debe activarse.
- Al menos 2 evaluadores por caso: uno sobre el resultado y otro sobre el proceso.
- Deterministas primero:
regexsobre ficheros largos;llmsolo para salidas cortas y con un PASS y un FAIL concretos. - El juez, de otro modelo que el evaluado: un modelo tiende a aprobar lo suyo.
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/.
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
--scaffold: ejecuta elfixture.shde 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 evaluadoresllm.--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>/.
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.
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:
- 10 por caso para empezar:
--runs 10oruns: 10en elprompt.md. - 30 en los casos que se solapan, solo esos, con
--case:
claude plugin eval .claude/skills/k-servicios --scaffold --runs 30 --case update-sin-filtrar
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? |
|---|---|---|---|---|---|---|
