Un arquitecto con IA y precios honestos
Describe un producto en un párrafo y Blueprint te dibuja un diagrama de sistema, un plan de tres fases, un equipo y un precio. Todo lo de esa lista es generado salvo lo único que de verdad le importa a la gente, y ese era todo el diseño.
En esta página
Un modelo de lenguaje que inventa tu precio no es una funcionalidad. Es un pasivo con una tipografía bonita. Alguien lee un número en nuestro sitio, hace una captura y aparece dos semanas después esperando que lo respetemos — y tendría razón. El número estaba en nuestra página.
Así que cuando construí Vaynerov Project Blueprint — pega una idea, obtén una arquitectura — escribí una regla antes de escribir una sola línea del prompt, y todo lo demás en la funcionalidad se deriva de ella: el modelo no pone precio a nada. Solo propone selecciones, y hasta esas se sanean antes de que el motor las vea.
Lo que sigue es la historia de una funcionalidad con IA en la que la ingeniería interesante está casi por completo alrededor del modelo.
- 8–12
- nodos repartidos en al menos 4 carriles
- 3
- fases, exactamente — impuesto dos veces
- $150–250
- banda de tarifa por unidad, de la calculadora
- 5 / 15 min
- generaciones por IP
El modelo propone; el motor pone el precio
La calculadora que hay detrás de /pricing es más antigua que Blueprint: entró con el propio sitio y en junio le creció una tabla de reglas. Es de lo más soso que puede ser el software: cada plataforma, funcionalidad, capacidad de IA, nivel de diseño y escalón de integración lleva un número de unidades base; se suman; la suma se multiplica por la complejidad del producto, después por un multiplicador de QA y después por uno de lanzamiento; el total ajustado se encuentra con una banda de tarifa por unidad y se convierte en un rango en dólares.
| Plataforma | Unidades base | Semanas de referencia |
|---|---|---|
| App web | 50 | 8 |
| iOS | 40 | 6 |
| Android | 40 | 6 |
| Backend / API | 30 | 5 |
| Panel de administración | 25 | 4 |
Cinco de las reglas por defecto del motor. Las funcionalidades se valoran igual: autenticación 8 unidades, pagos 15, tiempo real 10, un CMS de administración 20; IA con recuperación aumentada 25, agentes 30.
El calendario es la misma clase de aritmética. Se asume que las plataformas corren en paralelo, así que aportan el máximo de sus semanas de referencia en lugar de la suma; todo lo que no es una plataforma se convierte a quince unidades por semana; y luego dos semanas de colchón, porque siempre hay dos semanas.
Blueprint llama exactamente a ese motor, con exactamente esas reglas, cargadas desde la misma tabla calculator_rules que lee la página de precios — y si la tabla falta o es inalcanzable, los dos degradan a los mismos valores por defecto codificados y no a una suposición. Lo que aporta el modelo es una selección: una plataforma, un tipo de producto, una lista de funcionalidades. Después sanitizeSelections corre antes que el cálculo del precio. Los identificadores desconocidos se descartan en lugar de mapearse con buena voluntad. Los campos obligatorios de opción única que vuelven vacíos se rellenan con valores por defecto.
La parte que me tiene calladamente satisfecho: los enums del esquema JSON contra los que genera el modelo, el catálogo de opciones impreso en el prompt y la lista blanca del sanitizador se derivan todos del mismo objeto de reglas. Añade una funcionalidad a la calculadora y aparece en los tres. No pueden desincronizarse nunca, porque no hay nada que mantener sincronizado.
Salida estructurada, y dónde viven de verdad los límites
Blueprint usa Claude Opus 4.8 (claude-opus-4-8) con salida estructurada: un esquema JSON adjunto a la configuración de salida de la petición, en lugar de la vieja costumbre de definir una herramienta falsa y leer sus argumentos. La respuesta es JSON porque el decodificador quedó restringido a producir JSON, no porque lo pidiéramos con educación y después escribiéramos un parser con un try alrededor.
Esa restricción tiene una forma, eso sí, y no es la forma de una librería de validación. Un esquema que dirige la generación tiene que mantenerse simple: el nuestro, escrito a mano, pone additionalProperties: false y lista todas las propiedades como obligatorias, expresa los campos opcionales como anyOf: [{ type: "string" }, { type: "null" }] y no lleva ningún minItems, maxItems, minimum ni maximum en ninguna parte.
{
"type": "object",
"additionalProperties": false,
"required": ["id", "label", "lane", "tech", "note"],
"properties": {
"id": { "type": "string" },
"label": { "type": "string" },
"lane": { "enum": ["client", "edge", "services", "data", "infra"] },
"tech": { "type": "array", "items": { "type": "string" } },
"note": { "anyOf": [{ "type": "string" }, { "type": "null" }] }
}
}Un nodo del esquema del grafo. La opcionalidad es anyOf con null porque la gramática no tiene noción de clave obligatoria ausente; los recuentos y las longitudes están ausentes a propósito.
Los límites estrictos viven una capa más abajo, en zod, después del parseo: de tres a catorce nodos, treinta y dos aristas como mucho, un array de fases de longitud exactamente tres, de tres a seis roles de equipo, de dos a cuatro riesgos, un número de semanas entre uno y cuarenta y una idea de entre diez y mil caracteres. Así que el esquema garantiza la forma y zod garantiza la cordura, y el prompt pide en medio el objetivo estético: de ocho a doce nodos repartidos en al menos cuatro carriles con al menos una pieza de infraestructura, de ocho a dieciséis aristas, fases de dos a diez semanas con dos a cuatro entregables cada una. El único ejemplo few-shot del prompt es un sistema deliberadamente pequeño de seis nodos, porque un ejemplo es un pozo gravitatorio y prefiero que tire hacia abajo.
El prompt de sistema lleva también nuestra lista de palabras vetadas: las dos palabras de marketing que nunca se permiten en este sitio están nombradas y prohibidas para el modelo. Nunca ha sido la parte difícil del prompt, pero es la parte que reviso primero.
Todo lo que rodea al modelo
La petición tiene forma de caché a propósito. El caché de prompts es una coincidencia de prefijo, lo que significa que solo sobrevive si los bytes iniciales son idénticos absolutamente siempre — así que el prompt de sistema, el catálogo de opciones y el few-shot son estables byte a byte y están ordenados de forma determinista, y todo lo volátil (el idioma del visitante, su idea) viaja en el turno de usuario, que es su sitio. Ordenar el catálogo de opciones de forma no determinista nos habría costado el caché en cada petición sin hacer ruido, y nada habría parecido roto.
La idea del visitante se envuelve en etiquetas <idea> con una instrucción de que su contenido son datos, no instrucciones. Este no es un problema resuelto y no voy a fingir lo contrario. Es una reducción significativa de los ataques fáciles, en una superficie donde el peor resultado es un diagrama tonto y donde el precio — lo que de verdad vale la pena atacar — no es alcanzable desde el texto en absoluto.
La defensa más fuerte contra la inyección de prompts no es un prompt ingenioso. Es no hacer pasar nada valioso por el prompt.
Del lado de la respuesta, comprobamos stop_reason antes de mirar el contenido. Una negativa se convierte en un código refused; chocar contra el techo de 16,000 tokens se convierte en truncated; cada clase de error interno se mapea a un código pequeño y seguro para el cliente, y el texto crudo del error no sale nunca del servidor — el mensaje de un proveedor upstream es un detalle de nuestra infraestructura, no material de lectura para el visitante. Todo va en streaming, pero solo para mantener viva la conexión bajo una duración máxima de dos minutos; tomamos el mensaje final y lo validamos entero.
Después sanitizeBlueprint, que es indulgente por diseño, porque el objetivo es un grafo renderizable y no uno perfecto. Recorta más allá del techo de catorce nodos, deduplica ids, elimina bucles sobre sí mismos y aristas que apuntan a nodos inexistentes, poda las referencias de nodo de una fase que ya no resuelven y añade los nodos huérfanos a una fase declarada para que nada quede flotando. Devuelve null solo cuando el resultado es estructuralmente inservible. No hay bucle de reintento en la v1, una limitación que elegí y no una que descubriera: un reintento duplicaría la latencia del peor caso para rescatar una salida que no sabríamos describir de antemano.
El transporte son eventos enviados por el servidor, y la ruta responde siempre **HTTP 200 con text/event-stream**: los fallos llegan como un evento error dentro del stream, así que el cliente consume exactamente un protocolo en ambos casos y no tiene una segunda ruta de código pudriéndose. Cuatro tramas de estado se disparan en hitos reales y no por temporizador: en cola, redactando, validando, calculando el precio. El lector SSE del cliente está hecho a mano sobre fetch y un ReadableStream, partiendo por líneas en blanco, porque el EventSource del navegador no puede hacer un POST.
Un modo demo que no miente
Cuando no hay clave de API configurada, Blueprint sigue funcionando. El servidor informa de que la generación no está disponible, el cliente no llama nunca a la ruta y cuatro arquitecturas de referencia escritas a mano — un producto SaaS, un marketplace, una herramienta de soporte, una plataforma IoT — se emparejan por palabras clave con lo que hayas escrito. El progreso está escenificado para que la página siga pareciendo que piensa.
Y aquí lo que importa: los precios de la demo son reales. No están sacados del archivo de referencia. Pasan por calculateEstimate con las mismas reglas por defecto, así que los números que ve un visitante con el modelo apagado son los números que produciría /pricing para la misma configuración. Una demo que cotiza dinero falso es una demo que le enseña al visitante que nuestro dinero es falso.
Los controles antiabuso son baratos y un poco malvados. Hay un campo trampa aparcado muy lejos del borde izquierdo del viewport; un bot que lo rellena recibe un éxito completamente convincente — un blueprint enlatado, precios correctos, un diagrama animado — sin que gastemos ni un token, y si además pide un presupuesto, recibe un token con aspecto válido que da 404. El tráfico real está limitado a cinco generaciones cada quince minutos por dirección, contadas por una función atómica de ventana fija en Postgres, con un mapa en memoria como respaldo cuando la base de datos no está disponible.
Dibujar el diagrama sin canvas alguno
La salida de Blueprint es un diagrama de sistema y, tras una larga temporada de WebGL, disfruté construyendo este en SVG puro. Sin canvas, sin three.js, sin pasada de medición. El layout es un módulo puro sin React y sin acceso al DOM: cinco carriles fijos — cliente, borde, servicios, datos, infraestructura —, nodos de 148 por 54 unidades, columnas de 176 de ancho, un mínimo de cuatro columnas.
Esas tres constantes son una demostración, no una preferencia. El ancho de columna supera al ancho de nodo, los nodos se ordenan por fase y luego por orden original y se reparten de forma uniforme entre las columnas disponibles, así que dos nodos no pueden solaparse con ningún número de nodos hasta el techo: no hay pasada de colisiones, porque las colisiones son imposibles. Todos los fallos de layout que he publicado en mi vida estaban en una pasada de colisiones.
Las aristas son Béziers cúbicas. Las conexiones dentro del mismo carril arquean por encima de su carril un 85 % de la altura de un nodo para no atravesar las cajas intermedias; las conexiones entre carriles tiran al menos 26 unidades, escaladas según la distancia vertical recorrida. Los tres tipos de arista se distinguen por patrón de guiones y no por color — síncrona continua, asíncrona de guion largo, flujo de datos punteado fino — con leyenda, porque el color por sí solo no es información que le llegue a todo el mundo.
Unas cuantas decisiones menores que volvería a tomar. La opacidad transmite significado: un nodo filtrado por fase se queda en 0.14, un no vecino del nodo bajo el cursor en 0.22, un nodo fuera de la fase señalada en 0.30 — tres niveles de ahora mismo no. Los chips de tecnología se presupuestan en ancho aritméticamente a partir del número de caracteres, con un tope de dos chips y truncado a trece caracteres, porque SVG no tiene reflujo y el texto que se sale de una caja en SVG simplemente sigue. Los nodos son botones de verdad para el teclado, con Enter y Espacio, y los pulsos que viajan por las aristas se desactivan del todo cuando el visitante ha pedido movimiento reducido — la barra de progreso, por su parte, cambia su barrido indeterminado por un relleno proporcional estático.
El presupuesto se vuelve a calcular en el servidor
Cuando a un visitante le gusta lo que ve y pide un presupuesto real, el navegador envía las selecciones. También envía los números, y el servidor los ignora por completo. Campo trampa, después validación de esquema, después el límite de tasa, después sanear otra vez las selecciones, después volver a cargar las reglas, después ejecutar la estimación otra vez — en el servidor, desde cero — y solo ese resultado se escribe en el registro del presupuesto. El comentario que hay encima tiene cuatro palabras y hace el trabajo: nunca confíes en números del cliente.
Lo que devuelve la funcionalidad a su punto de partida. El diagrama que te entrega Blueprint es una propuesta: el esbozo de una forma razonable que podría tomar tu producto, y a un esbozo se le puede discutir. Para eso está la primera conversación con un estudio. El precio no es un esbozo. Salió del mismo motor, de la misma tabla de reglas, que cualquier otro número de este sitio, y seguirá siendo ese número cuando le hagas una captura.