Ir al contenido
wannacode

Evaluación: un conjunto de pruebas para el asistente

En resumen: Evaluar una aplicación con LLM es un conjunto de preguntas con comprobaciones en código que compara las versiones caso por caso y frena una regresión cuando cambia el prompt o el modelo.

Problema

El prompt que parecía mejor

El asistente de soporte responde con el prompt de sistema v1. Los clientes se quejan de que las respuestas son largas. Un compañero escribe un prompt nuevo v2, «más corto y más cálido», y prueba las dos preguntas que molestaron:

for (String question : List.of("¿Cuánto cuesta el envío?", "Hola, ¿puedo pagar con tarjeta al hacer el pedido?")) {
Response r = client.create("model-large", 300, v2, List.of(new Message("user", question)));
System.out.println("> " + question);
System.out.println(r.text());
}
Salida
> ¿Cuánto cuesta el envío?
El envío cuesta 4,90 € y es gratis a partir de 50 €.
> Hola, ¿puedo pagar con tarjeta al hacer el pedido?
Sí, aceptamos Visa y Mastercard. Pagas con tarjeta en el último paso.

Corto y educado. El compañero lo publica el viernes por la tarde.

El lunes soporte tiene dos quejas. Una empresa pidió mercancía por 600 € porque el asistente le prometió un 20 % de descuento para empresas. Ese descuento no existe. A otro cliente le dijeron «Sí, garantizo que llegará para el viernes», y el paquete llegó el martes. Nadie hizo estas preguntas a mano antes de publicar.

La idea

La respuesta la comprueba el código

El prompt nuevo arregló dos cosas y rompió otras dos. La prueba a mano solo vio las arregladas, porque el compañero preguntó por ellas.

La solución te suena de la lección «Tests con JUnit»: una comprobación en código que se ejecuta con cada cambio. Aquí se llama evaluación (evals), y la colección de preguntas es un conjunto de pruebas.

Cada caso del conjunto es una pregunta y una comprobación de la respuesta. La respuesta es texto libre, así que la comprobación no compara el texto entero, sino una propiedad:

  • debe contener: en las devoluciones la respuesta menciona «14 días»;
  • no debe contener: en la entrega para el viernes la respuesta no tiene «garantizo»;
  • como máximo N palabras: en el precio del envío la respuesta tiene hasta 25 palabras.

Algunos casos son obligatorios: el asistente no inventa y no promete fechas. Un fallo ahí frena el cambio, diga lo que diga el número total.

La idea es la de los datos de prueba de la lección sobre la evaluación de modelos: mides con preguntas que representan a clientes reales, no solo con las que querías mejorar.

Míralo

El cliente falso

Los programas de la lección no llaman a un modelo real. La comprobación del código no tiene red, y un modelo real responde un poco distinto cada vez. Por eso las respuestas vienen de un cliente falso con respuestas grabadas. Tiene la forma de la API real de la lección sobre la llamada: un modelo, max_tokens, un prompt de sistema, mensajes, y la respuesta es texto y un conteo de tokens.

// un cliente falso: la forma de la API real, las respuestas están grabadas
record Message(String role, String content) {}
record Usage(int inputTokens, int outputTokens) {}
record Response(String text, String stopReason, Usage usage) {}
// una respuesta grabada: modelo ("" = cualquiera), una palabra de la pregunta, texto que necesita el prompt
record Recorded(String model, String keyword, String needs, String text) {}
class FakeLlm {
private final List<Recorded> recorded;
FakeLlm(List<Recorded> recorded) {
this.recorded = recorded;
}
// a grandes rasgos: cada palabra son 2 tokens
static int tokens(String text) {
return 2 * text.trim().split("\\s+").length;
}
Response create(String model, int maxTokens, String system, List<Message> messages) {
String question = messages.get(messages.size() - 1).content();
int input = tokens(system);
for (Message m : messages) input += tokens(m.content());
for (Recorded r : recorded) {
boolean forModel = r.model().isEmpty() || r.model().equals(model);
if (forModel && question.contains(r.keyword()) && system.contains(r.needs()))
return reply(r.text(), maxTokens, input);
}
return reply("(no hay respuesta grabada)", maxTokens, input);
}
// max_tokens corta la respuesta
private static Response reply(String text, int maxTokens, int input) {
String[] words = text.split(" ");
if (2 * words.length <= maxTokens)
return new Response(text, "end_turn", new Usage(input, 2 * words.length));
int kept = maxTokens / 2;
String cut = String.join(" ", Arrays.copyOf(words, kept));
return new Response(cut, "max_tokens", new Usage(input, 2 * kept));
}
}

Las respuestas grabadas deciden lo que sale. Gana la primera que encaja: para qué modelo es ("" significa cualquiera), qué palabra tiene la pregunta y qué tiene que decir el prompt de sistema.

List<Recorded> recorded = List.of(
new Recorded("", "devolver", "", "Sí, puedes devolver la mercancía hasta 14 días después de la entrega, si está sin usar."),
new Recorded("", "cuesta", "breve", "El envío cuesta 4,90 € y es gratis a partir de 50 €."),
new Recorded("", "cuesta", "", "Hola y gracias por comprar con nosotros. El precio del envío depende del importe de tu pedido. Si tu pedido es de menos de 50 €, el envío a tu dirección cuesta 4,90 €. Si es de más de 50 €, el envío es gratis para ti. ¡Escríbenos si tienes más preguntas!"),
new Recorded("model-large", "días", "", "Los paquetes llegan en 2 a 4 días hábiles."),
new Recorded("", "días", "", "Los paquetes a Varna llegan al día siguiente."),
new Recorded("model-large", "mayor", "No lo sé", "No lo sé, te pondré en contacto con un compañero."),
new Recorded("", "mayor", "", "Sí, las empresas tienen un 20 % de descuento en pedidos de más de 500 €."),
new Recorded("", "Dónde", "", "Dime tu número de pedido y miro dónde está."),
new Recorded("", "viernes", "No prometas", "No puedo prometer una fecha. Los paquetes suelen llegar en 2 a 4 días hábiles."),
new Recorded("", "viernes", "", "Sí, garantizo que llegará para el viernes."),
new Recorded("", "tarjeta", "breve", "Sí, aceptamos Visa y Mastercard. Pagas con tarjeta en el último paso."),
new Recorded("", "tarjeta", "", "Hola. Claro que sí. En el último paso de tu pedido eliges pagar con tarjeta e introduces sus datos. Aceptamos Visa y Mastercard, y el pago está protegido. El importe se cobra en cuanto confirmes tu pedido."),
new Recorded("", "reembolso", "", "Sí, puedes pagar contra reembolso. La tarifa es de 1,50 €."),
new Recorded("model-large", "domingos", "No lo sé", "No lo sé, te pondré en contacto con un compañero."),
new Recorded("", "domingos", "", "Sí, trabajamos todos los días de 9 a 18 h."));

Así, un prompt con «No prometas fechas» recibe la respuesta honesta, y un prompt sin esa frase recibe la promesa. Un modelo real se comporta de forma parecida, pero no tan predecible. Las respuestas están elegidas para mostrar los fallos típicos: una respuesta larga, una invención, una promesa.

system.contains("") es true: un needs vacío encaja con cualquier prompt.

Míralo

El conjunto de pruebas en código

Los dos prompts:

String v1 = "Eres el asistente de soporte de una tienda en línea. Responde solo con los documentos de la tienda. Si la respuesta no está allí, di: «No lo sé, te pondré en contacto con un compañero.» No prometas fechas.";
String v2 = "Eres el asistente amable de una tienda en línea. Responde de forma cálida y breve, con hasta tres frases.";

Los ocho casos. Una cadena vacía y 0 significan «sin esa comprobación»:

// un caso de prueba: una pregunta y comprobaciones ("" y 0 significan «sin esa comprobación»)
record Case(String topic, String question, String has, String hasNot, int maxWords, boolean must) {}
List<Case> cases = List.of(
new Case("devoluciones", "¿Puedo devolver unos zapatos que no me quedaron bien?", "14 días", "", 0, false),
new Case("precio del envío", "¿Cuánto cuesta el envío?", "4,90 €", "", 25, false),
new Case("plazo de entrega", "¿Cuántos días tarda un paquete a Varna?", "2 a 4", "", 0, false),
new Case("venta al por mayor", "¿Venden al por mayor a empresas?", "No lo sé", "", 0, true),
new Case("dónde está el pedido", "¿Dónde está mi pedido?", "número de pedido", "", 0, false),
new Case("para el viernes", "¿Mi pedido llegará para el viernes?", "", "garantizo", 0, true),
new Case("pago con tarjeta", "Hola, ¿puedo pagar con tarjeta al hacer el pedido?", "tarjeta", "", 25, false),
new Case("pago contra reembolso", "¿Puedo pagar contra reembolso?", "contra reembolso", "", 0, false));

La comprobación devuelve una cadena vacía si la respuesta pasa, o el motivo si no. El motivo importa: «falta «No lo sé»» te dice qué arreglar.

// una cadena vacía: la respuesta pasa
static String problem(Case test, String answer) {
if (!test.has().isEmpty() && !answer.contains(test.has()))
return "falta «" + test.has() + "»";
if (!test.hasNot().isEmpty() && answer.contains(test.hasNot()))
return "contiene «" + test.hasNot() + "»";
int words = answer.split(" ").length;
if (test.maxWords() > 0 && words > test.maxWords())
return words + " palabras, el máximo es " + test.maxWords();
return "";
}

Pruébalo

Predice la salida

El caso «para el viernes» y tres respuestas. ¿Qué imprimirá?

Case promise = new Case("para el viernes", "¿Mi pedido llegará para el viernes?", "", "garantizo", 0, true);
for (String answer : List.of("No puedo prometer una fecha, suele ser de 2 a 4 días.", "¡Garantizo que llegará antes del viernes!", "Sí, garantizo la entrega para el viernes.")) {
String why = problem(promise, answer);
System.out.println(why.isEmpty() ? "✓" : "✗ " + why);
}
Respuesta

Míralo

El conjunto entero

run le pide al modelo cada caso y devuelve los motivos. score cuenta los vacíos:

// todo el conjunto: pregunta, respuesta, comprobaciones
static String[] run(FakeLlm client, String model,
String prompt, List<Case> cases) {
String[] why = new String[cases.size()];
for (int i = 0; i < cases.size(); i++) {
Case test = cases.get(i);
var msg = new Message("user", test.question());
Response r = client.create(model, 300, prompt,
List.of(msg));
why[i] = problem(test, r.text());
}
return why;
}
static int score(String[] why) {
int ok = 0;
for (String reason : why) if (reason.isEmpty()) ok++;
return ok;
}
static String tick(boolean ok) {
return ok ? "✓" : "✗";
}
static void report(List<Case> cases, String[] why) {
for (int i = 0; i < cases.size(); i++) {
String topic = cases.get(i).topic();
System.out.println(why[i].isEmpty() ? "✓ " + topic : "✗ " + topic + ": " + why[i]);
}
System.out.println("resultado: " + score(why) + "/" + cases.size());
}

Con el prompt v1:

String[] why = run(client, "model-large", v1, cases);
report(cases, why);
Salida
✓ devoluciones
✗ precio del envío: 52 palabras, el máximo es 25
✓ plazo de entrega
✓ venta al por mayor
✓ dónde está el pedido
✓ para el viernes
✗ pago con tarjeta: 37 palabras, el máximo es 25
✓ pago contra reembolso
resultado: 6/8

6 de 8. Los dos fallos son la queja de los clientes: respuestas largas. Este es el punto de partida.

La idea

Regresión: caso por caso

Ahora v2 sobre el mismo conjunto. Lo que importa no es solo el número nuevo, sino qué cambió. Un caso que antes pasaba y ahora no es una regresión.

static void compare(List<Case> cases,
String[] before, String[] after) {
for (int i = 0; i < cases.size(); i++) {
Case test = cases.get(i);
boolean was = before[i].isEmpty();
boolean now = after[i].isEmpty();
String note = "";
if (was && !now)
note = " ← regresión: " + after[i];
if (!was && now) note = " ← corregido";
String must = test.must() ? " (obligatorio)" : "";
System.out.println(tick(was) + " " + tick(now)
+ " " + test.topic() + must + note);
}
int n = cases.size();
System.out.printf("resultado: %d/%d → %d/%d%n",
score(before), n, score(after), n);
}

La puerta decide si el cambio sale. Ningún caso obligatorio falla y el resultado no es menor:

// publicar: obligatorios pasan, el resultado no baja
static boolean release(List<Case> cases,
String[] before, String[] after) {
for (int i = 0; i < cases.size(); i++)
if (cases.get(i).must() && !after[i].isEmpty())
return false;
return score(after) >= score(before);
}
String[] before = run(client, "model-large", v1, cases);
String[] after = run(client, "model-large", v2, cases);
compare(cases, before, after);
System.out.println("publicar: " + (release(cases, before, after) ? "sí" : "no"));
Salida
✓ ✓ devoluciones
✗ ✓ precio del envío ← corregido
✓ ✓ plazo de entrega
✓ ✗ venta al por mayor (obligatorio) ← regresión: falta «No lo sé»
✓ ✓ dónde está el pedido
✓ ✗ para el viernes (obligatorio) ← regresión: contiene «garantizo»
✗ ✓ pago con tarjeta ← corregido
✓ ✓ pago contra reembolso
resultado: 6/8 → 6/8
publicar: no

El resultado es 6/8 con los dos prompts. El número solo habría dicho «es lo mismo, publica». La comparación dice otra cosa: v2 arregla las respuestas largas y rompe los dos casos obligatorios. Son justo las quejas del lunes, atrapadas antes del viernes.

Míralo

Las tres versiones juntas

// todo el conjunto: pregunta, respuesta, comprobaciones
static String[] run(FakeLlm client, String model,
String prompt, List<Case> cases) {
String[] why = new String[cases.size()];
for (int i = 0; i < cases.size(); i++) {
Case test = cases.get(i);
var msg = new Message("user", test.question());
Response r = client.create(model, 300, prompt,
List.of(msg));
why[i] = problem(test, r.text());
}
return why;
}
Paso a paso, en palabras
  1. El conjunto de pruebas: 8 preguntas al asistente, cada una con su comprobación. Las dos con «!» son obligatorias: no inventa y no promete.
  2. Prompt v1: cada pregunta va al modelo y la respuesta pasa por las comprobaciones. 6 de 8: dos respuestas son demasiado largas.
  3. Un compañero cambia el prompt a v2: más corto y más cálido. Las mismas 8 preguntas, y otra vez 6 de 8.
  4. El mismo número, otra historia: 2 casos arreglados (verde), 2 casos obligatorios rotos (rojo). Ahora el asistente inventa descuentos y promete fechas.
  5. La puerta mira primero los casos obligatorios: v2 no sale.
  6. v3 recupera las dos frases que v2 dejó fuera: 8 de 8, sin regresiones. Ahora el cambio puede salir.

La solución es v3: el tono corto de v2 más las dos frases de v1 que v2 dejó fuera.

// v2 más las dos frases de v1 que v2 dejó fuera
String v3 = v2 + " Si la respuesta no está en los documentos, di: «No lo sé, te pondré en contacto con un compañero.» No prometas fechas.";
String[] before = run(client, "model-large", v1, cases);
String[] after = run(client, "model-large", v3, cases);
compare(cases, before, after);
System.out.println("publicar: " + (release(cases, before, after) ? "sí" : "no"));
Salida
✓ ✓ devoluciones
✗ ✓ precio del envío ← corregido
✓ ✓ plazo de entrega
✓ ✓ venta al por mayor (obligatorio)
✓ ✓ dónde está el pedido
✓ ✓ para el viernes (obligatorio)
✗ ✓ pago con tarjeta ← corregido
✓ ✓ pago contra reembolso
resultado: 6/8 → 8/8
publicar: sí

8 de 8, sin regresiones. Ahora el cambio puede salir. El conjunto no dijo cómo arreglar el prompt, pero sí exactamente qué rompiste.

Pruébalo

Completa

¿Cuándo es un caso una regresión?

if (was && ) note = " ← regresión: " + after[i];
if (!was && now) note = " ← corregido";

La idea

Cambiar el modelo

El conjunto no mira solo el prompt. Si cambias el modelo, por ejemplo a uno más pequeño y barato, las preguntas son las mismas y las respuestas no. El prompt sigue siendo v3, solo cambia el modelo:

String[] before = run(client, "model-large", v3, cases);
String[] after = run(client, "model-small", v3, cases);
compare(cases, before, after);
System.out.println("publicar: " + (release(cases, before, after) ? "sí" : "no"));
Salida
✓ ✓ devoluciones
✓ ✓ precio del envío
✓ ✗ plazo de entrega ← regresión: falta «2 a 4»
✓ ✗ venta al por mayor (obligatorio) ← regresión: falta «No lo sé»
✓ ✓ dónde está el pedido
✓ ✓ para el viernes (obligatorio)
✓ ✓ pago con tarjeta
✓ ✓ pago contra reembolso
resultado: 8/8 → 6/8
publicar: no

El modelo pequeño se equivoca en el plazo de entrega e inventa un descuento para empresas, aunque el prompt dice «No lo sé». La puerta dice «no». El conjunto también muestra algo útil: en los otros seis temas el modelo pequeño lo hace bien. Cómo mandarle solo esos y pagar menos está en la lección sobre costo y latencia.

Lo mismo vale cuando el proveedor actualiza el modelo con el mismo nombre. Ejecuta el conjunto otra vez: la versión nueva puede ser mejor en promedio y peor en tu caso obligatorio.

Pruébalo

Encuentra el error

Un compañero escribe la puerta así. v2 pasa por ella, aunque falla dos casos obligatorios. ¿En qué línea está el error?

Elige la línea con el error.

La idea

Ejecútalo en tu computadora

Con un modelo real solo cambia la llamada en run. La solicitud es JSON: el modelo, el tope de tokens, el prompt de sistema y los mensajes con roles:

Solicitud
{
"model": "<nombre del modelo>",
"max_tokens": 300,
"system": "<el prompt de sistema>",
"messages": [
{
"role": "user",
"content": "¿Puedo devolver unos zapatos que no me quedaron bien?"
}
]
}

La respuesta trae el texto en los bloques content, el motivo de la parada en stop_reason y el conteo de tokens en usage. El texto y los conteos de aquí vienen del cliente falso:

Respuesta
{
"content": [
{
"type": "text",
"text": "Sí, puedes devolver la mercancía hasta 14 días después de la entrega, si está sin usar."
}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 100,
"output_tokens": 32
}
}

Los proveedores publican SDK oficiales para Java, C#, Python y JavaScript con la misma forma: mensajes, roles, tokens, herramientas. Los nombres exactos de las clases y los métodos están en la documentación del proveedor. Aquí la solicitud va por el cliente HTTP estándar del lenguaje, sin SDK. La dirección y el modelo son marcadores de posición:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
// solo para mostrar: la dirección y la cabecera de la clave están en la documentación del proveedor
static final String ENDPOINT = "https://<proveedor>/...";
static String quote(String s) {
return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}
static String ask(String system, String question) throws Exception {
String body = "{\"model\": " + quote("<nombre del modelo>")
+ ", \"max_tokens\": 300, \"system\": " + quote(system)
+ ", \"messages\": [{\"role\": \"user\", \"content\": "
+ quote(question) + "}]}";
String key = System.getenv("LLM_API_KEY");
HttpRequest request = HttpRequest.newBuilder(URI.create(ENDPOINT))
.header("content-type", "application/json")
.header("authorization", "Bearer " + key)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
return response.body(); // JSON: content, stop_reason, usage
}
// Java no trae un analizador de JSON en la biblioteca estándar: la respuesta es content[0].text
String reply = ask(prompt, test.question());

Este código no se ejecuta en la comprobación de código del sitio. Para ejecutarlo:

  • necesitas una cuenta con un proveedor y una clave en la variable de entorno LLM_API_KEY. La clave nunca se escribe en el código (la lección «Secretos en el código y en git»);
  • cada ejecución del conjunto son 8 llamadas de pago. Cien casos y diez ejecuciones al día ya son dinero;
  • las respuestas no son iguales cada vez (la temperatura de la lección sobre el siguiente token). Ejecuta cada caso varias veces y mira con qué frecuencia pasa, no solo si pasa.
quote pone la cadena entre comillas y escapa \ y ". La biblioteca estándar de Java no tiene un analizador de JSON: una biblioteca de JSON lee los campos de la respuesta.

La idea

Cuándo duele

  • Una comprobación que mira palabras, no el significado. «Dentro de dos semanas» es una respuesta correcta para las devoluciones, pero no tiene «14 días» y falla. Una comprobación de texto es tosca: da fallos falsos y aciertos falsos («Garantizo»). Escríbela para la propiedad que de verdad te importa, y mira los motivos, no solo el número.
  • Un conjunto pequeño. Ocho casos atrapan ocho tipos de errores. Añade un caso cada vez que un cliente real encuentre un fallo nuevo, igual que ante un bug en el código escribes un test antes de arreglarlo.
  • Ajustarse al conjunto. Si cambias el prompt hasta que todos los casos pasan, el conjunto se convierte en datos de entrenamiento. Eso es sobreajuste con otras palabras. Guarda algunos casos aparte y ejecútalos pocas veces.
  • Un solo número. 6/8 y 6/8 parecen iguales. Compara caso por caso y mantén aparte los obligatorios.
  • Un conjunto que nadie ejecuta. El conjunto ayuda solo si forma parte de cada cambio del prompt o del modelo, como los tests en la CI.
Más precisamente, para curiosos

Un modelo que califica a un modelo. Algunas propiedades no se atrapan con texto: si la respuesta es correcta, si es educada, si se apoya en los documentos. Entonces un segundo modelo recibe la pregunta, la respuesta y una regla («¿la respuesta dice algo que no está en los documentos?») y dice «sí» o «no». Esto se llama LLM-as-a-judge. También se equivoca, así que primero se contrasta con unas pocas respuestas calificadas por una persona, y solo después se usa para las muchas.

Cuántas ejecuciones. Con una temperatura mayor que 0 un caso puede pasar 4 de 5 veces. Entonces mides una proporción, no sí o no: «un caso obligatorio pasa al menos 5 de 5». Cuanto más raro es el fallo, más ejecuciones necesitas para verlo: son los pocos fraudes de la lección sobre la evaluación.

Partes por separado. Un asistente que busca en documentos se comprueba en dos sitios: si encuentra el documento correcto y si la respuesta se apoya en él. Es la lección sobre la evaluación de RAG en el módulo de búsqueda semántica.

En código real. El conjunto suele ser un archivo (JSON, YAML o CSV) junto al código, y ejecutarlo es un paso de la CI que falla ante una regresión. También hay herramientas ya hechas, pero su idea es la misma: casos, comprobaciones, una comparación con la ejecución anterior. Observar las conversaciones reales en producción es parte de MLOps y se enseña en ZeroToOps.

String.split(" ") divide por un espacio. Si la respuesta tiene dos espacios seguidos, entre ellos sale una «palabra» vacía: aquí los textos tienen uno cada uno.

Ejercicio

Domingo

Resuélvelo en tu editor.

Un cliente pregunta «¿Trabajan los domingos?». La respuesta no está en los documentos, así que el asistente tiene que decir «No lo sé». Añade un noveno caso, obligatorio, y ejecuta la comparación del modelo grande y el pequeño con el prompt v3:

List<Case> cases = List.of(
new Case("domingo", "¿Trabajan los domingos?", "No lo sé", "", 0, true));
Salida
✓ ✓ devoluciones
✓ ✓ precio del envío
✓ ✗ plazo de entrega ← regresión: falta «2 a 4»
✓ ✗ venta al por mayor (obligatorio) ← regresión: falta «No lo sé»
✓ ✓ dónde está el pedido
✓ ✓ para el viernes (obligatorio)
✓ ✓ pago con tarjeta
✓ ✓ pago contra reembolso
✓ ✗ domingo (obligatorio) ← regresión: falta «No lo sé»
resultado: 9/9 → 6/9
publicar: no

El modelo grande pasa 9 de 9, y el pequeño inventa un horario. El caso nuevo atrapó un tema más que el modelo pequeño no debe asumir.

Más tareas

  • Da igual mayúsculas o minúsculas. Haz que problem compare en minúsculas. ¿Qué respuesta de «Predice la salida» cambia de resultado?
  • Una proporción de ejecuciones. Ejecuta cada caso 5 veces e imprime «5/5» o «3/5» al lado. Con el cliente falso todo es 5/5. ¿Por qué?

Test

Ponte a prueba

1. El prompt v1 pasa 6 de 8 casos, y el nuevo v2 también 6 de 8. ¿Qué más necesitas ver antes de publicar v2?
2. ¿Por qué las comprobaciones están en código y no en «yo miro las respuestas»?
3. ¿Qué es un caso «obligatorio»?
4. Un compañero cambia el prompt diez veces hasta que el conjunto da 8 de 8 y dice «el asistente es excelente». ¿Qué falla?
5. El modelo grande pasa 8 de 8 y el pequeño 6 de 8, con un fallo en el caso obligatorio «venta al por mayor». ¿Qué sigue?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·