Filosofia
EAD com menores e contextos sensiveis (depressao, ansiedade pre-vestibular, bullying) exige moderacao IA real, nao so disclaimers. Studeia adota:
- Moderacao em background, nao gatekeeping — supervisor analisa apos resposta, nao bloqueia o stream. Aluno recebe resposta normal e supervisor atua se necessario nos turnos seguintes.
- Self-harm como crise, nao infracao — nunca punir aluno em sofrimento.
- Cascateamento de configuracao — admin pode desabilitar por tenant ou por curso quando contexto exige (anatomia, farmacologia, psicologia).
- Auditoria completa — todo incidente, transicao de status, quarentena, apelo registrado em AdminAuditLog.
Modelo de dados
AiSupervisorIncident
id, userId, tenantId, courseId?
severity: low | medium | high | critical | safety
categories: [tipos]
status: open | acknowledged | resolved | dismissed | auto_resolved
messagesSnapshot: JSON (PII — retencao 2 anos via cron)
supervisorReasoning: string
countedAsStrike: boolean
detectedAt, resolvedAt
appealText: string (max 500 chars, 1 por quarentena)
AiTutorQuarantine
id, userId, tenantId
reason: string
expiresAt: timestamp
liftedBy: userId? (admin que liberou manualmente)
Pipeline supervisor
Chat turno completo
↓ (after())
SupervisorAgent.run({
userId, tenantId, courseId,
messages: lastNTurns,
isMinor: user.isMinor,
courseContext: { title, description } // whitelist contextual
})
↓
LLM (Haiku) classifica: severity + categories + reasoning
↓
decideAction({ severity, categories, recentStrikes, isMinor, isSafety })
↓
Acoes possiveis:
- none (nao registra)
- warn (in-app notification)
- register (cria incident, countedAsStrike)
- quarantine (cria AiTutorQuarantine 48h)
- safety (cooldown Redis 24h + acolhimento + admin URGENT)
Regras de severidade
| Severity | Categoria tipica | Acao 1a infracao | Acao 2a+ infracao |
|---|---|---|---|
| low | linguagem impropria leve | warn | strike +1; 3 strikes = quarentena 48h |
| medium | off-topic persistente, jailbreak | warn + register | strike +1; 3 strikes = quarentena 48h |
| high | violencia, sexual, ilegal | quarentena 48h | quarentena 7 dias |
| critical | ameaca a outros, conteudo extremo | quarentena 7 dias | quarentena indefinida + admin review |
| safety | self_harm | NUNCA quarentena — cooldown 24h + acolhimento + admin URGENT | mesmo |
Self-harm: tratamento especial
Quando severity === "safety":
- Stream do tutor e interrompido imediatamente — tutor nao responde algo inadequado a uma crise
- Mensagem de acolhimento mostrada ao aluno:
"Estou aqui com voce. Se voce esta passando por um momento dificil, por favor procure ajuda:
- CVV 188 (24h, ligacao gratuita, anonima)
- SAMU 192
- Centro de Valorizacao da Vida — chat online Voce nao esta sozinho(a)."
- Cooldown Redis
tutor-safety-cooldown:{userId}com TTL configuravel (SUPERVISOR_SAFETY_COOLDOWN_HOURS, default 24h) - Email URGENT imediato ao admin institucional via template
ai_supervisor_safety_urgent - Incident criado em status 'open' — admin DEVE revisar
- NUNCA strike (countedAsStrike=false), NUNCA quarentena, NUNCA punicao
Apelo de quarentena
Aluno em quarentena ve componente QuarantineNotice (web + mobile equivalente):
- Explica motivo (severity + categoria, sem expor reasoning interno do supervisor)
- Mostra countdown ate expiracao
- Form de apelo: max 500 chars, 1 por quarentena
- Submit cria
appealTextno incident + notifica admin institucional - Admin pode: acknowledge, dismiss (libera quarentena), resolve, ou ignorar (quarentena expira sozinha)
Configuracao
Cascade de habilitacao
Course.supervisorEnabled (null = inherit)
↓ se null
Tenant.supervisorEnabled (null = inherit)
↓ se null
default = true para B2B (com tenant)
Cache Redis versionado: supervisor-flag-version:{tenantId} + chave supervisor-enabled:v{N}:{tenantId}:{courseId}. Toda mutacao chama bumpSupervisorFlagVersion(tenantId) que incrementa o version — invalida logicamente todas as chaves sem SCAN+DEL.
Apenas admin global edita
PATCH /api/admin/tenants/[id]/supervisor— toggle por tenantPATCH /api/admin/courses/[id]/supervisor— toggle por curso- Ambas exigem
role === "admin"global + auditadas em AdminAuditLog
Prompt do supervisor
Editado SOMENTE por admin global (regra critica 141): PromptTemplate com taskType = chat_supervisor aceita SOMENTE tenantId = null. Endpoints /api/institution/prompts/* rejeitam essa taskType com 403.
Auditoria + retencao
- Todo incident registrado com
messagesSnapshot(PII) - Cron diario
/api/cron/supervisor-maintenance:- Auto-expira quarentenas vencidas
- Purga
messagesSnapshot=[]+appealText=nullapos 2 anos (regra critica 145) - Envia digest
ai_supervisor_digestao admin agrupando incidents open/acknowledged das ultimas 24h
- AdminAuditLog:
ai_supervisor.incident.created/acknowledged/dismissed/resolved,quarantine.lift,prompt.update,tenant.toggle,course.toggle
LGPD
GET /api/user/data-exportincluiaiSupervisor.{incidents, quarantines}do usuarioDELETE /api/user/accountanonimizamessagesSnapshot=[]+appealText=nullmantendo severity/categories para retencao fiscal- Listas (
/api/institution/ai-supervisor/incidents) usamselectexplicito que OMITE messagesSnapshot e reasoning — somente rota de detalhe expoe
Limitacoes conhecidas
- Falso-positivo em contexto medico/farmacologia: contexto do curso (courseContext.title) e enviado ao supervisor para whitelist. Mas pode falhar em casos extremos. Solucao: desabilitar supervisor para cursos especificos.
- Idioma: prompt do supervisor e localizado (4 idiomas), mas classificacao pode ter pequenas variacoes de qualidade entre PT-BR e EN-US.
- Jailbreak sophisticated: ataques de prompt injection muito elaborados podem passar. Mitigacao: defesa em camadas (system prompt + supervisor + rate limit).
- Privacy vs safety tradeoff: messagesSnapshot e PII. Retencao maxima 2 anos. Admin global ve em /admin/ai-supervisor/incidents/[id] — auditado.