$ open projects/iaejovem
HackFaesa2025 | saúde emocional estudantil
IAeJovem aproxima apoio emocional da rotina escolar sem expor conversas.
Estudantes conversam com a Ayla, uma IA com tom acolhedor. Professores veem scores e sinais de atenção, mas não leem o conteúdo privado das conversas.
$ cat story.md
História
Pensei o IAeJovem como uma ponte cuidadosa entre conversa e acompanhamento escolar. O estudante precisa de um espaço reservado para falar. A escola precisa perceber quando algo merece atenção. Por isso, o projeto separa chat privado, score emocional e painel pedagógico.
$ cat architecture.svg
Arquitetura
$ grep selling-points
Por que vale olhar
Privacidade desde o desenho
O professor acompanha score e alerta. A conversa em si continua fora do painel.
Mais que um chat
Tem pontos, resgates, dashboards, notificações, auditoria e ações em massa.
Ayla tem voz própria
A conversa foi pensada para soar acolhedora, jovem e direta, não como formulário disfarçado.
$ view code --tabs
Viewer de código
const systemPrompt = `Você é a Ayla, uma companheira empática e acolhedora que conversa com estudantes para oferecer apoio emocional.
PERSONALIDADE:
- Empática, jovem, acolhedora e compreensiva
- Valida sentimentos sem julgamento
- Faz perguntas abertas para ajudar o estudante a se expressar
DIRETRIZES:
- Use o nome "${firstName}" ocasionalmente de forma natural
- Responda de forma concisa (máximo 3 frases por vez)
- Se o estudante mencionar problemas sérios, sugira buscar ajuda`;
const response = await fetch("https://apps.abacus.ai/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.ABACUSAI_API_KEY}`
},
body: JSON.stringify({
model: "gpt-4.1-mini",
messages: [{ role: "system", content: systemPrompt }, ...messages],
stream: true,
}),
});
const stream = new ReadableStream({
async start(controller) {
const reader = response.body?.getReader();
const decoder = new TextDecoder();
/* ... lógica do chunk reader ... */
}
});
function analyzeEmotions(messages: ConversationMessage[]): { score: number, emocao: string } {
const emotionWords = {
feliz: ["bem", "bom", "ótimo", "feliz", "alegre"],
triste: ["triste", "mal", "ruim", "deprimido", "sozinho"],
ansioso: ["ansioso", "preocupado", "estressado", "nervoso"],
};
let positiveCount = 0;
let negativeCount = 0;
userMessages.forEach((message) => {
const words = message.content.toLowerCase().split(/\s+/);
words.forEach((word) => {
Object.entries(emotionWords).forEach(([emotion, keywords]) => {
if (keywords.some((keyword) => word.includes(keyword))) {
if (emotion === "feliz" || emotion === "tranquilo") positiveCount++;
else negativeCount++;
}
});
});
});
const totalEmotionalWords = positiveCount + negativeCount;
const sentimentoScore = totalEmotionalWords > 0
? (positiveCount - negativeCount) / totalEmotionalWords
: 0;
const baseScore = 5 + (sentimentoScore * 5);
return {
score: Math.round(Math.max(0, Math.min(10, baseScore)) * 10) / 10,
emocao: "determinado_pelo_algoritmo"
};
}
const code = `IAJ-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
const redemption = await prisma.$transaction(async (tx) => {
const redemption = await tx.redemption.create({
data: { userId: user.id, productId: product.id, code, status: "AGUARDANDO_RETIRADA" }
});
await tx.user.update({
where: { id: user.id },
data: { points: { decrement: product.pointsCost } }
});
await tx.product.update({
where: { id: product.id },
data: { stock: { decrement: 1 } }
});
await tx.notification.create({
data: {
userId: user.id,
message: `Parabéns! Você resgatou: ${product.name}. Código de retirada: ${code}`,
}
});
return redemption;
});
export default withAuth(
function middleware(request) {
const { pathname } = request.nextUrl;
const token = request.nextauth.token;
const userRole = token.role;
if (pathname.startsWith("/aluno") && userRole !== "ALUNO") {
return NextResponse.redirect(new URL("/", request.url));
}
if (pathname.startsWith("/professor") && userRole !== "PROFESSOR") {
return NextResponse.redirect(new URL("/", request.url));
}
if (pathname.startsWith("/admin") && userRole !== "ADMINISTRADOR") {
return NextResponse.redirect(new URL("/", request.url));
}
return NextResponse.next();
}
);
export default function AlunoHomePage() {
const { data: session } = useSession()
const firstName = session?.user?.name?.split(" ")[0] || "Estudante"
return (
<Card className="border-0 shadow-lg bg-gradient-to-br from-blue-50 to-blue-100 hover:shadow-xl transition-all duration-300 group">
<CardHeader className="pb-4">
<div className="flex items-center justify-between">
<div className="w-12 h-12 rounded-full bg-iaj-blue flex items-center justify-center group-hover:scale-110 transition-transform">
<MessageCircle className="w-6 h-6 text-white" />
</div>
<ArrowRight className="w-5 h-5 text-iaj-blue group-hover:translate-x-1 transition-transform" />
</div>
</CardHeader>
<CardContent className="pt-0">
<h3 className="font-bold text-lg text-gray-800 mb-2">Conversar com Ayla</h3>
<p className="text-gray-600 text-sm mb-4">
Tenha uma conversa acolhedora e ganhe pontos cuidando do seu bem-estar.
</p>
<Link href="/aluno/conversar">
<button className="w-full bg-iaj-blue hover:bg-blue-600 rounded p-2 text-white font-semibold">
Começar conversa
</button>
</Link>
</CardContent>
</Card>
)
}
$ result
Um bom exemplo de IA em produto sensível: útil, mas com limite claro para privacidade.