O Gargalo Não É o Modelo — É a Ida e Volta
Fala, dev! Vamos ser honestos: se o seu sistema de Text2SQL demora 25–30 segundos pra responder uma pergunta de negócio, ninguém vai usar. Essa é a real por trás de quase todo "demo que funciona" que nunca chega em produção — cada pergunta em linguagem natural dispara uma chamada completa pra um LLM, e cada chamada carrega junto o schema do banco, exemplos few-shot e instruções de domínio. Facilmente 60K tokens de entrada pra gerar algumas centenas de tokens de saída.
Cache tradicional não funciona com IA generativa. Usuário nunca pergunta a mesma coisa do mesmo jeito, e cachear a resposta quebra no momento em que os dados mudam (um total de vendas do Q3 cacheado hoje vira mentira amanhã). Mas cachear a query SQL — a representação determinística e estruturada da intenção — resolve os dois problemas de uma vez. A query continua válida mesmo com os dados mudando, e ataca exatamente o passo mais caro do pipeline.
Neste artigo, a gente abre a arquitetura por trás de uma redução de 80% na latência e mais de 50% de economia em tokens, direto de um deploy real. Se você quer entender o contexto de infraestrutura que viabiliza esse tipo de pipeline, dá uma olhada na nossa análise sobre economia de inferência em cloud e design de aceleradores.

A Ideia Central: Generalizar Queries em Templates
Olha só essas duas queries:
-- Vendas do Q3
SELECT SUM(revenue) FROM sales WHERE quarter = 'Q3';
-- Vendas do Q2
SELECT SUM(revenue) FROM sales WHERE quarter = 'Q2';
Mesma estrutura, só muda o valor do filtro. Generaliza:
SELECT SUM(revenue) FROM sales WHERE quarter = '{quarter}';
Um template cobre uma família inteira de perguntas. O pipeline fica assim:
- Extração de entidades — Puxa datas, nomes, categorias e valores numéricos da pergunta usando um modelo leve de NER (Amazon Nova 2 Lite ou NER customizado).
- Recuperação semântica de templates — Gera embedding da pergunta e faz busca vetorial contra os templates em cache. Como embeddings capturam significado, "Me mostra as vendas do Q3" e "Qual foi o faturamento no terceiro trimestre" caem no mesmo template.
- Preenchimento e execução — Mapeia entidades nos placeholders, valida formatos e executa via prepared statements parametrizados (nunca interpolação de string).
- Geração de resposta e checagem de suficiência — Um modelo pequeno (tipo Claude Haiku 4.5) confirma se o resultado realmente responde à pergunta e depois resume.
- Fallback + loop de reforço — Em caso de miss, gera SQL normalmente, generaliza a nova query em template e adiciona ao cache junto com o embedding.
Um esqueleto em Python da etapa de retrieval + fill:
import numpy as np
from typing import Optional
def retrieve_and_fill(
question: str,
entities: dict,
template_store: list[dict],
embed_fn,
threshold: float = 0.82,
) -> Optional[str]:
"""Casa a pergunta com um template SQL em cache e preenche os placeholders."""
q_vec = embed_fn(question)
best_score, best_template = -1.0, None
for entry in template_store:
score = float(np.dot(q_vec, entry["embedding"]))
if score > best_score:
best_score, best_template = score, entry["sql_template"]
# Rejeita matches com pouca confiança para não responder com a query errada
if best_score < threshold or best_template is None:
return None
# Preenche placeholders com entidades extraídas (validadas antes)
try:
return best_template.format(**entities)
except KeyError:
return None
Por que isso ganha de cachear resposta: o template SQL é independente dos dados, então não precisa invalidar nada. Executa contra o banco ao vivo e sempre traz resultado fresco.
Segurança: as entidades são validadas contra formatos esperados (um {quarter} precisa ser um valor conhecido, uma {date} precisa parsear) antes de chegar na query, e os placeholders são ligados via prepared statements — ou seja, valores são tratados como dados, nunca como SQL executável.

Os Números (e as Ressalvas)
| Caminho | Chamadas LLM | Latência Típica | Custo de Tokens |
|---|---|---|---|
| Sem cache | 1 (geração SQL) + 1 (resumo) | 25–30s | ~60K in + algumas centenas out |
| Cache hit | 1 (resumo + suficiência) | <5s | ~2K in |
| Cache miss | 3 (suficiência + geração SQL + resumo) | um pouco acima do sem cache | +1 chamada pequena |
Com a taxa de ~60% de hit observada depois de duas semanas em produção, a economia combinada passa de 50% em tokens e chega a cerca de 80% de redução de latência nos hits — mais ou menos 6x mais rápido.
Limitações e Cuidados
- A taxa de hit depende do domínio. Domínio estreito e repetitivo (schema único, poucos formatos de query) passa fácil de 70%. Analytics exploratório e amplo pode não chegar em 30%.
- Ajustar o threshold é trade-off entre precisão e recall. Muito alto rejeita paráfrases válidas; muito baixo deixa templates vagamente relacionados responderem com confiança a pergunta errada. Adicione um reranker leve (LLM pequeno ou cross-encoder) quando similaridade de embedding não for suficiente.
- Cache miss custa mais, não menos. A checagem de suficiência adiciona uma chamada. A economia só fecha com taxa de hit saudável.
- Extração de entidades falha em silêncio. Se o NER erra "mês passado" ou "minha região", você preenche o template com lixo. Valide com força e logue rejeições.
- Templates podem ficar defasados do schema. Uma migration que renomeia uma coluna quebra silenciosamente todo template que a referencia — precisa de validação schema-aware na escrita do cache.
Próximos Passos
- Adicione reranking em cima da busca vetorial em domínios críticos.
- Considere execução paralela top-K de templates pra devolver respostas mais ricas (totais e quebras) com latência mínima extra.
- Monte um dashboard de observabilidade do cache logando templates casados, scores de similaridade e motivos de rejeição — é aqui que você vai realmente calibrar os thresholds.
- Se você está otimizando a infraestrutura ao redor, vale ver como APIs nativas do navegador estão redefinindo tooling de frontend — é uma lição paralela sobre trocar abstrações pesadas por primitivas.

O Resumo da Ópera
Esse padrão vai muito além de Text2SQL. Em qualquer lugar onde requisições parecidas devem gerar saídas estruturalmente parecidas, cachear a estrutura (não a resposta) mais matching semântico na entrada é uma estratégia vencedora — pense em geração de código, templates de relatório, síntese de chamadas de API ou orquestração de workflow.
A mudança mental: pare de tratar o LLM como o pipeline inteiro. Trate como um passo caro que você frequentemente consegue pular. Todo o resto — extração de entidades, busca vetorial, binding de template, sumarização — roda em modelos baratos ou código puro.
Comece pequeno: instrumente o pipeline atual, meça quanto da latência e dos tokens vai pra chamada de geração SQL, e coloque um cache de template na frente. Mesmo uma taxa de 30% de hit se paga em poucas semanas.
Leitura complementar:
- Gerando valor a partir de dados corporativos: boas práticas para Text2SQL
- Amazon S3 Vectors: suporte vetorial nativo em escala
- Solução Text-to-SQL com Amazon Bedrock