
← Voltar ao blog
LLM UX limitado: inferência em dois saltos, streaming SSE, aterramento estruturado e os limites do produto que mantêm um portfólio de bate-papo honesto.
A maioria dos portfólios deixa de evoluir. Eu queria que este se comportasse um pouco como um produto: i18n-first, UI compartilhada em um monorepo e uma camada Lucas AI que responde em primeira pessoa sobre meu trabalho — somente a partir de contexto estruturado incorporado na implantação. Este post é para engenheiros que desejam a visão geral do sistema: forma de inferência, streaming, aterramento e metas explícitas — não um tour por onde os arquivos estão ou como executar um fork localmente.
Páginas estáticas são ruins para acompanhamentos. Se sua vantagem é julgamento — escopo, trade-offs, como você entregou sob restrição — um PDF e um formulário de contato não escalam curiosidade. O Lucas AI é um destino dedicado (/[locale]/ai): pergunte sobre experiência, o site ou o próprio recurso, sem fingir que estou do outro lado do fio em tempo real.
O navegador possui a UI de transcrição; o servidor possui política e gastos. Cada envio é um POST /api/chat com { message, locale } — sem anexos, sem chamadas de ferramentas, sem campos ocultos.
O manipulador constrói um sistema, encaminha para o Groq's OpenAI-compatible /v1/chat/completions, e proxya o fluxo upstream como Server-Sent Events (SSE) para que os tokens sejam renderizados incrementalmente sem armazenar o preenchimento completo na memória na borda. Esse é o mesmo padrão que você usaria por trás de qualquer API de inferência rápida: trate a rota como um adaptador fino, mantenha o formato de fio estável para o cliente.
O aterramento vive no código da aplicação: um objeto de carreira tipado é formatado uma vez em uma string CONTEXT e injetado na mensagem do sistema. Não há vetor de banco de dados e nenhum passo de recuperação no momento da solicitação — o modelo só vê o que você serializou quando construiu o prompt. Essa é uma troca de capacidade (você não pode responder de documentos arbitrários) em troca de latência previsível, custo e superfície de auditoria.
llama-3.3-70b-versatile no Groq (sobrecarga GROQ_MODEL). O streaming permanece ligado; a rota permite até 60s de geração — o suficiente para uma resposta cuidadosa sem transformar a borda em um trabalhador ilimitado.llama-3.1-8b-instant (configurável como CLASSIFIER_MODEL). É uma chamada separada, não de streaming, com max_tokens: 5, temperature: 0, e um prompt de sistema minimalista que colapsa a decisão em um único token: CAREER ou OFF_TOPIC.Se o veredito for OFF_TOPIC, a API nunca chama o grande modelo. Ela transmite uma recusa localizada fixa como SSE para que o código do cliente corresponda a uma conclusão “real” — sem ramificação de UI especial para curto-circuito. Se o classificador errar ou expirar, o manipulador falha aberta e executa o modelo principal de qualquer forma: um portão barato não deve se tornar um único ponto de falha para tráfego legítimo.
O portão ainda pode ser desabilitado no host (ambiente do servidor) quando você aceita conscientemente cada turno atingindo o grande modelo — útil se o endpoint pequeno estiver doente, se você estiver testando a carga da resposta sozinho, ou se as políticas do produto mudarem e você abandonar temporariamente o gating. Esse controle vive fora da visão do visitante; é uma alavanca de operações, não uma bandeira de recurso na UI.
Orçamento de saída: assistente max_tokens é limitado (CHAT_MAX_TOKENS, limitado a 256–8192, padrão 2048). Ele combina UX (“as respostas devem terminar”) com economia de unidade na fatura do provedor.
Cada solicitação contém:
message: a vez atual do usuário, aparada, com um teto de 2 000 caracteres (redução de superfície de abuso e injeção de prompt).locale: normalizado para um locale de site suportado. A mensagem do sistema termina com Visitor locale: … para que o modelo responda na idioma do site, não no idioma que a UI do chrome acontece de usar.O que não enviamos:
Então “memória” é: CONTEXT publicado + o que ainda é visível no cliente. Essa é uma fronteira deliberada: nenhum histórico de bate-papo do lado do servidor, nenhuma sincronização entre dispositivos, nenhum treinamento em conversas.
A UI persiste mensagens em sessionStorage (lucas-ai-messages). Atualizar na mesma guia mantém a thread; uma nova guia ou dispositivo começa limpa. Há uma segunda chave, lucas-ai-pending, usada quando redirecionamos o usuário após uma troca de locale (mais abaixo).
Se GROQ_API_KEY estiver ausente, a rota ainda retorna SSE: ela transmite a string de erro de configuração localizada para que o shell degrade sem jogar fora o layout.
O corpus de carreira começa como registros tipados — papéis, pilhas, iniciativas, métricas, tipo (trabalho vs voluntário vs educação vs pessoal), intervalos de tempo e campos narrativos opcionais. Um formatador transforma isso em um bloco markdown-ähnlich envolvido em delimitadores explícitos, por exemplo:
--- CONTEXT (verdade; não contradiga ou estenda além disso) ---
…
--- Fim do contexto ---
O que entra importa tanto quanto o que fica fora:
contexto / problema / solução / impacto quando você deseja que o modelo fale em resultados, não em palavras-chave.Sobreposto, o sistema é uma lei estrita: primeira pessoa, não fingindo estar ao vivo no Slack, sem acesso a sistemas privados, nenhum “Lucas disse…” de terceira pessoa, CONTEXT como única fonte factual e nenhuma fabricação além do que o CONTEXT afirma para detalhes de implementação.
Essa é como você transforma “não alucine” de uma vibe em escopo testável: o modelo é tão inteligente quanto o pacote que você envia, e o pacote é versionado como código.
O trabalho do pequeno modelo é apenas roteamento, não utilidade. CAREER é definido de forma ampla: histórico, habilidades, trabalho enviado, julgamento de produto e perguntas legítimas sobre o próprio site — pilha, pipeline de localização, como o assistente é conectado — na medida em que essa informação existe no CONTEXT. OFF_TOPIC captura tudo o mais (tempo, lição de casa, trivia não relacionada).
Tratar “meta” perguntas como dentro do escopo é uma decisão de produto: um assistente de portfólio deve explicar suas próprias condições de contorno sem abrir toda a web como fonte de conhecimento.
A locale da rota dirige idioma de resposta (por meio do prompt). Mas os usuários às vezes digitam em outro idioma enquanto permanecem no UI em inglês, por exemplo.
Ao enviar, o cliente executa franc-min na entrada (comprimento mínimo ~15 chars). Se o idioma detectado não corresponder ao mapeamento ISO 639-3 esperado da locale atual, não postamos silenciosamente para a API. Mostramos um cartão de oferta: botões para router.push(/${targetLocale}/ai) para locales correspondentes, além de “continuar no idioma atual”. Se eles mudarem de locale, armazenamos a mensagem pendente em sessionStorage, navegamos e enviamos automaticamente após a montagem para que a pergunta seja executada com a locale certa no corpo JSON.
Essa é o comportamento do produto: alinhar o idioma do site com o idioma que o usuário está realmente escrevendo, em vez de forçar o modelo a adivinhar ou misturar políticas.
O Lucas AI é um destino de navegação e uma seção inicial (distintivo, título, CTA, prompts de exemplo) — não um widget flutuante sobre o fluxo de leitura. O bate-papo de página inteira mantém o padrão opt-in e evita o anti-padrão “surpresa copiloto” onde a UI gerativa luta contra o resto do layout por atenção.
llm_auth para o cliente — sem corpo upstream cru (evite vazar chave ou dicas de modelo).O Lucas AI não é um assistente geral jogado em uma página de marketing. É um produto estreito: um corpo de fatos que você defende, um caminho de resposta de streaming, um pequeno modelo que apenas decide “dentro do escopo ou não” e um servidor que esquece cada turno de propósito. O objetivo é comportamento previsível — o que é enviado ao provedor, o que você paga à API de inferência por solicitação e o que o visitante pode tratar como factual.
Se você construir algo assim, a alavanca não é escolher o maior modelo. É tratar a mensagem do sistema e o CONTEXT como uma especificação — simples, factual, auditável — em vez de copy de marketing, e decidir na arquitetura quando o grande modelo é permitido executar (por exemplo, somente após um portão de tópico).