Skip to content

Vaynerov Technologies

Não nos limitamos a desenvolver — conjuramos cada linha de código e cada píxel.

Todos os artigosHistórias de construção

Um arquiteto de AI com preços honestos

Descreva um produto num parágrafo e o Blueprint desenha-lhe um diagrama de sistema, um plano de três fases, uma equipa e um preço. Tudo nessa lista é gerado, exceto a única coisa que às pessoas mais interessa — e foi esse todo o desenho.

Edward AmirainFundador, Vaynerov Technologies
Publicado 11 min de leitura
Nesta página
  1. O modelo propõe; o motor orça
  2. Saída estruturada, e onde vivem mesmo os limites
  3. Tudo o que está à volta do modelo
  4. Um modo de demonstração que não mente
  5. Desenhar o diagrama sem canvas nenhum
  6. O orçamento é calculado outra vez, no servidor

Um modelo de linguagem que inventa o seu preço não é uma funcionalidade. É uma responsabilidade com um bom tipo de letra. Alguém lê um número no nosso site, tira uma captura de ecrã e aparece duas semanas depois à espera que o honremos — e teria toda a razão. O número estava na nossa página.

Por isso, quando construí o Vaynerov Project Blueprint — cole uma ideia, receba uma arquitetura —, escrevi uma regra antes de escrever uma única linha do prompt, e tudo o resto na funcionalidade decorre dela: o modelo nunca orça nada. Apenas propõe seleções, e mesmo essas são sanitizadas antes de o motor as ver.

O que se segue é uma história de construção sobre uma funcionalidade de AI em que a engenharia interessante está quase toda à volta do modelo.

8–12
nós, em pelo menos 4 camadas
3
fases, exatamente — garantidas duas vezes
$150–250
faixa de taxa unitária, vinda da calculadora
5 / 15 min
gerações por IP
A linha que me recuso a mover. Tudo o que está à esquerda é uma proposta; tudo o que está à direita é aritmética que /pricing produziria para as mesmas seleções.

O modelo propõe; o motor orça

A calculadora por trás de /pricing é mais velha do que o Blueprint — entrou com o próprio site e ganhou uma tabela de regras em junho. É tão pouco glamorosa quanto o software consegue ser: cada plataforma, funcionalidade, capacidade de AI, nível de design e patamar de integração traz uma contagem de unidades de base; somam-se; a soma é multiplicada pela complexidade do produto, depois por um multiplicador de QA, depois por um multiplicador de lançamento; o total ajustado encontra uma faixa de taxa unitária e torna-se um intervalo em dólares.

PlataformaUnidades de baseSemanas de referência
App web508
iOS406
Android406
Backend / API305
Painel de administração254

Cinco das regras por omissão do motor. As funcionalidades são orçadas da mesma maneira — autenticação 8 unidades, pagamentos 15, tempo real 10, um CMS de administração 20; AI com geração aumentada por recuperação 25, agentes 30.

O prazo é aritmética do mesmo género. Assume-se que as plataformas correm em paralelo, portanto contribuem com o máximo das suas semanas de referência e não com a soma; tudo o que não é plataforma converte-se a quinze unidades por semana; depois duas semanas de folga, porque há sempre duas semanas.

O Blueprint chama exatamente esse motor, com exatamente essas regras, carregadas da mesma tabela calculator_rules que a página de preços lê — e, se a tabela faltar ou estiver inacessível, ambos degradam para os mesmos valores por omissão escritos no código, e não para um palpite. O que o modelo contribui é uma seleção: uma plataforma, um tipo de produto, uma lista de funcionalidades. Depois sanitizeSelections corre antes de haver orçamento. Os identificadores desconhecidos são descartados em vez de mapeados com boa vontade. Os campos obrigatórios de escolha única que voltaram vazios são preenchidos por omissão.

A parte de que estou discretamente satisfeito: os enums do esquema JSON contra os quais o modelo gera, o catálogo de opções impresso no prompt e a lista de permissões do sanitizador derivam todos do mesmo objeto de regras. Acrescente-se uma funcionalidade à calculadora e ela aparece nos três. Nunca podem afastar-se, porque não há nada para manter em sincronia.

Saída estruturada, e onde vivem mesmo os limites

O Blueprint usa o Claude Opus 4.8 (claude-opus-4-8) com saída estruturada — um esquema JSON anexado à configuração de saída do pedido, em vez do velho hábito de definir uma ferramenta falsa e ler os seus argumentos. A resposta é JSON porque o descodificador foi restringido a produzir JSON, e não porque pedimos com jeitinho e depois escrevemos um parser com um try à volta.

Essa restrição tem uma forma, porém, e não é a forma de uma biblioteca de validação. Um esquema que guia a geração tem de se manter simples: o nosso, escrito à mão, define additionalProperties: false e lista todas as propriedades como obrigatórias, exprime os campos opcionais como anyOf: [{ type: "string" }, { type: "null" }] e não traz nenhum minItems, maxItems, minimum ou maximum em lado nenhum.

{
  "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" }] }
  }
}

Um nó do esquema do grafo. A opcionalidade é anyOf-com-null porque a gramática não tem noção de uma chave obrigatória ausente; contagens e comprimentos estão deliberadamente ausentes.

Os limites apertados vivem uma camada abaixo, em zod, depois da análise: três a catorze nós, no máximo trinta e duas ligações, um array de fases com exatamente três elementos, três a seis papéis na equipa, dois a quatro riscos, uma contagem de semanas entre um e quarenta, e uma ideia entre dez e mil caracteres. Ou seja: o esquema garante a forma e o zod garante a sanidade, e o prompt pede pelo meio o alvo estético — oito a doze nós distribuídos por pelo menos quatro camadas com pelo menos uma peça de infraestrutura, oito a dezasseis ligações, fases de duas a dez semanas com duas a quatro entregas cada. O único exemplo few-shot do prompt é um sistema deliberadamente pequeno, de seis nós, porque um exemplo é um poço gravítico e prefiro que puxe para baixo.

O prompt de sistema carrega também a lista de proibições do nosso estilo da casa — as duas palavras de marketing que nunca são permitidas neste site estão lá nomeadas e vedadas ao modelo. Nunca foi a parte difícil do prompt, mas é a parte que verifico primeiro.

Tudo o que está à volta do modelo

O pedido tem a forma que tem por causa da cache. O caching de prompts é uma correspondência de prefixo, o que significa que só sobrevive se os primeiros bytes forem idênticos todas as vezes — por isso o prompt de sistema, o catálogo de opções e o few-shot são estáveis ao byte e ordenados de forma determinística, e tudo o que é volátil (a língua do visitante, a ideia do visitante) viaja no turno do utilizador, onde pertence. Ordenar o catálogo de opções de forma não determinística ter-nos-ia custado a cache em todos os pedidos, em silêncio, e nada teria parecido avariado.

A ideia do visitante é embrulhada em etiquetas <idea> com uma instrução de que o seu conteúdo são dados, não instruções. Isto não é um problema resolvido e não vou fingir o contrário. É uma redução significativa dos ataques fáceis, numa superfície onde o pior resultado é um diagrama disparatado, e onde o preço — a coisa que vale a pena atacar — não é sequer alcançável a partir do texto.

A melhor defesa contra a injeção de prompts não é um prompt inteligente. É não fazer passar nada de valioso pelo prompt.

Do lado da resposta, verificamos o stop_reason antes de olhar para o conteúdo. Uma recusa torna-se um código refused; bater no teto dos 16 000 tokens torna-se truncated; cada classe de erro interno mapeia para um código pequeno e seguro para o cliente, e o texto de erro em bruto nunca sai do servidor — a mensagem de um fornecedor a montante é um detalhe da nossa infraestrutura, não material de leitura para o visitante. Tudo é transmitido em streaming, mas apenas para manter a ligação viva sob uma duração máxima de dois minutos; pegamos na mensagem final e validamo-la inteira.

Depois sanitizeBlueprint, que é permissivo por desenho, porque o objetivo é um grafo renderizável e não um grafo perfeito. Corta acima do teto dos catorze nós, elimina ids duplicados, descarta laços sobre si próprios e ligações que apontam para nós inexistentes, poda as referências de fase a nós que já não resolvem e acrescenta os nós órfãos a uma fase declarada para que nada fique a flutuar. Só devolve null quando o resultado é estruturalmente inutilizável. Não há ciclo de repetição na v1, o que é uma limitação que escolhi e não uma que descobri: uma repetição duplicaria a latência do pior caso para salvar um resultado que não conseguíamos descrever de antemão.

O transporte são server-sent events, e a rota responde sempre **HTTP 200 com text/event-stream** — as falhas chegam como um evento error dentro do stream, portanto o cliente consome exatamente um protocolo em qualquer dos casos e não tem um segundo caminho de código a apodrecer. Quatro tramas de estado disparam em marcos reais e não a temporizador: em fila, a desenhar, a validar, a orçar. O leitor de SSE do cliente é feito à mão sobre fetch e uma ReadableStream, a dividir em linhas em branco, porque o EventSource do navegador não consegue fazer POST.

Um modo de demonstração que não mente

Quando não há chave de API configurada, o Blueprint continua a funcionar. O servidor reporta a geração como indisponível, o cliente nunca chega a chamar a rota, e quatro blueprints de referência escritos à mão — um produto SaaS, um marketplace, uma ferramenta de suporte, uma plataforma IoT — são associados por palavras-chave ao que quer que tenha escrito. O progresso é encenado para a página continuar a parecer que está a pensar.

E aqui está a parte que importa: os preços da demonstração são reais. Não são retirados do ficheiro de referência. Passam por calculateEstimate com as mesmas regras por omissão, portanto os números que um visitante vê com o modelo desligado são os números que /pricing produziria para a mesma configuração. Uma demonstração que orça dinheiro falso é uma demonstração que ensina aos visitantes que o nosso dinheiro é falso.

Os controlos antiabuso são baratos e ligeiramente malvados. Há um campo-armadilha estacionado bem à esquerda, fora do ecrã; um bot que o preencha recebe um sucesso completamente convincente — um blueprint enlatado, preços corretos, um diagrama animado — sem que gastemos um único token e, se ainda por cima pedir orçamento, recebe um token com bom aspeto que dá 404. O tráfego real está limitado a cinco gerações por quinze minutos e por endereço, contadas por uma função atómica de janela fixa em Postgres, com um mapa em memória como alternativa quando a base de dados está inacessível.

Desenhar o diagrama sem canvas nenhum

O resultado do Blueprint é um diagrama de sistema e, depois de uma longa época de WebGL, dei-me ao gosto de construir este em SVG simples. Sem canvas, sem three.js, sem passagem de medição. O layout é um módulo puro sem React e sem acesso ao DOM: cinco camadas fixas — cliente, edge, serviços, dados, infraestrutura —, nós de 148 por 54 unidades, colunas de 176 de largura, um mínimo de quatro colunas.

Essas três constantes são uma prova, não uma preferência. A largura da coluna excede a largura do nó, os nós são ordenados por fase e depois pela ordem original e distribuídos uniformemente pelas colunas disponíveis, portanto dois nós não se podem sobrepor com nenhum número de nós até ao teto — não há passagem de deteção de colisões, porque as colisões são impossíveis. Todos os defeitos de layout que alguma vez lancei estavam numa passagem de colisões.

As ligações são Béziers cúbicas. As ligações dentro da mesma camada arqueiam por cima dela em 85% da altura de um nó, para nunca atravessarem as caixas que ficam pelo meio; as ligações entre camadas puxam pelo menos 26 unidades, escaladas com a distância vertical percorrida. Os três tipos de ligação distinguem-se pelo padrão do traço e não pela cor — síncrona a cheio, assíncrona a tracejado longo, fluxo de dados finamente pontilhado —, com legenda, porque a cor sozinha não é informação que chegue a toda a gente.

Cinco camadas, ligações codificadas por traço, uma garantia rígida de não sobreposição vinda da aritmética das colunas. A cor da camada é decoração; o padrão do traço é a informação.

Algumas decisões mais pequenas que voltaria a tomar. A opacidade transporta significado: um nó filtrado por fase fica a 0.14, um nó que não é vizinho do nó sob o cursor a 0.22, um nó fora da fase focada a 0.30 — três graus de agora não. As etiquetas de tecnologia têm a largura orçada aritmeticamente a partir da contagem de caracteres, limitadas a duas etiquetas e truncadas aos treze caracteres, porque o SVG não tem refluxo e o texto que transborda de uma caixa em SVG simplesmente continua. Os nós são botões a sério para o teclado, com Enter e Espaço, e os pulsos que viajam ao longo das ligações são desativados por completo quando o visitante pede movimento reduzido — a barra de progresso troca igualmente a sua varredura indeterminada por um enchimento proporcional estático.

O orçamento é calculado outra vez, no servidor

Quando um visitante gosta do que vê e pede um orçamento a sério, o navegador envia as seleções. Também envia os números, e o servidor ignora-os por completo. Campo-armadilha, depois validação de esquema, depois o limite de tráfego, depois sanitizar outra vez as seleções, depois ir buscar outra vez as regras, depois correr outra vez a estimativa — no servidor, de raiz — e só esse resultado é escrito no registo do orçamento. O comentário por cima é curtíssimo e faz o trabalho todo: nunca confiar nos números do cliente.

O que traz a funcionalidade de volta ao ponto de partida. O diagrama que o Blueprint lhe entrega é uma proposta — um esboço de uma forma razoável que o seu produto poderia ter, e um esboço tem licença para ser discutido. É para isso que serve a primeira conversa com um estúdio. O preço não é um esboço. Saiu do mesmo motor, da mesma tabela de regras, que todos os outros números deste site, e continuará a ser esse número quando lhe tirar a captura de ecrã.