Ir al contenido principal
Laravel, thinking fast.

Una respuesta estructuralmente perfecta puede seguir siendo un riesgo, y este es el fallo de mayor alcance, porque nada en tu pila puede detectarlo.

Generamos una sección de preguntas frecuentes para cada sitio. Al principio la instrucción venía a ser: «escribe seis preguntas frecuentes y respóndelas». El modelo obedeció. El JSON era impecable.

Salieron a producción sitios reales citando un rango de precios que el negocio nunca había fijado y describiendo una política de cancelación de veinticuatro horas que el dueño nunca había acordado.

El modelo no estaba fallando. Se le pidió escribir unas FAQ para un negocio, y los negocios tienen precios y políticas de cancelación, así que escribió unos plausibles. Cualquier comprobación de esquema del mundo aprueba ese contenido. Las claves están, las cadenas no están vacías, la gramática es mejor que la mía.

El arreglo no es validación. Está en el prompt, y tiene dos mitades: aléjalo de los temas y dale algo que hacer en su lugar.

Genera una pregunta frecuente relacionada con este negocio, sobre qué esperar,
cómo funciona el servicio, cómo contactar o reservar, la preparación o la zona
atendida. NO preguntes por precios, descuentos ni reembolsos.

Responde usando ÚNICAMENTE los datos del perfil anterior. Si no se proporciona
una cifra concreta, invita al cliente a contactar con el negocio en lugar de
dar una.

Esa segunda frase es la que soporta el peso. «No te inventes un precio» deja al modelo con una pregunta que aún debe responder, y la responderá. «Invítale a contactar con el negocio» le da un movimiento correcto. Una prohibición sin alternativa es un prompt que el modelo tiene que desobedecer para terminar el trabajo.

La regla general: cualquier campo donde el modelo pueda producir un dato concreto, comprobable y externo —un precio, una fecha, un teléfono, una política, una estadística, el nombre de una persona— es un campo donde acabará inventándose uno, y ningún código posterior podrá notarlo. O le suministras el dato, o le instruyes para que remita a otro sitio.

Mantén el prompt y el validador en la misma habitación

Un esquema compartido evita la deriva entre servicios. No evita que el prompt y el esquema se separen entre sí, porque el prompt es prosa y el esquema es una lista, y la prosa no la comprueba nada.

Así que la comprueba la suite de pruebas:

it('builds a prompt that declares every schema section for the category', function (string $category) {
    $prompt = schemaSyncPrompt($category);

    foreach (ContentSchema::sections($category) as $section) {
        expect($prompt)->toContain('"'.$section.'":');
    }
})->with(['business', 'event']);

it('exposes exactly the schema section union in the SiteContent resource', function () {
    $resource = new SiteContentResource(SiteContentDTOMapper::toDTO([]));

    $keys = array_keys($resource->toArray(request()));
    $union = ContentSchema::allSections();

    sort($keys);
    sort($union);

    expect($keys)->toBe($union);
});

Ninguna de las dos llama a un modelo. Ninguna cuesta un token. Se ejecutan en milisegundos y están entre las pruebas más valiosas de la base de código, porque atrapan el error concreto que de otro modo es invisible: añadir una sección al esquema, olvidar pedírsela al modelo y publicar una función que pide una forma y valida otra.

Si te llevas una sola idea sobre pruebas de este libro antes del capítulo 12, llévate esta. Comprueba que lo que pides y lo que aceptas son lo mismo.

Cuando la validación falla

Ya tienes una respuesta que ha suspendido una comprobación de ruta obligatoria. Lo que pase a continuación depende por completo de si el trabajo es repetible.

Si aún no se ha escrito nada, reintenta: este es genuinamente uno de los casos en los que volver a intentarlo funciona, porque el fallo no es determinista. Ponle un tope de dos o tres intentos, y registra el fallo en lugar de tragártelo: una ruta obligatoria que falla con regularidad es un problema del prompt, y sólo te enterarás si los fallos son visibles.

Si algo se ha escrito, tienes un problema mucho peor, y ninguna cantidad de validación te salvará. Eso es trabajo del capítulo 10.

El instinto que hay que evitar es parchear. No rellenes un hero.title ausente con el nombre del negocio y sigas adelante. Parece servicial. Convierte un fallo ruidoso y contable en uno silencioso, y pierdes la señal que te decía que el prompt necesita trabajo. Si la respuesta no es utilizable, dilo.

Principios

  • Activa la salida estructurada, y conoce sus límites. Garantiza la sintaxis. No garantiza nada sobre las claves, ni sobre la verdad.
  • Quita el bloque de código, luego decodifica o falla. Nunca rescates a medias una respuesta malformada.
  • Falla hacia la forma que el llamante espera. Un fallo de análisis que devuelve un mensaje con la estructura correcta mantiene un solo camino; uno que devuelve null crea dos.
  • Describe la estructura una vez, en una clase sin dependencias del framework que puedan leer todos los servicios. Tres descripciones de un contrato se separan, y la separación es invisible.
  • Versiona el esquema. El contenido generado sobrevive al prompt que lo creó.
  • Obligatorio significa «tíralo y reintenta». Resérvalo para rutas cuya ausencia corrompe algo. Una buena ruta obligatoria vale más que cuarenta defensivas.
  • Cualquier dato externo comprobable acabará inventándose. Suminístralo, o instruye al modelo para que remita —y dale siempre un movimiento alternativo correcto.
  • Comprueba que el prompt pide lo que el validador acepta. Sin tokens, sin modelo, milisegundos, y atrapa el único error que nada más puede ver.