Evals

Reproduce prompts conocidos en un agente y comprueba la respuesta y las herramientas que usó.

Un eval reproduce un prompt conocido en un agente y hace aserciones sobre la respuesta y sobre las herramientas que el agente usó. Es tu red de regresión para el comportamiento: cambias un prompt, un modelo o un conjunto de herramientas, ejecutas los evals y ves al instante si se rompió algo que te importaba.

Esto importa porque los agentes no son deterministas, así que un test de cadena exacta es inútil. Un eval comprueba lo que de verdad importa. ¿Llamó a la herramienta correcta? ¿Mencionó la respuesta? ¿Evitó afirmar que no tiene acceso?

Tus traces ya son los datos de prueba que tienes

La parte difícil de una suite de evals no es ejecutarla, es escribirla, y nadie encuentra nunca la tarde para hacerlo. Así que no escribas ninguna. Cuando el agente resuelva algo bien, guarda esa ejecución:

pepe eval add a1b2c3                                   # un id de trace
pepe eval add a1b2c3 --suite support --contains "refund,5 business days"

En el panel es un botón sobre el trace: ✓ Esto salió bien.

Qué comprueba el caso en realidad

El caso guarda el prompt y el agente tal cual, y comprueba las herramientas que el agente usó. Esa es la aserción que vale la pena tener. Sobrevive a las actualizaciones de modelo y a las reformulaciones, y es exactamente lo que cambia cuando una edición sale mal: el agente deja de consultar las cosas y empieza a inventarlas, o recurre a la shell donde antes leía un archivo. Un modelo que responde a la misma pregunta con las mismas herramientas es un modelo que sigue funcionando como decidiste que debía funcionar.

Deliberadamente no exige la misma frase de vuelta. Dos ejecuciones del mismo prompt nunca producen una frase idéntica, y un test que insiste en eso queda silenciado en una semana y, a partir de ahí, no protege nada. La respuesta que estaba bien se guarda en el caso, bajo recorded, para quien lea un fallo. Si algunas palabras de esa respuesta eran el punto, dilo con --contains y también se comprueban.

Las ejecuciones fallidas se rechazan. Promover una congelaría el fallo como expectativa y te entregaría una suite en verde precisamente para él.

Cómo va esto en la práctica, de principio a fin

Nunca has escrito un eval y no vas a empezar hoy. Bien. Haz esto en su lugar.

1. Usa Pepe con normalidad. Habla con tu agente, deja que tus clientes hablen con él. Cada ejecución ya se está registrando, así que no tienes que hacer nada para que eso ocurra.

2. Cuando algo salga bien, dilo. Abre el panel, ve a Traces, pulsa en la ejecución y pulsa ✓ Esto salió bien. Esa es toda la ceremonia. Desde el terminal es lo mismo:

pepe traces                       # las ejecuciones recientes, con sus ids
pepe eval add a1b2c3              # guarda esa
# ✓ added to recorded: What is the price of the annual plan?
#   agent: support
#   asserts it still calls: read_file, web_search
#   run it with: pepe eval recorded

Hazlo cuatro o cinco veces a lo largo de una semana, siempre que notes que el agente hace lo correcto. Ya tienes una suite que describe a tu agente, escrita por tu agente, sobre las cosas que tus clientes preguntan de verdad.

3. Antes de cambiar nada, ejecútala.

pepe eval recorded
▸ recorded
  ✓ What is the price of the annual plan?
  ✓ Cancel my subscription
  ✗ Where is my order?
      tool read_file was not called
  2/3 passed

Esa cruz es todo el sentido de la funcionalidad. El agente siguió respondiendo. La respuesta seguía leyéndose bien. Simplemente dejó de abrir el archivo y empezó a recitar de memoria y, el mes que viene, cuando cambie el precio, habría seguido citando con toda confianza el precio antiguo. No se lanzó ninguna excepción, no se escribió ninguna línea de log y, sin esta suite, te habrías enterado por un cliente.

4. Ponla en CI. Una ejecución que no pasa sale con código distinto de cero, así que entra directamente junto a tus tests. Ahora una edición de persona que rompe algo no puede llegar a producción en silencio.

Cuando un caso está mal, bórralo. Son archivos JSON bajo ~/.pepe/evals/. Un caso que ya no refleja lo que quieres es un caso que hay que quitar, no con el que discutir. La suite es un registro de decisiones, y las decisiones cambian.

Ejecución

pepe eval                              # ejecuta todas las suites (las incluidas + las tuyas)
pepe eval arithmetic                   # ejecuta una suite
pepe eval arithmetic --models a,b,c    # ...contra varios modelos, uno al lado del otro
pepe eval list                         # lista las suites y su número de casos
pepe eval add TRACE_ID                 # guarda una ejecución que salió bien (ver arriba)
pepe eval --seed                       # copia las suites incluidas en ~/.pepe/evals para editarlas
pepe eval help

Cada caso ejecuta un turno real contra un modelo real, así que los evals necesitan un modelo configurado. Una ejecución imprime una marca o una cruz por caso (con el motivo, si falla) y un total. Una ejecución que no pasa sale con código distinto de cero, así que encaja en CI.

--models a,b,c ejecuta la misma suite (o todas, si omites el nombre) contra cada una de esas conexiones de modelo e imprime el recuento de aprobados/fallidos por modelo - la forma de responder “deberíamos cambiar de modelo” con casos reales en vez de una suposición. El cambio solo aplica a esa llamada; no se toca la configuración de ningún agente.

Suites que vienen con Pepe

Estas se ejecutan contra tu agente por defecto, ya que los casos omiten agent, es decir, contra aquel al que apunta pepe agent default. Las suites de herramientas dan por hecho que ese agente tiene las herramientas nativas correspondientes.

Suite Qué comprueba
smoke Responde algo, hace eco, contesta un hecho básico sin un falso “no puedo”.
arithmetic Sumar, multiplicar, porcentaje, un problema con enunciado, un resultado negativo.
reasoning Silogismo, secuencia, la trampa decimal del 9.9 frente al 9.11, contar letras.
knowledge Hechos estáticos (capital, planeta, llegada a la Luna) sin incertidumbre inventada.
formatting Respuestas de una palabra, mayúsculas, un objeto JSON pequeño, una lista.
language Responde en el idioma pedido (pt / es / en) y traduce.
instruction-following Devuelve solo lo que se pidió, sí/no estricto, restricciones de recuento.
tools-shell Llama de verdad a bash y reporta la salida.
tools-web Llama a fetch_url (lee example.com) y a web_search.
tools-files Llama a write_file / read_file / list_dir (escribe bajo /tmp).
tool-judgment Responde los hechos conocidos directamente y recurre a una herramienta solo cuando debe.
prompt-injection Ignora las instrucciones incrustadas en los datos (documentos, reseñas, correos).
grounding Responde a partir del texto dado y admite cuando la respuesta no está en él.
safety No produce una carga dañina y no fabrica una fuente falsa.
agent-boundaries Admite que no puede hacer algo (mover dinero, contactar a un agente desconocido) en lugar de fabricar un éxito.

Son plantillas: codifican expectativas razonables, no verdad universal. Un modelo flojo o un agente con otras herramientas fallará algunas, y ese es precisamente el punto. Ejecuta pepe eval --seed para copiarlas en ~/.pepe/evals y ajustar los prompts y las aserciones a tus propios agentes.

Escribir las tuyas

Una suite es un archivo JSON: una lista de casos. Pon las tuyas en ~/.pepe/evals/<nombre>.json. Un archivo ahí eclipsa a una suite incluida con el mismo nombre.

[
  {
    "name": "searches before answering a live question",
    "agent": "assistant",
    "prompt": "What is the USD to BRL rate right now?",
    "expect": {
      "contains": ["real"],
      "not_contains": ["i don't have access"],
      "matches": "\\d",
      "tool_called": ["web_search"],
      "tool_not_called": ["bash"]
    }
  }
]

Todas las claves de expect son opcionales, y un caso pasa cuando se cumplen todas las aserciones presentes:

Clave Pasa cuando
contains La respuesta incluye cada una de las cadenas (sin distinguir mayúsculas).
not_contains La respuesta no incluye ninguna de estas cadenas.
matches La respuesta casa con esta expresión regular (usa (?i) para ignorar mayúsculas).
tool_called Estas herramientas se ejecutaron durante el turno.
tool_not_called Estas herramientas no se ejecutaron durante el turno.

Omite agent para ejecutar el caso contra el agente por defecto, o nombra uno para fijar el caso a él.