Parecía bastante sencillo.
Teníamos un recetario en WordPress con contenidos creados en distintos momentos, estructuras diferentes y bastante trabajo pendiente para normalizarlo. La idea inicial tampoco parecía especialmente extraña. Bastaba con utilizar un asistente de inteligencia artificial que interpretase cada receta, ordenase la información y generase un fichero que WordPress pudiera importar.
Entraba un documento, la IA lo procesaba y WordPress recibía el resultado.
Ese era, más o menos, el proyecto.
Hasta que dejó de serlo.
Durante las pruebas empezaron a aparecer pequeños problemas. Después llegaron otros bastante mayores. Una receta apareció sin ingredientes. Una actualización modificó un dato SEO que debía permanecer intacto. Un mecanismo pensado para proteger las recetas acabó bloqueando una publicación que ni siquiera era una receta. Y cuando conseguimos ordenar perfectamente la información descubrimos algo aún más incómodo. La de que en algunos casos habíamos empeorado el contenido.
Cada fallo nos obligó a cambiar una pieza.
Cuando terminamos, aquel «importador de recetas» casi había desaparecido. En su lugar teníamos un sistema en el que la IA proponía, un contrato delimitaba lo permitido, WordPress volvía a comprobarlo y una persona seguía tomando las decisiones que no tenía sentido automatizar.
Ahí estaba lo interesante.
No en conseguir que la IA hiciera más, sino en aprender a impedir que hiciera demasiado.
Todo empezó con un importador que dejó de ser un importador
Partíamos de un recetario real, con recetas antiguas y nuevas, metacampos propios, contenido construido con Elementor, información SEO gestionada con Yoast y datos estructurados específicos para las recetas.
Queríamos normalizar ese material y facilitar también la creación de nuevas publicaciones.
La primera arquitectura parecía suficiente. El asistente recibía una fuente, identificaba los datos, preparaba un fichero estructurado y un plugin propio los aplicaba en WordPress.
Pero las primeras pruebas fueron ampliando el proyecto.
Ya no bastaba con importar.
También había que previsualizar, validar, actualizar sin destruir información, crear entradas nuevas, conservar el SEO, manejar metacampos, poder restaurar, registrar operaciones y distinguir una receta de una entrada normal.
Hasta el nombre dejó de describir bien lo que estábamos construyendo.
El «Importador de recetas» acabó evolucionando hasta convertirse en CLZ Recetas.
Puede parecer un simple cambio de alcance, pero ahí apareció el primer aprendizaje importante: cuando introduces IA en un proceso real, el trabajo difícil suele empezar después de obtener una respuesta del modelo.
Generar algo es fácil. Otra cosa es decidir si ese resultado tiene permiso para modificar producción.
Entonces llegó una receta sin ingredientes
Una de las pruebas resultó especialmente útil.
El asistente generó el fichero de una receta y faltaban los ingredientes.
No era una tilde ni una frase mejorable.
Faltaban los ingredientes.
Es un buen ejemplo de uno de los problemas más incómodos de los modelos de lenguaje, como una respuesta puede parecer perfectamente razonable y aun así estar incompleta.
El propio asistente tenía instrucciones para revisar su trabajo, pero no bastó.
Por suerte, el siguiente componente del sistema no necesitaba «entender» si la receta parecía convincente. Solo tenía que comprobar una regla mucho más sencilla, que determinados campos eran obligatorios.
El fichero fue rechazado.
Lo generamos de nuevo y la siguiente versión sí pudo continuar.

Aquel error cambió el proyecto.
Hasta entonces veíamos el formato estructurado como una forma cómoda de transportar información. A partir de ese momento empezó a funcionar también como una frontera.
La IA podía proponer lo que quisiera dentro de ella. El plugin no tenía por qué fiarse.
Aquella separación funcionaba también como una frontera de confianza. La salida del modelo dejaba de tratarse como una instrucción válida por defecto y pasaba a considerarse un dato no confiable que debía superar comprobaciones deterministas.
Ese principio forma parte del hardening de una integración con IA. Evitando intentar que el modelo sea infalible, sino diseñar el sistema suponiendo que puede producir una salida inesperada y limitar las consecuencias cuando ocurra.
El JSON Schema no podía comprobar si una receta era buena ni decidir si una clasificación culinaria tenía sentido.
Pero sí podía impedir que una receta sin ingredientes siguiera adelante.
Y con eso ya ganábamos mucho.
Construimos una barrera. Y la barrera empezó a molestar
El siguiente problema tuvo algo de irónico.
Habíamos añadido controles para impedir que una receta incorrecta llegase a producción. La lógica funcionaba.
Quizá demasiado.
En una de las pruebas, una publicación normal de WordPress acabó tratada como si fuese una receta. El mecanismo de protección se activó donde no correspondía y bloqueó una operación legítima.
Habíamos cambiado un riesgo por otro.
Primero nos preocupaba que la automatización hiciera cosas que no debía. Ahora el sistema encargado de detenerla también actuaba donde no le correspondía.
La solución no consistió en relajar todas las comprobaciones, sino en ajustar mejor la condición que determinaba cuándo aquellas reglas tenían autoridad.
Ese incidente nos dejó otra enseñanza.
Aquí también aprendimos a no utilizar guardrail y hardening como si fueran lo mismo. Un guardrail es un control concreto que permite, bloquea o condiciona una acción. El hardening es más amplio, porque consiste en diseñar el conjunto para reducir las oportunidades de que un error del modelo termine convirtiéndose en un error sobre el sistema real.
Por eso añadir más guardrailes no equivalía necesariamente a endurecer mejor la solución.
Más adelante nos ocurrió algo parecido con las copias de seguridad.
En un momento del desarrollo, el flujo comprobaba que se había descargado una copia y además pedía al usuario marcar una casilla para confirmar que la había descargado.
Dos controles parecían más seguros que uno.
En realidad, el segundo no aportaba ninguna protección nueva.
Solo añadía trabajo innecesario.
Lo eliminamos.
Empezábamos a entender que una automatización fiable no se diseña poniendo barreras por todas partes, sino colocando la barrera adecuada exactamente donde hacía falta.
Conseguimos ordenar las recetas. Y entonces descubrimos que las habíamos empeorado
Este fue probablemente el momento más interesante desde el punto de vista editorial.
Las recetas originales eran irregulares. Algunas tenían introducciones largas y otras estaban organizadas en fases. Había explicaciones sobre temperaturas, tiempos, textura, reposos, criterios visuales y pequeños detalles prácticos.
La IA podía convertir todo aquello en una estructura mucho más limpia.
Y lo hizo.
Demasiado limpia.
En algunas transformaciones las introducciones perdieron una parte importante de su contenido. Los pasos de elaboración que antes explicaban una fase completa acabaron convertidos en instrucciones telegráficas.
Técnicamente habíamos avanzado, pero editorialmente habíamos retrocedido.

Esta vez el problema no era la ausencia de un campo obligatorio. El esquema podía ser perfectamente válido y el resultado seguir siendo peor que el original.
Aquello nos obligó a separar dos conceptos que hasta entonces parecían casi equivalentes.
Estructurar no es resumir.
Una automatización puede ordenar la información sin tener permiso para empobrecerla.
El contrato volvió a cambiar.
Las instrucciones dejaron de plantearse únicamente como cadenas breves y pasaron a representar dos elementos distintos: un nombre para la fase y el texto completo que explicaba qué había que hacer.
Ese pequeño cambio permitió conservar algo esencial.
La máquina seguía obteniendo una estructura limpia y el lector recuperaba la explicación que daba valor a esa estructura.
Además, resolvía otra necesidad. La misma información podía alimentar el contenido visible de la receta y los pasos de sus datos estructurados sin mantener dos relatos distintos de lo que había que hacer.
No necesitábamos «más IA».
Necesitábamos un modelo de datos que representase mejor la realidad.
Un emoji nos enseñó quién tenía la última palabra
Después apareció un problema mucho más pequeño.
Precisamente por eso resultó tan revelador.
En una actualización, el asistente reconstruyó un título SEO y añadió un emoji que no debía estar allí.
No parecía grave y podía eliminarse en segundos.
Pero planteaba una pregunta bastante más importante.
¿Por qué estaba reconstruyendo ese dato?
WordPress ya tenía un título SEO válido. La automatización contaba además con el slug, el contenido visible, los datos estructurados y diferentes referencias internas. Todas eran señales relacionadas con la misma página, pero no todas tenían la misma autoridad.
Decirle al asistente «usa el DOM» no era suficiente.
Un DOM contiene muchas versiones parciales de la realidad.
A partir de ese fallo empezamos a definir qué fuente mandaba para cada campo.

En una actualización, el SEO existente podía tener prioridad sobre una inferencia construida desde el slug. Los datos de receta ya estructurados y válidos debían conservarse. La URL comercial tenía que proceder del catálogo canónico. Una decisión confirmada por el usuario podía prevalecer sobre una estimación anterior.
El cambio parecía técnico, pero contenía una idea aplicable a muchos otros casos.
Cuando una automatización maneja varias fuentes, importa menos preguntarse «¿qué información tengo?» que saber «cuál de estas fuentes tiene autoridad para decidir este dato».
Después apareció otra consecuencia.
Si una receta ya estaba bien, volver a procesarla no debería cambiarla solo porque el modelo encontrase otra forma de escribirla.
Introdujimos entonces la idempotencia como criterio editorial.
Una segunda ejecución debía corregir lo que estuviera mal, no reinventar por motivos estéticos lo que ya funcionaba.
También tuvimos que enseñar al sistema a decir «no lo sé»
Hubo otra inferencia que parecía completamente razonable.
Una receta utilizaba carne zamorana.
El sistema acabó clasificándola como cocina zamorana.
La relación tenía una lógica superficial.
Pero como criterio era falsa.
El origen de un ingrediente no determina automáticamente la tradición culinaria de una receta.
Este tipo de error tiene una dificultad especial porque ninguna validación de tipos puede detectarlo. El campo podía existir, contener uno de los valores permitidos y superar perfectamente un JSON Schema.
El problema estaba en el significado.
Por eso empezamos a distinguir de dónde procedía cada dato.
Un valor podía haberse extraído literalmente de una fuente, haberse generado, inferido, recomendado o decidido manualmente.
La trazabilidad no hacía correcta una inferencia.
Pero sí evitaba que todas las afirmaciones pareciesen tener el mismo grado de certeza.
Eso nos permitió definir mejor cuándo tenía que intervenir una persona.
Pero introducir una persona en cada paso tampoco es una buena arquitectura.
La cuestión era decidir qué podía resolver el software de forma determinista y qué necesitaba realmente criterio humano.
No queríamos aprobar manualmente cada campo porque habríamos convertido la automatización en una burocracia.
Queríamos intervenir solo cuando había una decisión de verdad.
- Una cantidad contradictoria
- Un tiempo que dos fuentes resolvían de forma distinta
- Una clasificación que exigía interpretar contexto
- Un posible problema de seguridad alimentaria
- Un dato necesario que no podía inferirse de forma suficientemente fiable
El objetivo dejó de ser «eliminar al humano del proceso».
Pasó a ser algo bastante más útil:
No hacerle perder tiempo con aquello que una máquina sí puede comprobar y reservar su criterio para lo que todavía lo necesita.
Cuando acabamos, habíamos construido casi lo contrario de la idea inicial
Al cerrar la fase de desarrollo y QA, el sistema estaba formado por CLZ Recetas 1.3.0, un contrato de datos 1.2.0 y un asistente especializado cuya configuración y documentación también habían evolucionado durante las pruebas.
Pero esa descripción técnica deja fuera lo más importante.
Cada componente existía porque antes algo había fallado.
| Lo que ocurrió | Lo que cambió |
|---|---|
| Un fichero llegó sin ingredientes. | La validación externa dejó de ser opcional. |
| Un guardrail bloqueó una publicación que no era una receta. | Se delimitó mejor cuándo tenía autoridad el sistema. |
| Un dato SEO cambió desde una señal secundaria. | Se definió la precedencia de fuentes por campo. |
| Una segunda ejecución podía modificar contenido ya válido. | La idempotencia pasó a ser un requisito. |
| La normalización reducía contenido útil. | El modelo de datos evolucionó para conservar el nombre y la explicación de cada fase. |
| Una inferencia plausible terminó tratándose como un hecho. | Se reforzó la trazabilidad sobre la procedencia de los datos. |
| La interfaz mostraba conceptos demasiado técnicos. | La herramienta empezó a hablar de tareas del editor en lugar de detalles de implementación. |
| Una confirmación duplicaba una protección existente. | Se eliminó un paso innecesario que no añadía seguridad real. |

Visto así, el desarrollo no fue una carrera por conceder más autonomía a la inteligencia artificial.
Fue una sucesión de decisiones para definir mejor sus límites.
Probablemente esa sea la enseñanza más valiosa que nos dejó el proyecto.
La automatización madura cuando también sabe detenerse
Cuando se trabaja con IA es fácil caer en una tentación.
Si ya consigue hacer ocho pasos, ¿por qué no dejarle hacer el noveno?
Después el décimo.
Y después publicar.
Nuestro proyecto avanzó justo en la dirección contraria.
Cuanto mejor entendíamos los posibles fallos, más clara quedaba la separación entre interpretar, validar y ejecutar.
Con la perspectiva que tenemos ahora, buena parte de aquella evolución puede interpretarse como un trabajo de harness engineering y hardening.
El modelo era solo una pieza. Alrededor fueron apareciendo el contrato, las validaciones, las reglas de precedencia, las comprobaciones del plugin, los registros y los puntos de intervención humana. Cada elemento reducía la distancia entre «la IA ha propuesto esto» y «el sistema puede ejecutar esto con unas condiciones conocidas».
Lo relevante no era hacer más inteligente al modelo. Era diseñar mejor todo lo que existía entre el modelo y WordPress.
Terminamos entendiendo mucho mejor qué problema estábamos resolviendo.

Al principio queríamos que una IA transformase recetas.
Al final habíamos diseñado un proceso en el que cada participante tenía una responsabilidad distinta, donde el modelo interpretaba y proponía, el software comprobaba aquello que podía verificar de forma determinista y una persona seguía tomando las decisiones que necesitaban contexto.
El sistema se volvió más controlable cuando dejamos de preguntarnos cuánto podíamos automatizar.
Empezamos a preguntarnos qué no debíamos automatizar sin una segunda barrera.
La misma pregunta sirve mucho más allá de un recetario.
- Fichas de producto
- Migraciones
- Metadatos
- Documentación
- Catálogos
- Procesos internos
Antes de conectar una IA con cualquiera de ellos conviene hacerse una pregunta incómoda:
Si mañana genera algo convincente, incorrecto y completamente inesperado, ¿qué existe entre esa respuesta y producción?
Si la respuesta es «nada», probablemente el siguiente paso no sea escribir un prompt mejor.
Puede que sea diseñar el sistema.
Si tienes un proceso repetitivo en WordPress y estás valorando automatizarlo, las soluciones de inteligencia artificial aplicada que aplicamos parten de esa decisión, la de entender primero qué merece automatización, qué datos y reglas gobiernan el proceso y dónde conviene mantener la supervisión.