en
· 3 min de leitura

Um orçamento de latência para features com LLM

O modelo é a dependência mais lenta que você já adicionou. Orce como orça um banco de dados, por feature, por etapa.

aiperformance

Antes dos modelos de linguagem, a coisa mais lenta na maioria dos meus sistemas era uma query no banco com cache frio, medida em dezenas de milissegundos. Uma chamada de modelo é medida em segundos, e a cauda em dezenas de segundos. É outro regime. Não dá para pendurar um componente desses num caminho de requisição projetado para milissegundos e esperar que a experiência do usuário sobreviva. É preciso orçar, e o orçamento tem que partir do usuário, não do modelo.

Para onde o tempo vai

Uma chamada de modelo tem três partes. Tempo até o primeiro token, dominado pelo tamanho da entrada e pela fila no provedor. Tempo de geração, mais ou menos linear nos tokens de saída. E tudo em volta da chamada: retrieval, montagem do prompt, validação, retries. Features agênticas multiplicam tudo isso pelo número de passos. Quando um time me diz que uma feature está lenta, peço um trace com esses segmentos separados, porque a correção para entrada lenta é diferente da correção para saída longa, e a correção para um loop tagarela é diferente das duas.

Defina o orçamento a partir do usuário, não do modelo

O orçamento é o tempo máximo que um usuário tolera para essa ação, nesse contexto, antes de a feature parecer quebrada. Para uma sugestão inline, é menos de um segundo. Para uma resposta tipo busca, são alguns segundos com progresso visível. Para um relatório em background podem ser minutos, desde que o usuário não esteja olhando um spinner. Escrevo esse número primeiro e projeto de trás para frente: o que cabe, o que precisa ser transmitido em streaming, o que precisa sair do caminho da requisição por completo. Uma feature projetada de frente para trás, a partir do que o modelo leva, termina com um spinner e um usuário perdido.

Alavancas

Com o orçamento definido, as alavancas são concretas. Envie menos tokens de entrada: prompts mais curtos, retrieval mais enxuto, histórico resumido. Peça menos tokens de saída: structured outputs em vez de prosa, resposta curta com explicação expansível. Use um modelo pequeno nas etapas que não precisam de um de fronteira. Paralelize chamadas independentes em vez de encadear. Cacheie o que se repete, inclusive as respostas do próprio modelo para requisições idênticas. E defina timeouts que refletem o orçamento, com um fallback útil, porque uma requisição que leva vinte segundos e depois falha custou ao usuário mais do que um erro honesto e imediato.

Streaming e latência percebida

Streaming não deixa o modelo mais rápido. Deixa a espera parecer mais curta, porque o usuário vê progresso em vez de nada, e isso vale muito. Mas streaming tem custo no backend: a conexão fica aberta, o servidor segura estado, e cada camada no meio precisa repassar os chunks sem bufferizar. Também muda o que dá para validar, porque não se checa um schema em metade de um objeto JSON. Minha regra é transmitir para a pessoa e não para o programa: texto que um humano lê vai em streaming, saída estruturada que um sistema consome é entregue inteira, depois da validação.

Meça o p95, não a demo

A demo roda no p50 numa tarde tranquila. Produção roda no p95 na hora mais cheia do provedor, com retry. Meço o orçamento completo por feature na cauda, com a chamada do modelo separada do resto, e alerto pela cauda, não pela média. Uma feature com mediana de dois segundos e p95 de dezoito não é uma feature de dois segundos. É uma feature de dezoito segundos que às vezes é rápida, e os usuários que pegam os dezoito segundos são os que escrevem as avaliações. O orçamento é uma promessa, e a cauda é onde promessas são cumpridas ou quebradas.