Agent Mentor Learn
Verificación y control de calidad: que no se cuele lo que «parece correcto» · Lección 6 de 6

Lección 6: Práctica: construye un circuito de evaluación para tu agente

Objetivos de aprendizaje:

  • Unir conjuntos de evaluación, calificación estratificada y bucles de arnés en un eval-runner.mjs ejecutable — una tarea de evaluación por bucle independiente
  • Hacer que los reportes capturen no solo la tasa de aprobación, sino también la duración por tarea, el número de llamadas a herramientas, el consumo de tokens y los errores de herramienta, y usar esas columnas para diagnosticar problemas
  • Usar este circuito para medir el impacto real de un cambio en el prompt de sistema, y atrapar un verificador demasiado estricto que rechaza salidas correctas

Requisitos: Leer las lecciones 1–5, tener a mano el bucle de arnés del curso 7 y poder ejecutarlo | Anterior: Lección 5 <<

Las primeras cinco lecciones fueron todas componentes: verifica el estado final y no paso por paso (lección 2), verificaciones deterministas primero y ojo con los verificadores demasiado estrictos (lección 3), el texto de forma libre solo admite jueces LLM (lección 4), los conjuntos de evaluación arrancan con unas veinte tareas reales (lección 5). Cada uno tiene sentido por su cuenta, pero después de cambiar tu prompt sigues sin tener algo que puedas ejecutar con un solo comando para que los números te digan «mejor o peor».

Esta lección suelda los componentes. Lo que obtienes es un archivo de trescientas líneas que corre en menos de dos segundos. La guía oficial sobre «cómo ejecutar evaluaciones» es directa: usa llamadas programáticas y directas a la API del LLM; usa bucles agénticos simples — bucles while que alternan llamadas al LLM y llamadas a herramientas — una tarea de evaluación por bucle1. Ese es exactamente el bucle guiado por stop_reason del curso 7 de esta serie. Puedes trasplantarlo tal cual.

Cómo se ve cuando corre

Guarda el eval-runner.mjs completo que aparece más adelante en esta lección y después node eval-runner.mjs:

text
=== Reporte · Prompt v1 · Verificador normalizado (corregido) ===Tarea             Calificador   Resultado    Puntuación  Llamadas  Errores    Tokens  Duración----------------------------------------------------------------------------------------------t1-total          determinista  pass               1.00         3        0     1,800     123mst2-pending        determinista  pass               1.00         1        0       995      82mst3-no-orderid     determinista  FAIL               0.00         2        1     1,550     123mst4-refund-note    juez LLM      FAIL               0.67         1        0     1,432     123mst5-missing-order  determinista  pass               1.00         1        1       966      83ms----------------------------------------------------------------------------------------------Tasa de aprobación 3/5 (60%) · Llamadas a herramientas 8 · Errores de herramienta 2 · Tokens 6,743 · Total 534ms
Casos fallidos:  [t3-no-orderid] Criterio: Con parámetros incompletos debería llamar cero herramientas y preguntar por el ID de pedido  Respuesta del agente: El estado del pedido SO-1001 es completado.  [t4-refund-note] Criterio: El monto coincide con el pedido y el tono es apropiado; pero falta el tiempo de acreditación del reembolso, el cliente se queda sin expectativa, falta 1 de 3 ítems.  Respuesta del agente: Hola, recibimos la cancelación del pedido SO-1003 (monto ¥320.00), el reembolso se devolverá al método de pago original. Lamentamos las molestias.
=== Reporte · Prompt v2 · Verificador normalizado (corregido) ===Tarea             Calificador   Resultado    Puntuación  Llamadas  Errores    Tokens  Duración----------------------------------------------------------------------------------------------t1-total          determinista  pass               1.00         3        0     1,800     124mst2-pending        determinista  pass               1.00         1        0       995      80mst3-no-orderid     determinista  pass               1.00         0        0       487      41mst4-refund-note    juez LLM      pass               1.00         1        0     1,518     124mst5-missing-order  determinista  pass               1.00         1        1       966      81ms----------------------------------------------------------------------------------------------Tasa de aprobación 5/5 (100%) · Llamadas a herramientas 6 · Errores de herramienta 1 · Tokens 5,766 · Total 450ms
=== Cambio de puntuación v1 -> v2 ===Tarea                  v1     v2  Cambio--------------------------------------------------t1-total             1.00   1.00  sin cambiot2-pending           1.00   1.00  sin cambiot3-no-orderid        0.00   1.00  fail => passt4-refund-note       0.67   1.00  fail => passt5-missing-order     1.00   1.00  sin cambio--------------------------------------------------Tasa de aprobación 3/5 -> 5/5

Esto no es un ejemplo hecho a mano — está copiado textualmente de una ejecución real en un directorio temporal. Copia el código completo y córrelo una vez; todo excepto la columna «Duración» (tiempo real de reloj, que varía con la carga de la máquina) va a coincidir hasta el milisegundo. Los números son los mismos porque el cliente stub devuelve respuestas prefijadas.

Esta salida contiene todo lo que enseña esta lección: cinco tareas ejecutando cada una su propio bucle, dos modos de calificación mezclados en una sola tabla, tasa de aprobación más cuatro columnas de diagnóstico, y la diferencia entre dos versiones colapsada en una tabla comparativa. El resto de la lección lo desempaqueta.

Las cinco piezas de un circuito

  1. Sistema bajo prueba: definiciones de herramientas, implementaciones reales de las herramientas y los datos detrás de ellas. La evaluación ejecuta «el agente usa tus herramientas para trabajar» — las herramientas son parte de lo que estás probando.
  2. Cliente stub: un messages.create falso que devuelve respuestas prefijadas en una cola fija, y que vuelve reproducible todo el circuito.
  3. Conjunto de evaluación: un arreglo tasks, cada entrada es {id, prompt, verify}. Requisito oficial: cada prompt de evaluación debería estar emparejado con una respuesta o un resultado verificable1 — un prompt sin verificador no es una tarea de evaluación, es una demo.
  4. Calificación: lo que se puede calificar de forma determinista va a una función verify; el texto de forma libre va al juez.
  5. Bucle y reporte: una tarea, un bucle while; al terminar, agrega las métricas en una tabla.

Una cosa que conviene dejar clara desde ya: las tareas no comparten messages. El messages de cada tarea arranca solo con el prompt de usuario de esa tarea, corre su propio bucle y después se descarta1. Por qué esto importa tanto — el cuestionario del medio lo va a preguntar directamente.

Pieza uno: las herramientas y los datos detrás de ellas

El sistema bajo prueba es un asistente de pedidos, cuatro pedidos, dos herramientas: search_orders (busca por nombre de cliente o estado, devuelve una lista de IDs de pedido) y get_order (consulta el detalle de un solo pedido por su ID). Dos detalles son deliberados: search_orders solo devuelve IDs de pedido sin montos, lo que obliga al agente a llamar get_order de nuevo por cada pedido — la columna «Llamadas» del reporte va a exponer este defecto de diseño. El otro: lanza un error cuando ambas condiciones de filtro vienen vacías:

Este es el error de herramienta por «parámetro inválido». La guía oficial dice que cuando estos errores se agrupan, normalmente significa que las descripciones de herramientas deberían ser más claras o necesitan ejemplos1. Lo vamos a ver en el reporte en un momento. Los errores de herramienta no son caídas — el bloque de ejecución de herramientas atrapa la excepción, la envuelve en un tool_result con is_error: true, se lo devuelve al modelo e incrementa un contador. tool_use y tool_result se emparejan por tool_use_id — esa es la base que puso el curso 7, aquí solo agregamos dos contadores.

Pieza dos: el cliente stub y el interludio de verificación

Hace falta una pausa acá, si no, ninguno de los números de abajo se sostiene.

Claude de verdad no es determinista: el mismo prompt ejecutado dos veces puede tomar caminos completamente distintos2. Eso es bueno para producción y desastroso para lecciones de demostración — corres hoy y sacas 3/5, mañana 4/5, y no puedes decir si la diferencia viene del cambio de prompt o del humor del modelo. Así que las lecciones prácticas de los cursos 8 y 9 usan todas el mismo método: cambia el modelo por un stub que devuelve respuestas prefijadas en una cola fija, para volver el comportamiento probado una variable controlada. Esto verifica la lógica de control que escribiste, no el desempeño del modelo ese día.

Cuando la cola se agota lanza un error, sin respuesta de reserva — si el bucle da una vuelta de más lo ves de inmediato: Error: [stub] cola de respuestas de v1/t2-pending agotada (1 solicitudes emitidas) (ese es el texto de error real después de que borré la última respuesta de la cola de t2), y no un end_turn falso colándose. Cada respuesta carga su propio latency_ms; el stub duerme realmente ese tiempo, así que la columna «Duración» mide cuántos turnos tomó el bucle. Cada tarea recibe un cliente fresco con su propio script; los cursores no se cruzan entre tareas.

La diferencia entre las dos versiones del prompt está fijada en las dos colas de respuestas del stub. En un escenario real cambias el prompt de sistema y el comportamiento del modelo lo sigue; acá no tengo modelo, así que preescribí SCRIPT_V1 y SCRIPT_V2, dejando que v2 devuelva respuestas distintas en dos tareas — «supongamos que el prompt v2 hace efecto y el modelo responde así» queda codificado como datos:

Usa la sintaxis de propagación para heredar de v1 y lista solo las entradas que cambian — quien lea el código ve de un vistazo el alcance de la diferencia. Este circuito se verifica a sí mismo: si los verificadores califican bien, si las métricas se registran con exactitud, si los reportes calculan correcto, si dos ejecuciones se pueden comparar. Cuando cambies a un cliente real, el circuito no cambia — solo los números empiezan a saltar.

Pieza tres: el conjunto de evaluación — cuatro normales más un caso límite

La lección 5 dijo que los conjuntos de evaluación deberían corresponder a la distribución real y cubrir casos límite3; lo oficial también advirtió contra los entornos sandbox demasiado simplistas que no ponen las herramientas bajo suficiente complejidad1. Acá solo entran cinco tareas por espacio, pero la estructura sigue la de los conjuntos de evaluación reales:

TareaQué pruebaCalificación
t1-totalAgregación de varios pasos: buscar la lista y después traer el monto de cada unoDeterminista
t2-pendingFiltrado de conjunto: los IDs de pedido deberían ser exactamente estos, ni más ni menosDeterminista
t3-no-orderidCaso límite: el usuario no dio el ID de pedidoDeterminista
t4-refund-noteTexto de forma libre: aviso de reembolso al clienteJuez LLM
t5-missing-orderTras un error de herramienta, reportar con honestidad, no fabricar datosDeterminista

t3-no-orderid merece mención especial. El prompt es «Ayúdame a revisar el estado de ese pedido» — ¿cuál? No lo especifica. El comportamiento ideal es pedir el ID de pedido en vez de adivinar uno para consultarlo. La documentación oficial es cuidadosa con este comportamiento: si el prompt del usuario no provee suficiente información para llenar todos los parámetros requeridos, Claude Opus tiene muchas más probabilidades de reconocer el parámetro faltante y pedirlo, pero ese comportamiento no está garantizado, especialmente para prompts más ambiguos y modelos menos capaces4. Los comportamientos «no garantizados» son exactamente los que un conjunto de evaluación debería cubrir — las cosas garantizadas no necesitan prueba.

El r que recibe verify contiene no solo answer sino también toolCalls, toolErrors y tokens, así que el verificador puede revisar «estado final más métricas clave» y no solo texto: t3 de hecho revisa «llamó cero herramientas», t5 revisa «reportó exactamente un error y dijo con honestidad que no lo encontró» — el «estado final primero» de la lección 2 se materializa a través de estos campos. note es para humanos; cuando una tarea falla, el reporte imprime el criterio junto a la respuesta real del agente.

Si tu tarea de la lección 5 usó el conjunto de campos {id, prompt, expected, verifier, rubricRef, tags, split}, mapéalo ahora para evitar confusiones: el verifier de la lección 5 acá se llama grader y es solo para mostrar — el tipo de calificación real lo determina si esta tarea tiene una función verify o judge: true. Las afirmaciones declarativas de expected acá se escriben directamente dentro del cuerpo de la función verify (las afirmaciones de cada tarea se ven distintas; escribirlas como funciones es más simple que diseñar un formato universal de afirmaciones). rubricRef queda incorporado como JUDGE_PROMPT, ya que la suite entera tiene un solo caso de juez. tags y split se omiten por brevedad; la disciplina del conjunto reservado se repite como siempre en la sección «Alcance». Tu JSON de la lección 5 no quedó obsoleto — es la versión declarativa de este arreglo TASKS. Avanzar significa traducir cada afirmación a una función.

Pieza cuatro: calificación estratificada, primero lo determinista

Los métodos de calificación tienen un orden: la calificación basada en código es la más rápida y la más confiable, escala extremadamente bien pero le falta matiz para juicios complejos; la calificación basada en LLM es rápida y flexible, puede manejar juicios complejos, pero primero prueba que es confiable y después escala; la calificación humana es la más flexible y de mayor calidad pero lenta y cara, evítala si es posible3.

Así que la regla es: lo que se puede calificar por código nunca va a un juez. Cuatro de las cinco tareas de acá usan verify; solo t4-refund-note, ese pedazo de texto de forma libre, va al juez — «¿este párrafo se le puede mandar a un cliente?» no se responde con coincidencia de cadenas. La forma del juez sigue a la lección 4: rúbrica fijada en tres ítems, formato de salida fijado en JSON, primero razonar y después puntuar:

Cada punto tiene su fuente: haz que el juez primero razone y después puntúe, y luego descarta el razonamiento — mejora la calidad de la calificación, en especial para tareas que requieren juicios complejos3; la salida debería ser empírica o específica, no una evaluación puramente cualitativa3; y «una sola llamada al LLM, un solo prompt, salida de 0.0 a 1.0 más un aprobado/fallido» es la combinación que lo oficial encontró más consistente y más alineada con los juicios humanos después de probar varios esquemas de juez en su sistema multiagente de investigación2.

El juez acá también es un stub: la respuesta de v1 no tiene el tiempo de acreditación, dos de tres ítems dan 0.67 y se califica fallido; v2 lo agregó, los tres aciertan y dan 1.00, calificado aprobado. La puntuación es autoconsistente con la rúbrica — tres ítems binarios promediados solo pueden caer en 0, 0.33, 0.67 o 1.00; una puntuación de 0.85 significaría que el juez no siguió la aritmética de la rúbrica. El juez mismo quema tokens; su consumo se suma a los tokens de esa tarea, y por eso t4 llama una sola herramienta pero sus tokens no son bajos.

Una disciplina más de la lección 4: el modelo que trabajó no debería calificarse a sí mismo. Lo oficial dice que hagas que una instancia fresca del modelo intente refutar el resultado — el que hace el trabajo no es el que lo califica5. En código: el juez usa su propio cliente, su propio prompt de sistema, su propio arreglo de messages, solo ve el prompt de la tarea y la respuesta a calificar, y no ve la transcripción de llamadas a herramientas del agente.

Pieza cinco: bucle y reporte

El bucle es el bucle del curso 7 textual, con el esqueleto sin cambios — solo se agregaron el model y el max_tokens que la API real exige (el stub los ignora), y después se envolvió con contadores:

messages es una variable local dentro de runTask; la función retorna y desaparece. Esa es la implementación entera de «las tareas no comparten contexto» — no hace falta ningún mecanismo extra, basta con no sacarla de ahí.

Para las métricas, la lista de verificación oficial es: más allá de la exactitud de alto nivel, recolecta también el tiempo total de ejecución de cada llamada a herramienta y de cada tarea, el número total de llamadas a herramientas, el consumo total de tokens y los errores de herramienta1. Las columnas de la tabla del reporte siguen exactamente esa lista. La tasa de aprobación solo te dice «si aprobó», estas columnas te dicen «cómo aprobó» — una tarea que aprueba llamando doce herramientas y una que aprueba con dos llamadas son dos niveles de calidad distintos. Estas columnas también se autodocumentan: muchas llamadas a herramientas redundantes normalmente sugieren que los parámetros de paginación o de límite de tokens necesitan ajuste; muchos errores de herramienta por parámetros inválidos normalmente sugieren que las descripciones de herramientas podrían ser más claras o necesitan mejores ejemplos1. Los ejercicios van a usar esto directamente.

Que el reporte sea legible para humanos tiene valor intrínseco. La sugerencia oficial es: haz que Claude muestre evidencia en vez de afirmaciones de éxito — la salida de las pruebas, el comando que ejecutó y lo que devolvió, o una captura de pantalla del resultado; revisar evidencia es más rápido que volver a ejecutar la verificación tú mismo, y funciona para sesiones que no estuviste mirando5. Esta tabla del reporte es esa evidencia — pégala en la descripción de un PR o mándasela a un colega, y puede juzgar sin volver a ejecutar nada. (El único detalle al imprimir es que los caracteres CJK de ancho completo cuentan como ancho 2 y un padEnd crudo desalinea — el código tiene un pad consciente del ancho.)

El eval-runner.mjs completo

Cópialo y guárdalo como eval-runner.mjs; node eval-runner.mjs lo ejecuta directo. Sin dependencias, sin package.json, Node 18+ (usa await de nivel superior, así que la extensión debe ser .mjs).

Recuperando la trampa de la lección 3: verificadores demasiado estrictos

La lección 3 cubrió una trampa, en las palabras exactas de lo oficial: evita los verificadores demasiado estrictos que rechazan respuestas correctas por diferencias espurias como el formato, la puntuación o formulaciones alternativas válidas1. Suena de sentido común pero es casi inevitable en código, porque los verificadores demasiado estrictos son los más fáciles de escribir.

El circuito tiene uno incrustado. t1-total tiene dos versiones de verificador; la antigua es pass: r.answer.includes("1280.00") — parece a prueba de balas: la respuesta correcta es 1280.00, así que revisa si la respuesta contiene esa cadena. Ejecuta node eval-runner.mjs --strict-verify (abajo se pega solo el reporte de v1; el reporte de v2 y la tabla de diferencias se imprimen como siempre):

text
=== Reporte · Prompt v1 · Verificador estricto (antiguo, sin normalizar) ===Tarea             Calificador   Resultado    Puntuación  Llamadas  Errores    Tokens  Duración----------------------------------------------------------------------------------------------t1-total          determinista  FAIL               0.00         3        0     1,800     123mst2-pending        determinista  pass               1.00         1        0       995      82mst3-no-orderid     determinista  FAIL               0.00         2        1     1,550     124mst4-refund-note    juez LLM      FAIL               0.67         1        0     1,432     122mst5-missing-order  determinista  pass               1.00         1        1       966      83ms----------------------------------------------------------------------------------------------Tasa de aprobación 2/5 (40%) · Llamadas a herramientas 8 · Errores de herramienta 2 · Tokens 6,743 · Total 534ms
Casos fallidos:  [t1-total] Criterio: La respuesta debe contener la cadena literal 1280.00  Respuesta del agente: El cliente Qiming Tech tiene 2 pedidos completados en agosto de 2026 (SO-1001, SO-1002), por un total de ¥1,280.00.  [t3-no-orderid] Criterio: Con parámetros incompletos debería llamar cero herramientas y preguntar por el ID de pedido  Respuesta del agente: El estado del pedido SO-1001 es completado.  [t4-refund-note] Criterio: El monto coincide con el pedido y el tono es apropiado; pero falta el tiempo de acreditación del reembolso, el cliente se queda sin expectativa, falta 1 de 3 ítems.  Respuesta del agente: Hola, recibimos la cancelación del pedido SO-1003 (monto ¥320.00), el reembolso se devolverá al método de pago original. Lamentamos las molestias.

Esto también sale de una ejecución real. Mira el detalle de t1-total: el agente respondió «por un total de ¥1,280.00» — monto correcto, pedidos correctos, redacción normal. Su único crimen es poner una coma como separador de miles entre el 1 y el 280, así que includes("1280.00") devuelve false y una respuesta completamente correcta se califica fallida.

En este punto arregla el verificador, no el agente. Los reportes solo te dicen «t1 fallido», no te dicen de quién es la culpa; la forma de saberlo es leer las palabras reales del agente en el detalle — y para eso exactamente es que los reportes imprimen la respuesta cruda. El arreglo es la normalización. La descripción oficial de la coincidencia exacta ya incluye este paso: las evaluaciones por coincidencia exacta miden si la salida del modelo coincide con una respuesta correcta predefinida, típicamente después de normalizar los espacios en blanco y las mayúsculas3. Los escenarios con montos necesitan más lavado — símbolos de moneda, separadores de miles, unidades — así que el verificador corregido lava primero el ruido, extrae los números y después compara numéricamente:

Quita --strict-verify y ejecuta otra vez; t1-total pasa de 0.00 a 1.00, y la línea base de v1 sube de 2/5 de vuelta a 3/5 — y entre medio, el agente no cambió ni un carácter, y la cola de respuestas del stub tampoco cambió ni un carácter. La puntuación cambió pero el sistema bajo prueba no — esa es la prueba de fuego del «problema del verificador».

Un comentario al margen sobre el alcance: normalizar no es «cuanto más flojo mejor». Afloja hasta «si parece contener 1280, aprueba», y el agente que responde «total de 1280 pedidos, monto desconocido» también aprueba. Los verificadores deberían pararse en «deja pasar las diferencias irrelevantes, bloquea los errores sustantivos» — y el único método para encontrar esa posición es probar con respuestas reales.

Cambia un solo lugar del prompt y mira moverse la puntuación

Circuito calibrado, listo para trabajo real. Cambié un solo lugar — el prompt de sistema, agregándole dos reglas después de v1:

Esas dos no son inventadas; salen de leer los «Casos fallidos» del reporte de v1: t3 falla porque adivinó un ID de pedido con parámetros incompletos, t4 recibió descuento por faltarle el tiempo de acreditación. El reporte dice qué, tú cambias eso — esa es la diferencia más concreta entre tener circuito y no tenerlo. Sin circuito, después de cambiar el prompt puedes echarle un ojo a la salida y sentir que «parece mejor»; con circuito, «cuál mejoró, cuál se quedó igual, algo retrocedió» son tres líneas de números.

Vuelve a ejecutar: la tabla de diferencias es el último segmento de la salida del comienzo — tasa de aprobación de 60% a 100%, dos tareas pasan de fallido a aprobado, las otras tres no se mueven. Esa última media oración importa tanto como la primera: dice que este cambio no rompió lo que ya funcionaba. Sin circuito, después de cambiar el prompt solo miras la salida una vez y piensas «se ve mejor»; con circuito, «cuál mejoró / cuál igual / algo retrocedió» son tres filas de números.

La formulación oficial para esto es: con evaluaciones puedes medir con mucha más confianza el impacto de tu ingeniería de prompts; incluso refinamientos pequeños a las descripciones de herramientas pueden rendir mejoras dramáticas1. Acá también hay una ganga para agarrar: en el desarrollo temprano de agentes los cambios tienden a tener un impacto dramático porque todavía abunda la fruta al alcance de la mano — un retoque del prompt podría subir la tasa de éxito de 30% a 80%; con tamaños de efecto así de grandes puedes detectar los cambios con apenas unos pocos casos de prueba2. Ahora tienes solo cinco tareas — eso no es un déficit, es el punto de partida.

Mira otra vez las columnas de métricas: las llamadas a herramientas de v2 bajaron de 8 a 6, los errores de herramienta de 2 a 1, y los tokens bajaron casi mil, porque t3 ya no adivina a ciegas para llamar herramientas. El mismo cambio mejoró simultáneamente la exactitud y el costo — este tipo de cosa solo se vuelve visible cuando registras estas columnas juntas.

Alcance: qué administra este circuito y qué no

Qué administra: un agente, un lote de tareas, una ejecución en tu máquina, un reporte legible para humanos.

Cambiar a un modelo real — la estructura del circuito no cambia. Reemplaza stubClient(...) por el cliente real de @anthropic-ai/sdk; el bucle while de runTask no cambia ni una línea — ya está escrito con la forma de stop_reason / tool_use / tool_result de la API real; los parámetros requeridos model y max_tokens ya están ahí (el stub los ignora, el cliente real los usa). Después del cambio dos cosas se mueven: las puntuaciones van a temblar porque los agentes no son deterministas entre ejecuciones ni siquiera con prompts idénticos2, así que no leas de más una sola ejecución; y correr una ronda cuesta dinero y tiempo, con cinco tareas da igual, pero con doscientas ya conviene pensar en concurrencia y costo.

Qué no administra: enganchar las evaluaciones a CI, ejecutarlas en cada commit, compararlas contra versiones históricas, bloquear merges cuando las puntuaciones bajan de cierto umbral — todas son prácticas de ingeniería comunes y sí funcionan bien, pero esta lección no las desarrolla. El Nivel 2 de los ejercicios te va a llevar por «comparar dos reportes», y el resto de la orquestación es trabajo de tu CI.

Una disciplina más de la lección 5 para repetir: no ajustes contra el conjunto reservado. Sigues los reportes para cambiar prompts; después de varias rondas las puntuaciones definitivamente van a subir, pero la subida podría ser solo «puntuaciones en estas cinco tareas». La práctica oficial es apoyarse en conjuntos de prueba reservados para asegurar que no hay sobreajuste a las evaluaciones «de entrenamiento»1. Así que en un montaje real las tareas deberían dividirse en dos pilas: una corre a diario para orientarte, la otra queda bajo llave y solo se abre cuando piensas «esta versión debería funcionar» — las puntuaciones de la primera pila son navegación, las de la segunda son veredicto.

Último recordatorio viejo: las evaluaciones automáticas se van a perder cosas. Los evaluadores humanos siempre dan con casos límite que las evaluaciones no ven — alucinaciones ante consultas inusuales, fallos sistémicos, sesgos sutiles de selección de fuentes2. Que el circuito corra sin problemas no significa que dejes de usarlo tú mismo.

💻 Ejercicios

Resumen

  • La forma estándar de ejecutar evaluaciones son llamadas programáticas y directas a la API más bucles agénticos simples — una tarea de evaluación por bucle; las tareas no comparten messages, o el contexto de la tarea anterior contamina a la siguiente y los resultados dejan de ser comparables1.
  • Cada prompt de evaluación debería estar emparejado con un resultado verificable; los verificadores forman un espectro que va de la comparación exacta de cadenas a pedirle al modelo que juzgue — lo que se puede calificar por código nunca va a un juez, porque la calificación basada en código es la más rápida, la más confiable y escala extremadamente bien1 3.
  • El texto de forma libre va al juez; la forma es una sola llamada, un solo prompt, salida de 0.0 a 1.0 más aprobado/fallido; la rúbrica debe razonar primero y puntuar después, con el formato de salida fijado2 3.
  • Más allá de la tasa de aprobación, los reportes deben registrar la duración de la tarea, el número de llamadas a herramientas, el consumo de tokens y los errores de herramienta; estas columnas se autodocumentan — las llamadas redundantes apuntan a parámetros de paginación/volumen de retorno que necesitan ajuste, y los errores de parámetro inválido apuntan a descripciones de herramientas que necesitan claridad1.
  • Los verificadores demasiado estrictos rechazan respuestas correctas: el formato, la puntuación y formulaciones distintas pero razonables pueden tumbar una comparación literal; normaliza antes de la coincidencia exacta1 3. La puntuación cambió pero el sistema bajo prueba no — es culpa del verificador.
  • Con un circuito, el impacto de un cambio de prompt se vuelve medible; incluso refinamientos pequeños pueden rendir mejoras dramáticas; los tamaños de efecto tempranos son grandes y unos pocos casos bastan para ver diferencias1 2. El reporte mismo es evidencia que otros pueden revisar, más rápido que volver a ejecutar la verificación tú mismo, y sirve para sesiones que no estuviste mirando5.
  • Sigue los reportes para cambiar prompts y las puntuaciones van a subir, pero la subida podría ser solo sobre este lote de tareas; guarda bajo llave el conjunto reservado para evitar el sobreajuste1. Las evaluaciones automáticas tienen puntos ciegos; los evaluadores humanos siguen atrapando casos límite que las evaluaciones no ven2.

Después de terminar este curso

Mirando hacia atrás, el hilo principal es en realidad corto. La lección 1 separó «parece terminado» de «está terminado» — sin verificaciones ejecutables, «parece terminado» es la única señal disponible y tú te conviertes en el paso de verificación5. La lección 2 fijó qué verificar: los agentes podrían recorrer caminos razonables completamente distintos hacia el mismo objetivo, así que evalúa el estado final, no revises la trayectoria paso por paso2. La lección 3 convirtió las «verificaciones» en verificadores deterministas ejecutables que emiten aprobado/fallido, y también advirtió que los verificadores demasiado estrictos rechazan respuestas correctas1. La lección 4 se ocupó del texto de forma libre — rúbricas, formato de salida, y que el modelo que trabajó no debería calificarse a sí mismo2 5. La lección 5 resolvió «con cuántos casos verificar»: unas veinte tareas reales alcanzan para arrancar, no esperes a acumular cientos para empezar2. Esta lección soldó las primeras cinco en un archivo de trescientas líneas.

Ese archivo no es complejo, corre en menos de dos segundos, pero lo que cambia es concreto: desde hoy, cuando cambies una versión del prompt, no dependes de «leer unos párrafos de salida y sentir que está mejor» para juzgar — ejecutas un comando y la tabla de diferencias de v1 a v2 habla por ti, igual que esta vez, cuando t3 y t4 se pusieron en verde mientras las otras tres se quedaron sin cambio. La próxima vez que tu agente diga «listo», tienes dos comandos y un código de salida para verificar esa afirmación.

La próxima vez que tu agente diga «listo», tienes un circuito ejecutable para verificarlo.

Footnotes

  1. Writing effective tools for agents — with agents — Anthropic Engineering — https://www.anthropic.com/engineering/writing-tools-for-agents 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

  2. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system 2 3 4 5 6 7 8 9 10 11

  3. Define success criteria and build evaluations — Claude API documentation — https://platform.claude.com/docs/en/test-and-evaluate/develop-tests 2 3 4 5 6 7 8

  4. Tool use with Claude — Claude API documentation — https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview

  5. Best practices for Claude Code — Claude Code official documentation — https://code.claude.com/docs/en/best-practices 2 3 4 5

Ejercicios

01

Sin código. Vuelve a los dos reportes del comienzo de la lección (línea base v1 y v2 posterior al cambio), cuyas líneas de resumen son:

Nivel 1: Leer reportes, sin apurarse a cambiar código
text
v1: Tasa de aprobación 3/5 (60%)  · Llamadas a herramientas 8 · Errores de herramienta 2 · Tokens 6,743v2: Tasa de aprobación 5/5 (100%) · Llamadas a herramientas 6 · Errores de herramienta 1 · Tokens 5,766

Contra las dos tablas completas responde tres preguntas, de tres a cinco oraciones cada una:

  1. t1-total aprueba en ambos reportes pero tiene la cuenta de llamadas a herramientas más alta, 3. ¿Qué problema indica esto? ¿Qué debería cambiarse?
  2. v1 tiene 2 errores de herramienta, v2 tiene 1. ¿Son estos dos errores la misma clase de problema? ¿Qué significa cada uno, debería arreglarse cada uno?
  3. La lección tiene un tercer reporte (el de --strict-verify) donde t1-total da 0.00. La misma tarea, un reporte 0.00 y otro 1.00 — ¿cómo distingues que esta diferencia de puntuación es problema del verificador y no del agente?
Criterios de finalización · marcado local
02

Escribe código, debe ser ejecutable. Agrégale dos cosas a eval-runner.mjs:

Nivel 2: Agregarle al circuito la «comparación de dos ejecuciones»
  1. Persistencia del reporte: agrega writeJsonAtomic(file, obj), usando la escritura atómica del curso 9 (escribir primero .tmp y después rename) para guardar el reporte de una ejecución como JSON. La línea de comandos admite --version v1 --out reports/v1.json.
  2. Escribe un compare.mjs: lee dos JSON de reporte, imprime la diferencia de puntuación por tarea (puntuación base, puntuación nueva, delta, estado), imprime al final el cambio de la tasa de aprobación; si alguna tarea pasa de aprobado a fallido, imprime un resumen a stderr y sale con código distinto de cero.

Ejecuta estos cuatro comandos y pega la salida:

text
node eval-runner.mjs --version v1 --out reports/v1.jsonnode eval-runner.mjs --version v2 --out reports/v2.jsonnode compare.mjs reports/v1.json reports/v2.json   # debería salir con 0node compare.mjs reports/v2.json reports/v1.json   # debería salir con 1

(Invertir el orden de los parámetros simula «la versión nueva es peor que la línea base», verificando que la ruta de salida distinta de cero de verdad funciona.)

Criterios de finalización · marcado local