# FAQ técnico   Lucas Hiago

> Respostas diretas às perguntas que mais aparecem em entrevista e em conversa técnica.
> Cada resposta começa pelo fato e depois abre o detalhe. Use como consulta rápida quando
> não lembrar de qual projeto veio o quê   o histórico longo está em
> [perfil-completo.md](perfil-completo.md).

---

## Experiência com Angular

### Quantas migrações de versão do Angular o Lucas já conduziu?

**Três, todas com o produto em produção.** Angular 8 → 16 no framework de e-commerce NEO;
Angular 9 → 14 na loja Elegance da JET, junto com a subida do Node para 18; e Angular
16 → 19 no Portal de Suporte TOTVS, pela Supero Tecnologia. Trabalha com o framework desde
a versão 8 e hoje usa a 20.

### Qual foi a parte mais difícil dessas migrações?

**A versão 13 do Angular, que removeu o ViewEngine.** Toda biblioteca de terceiro que não
tinha build em Ivy simplesmente parava de compilar. A saída foi levantar dependência por
dependência e decidir caso a caso: atualiza, troca ou remove. A regra que ele aplica é uma
major por vez, com smoke test entre elas, e nunca misturar migração com reescrita.

### Como ele resolveu manter quatro temas de loja sem duplicar código?

**Um núcleo único com a lógica de loja e o tema como camada de apresentação.** Catálogo,
carrinho e checkout ficam no core; o tema tem variáveis SCSS e componentes de layout
trocáveis, e nenhuma regra de negócio. O núcleo não sabe qual tema está rodando. Quatro
cópias do projeto significaria corrigir todo bug quatro vezes e ter quatro produtos
diferentes em três meses.

### Ele usa NgRx?

**Não nos projetos atuais   e isso é uma decisão consciente, não uma lacuna.** O padrão
dele é RxJS com um serviço como fonte única de estado (`shareReplay` para quem chega
depois) e, nos projetos recentes em Angular 19 e 20, signals com `rxResource`. O critério
para levar NgRx: estado compartilhado entre features distantes de verdade, ou time grande
que precisa de convenção rígida e de depurar por histórico de ações. Para cache de servidor,
NgRx é boilerplate sem retorno.

### Quais operadores de RxJS ele usa e por quê?

- **`switchMap`** para carregar dados: cancela a requisição anterior quando o filtro muda,
  evitando que a resposta velha chegue depois e sobrescreva a nova.
- **`exhaustMap`** para ações de envio: ignora o segundo clique enquanto o primeiro não
  terminou, resolvendo duplo submit sem depender de desabilitar botão.
- **`concatMap`** para escritas em sequência, quando a ordem importa e não se pode perder
  nenhuma.
- **`debounceTime` + `distinctUntilChanged`** em campo de busca, para não disparar uma
  requisição por tecla nem repetir o mesmo termo.
- **`catchError` dentro do pipe interno**, com `retry` e backoff. No pipe de fora, o
  primeiro erro mata o stream e a funcionalidade para de responder pelo resto da sessão.
- **`takeUntilDestroyed`** nas assinaturas de componente.

### O que ele fez de performance em Angular moderno?

**SSR com prerender e zoneless.** No site institucional em Angular 19: mais de 600 rotas
geradas em HTML no build, hidratação com event replay   o clique dado antes de hidratar não
se perde   e transfer cache do HTTP, para a requisição feita no servidor não ser refeita no
cliente. No app de prospecção em Angular 20: aplicação zoneless, sem zone.js, então não
existe ciclo global de detecção de mudança; quem atualiza a tela é o signal que mudou. Tudo
standalone, OnPush, com estado derivado em `computed`.

### Como ele trata erro de API no front-end?

**Interceptor centraliza a tradução, mas relança o erro.** A mensagem vinda do backend é
convertida em texto legível e mostrada ao usuário de forma consistente no app inteiro, mas
o erro segue adiante, porque quem decide o que a tela faz   mostrar vazio, manter o dado
velho, voltar de rota   é o componente.

### Como ele decide o que testar em unitário e o que testar em E2E?

**Unit para lógica, E2E para fluxo: se dá para provar com uma função, não sobe navegador.**
No unitário ficam cálculo de preço e frete, mapeadores entre a API e o modelo da tela,
validação de formulário, guards e   quando existe   reducers e selectors, com o HTTP
mockado pelo módulo de teste do HttpClient. No E2E ficam só os caminhos que exigem
integração: o caminho do dinheiro de ponta a ponta e rota protegida redirecionando quem não
tem sessão. Ferramenta usada: **Playwright** (o raciocínio vale igual para Cypress).
Critério de cobertura: nada de meta percentual global, cobertura alta onde há regra de
negócio, caminho de erro obrigatório, e todo bug corrigido nasce com um teste que falha
antes do fix.

---

## Fluxos agênticos e automação

### Qual foi o fluxo agêntico mais completo que ele construiu?

**O perf-bot, no NETPROJECT.** Um agente que escuta comentário de issue no GitLab e menção
no Slack por um único endpoint que distingue os dois pela assinatura, cobre 3 repositórios,
decide sozinho o que merece resposta, consulta o banco de staging por MCP com allowlist
somente-leitura, e implementa a demanda quando um usuário autorizado pede. Ele **não abre o
Merge Request**: deposita o pedido numa caixa de saída, e a abertura acontece com a
credencial humana.

### Que problemas de confiabilidade apareceram nesse agente?

Três, e todos de engenharia, não de IA:

1. **Auto-resposta.** Ele lia e escrevia comentário, então respondia a si mesmo. Resolvido
   marcando toda resposta do bot e ignorando essas na leitura.
2. **Flood retroativo.** Ao ampliar o escopo, ele responderia meses de histórico. Resolvido
   com um piso de ID: abaixo da marca, nunca responde.
3. **Roubo de branch.** Executar no diretório de trabalho do desenvolvedor trocaria a branch
   debaixo de quem estava codando. Resolvido executando em clones próprios do bot, com lock
   de uma execução por vez.

### O que é o SDD harness que ele criou?

**Um plugin distribuído por marketplace privado que instala um processo de desenvolvimento
inteiro num projeto sem copiar arquivo nenhum para o repositório alvo.** Traz regras sempre
ativas, skills de fluxo (feature, bugfix, chore, wiki), convenções de stack e agentes
especializados   PO que classifica a demanda, tech lead que desenha a solução mínima e
implementadores de front e back. Instalado no escopo do projeto, o time inteiro herda ao
clonar, e o release é versionado por script.

### Como ele lidou com uma story que toca vários microsserviços?

**Criando a branch e a worktree depois da implementação, agrupadas por serviço.** No
ecossistema da Dígitro, com 19+ microsserviços, criar a branch antes não funciona porque
uma story quase sempre altera mais de um serviço. O agente primeiro implementa, depois
agrupa por serviço impactado, e só então cria uma worktree por serviço   cada worktree vira
um MR.

### Como ele integraria uma plataforma agêntica a um sistema legado de RH, como SAP SuccessFactors?

**Com uma camada de integração no meio: o agente nunca fala com o legado direto.** O
desenho:

1. **Camada única com credencial**, em Node, autenticando por OAuth 2.0 com asserção SAML,
   token cacheado no Redis, chave privada no Vault e usuário técnico com permissão limitada
   por campo.
2. **Dois caminhos de ingestão**: evento do Intelligent Services num webhook (n8n) para
   reagir na hora, e delta agendado por data de modificação como rede de segurança, porque
   evento se perde. O resultado aterrissa num modelo canônico em Postgres, e o agente lê de
   lá   não do legado ao vivo.
3. **Leitura barata, escrita cara**: lista fixa de operações de consulta com PII mascarada;
   escrita vira proposta numa fila de aprovação, com simulação do antes e depois e chave de
   idempotência.

Armadilhas específicas do SuccessFactors que ele cita: vigência (a entidade de cargo
devolve todas as versões, não a atual), identidade tripla (`userId` ≠ `personIdExternal` ≠
`assignmentId`, e recontratado é uma pessoa com dois vínculos), paginação por cursor em vez
de deslocamento, e o modo de purga errado no upsert apagando histórico.

---

## IA em produção, CI/CD e conformidade

### Que subsistema de IA ele entregou em produção?

**Um subsistema de ~7 mil linhas dentro de uma base Laravel de 134 mil linhas**, com 228
endpoints e 156 models, no NETPROJECT: integração com LLM, compreensão de documentos com
drivers plugáveis, automação de workflow e o AutoTimesheet. Com OpenAI (GPT-4o, 4o-mini,
4-turbo, 3.5-turbo), Whisper, Vision, Ollama, streaming por SSE e roteamento multi-modelo.

### Como ele aplica CI/CD num projeto de LLM?

**Tratando prompt como código.** Prompt, configuração de agente e definição de ferramenta
versionados no git, com PR para qualquer alteração, e modelo fixado em versão específica  
nunca "latest". No pipeline, três camadas: teste unitário nas ferramentas; **evals**, um
conjunto de conversas reais com resultado esperado, travando o merge se a taxa de acerto
cair; e uma suíte de red team com injeção de prompt e perguntas que o sistema tem que
recusar. Versão de prompt é artefato, então canário e rollback são instantâneos. A métrica
de produção que ele acompanha é a taxa de escalonamento para humano   se sobe, alguma coisa
regrediu.

### Como ele garante conformidade com LGPD num sistema de IA?

Cinco medidas concretas: dado de produção não entra em CI (base de teste anonimizada ou
sintética); camada de redação de PII antes de qualquer coisa ir para o modelo, com log
guardando a conversa já mascarada; contrato com retenção zero no provedor e região de
processamento verificada; filtro de permissão aplicado **na busca** do RAG, não depois; e
campo de texto livre tratado como entrada não confiável, porque currículo pode conter
instrução   dado nunca vira comando.

### Como ele mitiga viés em IA aplicada a contratação?

**Automatizando o apoio à decisão, não a decisão.** A IA estrutura evidência e é obrigada a
citar a fonte   em vez de "candidato forte", responde "atende ao requisito X, trecho tal do
currículo". Remove atributos protegidos e seus proxies (nome, foto, CEP, faculdade, ano de
formatura, que entrega idade), com triagem cega nas etapas iniciais. E roda **teste
contrafactual no CI**: mesmo currículo, trocando só o nome ou o bairro, tem que dar a mesma
nota   se mudou, trava o deploy. Em produção, monitora taxa de aprovação por grupo com a
regra dos quatro quintos. Contexto legal: a LGPD garante revisão humana de decisão
automatizada e o AI Act europeu classifica seleção de pessoal como alto risco.

---

## Arquitetura de IA e LLM

> Estas nove respostas são a versão em texto dos **deep-dives técnicos** publicados em
> [lucashiago.com.br](https://www.lucashiago.com.br/#section-deepdives), onde cada uma vem
> com um diagrama do mecanismo.

### Como ele arquitetaria um RAG em produção com múltiplos agentes, controle de custo por token e fallback?

**Com OpenRouter como camada de roteamento, porque é onde fallback e multi-provider ficam
triviais.** Cinco decisões:

1. **Ingestão (offline)**   chunking semântico com metadados (fonte, data,
   tenant/permissão). Provider de embedding **fixo**: trocar de modelo sem reembeddar tudo
   deixa os vetores incompatíveis. Cada modelo tem tokenizer próprio, então o custo vem do
   `usage` real retornado, não de um tokenizer único para todo o catálogo.
2. **Retrieval (online)**   híbrido (vetorial + BM25) com **rerank**. O rerank é a maior
   alavanca de custo do sistema: chunk irrelevante é input pago em *toda* chamada.
3. **Orquestração**   papel → modelo, com o custo forçado pelo provider: roteador em
   `gemini-flash` ou `:nitro` barato com `sort: "price"`; workers de extração em
   `sonnet`/`deepseek`; síntese em `opus`/`gpt-4o` com `sort: "throughput"`.
4. **Custo por token**   roteamento por preço, teto por requisição (`max_price`), prompt
   caching repassado pelo provider e `usage: { include: true }` para budget por tenant.
5. **Fallback**   dois níveis automáticos: entre modelos (`models` é lista ordenada, cai
   para o próximo na mesma requisição) e entre provedores do mesmo modelo
   (`allow_fallbacks: true`).

**A pegadinha:** ao cair para outro modelo o cache se perde (é por modelo/provider) e o
comportamento muda. A lista se ordena por *similaridade de comportamento*, não só por preço.

### Como funciona o prompt caching da Anthropic, e quando ele de fato economiza?

**Caching é prefix match: a chave vem dos bytes exatos do prompt renderizado até cada
breakpoint `cache_control`.** Qualquer byte que muda na posição N invalida tudo depois. A
ordem de render é `tools → system → messages`, então um breakpoint no último bloco de system
cacheia tools + system juntos.

- **Como projetar:** estável *fisicamente antes* do volátil   system congelado e documentos
  recuperados no prefixo; pergunta da vez, timestamps e IDs no fim.
- **Economia real:** leitura ~**0,1×** o input; escrita **1,25×** (TTL 5 min) ou **2×** (1h).
  Break-even: com 5 min, duas requisições já pagam; com 1h, pelo menos três.
- **Pegadinhas:** máximo de 4 breakpoints; abaixo do prefixo mínimo do modelo não cacheia
  **em silêncio** (~4096 tokens em Opus/Haiku, ~1024 a 2048 em Sonnet). Invalidadores que ele
  caça: `datetime.now()` ou UUID no system, `json.dumps` sem `sort_keys`, tools variando por
  usuário, troca de modelo no meio. Confere com `usage.cache_read_input_tokens`.

### Como ele decide entre prompt engineering, RAG e fine-tuning?

**A pergunta é se falta conhecimento ou falta comportamento.** Ordem default: prompt
engineering → RAG → fine-tuning, escalando só quando o anterior comprovadamente não resolve.

- **Prompt engineering**   o conhecimento já está no modelo e o problema é formato, tom ou
  raciocínio. 70% dos casos morrem aqui.
- **RAG**   o problema é *conhecimento*: dado privado, atual ou grande demais. A fonte da
  verdade vive fora do modelo; atualizar é reembeddar um documento, não retreinar.
- **Fine-tuning**   último recurso, e quase nunca para "ensinar fatos". Vale para
  *comportamento* consistente que prompt não estabiliza. Custo: dataset, pipeline e perda de
  reversibilidade.

O caso mais comum em produção é RAG + prompt bem-feito, sem fine-tuning nenhum.

### Como ele garante saída estruturada confiável (JSON) sem o parser quebrar?

**Restringindo a saída na API, não pedindo JSON com educação no prompt.** Três camadas,
porque cada uma cobre um modo de falha diferente:

1. **Restrição nativa**   `output_config.format` com JSON Schema, ou tool use com
   `strict: true` (`additionalProperties: false` + `required`), que garante que o
   `tool_use.input` valida exato. Para classificação, tool com `enum` dos rótulos é mais
   robusto que pedir rótulo em texto.
2. **Parsing defensivo**   mesmo com schema, sempre `json.loads()`/`JSON.parse()`; *nunca*
   regex na string serializada, porque modelos escapam Unicode e barras de formas diferentes.
3. **Modos de falha restantes**   `stop_reason: max_tokens` devolve JSON truncado;
   `refusal` pode não bater com o schema. Checar `stop_reason` antes de tratar como válido.

### Qual a diferença entre LangGraph StateGraph e só encadear chamadas?

**Encadeie quando o fluxo é fixo; use grafo quando o próprio fluxo é decisão do modelo.**
Encadear é pipeline linear (saída de A vira entrada de B). StateGraph é máquina de estados:
nós que leem/escrevem num estado compartilhado inspecionável, com arestas *condicionais* que
decidem o próximo nó em runtime, incluindo ciclos.

- **Não se paga** em fluxo determinístico e linear (extrai → transforma → resume): três
  `await` são mais legíveis e baratos. Trazer o framework só porque "é multiagente" é o erro
  mais comum.
- **Se paga** em ciclos com parada decidida em runtime, branching real, estado compartilhado
  entre nós heterogêneos, ou checkpoint/retomada de execuções longas. No lab dele
  (`agentes-langchain-lab`) o researcher é um agente ReAct que repete pensa→busca→observa até
  ter evidência   um ciclo, que não se expressa limpo numa cadeia.

### Como ele cortou ~68% do consumo de tokens num time multi-repo?

**O gargalo não é o que o modelo gera: é o que fica sempre carregado no contexto**
(regras, comandos, skills, CLAUDE.md), multiplicado por toda interação. Numa tarefa
representativa eram **377K** tokens; caiu para **28K**, num time de **6**.

A estratégia é **slim rule + skill sob demanda**: a regra slim carrega um índice magro (o que
existe e quando puxar) e a orientação detalhada vive em skills lidas *on-demand*. Não
degrada porque nenhuma informação foi removida   mudou *quando* ela entra. Tocou o auth, o
skill do auth é carregado integralmente. O que foi cortado era carregar a guideline dos 18
serviços para editar um. É a lógica do prefixo cacheável levada para arquitetura de contexto.

### Como ele instrumenta observabilidade de LLM em produção?

**Cada chamada de LLM é um span de trace distribuído (OpenTelemetry), não uma linha de log.**
Log de texto não responde o que importa: quanto custou, por que ficou lento, onde a qualidade
caiu.

- **Atributos do span:** modelo, versão do prompt, tokens de entrada/saída, `cache hit/miss`,
  TTFT, `stop_reason` e custo calculado na hora.
- **Correlação:** o span da chamada vive dentro do trace da request, então dá para responder
  "por que essa tela demorou" apontando o span exato. Payloads por amostragem, com redação de
  PII antes de persistir.
- **Alerta só do acionável:** custo por dia e por tenant, p95 de TTFT, taxa de fallback entre
  modelos, taxa de `refusal`. Alerta de erro genérico em LLM é ruído; custo dobrando em 24h é
  incidente.

É a tese do **Projeto Atena**, a IDE de IA da Steply.

### Observabilidade de agente é diferente da de um chatbot?

**Muda a unidade de análise: chatbot é request/response, agente é trajetória.** O objeto
observado é a árvore de passos   cada tool call com input e output, cada decisão intermediária
e o contexto que o agente enxergava naquele momento.

- **Custo cumulativo e loops:** custo acumulado monitorado por trajetória, com corte no teto;
  detecção de laço (mesma tool com os mesmos argumentos repetidamente) dispara interrupção
  antes de queimar orçamento.
- **Replay determinístico:** guardar o contexto exato de cada decisão permite reproduzir a
  trajetória e auditar *por que o agente escolheu X no passo 4*. Sem replay, debugging de
  agente é adivinhação com log.

### Como medir qualidade de resposta de LLM sem ground truth?

**Com proxies em camadas, nunca com uma métrica só:**

1. **Feedback implícito**   o usuário aceitou, editou pesado ou refez a pergunta? É o sinal
   mais barato e mais honesto que existe, e a maioria ignora.
2. **Golden set como teste de regressão**   conjunto pequeno e curado que roda a cada mudança
   de prompt ou modelo. Mudar prompt sem rodar o golden set é deploy sem CI.
3. **LLM-as-judge calibrado**   juiz automático só vale depois de calibrado contra avaliação
   humana numa amostra, e nunca o mesmo modelo julgando a si mesmo sem calibração.
4. **Drift de distribuição**   tamanho médio de resposta, taxa de recusa e latência mudando
   *sem deploy seu* significa que o provider mexeu por baixo.

---

## Projetos autorais

### O que é o Asteroth?

**Um MMORPG isométrico num planeta esférico vivo, com engine própria escrita em C++.**
Gravidade radial, horizonte curvo, ciclo dia/noite com fusos por longitude e duas luas com
períodos distintos. A civilização é inteiramente construída por jogadores   sem safe zones
e sem mapa partido por loading. A engine (**angine**) usa C++, SDL e OpenGL/GLSL com CMake,
por escolha deliberada de YAGNI: sem Unity, sem Unreal, a engine cresce junto com o jogo.

### Que outros projetos próprios ele mantém?

- **luket**   assistente pessoal de terminal com CLI, painel Electron e backend próprio,
  tudo em loopback, com histórico persistido em SQLite.
- **App de ditado por voz**   faster-whisper large-v3-turbo na GPU, transcrição em tempo
  real com trimming incremental e digitação direta no cursor.
- **Editor de roteiros e Reels da Steply**   pipeline ffmpeg + Whisper que transcreve o
  áudio em segmentos editáveis e renderiza vídeo vertical com legenda queimada.
- **step-ai**   IDE em Electron para arquitetura de IA, com topologia de agentes declarada
  em arquivo, uma worktree por agente e observabilidade por proxy de interceptação.
- **GoAnime**   app Electron para buscar, baixar e assistir animes.

---

## Perguntas rápidas

**Quantos anos de experiência?** Mais de 10, desde 2015.

**Onde mora?** Belo Horizonte, Minas Gerais, Brasil.

**Fala inglês?** Sim   dois certificados de inglês no perfil.

**Formação?** Técnico em Programação de Jogos Digitais, Universidade Salgado de Oliveira
(2014–2016).

**Qual a linguagem principal?** TypeScript, no front (Angular) e no back (Nest.js). C++ no
projeto de engine.

**Já liderou time?** Sim   na SquadEvops como líder, e como CTO as a Service pela Steply,
definindo arquitetura e mentorando times.

**Trabalha com qual banco?** PostgreSQL principalmente; também MySQL e MSSQL, com
modelagem e otimização de query.

**Usa qual ferramenta de IA no dia a dia?** Claude Code e Claude Agent SDK, com servidores
MCP, além de OpenAI, Ollama, OpenRouter e Groq conforme o caso.

**Onde ele explica como um LLM funciona?** Na **série didática** de 13 páginas interativas em
[lucashiago.com.br](https://www.lucashiago.com.br/#section-serie), onde a conta roda de
verdade no navegador: `llm.html` (porta de entrada), `axiomas.html`, `anatomia.html`,
`transformer.html`, `cache.html`, `finetuning.html`, `embeddings.html`, `grafos.html`,
`gnn.html`, `rlm.html`, `local.html`, `agente.html` (passo a passo de um agente com
LangChain e LangGraph) e `difusao.html`. Em PT, EN e ES.
