chatskills-quiz-ai-gen (8 horas)Documento executivo · v1.0 · 2026-05-14
Substituição do workflow n8n
QuizAIGen-Core-v1pelo microserviço Pythonchatskills-quiz-ai-gen, com paridade funcional 1:1, sem mudanças no backend de produção.
| Item | Valor |
|---|---|
| Esforço total | 8 horas (1 dia útil) |
| Janela recomendada | Terça ou quarta, 09:00–18:00 (evita segunda/sexta) |
| Equipe mínima | 1 dev backend + 1 QA (parcial, 2h) + 1 DevOps (parcial, 1h) |
| Risco | Baixo — rollback é trocar 1 URL no produtor |
| Indisponibilidade prevista | Zero (rollout paralelo, switch atômico) |
| Custo OpenAI estimado por quiz | ~$0.015 (text gpt-4.1-mini) + |
pytest -v → 25/25 verde no ambiente de stagingjobId como correlation idA ser checado antes do dia D para não consumir as 8 horas:
| # | Item | Responsável | Verificação |
|---|---|---|---|
| 1 | Acesso ao repositório chatskills |
Dev | git clone funciona |
| 2 | Python 3.12+ instalado | Dev | python --version |
| 3 | Docker + Docker Compose disponíveis | DevOps | docker compose version |
| 4 | .env populado para staging |
DevOps | secrets em cofre |
| 5 | OpenAI API key com saldo ≥ $20 | PM | dashboard.openai.com |
| 6 | CHATSKILLS_JOB_TOKEN rotacionado (token antigo está no JSON do n8n) |
DevOps | novo token testado |
| 7 | CHATSKILLS_API_KEY + API_SECRET rotacionados |
DevOps | novo par testado |
| 8 | URL de staging do ChatSkills disponível | DevOps | curl /health no staging |
| 9 | 3 PDFs de teste preparados em tests/fixtures/ (texto OK, scan/sem texto, corrompido) |
QA | arquivos commitados |
| 10 | Acesso ao painel admin do ChatSkills (validar quiz criado) | QA | login funciona |
Se algum item acima estiver vermelho no início do dia D, abortar a integração e reagendar.
Objetivo: ambiente de dev funcionando, testes passando, app rodando localmente.
| Passo | Comando | Tempo | Critério |
|---|---|---|---|
| 1.1 | git clone do repo e cd chatskills-quiz-ai-gen |
5 min | repo presente |
| 1.2 | python -m venv .venv && .venv\Scripts\activate |
2 min | venv ativo |
| 1.3 | pip install -r requirements.txt |
5 min | sem erros |
| 1.4 | copy .env.example .env e preencher com credenciais de staging |
10 min | .env populado |
| 1.5 | pytest -v |
5 min | 25/25 PASSED |
| 1.6 | uvicorn app.main:app --reload --port 8001 |
1 min | logs service_starting |
| 1.7 | curl http://localhost:8001/health |
1 min | {"status":"ok"} |
| 1.8 | curl http://localhost:8001/docs |
1 min | Swagger UI abre |
Checkpoint H1: ambiente verde, suite de testes verde, serviço respondendo.
Objetivo: validar end-to-end com OpenAI real e callback para staging do ChatSkills.
| Passo | Ação | Tempo | Critério |
|---|---|---|---|
| 2.1 | Preparar PDF real de 1–3 páginas em tests/fixtures/sample.pdf (ex.: o PDF "ETAPA 1 — A STAGE.pdf" usado no n8n) |
5 min | arquivo presente |
| 2.2 | Disparar webhook via curl (ver bloco abaixo) | 1 min | 202 retornado |
| 2.3 | Acompanhar logs no terminal do uvicorn | 5 min | sequência: uploadFile→extractFile→workingWithInstructions→generatingImage→generatingQuiz |
| 2.4 | Validar PUTs de status chegando no staging do ChatSkills (logs do backend ou DB) | 10 min | 7–9 PUTs visíveis |
| 2.5 | Validar quiz criado no painel admin do ChatSkills (staging) | 10 min | quiz com 5 perguntas + ícone PNG aparece |
| 2.6 | Inspecionar o quiz: 5 perguntas, cada uma 5 opções, exatamente 1 correta | 10 min | schema OK |
| 2.7 | Comparar qualidade da geração com output histórico do n8n (mesmo PDF) | 15 min | linguagem/dificuldade equivalentes |
| 2.8 | Documentar jobId de teste para audit trail |
4 min | anotado |
Comando de smoke:
curl -u $BASIC_AUTH_USER:$BASIC_AUTH_PASS \
-X POST http://localhost:8001/webhook/quiz/upload \
-F "file=@tests/fixtures/sample.pdf;type=application/pdf" \
-F "jobId=smoke-h2-$(date +%s)" \
-F "color=#5D3FD3" \
-F "difficulty=easy" \
-F "userId=4OJMRrYdAIdzSNr5pcPmjfT3eA82" \
-F "groupId=fdafdc37-31ca-4a90-a613-d2dd65edb565"
Checkpoint H2: quiz gerado e visível no ChatSkills staging. Se falhar, ir para hora 8 (rollback).
Objetivo: confirmar que os 4 caminhos de erro produzem os eventStatus=ERROR corretos.
| # | Cenário | Input | eventStatus esperado |
Critério |
|---|---|---|---|---|
| 3.1 | Auth ausente | sem -u |
n/a | 401 retornado |
| 3.2 | Auth errado | -u tester:wrong |
n/a | 401 retornado |
| 3.3 | Campos faltando | sem groupId |
n/a | 422 retornado |
| 3.4 | Arquivo não-PDF | file=@README.md |
uploadFile=ERROR |
mensagem "Nenhum arquivo PDF válido" |
| 3.5 | PDF corrompido | bytes %PDF mas conteúdo lixo |
uploadFile=ERROR (PDF inválido) |
erro logado, sem chamada OpenAI |
| 3.6 | PDF scan sem texto | PDF imagem-only | textEmpty=ERROR |
"texto extraído do PDF está vazio" |
| 3.7 | OpenAI fora do ar (text) | desconectar internet ou trocar OPENAI_API_KEY por inválida |
workingWithInstructions=ERROR |
erro propagado |
| 3.8 | OpenAI fora do ar (image) | mock indisponível | generatingImage=ERROR mas pipeline continua → generatingQuiz=DONE sem ícone |
quiz criado sem imagem |
Time-box: 7 min por cenário (~55 min). Documentar resultados em planilha.
Checkpoint H3: todos os erros produzem o evento certo. Nenhum erro deixa o pipeline em estado indefinido.
Buffer para cobrir atrasos das horas 1–3. Se tudo estiver no prazo, almoço de 1h.
Objetivo: containerizar e subir em ambiente de staging.
| Passo | Comando | Tempo |
|---|---|---|
| 5.1 | docker compose -f docker/docker-compose.yml build |
8 min |
| 5.2 | Testar container localmente: docker compose up -d + curl localhost:8001/health |
5 min |
| 5.3 | docker compose logs -f para confirmar boot limpo |
2 min |
| 5.4 | Tag da imagem: docker tag chatskills-quiz-ai-gen:local <registry>/chatskills-quiz-ai-gen:v1.0.0 |
1 min |
| 5.5 | Push para registry: docker push <registry>/chatskills-quiz-ai-gen:v1.0.0 |
5 min |
| 5.6 | Provisionar serviço em staging (ECS/Kubernetes/VPS — conforme infra do chatskills) |
20 min |
| 5.7 | Configurar .env em staging via cofre de secrets |
10 min |
| 5.8 | Validar curl https://quiz-ai-gen-staging.chatskills.com.br/health |
5 min |
| 5.9 | Configurar reverse proxy / DNS interno se aplicável | 4 min |
Checkpoint H5: serviço acessível em URL pública de staging com HTTPS.
Objetivo: apontar o produtor de staging para o novo serviço e validar sob carga leve.
| Passo | Ação | Tempo |
|---|---|---|
| 6.1 | No produtor (mobile dev/backend de staging), trocar URL de https://n8n.chatskills.com.br/webhook/quiz/upload para https://quiz-ai-gen-staging.chatskills.com.br/webhook/quiz/upload |
10 min |
| 6.2 | Disparar 10 quizzes em sequência via produtor real | 15 min |
| 6.3 | Disparar 5 quizzes em paralelo (xargs -P 5) — validar BackgroundTasks aguenta concorrência |
10 min |
| 6.4 | Coletar métricas: latência do 202 (deve ser < 500ms), tempo total até generatingQuiz=DONE (esperado: 15–40s, dominado pela imagem) |
10 min |
| 6.5 | Validar 100% dos quizzes aparecem corretos no admin | 10 min |
| 6.6 | Comparar custo OpenAI consumido com previsão (~$0.055 × 15 = $0.83) | 5 min |
Comando de carga sintética:
seq 1 5 | xargs -P 5 -I {} curl -u $USER:$PASS \
-X POST https://quiz-ai-gen-staging.chatskills.com.br/webhook/quiz/upload \
-F "file=@sample.pdf" -F "jobId=load-{}" -F "color=#5D3FD3" \
-F "difficulty=easy" -F "userId=u1" -F "groupId=g1"
Checkpoint H6: 15/15 quizzes processados corretamente em staging. Sem erro 5xx. Latência aceitável.
Objetivo: mover o tráfego de produção do n8n para o novo serviço, com janela de observação curta entre cada passo.
Esta hora consome 2 blocos (15:00–17:00 = 2h) porque exige observação ativa entre etapas. Se o time só puder dedicar 8 horas totais, encolher H1+H4 para liberar este tempo.
| Passo | Ação | Tempo | Observação |
|---|---|---|---|
| 7.1 | Deploy do mesmo container em produção (push + provision) | 20 min | mesma imagem v1.0.0 |
| 7.2 | .env de produção com tokens reais (rotacionados) em cofre |
10 min | nunca commit |
| 7.3 | Validar GET /health em produção |
5 min | 200 OK |
| 7.4 | Canary 10%: feature flag ou roteador que envia 10% do tráfego para o novo serviço, 90% segue no n8n | 20 min | monitorar 20 min |
| 7.5 | Observar 30 minutos: logs estruturados (filtro por jobId), taxa de erro, latência p50/p95 |
30 min | erro < 1% |
| 7.6 | Se canary saudável: subir para 100% | 5 min | toggle 100% |
| 7.7 | Observar 30 minutos sob carga real total | 30 min | sem alerta |
Gatilhos de rollback automático durante esta hora:
generatingQuiz=DONE ausente em > 10% dos jobsComando de rollback (~30 segundos): ver hora 8.
Checkpoint H7: 100% do tráfego em produção, ≥ 30 minutos saudável.
Objetivo: deixar o sistema observável e a equipe equipada para operar.
| Passo | Ação | Tempo |
|---|---|---|
| 8.1 | Configurar log aggregator (Datadog/Grafana Loki/CloudWatch) com filtro por service=chatskills-quiz-ai-gen |
15 min |
| 8.2 | Criar dashboard mínimo: req/min, erro 5xx, tempo total do pipeline, custo OpenAI estimado | 15 min |
| 8.3 | Criar 1 alerta: > 5 erros em 5 minutos → notificação | 10 min |
| 8.4 | Atualizar runbook interno com: como ver logs, como rebootar container, como rollback | 10 min |
| 8.5 | Marcar workflow Core no n8n como inativo mas não deletado (fallback por 7 dias) |
2 min |
| 8.6 | Comunicar conclusão para stakeholders (Slack/email com link do dashboard) | 5 min |
| 8.7 | Agendar review pós-7-dias para desativar definitivamente o n8n | 3 min |
Checkpoint H8 — sistema entregue.
Se algo der errado em qualquer momento, o rollback é trivial:
https://n8n.chatskills.com.br/webhook/quiz/uploadCore está com active: true| ID | Categoria | Tipo | Cenário | Status esperado | Cobertura |
|---|---|---|---|---|---|
| T01 | Unitário | Modelo | Quiz com 5 perguntas válidas | parsed OK | test_models_quiz.py |
| T02 | Unitário | Modelo | Quiz com 4 perguntas | ValidationError | idem |
| T03 | Unitário | Modelo | Quiz com 4 opções | ValidationError | idem |
| T04 | Unitário | Modelo | Quiz com 0 correctas | ValidationError | idem |
| T05 | Unitário | Modelo | Quiz com 2 correctas | ValidationError | idem |
| T06 | Unitário | Modelo | Quiz com título vazio | ValidationError | idem |
| T07 | Unitário | Modelo | Serialização round-trip | bytes idênticos | idem |
| T08 | Unitário | PDF válido com texto | string não-vazia | test_pdf_extractor.py |
|
| T09 | Unitário | bytes vazios | ValueError | idem | |
| T10 | Unitário | bytes inválidos | ValueError | idem | |
| T11 | Unitário | Quiz Gen | OpenAI retorna JSON válido | Quiz parsed | test_quiz_generator.py |
| T12 | Unitário | Quiz Gen | OpenAI retorna lixo | QuizValidationError | idem |
| T13 | Unitário | Quiz Gen | OpenAI retorna schema inválido | QuizValidationError | idem |
| T14 | Unitário | Quiz Gen | Quiz sem color/difficulty → fallback | preenchido a partir do input | idem |
| T15 | Unitário | Icon Gen | OpenAI retorna b64 válido | bytes PNG | test_icon_generator.py |
| T16 | Unitário | Icon Gen | OpenAI falha 2× | None retornado | idem |
| T17 | Integração | Webhook | sem auth | 401 | test_webhook.py |
| T18 | Integração | Webhook | senha errada | 401 | idem |
| T19 | Integração | Webhook | request válido | 202 + payload correto | idem |
| T20 | Integração | Webhook | campos faltando | 422 | idem |
| T21 | Integração | Webhook | healthcheck | 200 | idem |
| T22 | E2E (mocked) | Pipeline | happy path completo | sequência de status correta + POST final | test_pipeline.py |
| T23 | E2E (mocked) | Pipeline | PDF inválido | uploadFile=ERROR | idem |
| T24 | E2E (mocked) | Pipeline | texto vazio | textEmpty=ERROR | idem |
| T25 | E2E (mocked) | Pipeline | imagem falha → continua | generatingImage=ERROR mas POST final ocorre | idem |
| T26 | Manual | Staging | PDF texto real | quiz visível no admin | hora 2 |
| T27 | Manual | Staging | PDF scan | textEmpty=ERROR | hora 3 |
| T28 | Manual | Staging | PDF corrompido | uploadFile=ERROR | hora 3 |
| T29 | Manual | Staging | OpenAI indisponível | erro propagado | hora 3 |
| T30 | Manual | Staging | 10 sequenciais | 100% sucesso | hora 6 |
| T31 | Manual | Staging | 5 paralelos | 100% sucesso | hora 6 |
| T32 | Manual | Produção | canary 10% | erro < 1% por 30 min | hora 7 |
| T33 | Manual | Produção | 100% por 30 min | sem alerta | hora 7 |
Cobertura: 25 automatizados + 8 manuais = 33 verificações.
| Risco | Probabilidade | Impacto | Mitigação |
|---|---|---|---|
| OpenAI rate-limit em produção | Média | Alto | max_attempts=2 no icon gen; chat já tem retry interno do SDK; monitorar 429 |
BackgroundTasks não dá conta de pico |
Baixa | Médio | v2: migrar para arq+Redis. Para volume atual (~dezenas/dia), suficiente |
| Token rotacionado mas n8n ainda usa antigo | Baixa | Alto | Rotação só após desativação do n8n (passo 8.5) ou suportar ambos por 7 dias |
| Quiz gerado tem qualidade pior que n8n | Baixa | Médio | Prompt copiado verbatim — qualidade idêntica garantida |
| Custo OpenAI dispara | Baixa | Médio | Alerta de gasto diário > $50 no dashboard OpenAI |
| Latência total inaceitável | Baixa | Médio | Maior custo de tempo é gpt-image-1 (10-30s) — mesmo do n8n. Sem regressão |
Monitorar diariamente nos 7 dias após o switch:
| Métrica | Meta | Ação se exceder |
|---|---|---|
| Taxa de erro (5xx) | < 1% | Investigar logs, considerar rollback |
| p95 latência do 202 | < 1s | Verificar saturação do worker |
| Tempo total p95 (202 → ai-create POST) | < 60s | Investigar OpenAI |
Erros generatingImage=ERROR |
< 5% | Investigar prompt/quota |
| Custo OpenAI diário | < $20 (ajustar conforme volume real) | Avaliar caching ou redução de retry |
| Etapa | Aprovador | Antes de quê |
|---|---|---|
| Provisionar serviço em staging (H5) | DevOps lead | criar recursos |
| Cutover canary 10% (H7.4) | Tech lead | mudar produtor de prod |
| Cutover 100% (H7.6) | Tech lead + Product | switch total |
| Desativar n8n permanentemente (D+7) | Tech lead | deletar workflow |
plans/c-users-yuri-downloads-quizaigen-core-v-compiled-petal.mdREADME.mdQuizAIGen-Core-v1.json (export de 2026-05-14).env.exampleDocumento mantido sob versionamento em chatskills-quiz-ai-gen/docs/. Atualizar após cada cutover real.