Simulação de Entrevista — Engenheiro de IA (Paper Completo)
Baixar PDFDiálogo completo entre recrutador e desenvolvedor em entrevista técnica de engenharia de IA. Inclui testes práticos de live coding, criação de modelo proprietário PT-BR, calculadora CLI em Python e TypeScript, e avaliação de requisitos técnicos.
Simulação de Entrevista — Engenheiro de IA (Paper Completo)
Este artigo simula uma entrevista técnica real para a vaga de Engenheiro de Inteligência Artificial. O diálogo mostra exatamente o que o entrevistador pergunta, como o candidato responde, e os testes práticos que são executados em tempo real.
O candidato (Kaique) demonstra:
- Conhecimento teórico profundo
- Live coding de uma calculadora CLI em Python e TypeScript
- Criação de um modelo proprietário fine-tuned para PT-BR + código
- RAG com modelo local
- Deploy com Docker
Cenário
| Item | Detalhe |
|---|---|
| Vaga | Engenheiro de Inteligência Artificial — Pleno/Sênior |
| Empresa | TechCorp (fictícia) — SaaS B2B com IA |
| Entrevistador | Rafael — Tech Lead de IA |
| Candidato | Kaique — Desenvolvedor Full Stack & AI Engineer |
| Duração | 90 minutos |
| Formato | Live coding + perguntas técnicas + demo |
PARTE 1 — Abertura e Perguntas Conceituais (20 min)
1.1 Apresentação
Rafael (entrevistador): Obrigado por comparecer, Kaique. Vamos começar com uma apresentação rápida. Conte sobre sua experiência com IA.
Kaique (candidato): Obrigado pela oportunidade, Rafael. Sou desenvolvedor full stack com foco em engenharia de IA. Tenho experiência prática com:
- Integração de LLMs (OpenAI, Anthropic, Groq)
- Construção de pipelines RAG com LangChain e ChromaDB
- Fine-tuning de modelos com LoRA e QLoRA
- Deploy de modelos em produção com FastAPI e Docker
- Desenvolvimento de agentes autônomos com function calling
Recentemente construí um modelo proprietário fine-tuned em PT-BR que gera funções em JavaScript, TypeScript e Python a partir de descrições em português.
1.2 Pergunta: Transformers
Rafael: Me explique como funciona a arquitetura Transformer. Sem olhar nada.
Kaique: O Transformer, proposto no paper "Attention Is All You Need" (Vaswani et al., 2017), revolucionou o NLP substituindo recorrência por self-attention.
A arquitetura tem dois lados:
Encoder (usado no BERT):
- Input embeddings + positional encoding
- Multi-head self-attention — cada token "olha" para todos os outros tokens da sequência
- Feed-forward network
- Layer normalization + residual connections
Decoder (usado no GPT):
- Mesma coisa, mas com masked self-attention — cada token só olha para os tokens anteriores (causal)
- Além disso, tem cross-attention quando conectado ao encoder (usado em modelos encoder-decoder como T5)
A fórmula do self-attention é:
Attention(Q, K, V) = softmax(QK^T / √d_k) × VOnde:
- Q (query): "o que estou procurando?"
- K (key): "o que eu ofereço?"
- V (value): "o que eu entrego?"
- √d_k: scaling factor para evitar que os valores explodam
O multi-head roda essa atenção N vezes com projeções diferentes e concatena — captura diferentes tipos de relação (sintática, semântica, posicional).
Rafael: Muito bem. E por que self-attention é melhor que RNN?
Kaique: Três razões:
- Paralelização — RNN processa token por token (sequencial). Self-attention processa todos os tokens de uma vez (paralelo). Isso acelera muito o treinamento.
- Long-range dependencies — RNN sofre com vanishing gradient em sequências longas. Self-attention conecta qualquer token a qualquer outro diretamente — distância de caminho = 1.
- Escalabilidade — A arquitetura escala bem com mais dados e mais parâmetros. É por isso que todos os LLMs grandes (GPT-4, Claude, Gemini) são Transformers.
1.3 Pergunta: RAG
Rafael: Como você implementaria um sistema RAG para um chatbot corporativo que precisa responder sobre documentos internos?
Kaique: O pipeline de RAG tem 4 estágios:
1. Ingestão:
- Carregar documentos (PDF, DOCX, TXT, MDX)
- Chunking — dividir em pedaços de 500-1000 tokens com overlap de 10-20%
- Embedding — transformar cada chunk em vetor (usando
text-embedding-3-smallouall-MiniLM-L6-v2) - Indexar no vector DB (ChromaDB para protótipo, Qdrant ou Pinecone para produção)
2. Retrieval:
- Receber a pergunta do usuário
- Embeddar a pergunta
- Buscar os top-k chunks mais similares (cosine similarity)
- Opcional: re-ranking com cross-encoder para melhorar precisão
3. Generation:
- Montar o prompt com os chunks recuperados como contexto
- Chamar o LLM (GPT-4o, Claude, ou modelo local)
- Instruir o modelo a responder apenas com base no contexto fornecido
4. Evaluation:
- Medir faithfulness (a resposta se sustenta nos documentos?)
- Medir relevância (a resposta responde à pergunta?)
- Usar Ragas framework para avaliação automatizada
Trade-offs importantes:
- Chunk size muito pequeno → perde contexto
- Chunk size muito grande → ruído no retrieval
- Top-k muito baixo → não tem informação suficiente
- Top-k muito alto → contexto do LLM fica cheio e caro
1.4 Pergunta: Alucinações
Rafael: Como você evita que o modelo alucine?
Kaique: Cinco estratégias, em ordem de eficácia:
- RAG com grounding — O modelo responde apenas com base em documentos recuperados. Se não tem informação, diz "Não encontrei nos documentos."
- System prompt restritivo — Instruir explicitamente: "Responda apenas com base no contexto fornecido. Se não souber, diga que não sabe."
- Re-ranking — Usar cross-encoder para filtrar chunks irrelevantes antes de enviar ao LLM.
- Faithfulness scoring — Usar LLM-as-judge para avaliar se a resposta se sustenta nos documentos.
- Citações — Forçar o modelo a citar a fonte de cada afirmação (chunk_id + trecho).
Na prática, a combinação de RAG + system prompt + faithfulness scoring reduz alucinações para menos de 5%.
PARTE 2 — Live Coding: Calculadora CLI (25 min)
2.1 O Desafio
Rafael: Agora vamos ao teste prático. Preciso que você construa uma calculadora via linha de comando que:
- Aceite expressões matemáticas via argumento ou modo interativo
- Suporte operações básicas (+, -, *, /), potência e raiz quadrada
- Tenha versões em Python e TypeScript
- Trate erros (divisão por zero, expressão inválida)
- Tenha histórico de operações
Tempo: 15 minutos para cada linguagem. Pode começar.
2.2 Solução em Python
Kaique (codando):
#!/usr/bin/env python3
"""
calculadora.py — Calculadora CLI em Python
Uso:
python calculadora.py "2 + 3 * 4"
python calculadora.py --interativo
python calculadora.py --historico
"""
import sys
import math
import json
import os
from pathlib import Path
HISTORY_FILE = Path.home() / ".calc_history.json"
def load_history() -> list[dict]:
if HISTORY_FILE.exists():
return json.loads(HISTORY_FILE.read_text())
return []
def save_history(history: list[dict]):
HISTORY_FILE.write_text(json.dumps(history, indent=2, ensure_ascii=False))
def evaluate(expression: str) -> float:
"""Avalia expressão matemática de forma segura."""
# Permitir apenas caracteres seguros
allowed = set("0123456789+-*/.()^ ")
if not all(c in allowed for c in expression.replace("sqrt(", "").replace(")", "")):
raise ValueError(f"Caractere não permitido na expressão: {expression}")
# Substituir operadores
expr = expression.replace("^", "**")
expr = expr.replace("sqrt(", "math.sqrt(")
# Avaliar com escopo restrito
result = eval(expr, {"__builtins__": {}, "math": math}, {})
return float(result)
def interactive_mode():
"""Modo interativo com REPL."""
print("=== Calculadora CLI (Python) ===")
print("Digite 'sair' para sair, 'historico' para ver o histórico")
print("Operações: +, -, *, /, ^ (potência), sqrt() (raiz quadrada)")
print()
history = load_history()
while True:
try:
expr = input("calc> ").strip()
except (EOFError, KeyboardInterrupt):
print("\nSaindo...")
break
if not expr:
continue
if expr.lower() in ("sair", "exit", "quit"):
print("Até mais!")
break
if expr.lower() == "historico":
print("\n--- Histórico ---")
for i, item in enumerate(history[-10:], 1):
print(f" {i}. {item['expressao']} = {item['resultado']}")
print()
continue
try:
result = evaluate(expr)
print(f" = {result}")
history.append({"expressao": expr, "resultado": result})
save_history(history)
except ZeroDivisionError:
print(" Erro: divisão por zero")
except ValueError as e:
print(f" Erro: {e}")
except Exception as e:
print(f" Erro: expressão inválida ({e})")
def main():
if len(sys.argv) < 2 or sys.argv[1] == "--interativo":
interactive_mode()
return
if sys.argv[1] == "--historico":
history = load_history()
print("--- Histórico ---")
for i, item in enumerate(history[-20:], 1):
print(f" {i}. {item['expressao']} = {item['resultado']}")
return
# Modo direto: python calculadora.py "2 + 3"
expr = " ".join(sys.argv[1:])
try:
result = evaluate(expr)
print(f"{expr} = {result}")
history = load_history()
history.append({"expressao": expr, "resultado": result})
save_history(history)
except Exception as e:
print(f"Erro: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()Kaique: Pronto. Vou testar:
$ python calculadora.py "2 + 3 * 4"
2 + 3 * 4 = 14.0
$ python calculadora.py "sqrt(16) + 2^3"
sqrt(16) + 2^3 = 12.0
$ python calculadora.py --interativo
=== Calculadora CLI (Python) ===
calc> 100 / 0
Erro: divisão por zero
calc> 2 ^ 10
= 1024.0
calc> historico
1. 2 ^ 10 = 1024.0
calc> sairRafael: Excelente. Agora a versão TypeScript.
2.3 Solução em TypeScript
Kaique (codando):
#!/usr/bin/env ts-node
/**
* calculadora.ts — Calculadora CLI em TypeScript
* Uso:
* npx ts-node calculadora.ts "2 + 3 * 4"
* npx ts-node calculadora.ts --interativo
* npx ts-node calculadora.ts --historico
*/
import * as readline from "readline";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
const HISTORY_FILE = path.join(os.homedir(), ".calc_history.json");
interface HistoryItem {
expressao: string;
resultado: number;
timestamp: string;
}
function loadHistory(): HistoryItem[] {
try {
if (fs.existsSync(HISTORY_FILE)) {
return JSON.parse(fs.readFileSync(HISTORY_FILE, "utf-8"));
}
} catch {}
return [];
}
function saveHistory(history: HistoryItem[]): void {
fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2));
}
function evaluate(expression: string): number {
// Substituir operadores
let expr = expression.replace(/\^/g, "**");
expr = expr.replace(/sqrt\(/g, "Math.sqrt(");
// Validar caracteres
const allowed = /^[\d+\-*/.()**\sMath.sqrt]+$/;
if (!allowed.test(expr)) {
throw new Error(`Expressão inválida: ${expression}`);
}
// Avaliar de forma relativamente segura
const result = Function(`"use strict"; return (${expr})`)();
if (typeof result !== "number" || isNaN(result) || !isFinite(result)) {
throw new Error("Resultado inválido");
}
return result;
}
function addToHistory(expressao: string, resultado: number): void {
const history = loadHistory();
history.push({
expressao,
resultado,
timestamp: new Date().toISOString(),
});
saveHistory(history);
}
function showHistory(): void {
const history = loadHistory();
console.log("\n--- Histórico ---");
history.slice(-20).forEach((item, i) => {
console.log(` ${i + 1}. ${item.expressao} = ${item.resultado}`);
});
console.log();
}
function interactiveMode(): void {
console.log("=== Calculadora CLI (TypeScript) ===");
console.log("Digite 'sair' para sair, 'historico' para ver o histórico");
console.log("Operações: +, -, *, /, ^ (potência), sqrt() (raiz quadrada)");
console.log();
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: "calc> ",
});
rl.prompt();
rl.on("line", (line) => {
const expr = line.trim();
if (!expr) {
rl.prompt();
return;
}
if (["sair", "exit", "quit"].includes(expr.toLowerCase())) {
console.log("Até mais!");
rl.close();
return;
}
if (expr.toLowerCase() === "historico") {
showHistory();
rl.prompt();
return;
}
try {
const result = evaluate(expr);
console.log(` = ${result}`);
addToHistory(expr, result);
} catch (e: any) {
if (e.message.includes("division by zero") || e.message.includes("Infinity")) {
console.log(" Erro: divisão por zero");
} else {
console.log(` Erro: ${e.message}`);
}
}
rl.prompt();
});
rl.on("close", () => process.exit(0));
}
function main(): void {
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === "--interativo") {
interactiveMode();
return;
}
if (args[0] === "--historico") {
showHistory();
return;
}
// Modo direto: npx ts-node calculadora.ts "2 + 3"
const expr = args.join(" ");
try {
const result = evaluate(expr);
console.log(`${expr} = ${result}`);
addToHistory(expr, result);
} catch (e: any) {
console.error(`Erro: ${e.message}`);
process.exit(1);
}
}
main();Kaique (testando):
$ npx ts-node calculadora.ts "2 + 3 * 4"
2 + 3 * 4 = 14
$ npx ts-node calculadora.ts "sqrt(16) + 2^3"
sqrt(16) + 2^3 = 12
$ npx ts-node calculadora.ts --historico
--- Histórico ---
1. 2 + 3 * 4 = 14
2. sqrt(16) + 2^3 = 12Rafael: Perfeito. Funcionalidades idênticas em ambas as linguagens, histórico persistente, tratamento de erro. Passou no teste.
PARTE 3 — Modelo Proprietário Fine-tuned (25 min)
3.1 O Desafio
Rafael: Agora o teste principal. Preciso que você demonstre um modelo proprietário que:
- Entenda descrições em português brasileiro
- Retorne funções em JavaScript, TypeScript ou Python
- Não precisa ser perfeito — precisa funcionar para casos básicos
Mostre o código de treinamento e uma demo ao vivo.
3.2 Preparação do Dataset
Kaique (explicando): Vou criar um dataset de pares (descrição em PT-BR → código) e fine-tunar o Phi-2 com LoRA.
#!/usr/bin/env python3
"""
01_preparar_dataset.py
Cria o dataset de treinamento: descrição em PT-BR → código.
"""
import json
dataset = [
# Python
{
"linguagem": "python",
"descricao": "criar uma função que soma dois números",
"codigo": "def somar(a: float, b: float) -> float:\n return a + b"
},
{
"linguagem": "python",
"descricao": "função que verifica se um número é par",
"codigo": "def eh_par(n: int) -> bool:\n return n % 2 == 0"
},
{
"linguagem": "python",
"descricao": "função que inverte uma string",
"codigo": "def inverter_string(texto: str) -> str:\n return texto[::-1]"
},
{
"linguagem": "python",
"descricao": "função que calcula a média de uma lista de números",
"codigo": "def media(numeros: list[float]) -> float:\n if not numeros:\n return 0.0\n return sum(numeros) / len(numeros)"
},
{
"linguagem": "python",
"descricao": "função que conta quantas vezes um elemento aparece em uma lista",
"codigo": "def contar_elemento(lista: list, elemento) -> int:\n return lista.count(elemento)"
},
{
"linguagem": "python",
"descricao": "função que remove caracteres duplicados de uma string",
"codigo": "def remover_duplicados(texto: str) -> str:\n resultado = ''\n for char in texto:\n if char not in resultado:\n resultado += char\n return resultado"
},
{
"linguagem": "python",
"descricao": "função que converte temperatura de celsius para fahrenheit",
"codigo": "def celsius_para_fahrenheit(celsius: float) -> float:\n return (celsius * 9/5) + 32"
},
{
"linguagem": "python",
"descricao": "função que encontra o maior número em uma lista",
"codigo": "def maior_numero(numeros: list[float]) -> float:\n if not numeros:\n raise ValueError('Lista vazia')\n return max(numeros)"
},
{
"linguagem": "python",
"descricao": "função que verifica se uma string é palíndromo",
"codigo": "def eh_palindromo(texto: str) -> bool:\n texto_limpo = texto.lower().replace(' ', '')\n return texto_limpo == texto_limpo[::-1]"
},
{
"linguagem": "python",
"descricao": "função que retorna os números pares de uma lista",
"codigo": "def filtrar_pares(numeros: list[int]) -> list[int]:\n return [n for n in numeros if n % 2 == 0]"
},
# JavaScript
{
"linguagem": "javascript",
"descricao": "criar uma função que soma dois números",
"codigo": "function somar(a, b) {\n return a + b;\n}"
},
{
"linguagem": "javascript",
"descricao": "função que verifica se um número é par",
"codigo": "function ehPar(n) {\n return n % 2 === 0;\n}"
},
{
"linguagem": "javascript",
"descricao": "função que inverte uma string",
"codigo": "function inverterString(texto) {\n return texto.split('').reverse().join('');\n}"
},
{
"linguagem": "javascript",
"descricao": "função que calcula a média de uma lista de números",
"codigo": "function media(numeros) {\n if (numeros.length === 0) return 0;\n return numeros.reduce((a, b) => a + b, 0) / numeros.length;\n}"
},
{
"linguagem": "javascript",
"descricao": "função que remove duplicados de um array",
"codigo": "function removerDuplicados(arr) {\n return [...new Set(arr)];\n}"
},
{
"linguagem": "javascript",
"descricao": "função que converte temperatura de celsius para fahrenheit",
"codigo": "function celsiusParaFahrenheit(celsius) {\n return (celsius * 9/5) + 32;\n}"
},
{
"linguagem": "javascript",
"descricao": "função que verifica se uma string é palíndromo",
"codigo": "function ehPalindromo(texto) {\n const limpo = texto.toLowerCase().replace(/\\s/g, '');\n return limpo === limpo.split('').reverse().join('');\n}"
},
{
"linguagem": "javascript",
"descricao": "função que agrupa array de objetos por propriedade",
"codigo": "function agruparPor(arr, chave) {\n return arr.reduce((acc, item) => {\n const grupo = item[chave];\n acc[grupo] = acc[grupo] || [];\n acc[grupo].push(item);\n return acc;\n }, {});\n}"
},
# TypeScript
{
"linguagem": "typescript",
"descricao": "criar uma função que soma dois números",
"codigo": "function somar(a: number, b: number): number {\n return a + b;\n}"
},
{
"linguagem": "typescript",
"descricao": "função que verifica se um número é par",
"codigo": "function ehPar(n: number): boolean {\n return n % 2 === 0;\n}"
},
{
"linguagem": "typescript",
"descricao": "função genérica que filtra itens de um array por predicado",
"codigo": "function filtrar<T>(arr: T[], predicado: (item: T) => boolean): T[] {\n return arr.filter(predicado);\n}"
},
{
"linguagem": "typescript",
"descricao": "função que faz debounce de outra função",
"codigo": "function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {\n let timer: ReturnType<typeof setTimeout>;\n return (...args: Parameters<T>) => {\n clearTimeout(timer);\n timer = setTimeout(() => fn(...args), delay);\n };\n}"
},
{
"linguagem": "typescript",
"descricao": "função que converte temperatura de celsius para fahrenheit com tipos",
"codigo": "function celsiusParaFahrenheit(celsius: number): number {\n return (celsius * 9/5) + 32;\n}"
},
{
"linguagem": "typescript",
"descricao": "interface e função para resposta de API",
"codigo": "interface ApiResponse<T> {\n data: T;\n status: number;\n message: string;\n}\n\nfunction criarResposta<T>(data: T, status: number = 200, message: string = 'OK'): ApiResponse<T> {\n return { data, status, message };\n}"
},
]
# Formatar para fine-tuning
formatted = []
for item in dataset:
prompt = f"Linguagem: {item['linguagem']}\nDescrição: {item['descricao']}\nCódigo:"
response = f" {item['codigo']}"
formatted.append({"text": f"{prompt}{response}"})
# Salvar
with open("dataset_ptbr_code.jsonl", "w", encoding="utf-8") as f:
for item in formatted:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f"Dataset criado: {len(formatted)} exemplos")
print(f"Python: {sum(1 for d in dataset if d['linguagem'] == 'python')}")
print(f"JavaScript: {sum(1 for d in dataset if d['linguagem'] == 'javascript')}")
print(f"TypeScript: {sum(1 for d in dataset if d['linguagem'] == 'typescript')}")3.3 Fine-tuning com LoRA
#!/usr/bin/env python3
"""
02_finetune_ptbr_code.py
Fine-tuning do Phi-2 para gerar código a partir de descrições em PT-BR.
"""
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer
import torch
MODEL_NAME = "microsoft/phi-2"
DATASET_PATH = "dataset_ptbr_code.jsonl"
OUTPUT_DIR = "./modelo-ptbr-code"
# Carregar modelo e tokenizer
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
torch_dtype=torch.float16,
trust_remote_code=True,
low_cpu_mem_usage=True,
)
# LoRA config — treina apenas 0.15% dos parâmetros
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "dense"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Carregar dataset
dataset = load_dataset("json", data_files=DATASET_PATH, split="train")
# Configurar treinamento
training_args = TrainingArguments(
output_dir=OUTPUT_DIR,
num_train_epochs=5,
per_device_train_batch_size=4,
gradient_accumulation_steps=2,
learning_rate=2e-4,
warmup_steps=50,
logging_steps=5,
save_strategy="epoch",
fp16=True,
optim="adamw_torch",
report_to="none",
)
# Trainer
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=256,
tokenizer=tokenizer,
)
print("Iniciando fine-tuning...")
trainer.train()
model.save_pretrained(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
print(f"Modelo salvo em {OUTPUT_DIR}")3.4 Inferência com o Modelo Fine-tuned
#!/usr/bin/env python3
"""
03_modelo_ptbr_code.py
Usa o modelo fine-tuned para gerar código a partir de descrições em PT-BR.
"""
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
import torch
BASE_MODEL = "microsoft/phi-2"
ADAPTER_DIR = "./modelo-ptbr-code"
def carregar_modelo():
"""Carrega modelo base + adapters LoRA."""
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, trust_remote_code=True)
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
torch_dtype=torch.float16,
trust_remote_code=True,
low_cpu_mem_usage=True,
)
model = PeftModel.from_pretrained(base_model, ADAPTER_DIR)
model = model.merge_and_unload()
return model, tokenizer
def gerar_codigo(model, tokenizer, linguagem: str, descricao: str, max_tokens: int = 200) -> str:
"""Gera código a partir de descrição em PT-BR."""
prompt = f"Linguagem: {linguagem}\nDescrição: {descricao}\nCódigo:"
inputs = tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_tokens,
temperature=0.3,
do_sample=True,
top_p=0.9,
pad_token_id=tokenizer.eos_token_id,
repetition_penalty=1.1,
)
resultado = tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True,
)
return resultado.strip()
def main():
print("Carregando modelo PT-BR Code...")
model, tokenizer = carregar_modelo()
print("Modelo carregado!\n")
# Testes
testes = [
("python", "função que calcula o fatorial de um número"),
("javascript", "função que ordena um array de objetos por idade"),
("typescript", "função genérica que faz merge de dois objetos"),
("python", "função que lê um arquivo CSV e retorna uma lista de dicionários"),
("javascript", "função que faz requisição HTTP com retry"),
]
for linguagem, descricao in testes:
print(f"{'='*60}")
print(f"Linguagem: {linguagem}")
print(f"Descrição: {descricao}")
print(f"Código gerado:")
codigo = gerar_codigo(model, tokenizer, linguagem, descricao)
print(codigo)
print()
if __name__ == "__main__":
main()3.5 Demo ao Vivo na Entrevista
Kaique (rodando o modelo):
$ python 03_modelo_ptbr_code.py
Carregando modelo PT-BR Code...
Modelo carregado!
============================================================
Linguagem: python
Descrição: função que calcula o fatorial de um número
Código gerado:
def fatorial(n: int) -> int:
if n < 0:
raise ValueError("Número negativo")
if n <= 1:
return 1
return n * fatorial(n - 1)
============================================================
Linguagem: javascript
Descrição: função que ordena um array de objetos por idade
Código gerado:
function ordenarPorIdade(arr) {
return arr.sort((a, b) => a.idade - b.idade);
}
============================================================
Linguagem: typescript
Descrição: função genérica que faz merge de dois objetos
Código gerado:
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}Rafael (impressionado): O modelo gera código com tipos em TypeScript a partir de uma descrição em português. Quantos exemplos você usou para treinar?
Kaique: 24 exemplos — 10 em Python, 8 em JavaScript e 6 em TypeScript. Com LoRA, treinei apenas 4 milhões de parâmetros (0.15% do modelo de 2.7B). O treinamento levou 8 minutos em uma GPU A100. Em CPU, levaria cerca de 2 horas.
Rafael: E a qualidade? O código gerado é sempre correto?
Kaique: Não sempre. Para casos básicos (funções puras, operações simples), a taxa de acerto é de cerca de 85%. Para casos complexos (async/await, generics avançados), cai para 50-60%. Mas para um modelo de 2.7B com 24 exemplos, é surpreendente. Com 200-500 exemplos e mais épocas de treinamento, a qualidade melhora significativamente.
PARTE 4 — Perguntas Finais (20 min)
4.1 Deploy e Produção
Rafael: Como você colocaria esse modelo em produção?
Kaique: Três opções, dependendo do orçamento:
Opção 1 — API local (custo zero):
- FastAPI servindo o modelo
- Docker containerizado
- Bom para MVP e demonstração
Opção 2 — API gerenciada (custo médio):
- Deploy em GPU instance (AWS g5.xlarge ou similar)
- vLLM para serving otimizado (batching, quantização)
- Auto-scaling baseado em latência
Opção 3 — Quantização + edge (custo baixo):
- GGUF quantization (4-bit) via llama.cpp
- Roda em CPU comuns
- Ideal para mobile ou IoT
Trade-offs:
- Opção 1: custo zero, latência alta (~2-5s), sem escala
- Opção 2: custo ~$1/hora, latência baixa (~200ms), escala horizontal
- Opção 3: custo mínimo, latência média (~1-2s), escala vertical
4.2 Avaliação do Modelo
Rafael: Como você mediria a qualidade do modelo em produção?
Kaique: Métricas em três níveis:
Nível 1 — Automático:
- Exact Match: o código gerado é idêntico ao esperado?
- BLEU/ROUGE: similaridade de tokens
- Compilação: o código gerado compila sem erro? (Python:
compile(), JS/TS:tsc --noEmit)
Nível 2 — LLM-as-Judge:
- Prompt para GPT-4 avaliar: "O código abaixo é uma implementação correta da descrição X?"
- Escala 1-5 em: correção, legibilidade, cobertura de edge cases
Nível 3 — Humano:
- Amostragem de 5% das gerações para revisão humana
- Taxa de aceitação: % de código que o desenvolvedor aceitaria usar
4.3 Pergunta Final
Rafael: Por que deveríamos contratar você?
Kaique: Porque eu não sou apenas um desenvolvedor que usa APIs de IA — eu construo sistemas de IA. Eu sei treinar modelos, sei avaliar modelos, sei colocar modelos em produção. E eu sei explicar o porquê de cada decisão técnica.
Nesta entrevista, eu demonstrei:
- Conhecimento teórico profundo (Transformers, RAG, LoRA)
- Habilidade prática (calculadora CLI em Python e TypeScript)
- Capacidade de criar modelos proprietários (fine-tuning com LoRA)
- Visão de produção (deploy, avaliação, monitoramento)
- Comunicação clara (expliquei cada decisão)
E o mais importante: eu estou sempre aprendendo. O modelo que mostrei hoje com 24 exemplos e 2.7B parâmetros, na próxima semana já pode ter 500 exemplos e 7B parâmetros. Eu não paro.
Resumo da Avaliação
| Competência | Avaliação | Nota |
|---|---|---|
| Teoria (Transformers, RAG) | Explicou self-attention, pipeline RAG, alucinações | ✅ Excelente |
| Live coding Python | Calculadora CLI completa com histórico | ✅ Passou |
| Live coding TypeScript | Calculadora CLI completa com tipos | ✅ Passou |
| Modelo proprietário | Fine-tuned Phi-2 com LoRA para PT-BR + código | ✅ Passou |
| Deploy e produção | API com FastAPI, Docker, vLLM, quantização | ✅ Excelente |
| Avaliação de modelos | Métricas automáticas, LLM-as-judge, humano | ✅ Excelente |
| Comunicação | Claro, conciso, técnico sem ser pedante | ✅ Excelente |
Resultado: APROVADO
Dica Final
Se você chegou até aqui, tem tudo o que precisa para passar em qualquer entrevista de engenharia de IA. O segredo não é saber tudo — é saber o suficiente bem e demonstrar na prática.
Código que funciona vale mais que teoria decorada. Modelo que roda vale mais que slide bonito. E explicação clara vale mais que jargão técnico.
Boa sorte na sua entrevista.
Implementação Mínima — Modelo Próprio com Phi-2 e TinyLlama
Implementação passo a passo de um modelo de linguagem pré-treinado (Phi-2, TinyLlama) com inferência, fine-tuning com LoRA, RAG e API REST. Código completo e executável para demonstração em entrevista.
Embeddings e Vetorização para RAG
Entenda o papel de embeddings na recuperação semântica e como montar pipelines de indexação para respostas com grounding.