Lección 3: Verificadores deterministas: solo cuentan las comprobaciones que dan aprobado/fallido
Objetivos de aprendizaje:
- Ordenar los verificadores candidatos por «el más rápido, el más confiable, el más escalable» y elegir el correcto para una salida específica
- Escribir un script de verificación determinista que devuelva un aprobado/fallido en una forma que el agente pueda leer y sobre la que pueda iterar
- Reconocer los falsos negativos donde un verificador demasiado estricto rechaza salidas correctas, arreglarlos con normalización, y entender dónde tocan techo las comprobaciones deterministas
Requisitos: Lecciones 1 y 2 (sin una comprobación que se pueda ejecutar, «parece terminado» es la única señal; primero el estado final, el proceso como respaldo; los criterios de éxito deben ser medibles) | Anterior: << Lección 2 | Siguiente: Lección 4 >>
De «qué verificar» a «con qué verificarlo»
Al final de la Lección 2 deberías tener escrito un criterio de éxito concreto. Toma el ejemplo que usa esta lección: el agente lee un lote de CSV de ventas y los agrega en un report.json con un título, ítems por canal y un total. La Lección 2 te enseñó a plantear el criterio como estado final —el archivo existe, los campos están presentes, el total es igual a la suma de los count de los ítems— y no como comprobaciones de proceso turno por turno del tipo «lee el archivo, después calcula la suma, después escribe el archivo».
Ya tienes el criterio. La pregunta siguiente es práctica: ¿con qué lo compruebas?
Puedes mirarlo a ojo. Puedes hacer que otro modelo lo lea y dé su devolución. O puedes escribir una docena de líneas de Node que cargue el JSON, sume los count y salga con un código distinto de cero si no coinciden. Los tres llegan a una conclusión, pero el costo y la confiabilidad difieren muchísimo.
La documentación oficial ofrece un principio de ordenamiento: elige el método de calificación más rápido, más confiable y más escalable1. Con esa vara, las tres categorías quedan en un orden claro:
- Calificación basada en código: la más rápida y la más confiable, extremadamente escalable; su debilidad es que le falta matiz para los juicios complejos que necesitan menos rigidez de reglas1.
- Calificación mediante LLM: rápida y flexible, escalable y apta para el juicio complejo, pero primero pruébala para asegurar su confiabilidad y después escala1. (Eso es la Lección 4.)
- Calificación humana: la más flexible y de más alta calidad, pero lenta y cara; evítala si es posible1.
«Confiable» aquí significa misma entrada, mismo veredicto siempre. La calificación basada en código queda primera no porque sea inteligente, sino porque es predeciblemente tonta: no te va a aprobar hoy por buena onda y reprobar mañana por una formulación. En computación, los sistemas deterministas producen la misma salida cada vez que reciben entradas idénticas, mientras que los sistemas no deterministas —como los agentes— pueden generar respuestas variadas incluso con las mismas condiciones iniciales2. Los «verificadores deterministas» que cubre esta lección son esa clase de comprobación predeciblemente tonta: una cosa determinista que evalúa una cosa no determinista.
Un principio relacionado para diseñar tareas de evaluación: estructura las preguntas de modo que permitan la calificación automatizada (por ejemplo, opción múltiple, coincidencia de cadenas, calificadas por código, calificadas por LLM)1. Donde tengas una oportunidad de automatizar, tómala.
El menú de verificadores: cualquier cosa que devuelva una señal
«Verificador» suena a framework especializado, pero la vara está mucho más baja. La definición de la documentación oficial es casi brusca: la comprobación es cualquier cosa que devuelva una señal que Claude pueda leer en la conversación: una suite de pruebas, el código de salida de una compilación, un linter, un script que compare la salida con un fixture, o una captura de pantalla del navegador comparada con un diseño3.
Desarmemos esa lista. Ejecutar npm test y que salga todo en verde es un aprobado, perfecto para los entregables de código: las soluciones de código son verificables mediante pruebas automatizadas4. Los códigos de salida de una compilación son lo más fácil: la cadena de herramientas ya escribió las aserciones por ti. Un linter por sí solo no alcanza, pero es excelente como piso de «errores que no deberías cometer». Un script de comparación coteja la salida de esta ejecución con un archivo fixture que preparaste (una muestra que se sabe buena), apropiado cuando la salida es estable y el formato es fijo. La comparación de capturas de pantalla es para cuando estás retocando estilos de frontend.
Una nota de práctica de ingeniería: los compiladores y los verificadores estáticos de tipos (como tsc --noEmit) se usan seguido de esta manera en los proyectos reales, porque también emiten códigos de salida y ubicaciones de error legibles. Eso es un agregado mío, no está en la lista oficial: no lo tomes como avalado oficialmente.
Estos métodos no son mutuamente excluyentes. Los verificadores forman un espectro: en un extremo está «la coincidencia exacta de cadenas con un fixture», en el otro extremo está «pedirle a Claude que juzgue»2. Esta lección cubre la mitad izquierda; la Lección 4 va hacia la derecha.
Forma mínima: output == golden_answer
El borde izquierdo del espectro se ve así1:
Solo una comprobación de igualdad. A esto se lo llama coincidencia exacta. Mide si la salida del modelo coincide con una respuesta correcta predefinida, normalmente después de normalizar los espacios en blanco y las mayúsculas y minúsculas; es una métrica simple y sin ambigüedad, perfecta para las tareas con respuestas categóricas y bien delimitadas, como el análisis de sentimiento (positivo, negativo, neutral)1.
La «normalización» aquí es simple: antes de comparar, borra las diferencias que no cargan significado. En código:
Vale la pena quedarse mirando el resultado de la segunda llamada. trim() y toLowerCase() rescatan los saltos de línea y las mayúsculas, pero no ese punto. Hasta dónde debería llegar la normalización depende de qué diferencias son irrelevantes para tu tarea, un juicio que ninguna biblioteca puede hacer por ti. La sección de la trampa, más adelante en esta lección, trata de qué pasa cuando ese juicio sale mal.
Haz que la salida del verificador sea legible
Esa definición traía una cláusula que se suele saltear: la señal tiene que ser algo que Claude pueda leer en la conversación3. Esa cláusula determina cómo debería escribir su salida tu script de verificación. Compara dos mensajes de falla:
El primero le dice al agente «estás mal» y lo deja adivinando. El segundo le dice qué campo falló, qué se esperaba y qué había en realidad: en el turno siguiente puede ir a arreglar ese número directo. Mismo aprobado/fallido, un orden de magnitud de diferencia en información. Otra pieza de la guía oficial dice lo mismo: haz que Claude muestre evidencia en lugar de afirmar el é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 las sesiones que no estuviste mirando3. Tu script de verificación es el productor de esa evidencia. Si es vago, la evidencia es vaga.
Dos reglas prácticas: usa el código de salida 0 para el aprobado y uno distinto de cero para el fallido (CI y el && de la shell lo pueden usar directo). En stdout, una falla por línea, indicando «qué campo, qué se esperaba, qué se obtuvo».
Con un aprobado/fallido, el comportamiento del agente cambia
Durante la ejecución, los agentes necesitan la «ground truth» del entorno en cada paso (por ejemplo, los resultados de las llamadas a herramientas o la ejecución de código) para evaluar su progreso4. Sin un verificador, la única señal de progreso que puede obtener es el párrafo que acaba de escribir: si cree que terminó, terminó. Con un verificador, el entorno contiene una fuente de hechos independiente de su propio juicio.
Así que la cadena de comportamiento cambia: dale a Claude algo que produzca un aprobado o un fallido y el bucle se cierra solo; Claude hace el trabajo, ejecuta la comprobación, lee el resultado e itera hasta que la comprobación pasa3. Dicho de otra manera: los agentes pueden iterar sobre las soluciones usando los resultados de las pruebas como retroalimentación4.
En la clase de bucle de arnés que escribiste a mano en el curso 7 de esta serie, la implementación consiste en exponer la comprobación como una herramienta:
La salida del verificador vuelve a fluir a la conversación vía tool_result. El modelo lee total no coincide: declarado 48, la suma de los count de items da 50 y en el turno siguiente va a arreglarlo. Tú participaste en cero pasos.
Dos extensiones cómodas. Primero, las comprobaciones no tienen que ir solo al final. La Lección 2 cubrió cómo los flujos de trabajo complejos se pueden dividir en puntos de control discretos donde deberían haber ocurrido cambios de estado específicos, en lugar de validar cada paso intermedio5. Esos puntos de control de verificación son lugares naturales para aterrizar comprobaciones deterministas, como «después de leer todos los CSV, la cantidad de filas debería ser igual a la suma de las cantidades de filas de cada archivo». La misma retrospectiva también menciona combinar la adaptabilidad de los agentes con salvaguardas deterministas como la lógica de reintentos y los puntos de control regulares5 (ten en cuenta que ahí «puntos de control» se refiere a la clase de puntos de control de recuperación con guardado de estado del curso 9 de esta serie).
Segundo, una clase de verificación se puede mover aguas arriba, a la capa de la API. Agrega strict: true a tus definiciones de herramientas para asegurar que las llamadas a herramientas de Claude siempre coincidan exactamente con tu esquema6. El esquema es tu declaración de la estructura de parámetros.
Con esa sola línea, los problemas estructurales como «typo en el nombre del campo» o «count pasado como cadena» dejan de ser «algo para lo que escribes código de comprobación» y pasan a ser una garantía a nivel de plataforma. Las herramientas son un contrato entre los sistemas deterministas y los agentes no deterministas2, y strict es la manera de escribir ese contrato dentro de la interfaz.
Pero vigila la estructura, no la semántica. Si total de verdad es igual a la suma de todos los valores de count, el esquema no tiene nada que decir: esa parte la sigues verificando tú.
La trampa: los verificadores demasiado estrictos rechazan salidas correctas
Esta es la manera más común en que fallan las comprobaciones deterministas, y muchas veces no te vas a dar cuenta cuando pasa.
La guía oficial para las evaluaciones de herramientas es filosa: evita los verificadores demasiado estrictos que rechazan respuestas correctas por diferencias espurias como el formato, la puntuación o formulaciones alternativas válidas2.
«Diferencias espurias» es la frase clave. La misma respuesta correcta podría llevar un espacio al final, podría escribir positive como Positive, podría tener dos espacios entre palabras en lugar de uno. Estas diferencias no significan nada para la tarea, pero para un verificador que compara byte por byte son catastróficas. La dirección de la falla también es insidiosa: no deja pasar errores, castiga salidas correctas; eso es un falso negativo.
Acá va un choque concreto con su arreglo. El título del fixture es Resumen de canales Q1 2026. El report.json del agente tiene todos los datos correctos, solo que con espacios antes y después del título y dos espacios entre palabras. El verificador estricto ingenuo se ve así:
Al ejecutarlo, la salida real es:
Un reporte con el contenido completamente correcto, reprobado por un salto de línea al final. Si este resultado se le devuelve al agente, va a ponerse a juguetear con los espacios del título: una dirección que no tiene relación con la tarea.
El arreglo es una función:
replace(/\s+/g, " ") pliega los espacios consecutivos en uno solo, trim() quita los de los extremos y toLowerCase() unifica mayúsculas y minúsculas. Después del cambio, el mismo archivo pasa: el script completo y los resultados reales de la ejecución están en el Nivel 2 de los ejercicios de más abajo.
Un recordatorio en la dirección contraria: la normalización no es una cosa de «cuanto más, mejor». Si además quitas la puntuación, podrías alisar errores reales como total: 48 frente a total: 4.8. El criterio de juicio es siempre el mismo: ¿esta diferencia carga significado? Si sí, sé estricto. Si no, normalízala.
Dónde tocan techo las comprobaciones deterministas
Los verificadores deterministas tienen un límite de aplicabilidad bien definido.
El primer límite es el texto libre. Las salidas de investigación son difíciles de evaluar por programa, porque son texto de forma libre y rara vez tienen una única respuesta correcta5. No puedes escribir output == golden_answer para un resumen: con el mismo material, dos resúmenes bien escritos pueden usar una redacción completamente distinta. Esa clase de juicio pasa al dominio de la Lección 4.
El segundo límite es el «encaja con los requisitos más amplios del sistema». Las soluciones de código son verificables mediante pruebas automatizadas, pero para asegurar que las soluciones se alineen con los requisitos más amplios del sistema, la revisión humana sigue siendo crucial4. Un parche puede pasar todas las pruebas y aun así ser un mal diseño que vuelve inmantenible el módulo entero. La retrospectiva multiagente hace eco de esto: incluso en un mundo de evaluaciones automatizadas, las pruebas manuales siguen siendo esenciales5.
Las comprobaciones deterministas son dueñas del piso de «las cosas que no deberían estar mal no están mal». Por encima de ese piso necesitas otras herramientas.
Proporción: no todo merece un verificador
El error opuesto también es común: construir una suite completa de verificación para un script de una sola vez.
El agente escribe un script de migración de datos que se ejecuta una vez y después se borra, y tú montas validación de estructura, comparación con fixture y muestras de regresión: el tiempo que gastaste escribiendo el verificador supera al tiempo de simplemente mirar la salida a ojo.
La vara de juicio sigue siendo esa línea vieja: habría que considerar agregar complejidad solo cuando mejora los resultados de manera demostrable4. Para los verificadores en concreto, hazte una pregunta:
- ¿Cuántas veces se va a ejecutar esta comprobación? Si es una sola y estás parado ahí mirando, tus ojos pueden ser más rápidos.
- Sin ella, ¿cuánto tarda en descubrirse un error? «De inmediato, lo estoy mirando» frente a «cuando se rompa algo aguas abajo, dos días después» dan conclusiones totalmente distintas.
- ¿Cuántas iteraciones de prompt tienes planeadas para esta tarea? En cuanto sea más de una, necesitas una vara estable para comparar el antes y el después, o el «¿esto mejoró?» va a ser siempre una conjetura.
En cuanto a cuándo tienes que escribir uno, la guía oficial es dura: provee siempre verificación (pruebas, scripts, capturas de pantalla); si no lo puedes verificar, no lo lances3.
💻 Ejercicios
Resumen
- El principio de ordenamiento de los métodos de calificación es «el más rápido, el más confiable, el más escalable»; la calificación basada en código queda primera en los tres, y la contrapartida es que le falta matiz para los juicios complejos que necesitan menos rigidez de reglas1.
- Una comprobación puede ser cualquier cosa que devuelva una señal que Claude pueda leer en la conversación: una suite de pruebas, el código de salida de una compilación, un linter, un script que compare la salida con un fixture, o una captura de pantalla comparada con un diseño3.
- Los verificadores forman un espectro; el borde izquierdo es la coincidencia exacta de cadenas con un fixture, el borde derecho es pedirle a Claude que juzgue2; la forma mínima es apenas
output == golden_answer, normalmente después de normalizar los espacios en blanco y las mayúsculas y minúsculas, perfecta para las tareas con respuestas categóricas y bien delimitadas1.
- Con un aprobado/fallido, el bucle se cierra solo: hace el trabajo, ejecuta la comprobación, lee el resultado, itera hasta que pasa3; y eso es porque los agentes necesitan la ground truth del entorno en cada paso para evaluar su progreso4, y por lo mismo pueden iterar usando los resultados de las pruebas como retroalimentación4.
- Los flujos de trabajo complejos se pueden dividir en puntos de control de verificación discretos donde deberían haber ocurrido cambios de estado específicos, en lugar de validar cada paso intermedio5; una clase de validación de estructura incluso se puede mover aguas arriba a la capa de la API con
strict: true, para que las llamadas a herramientas se ajusten estrictamente al esquema6.
- La trampa más grande son los verificadores demasiado estrictos: rechazan respuestas correctas por diferencias espurias como el formato, la puntuación o formulaciones alternativas válidas2. El arreglo es normalizar primero y comparar después, y guardar el rigor para las partes que de verdad cargan significado.
- Las comprobaciones deterministas tienen un techo: las salidas de investigación son difíciles de evaluar por programa5; las pruebas automatizadas verifican la funcionalidad, pero para asegurar que las soluciones se alineen con los requisitos más amplios del sistema, la revisión humana sigue siendo crucial4, e incluso con evaluaciones automatizadas maduras, las pruebas manuales siguen siendo esenciales5.
- No construyas un verificador completo para un script de una sola vez: habría que considerar agregar complejidad solo cuando mejora los resultados de manera demostrable4; pero en la otra dirección, si no lo puedes verificar, no lo lances3.
>> Lección 4: Juez LLM: rúbricas, formatos y lo que no debes dejarle juzgar