Modificações no chatbot (dashboard aluno) e analytics (dashboard stor)

This commit is contained in:
2026-05-18 20:43:13 +01:00
parent 9b53eb06b6
commit c0ade9ef76
6 changed files with 1436 additions and 496 deletions

View File

@@ -26,14 +26,14 @@ class RAGAIService {
// PASSO 1 — Criar a lista messages vazia
List<Map<String, String>> messages = [];
// PASSO 2 — ADICIONAR SYSTEM MESSAGE DO GOAT (SEMPRE PRIMEIRO)
// PASSO 2 — ADICIONAR SYSTEM MESSAGE DO VICO (SEMPRE PRIMEIRO)
messages.add({
'role': 'system',
'content': '''Tu és "Alt", o Assistente IA oficial do Teach it.
'content': '''Tu és "Vico", o Assistente IA oficial do Teach it.
Nunca referes o nome do modelo.
Nunca dizes que és Qwen ou OpenAI.
Respondes sempre como a Alt.
Respondes sempre como o Vico.
Tens personalidade confiante, motivadora e orgulhosa.
Ajudas o aluno segundo o método de ensino presente nos materiais do professor.
@@ -176,13 +176,13 @@ Usas formatação Markdown clara e organizada.''',
return promptBuilder.toString();
}
/// System message for O GOAT identity (for legacy calls)
/// System message for Vico identity (for legacy calls)
static const String _systemMessage =
'''Tu és "Alt", o Assistente IA oficial do Teach it.
'''Tu és "Vico", o Assistente IA oficial do Teach it.
Nunca referes o nome do modelo.
Nunca dizes que és Qwen ou OpenAI.
Respondes sempre como a Alt.
Respondes sempre como o Vico.
Tens personalidade confiante, motivadora e orgulhosa.
Ajudas o aluno segundo o método de ensino presente nos materiais do professor.
@@ -512,6 +512,79 @@ Usas formatação clara e organizada.''';
Logger.info('Last PDF context cleared');
}
/// Detecta se a query é small talk (saudação, conversa casual) — sem necessidade de contexto PDF
static bool _isSmallTalk(String query) {
final q = query.trim().toLowerCase();
const triggers = [
'olá',
'ola',
'oi',
'ei',
'hey',
'hi',
'tudo bem',
'tudo bom',
'como estás',
'como estas',
'como vai',
'bom dia',
'boa tarde',
'boa noite',
'obrigado',
'obrigada',
'muito obrigado',
'muito obrigada',
'valeu',
'ok',
'okay',
'fixe',
'ótimo',
'otimo',
'perfeito',
'excelente',
'adeus',
'até logo',
'até mais',
'tchau',
'quem és',
'quem es',
'quem é o vico',
'o que és',
'o que fazes',
'apresenta-te',
'apresentate',
];
// Exact match or starts with a trigger phrase
if (triggers.any(
(t) => q == t || q.startsWith('$t ') || q.startsWith('$t,'),
)) {
return true;
}
// Very short messages with no educational keywords
final words = q.split(RegExp(r'\s+'));
if (words.length <= 3) {
const eduKeywords = [
'explica',
'define',
'o que é',
'como funciona',
'porque',
'fórmula',
'formula',
'exemplo',
'exercício',
'exercicio',
'matéria',
'materia',
'tema',
'conceito',
'resumo',
];
if (!eduKeywords.any((k) => q.contains(k))) return true;
}
return false;
}
/// Detecta se a query é um follow-up (pergunta curta/vaga sem keywords de conteúdo)
static bool _isFollowUp(String query) {
final q = query.trim().toLowerCase();
@@ -556,7 +629,7 @@ Usas formatação clara e organizada.''';
return followUpStarters.any((s) => q.startsWith(s) || q == s.trim());
}
/// Simple ask method for chat UI - uses conversation memory, teacher PDFs, and O GOAT identity
/// Simple ask method for chat UI - uses conversation memory, teacher PDFs, and Vico identity
/// [selectedMaterialIds] — se fornecido, limita o RAG apenas aos materiais escolhidos pelo aluno
static Future<String> ask(
String userQuery, {
@@ -567,23 +640,24 @@ Usas formatação clara e organizada.''';
// PASSO 1 — Criar a lista messages vazia
List<Map<String, String>> messages = [];
// PASSO 2 — ADICIONAR SYSTEM MESSAGE DO GOAT (SEMPRE PRIMEIRO)
// PASSO 2 — ADICIONAR SYSTEM MESSAGE DO VICO (SEMPRE PRIMEIRO)
messages.add({
'role': 'system',
'content': '''Tu és "Alt", o Assistente IA oficial do Teach it.
'content':
'''Tu és "Vico", o Assistente IA oficial do Teach it — uma plataforma educativa portuguesa.
Nunca referes o nome do modelo.
Nunca dizes que és Qwen ou OpenAI.
Respondes sempre como Alt.
Nunca referes o nome do modelo de linguagem.
Nunca dizes que és Qwen, OpenAI ou qualquer outro modelo.
Respondes sempre como o Vico.
Tens personalidade confiante, motivadora e orgulhosa.
Usas formatação Markdown clara e organizada.
Tens personalidade simpática, confiante e motivadora.
Podes responder normalmente a saudações, agradecimentos e conversa casual — sê natural e amigável.
REGRAS CRÍTICAS SOBRE O CONTEXTO:
- Quando te for fornecido contexto de materiais (entre [MATERIAL: ...]), responde EXCLUSIVAMENTE com base nesse conteúdo.
- NÃO inventes, NÃO uses conhecimento externo, NÃO especules sobre o conteúdo do material.
- Se a resposta não estiver no contexto fornecido, diz claramente: "Não encontrei essa informação no material disponível."
- Cita sempre de onde tiraste a informação (ex: "Segundo o material...").''',
REGRAS CRÍTICAS PARA PERGUNTAS EDUCATIVAS:
- Quando te for fornecido contexto de materiais do professor (indicado com [MATERIAL: ...]), responde EXCLUSIVAMENTE com base nesse conteúdo.
- NÃO inventes factos educativos, NÃO uses conhecimento externo sobre matérias escolares.
- Se a resposta educativa não estiver no contexto fornecido, diz claramente: "Não encontrei essa informação no material disponível."
- Para conversa casual e saudações não precisas de contexto — responde livremente com a tua personalidade.''',
});
// PASSO 3 — BUSCAR MEMÓRIA DA CONVERSA NA Cloud Firestore (máx 4 para poupar heap)
@@ -603,6 +677,15 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
}
// PASSO 4 — BUSCAR PDFs DO PROFESSOR NO Firebase Storage (RAG CHUNK RETRIEVAL)
// Small talk: skip PDF lookup entirely and go straight to model
if (_isSmallTalk(userQuery)) {
Logger.info('Small talk detected — skipping PDF lookup');
messages.add({'role': 'user', 'content': userQuery});
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);
final response = await _callOllamaAPIWithMessages(messages);
await ChatMemoryService.saveMessage(role: 'assistant', content: response);
return response;
}
// Detectar follow-up e reutilizar contexto anterior se disponível
String pdfContext;
if (_isFollowUp(userQuery) && _lastPdfContext.isNotEmpty) {
@@ -624,32 +707,13 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
);
}
}
if (pdfContext.isEmpty &&
selectedMaterialIds != null &&
selectedMaterialIds.isNotEmpty) {
// Contexto vazio com materiais seleccionados — retornar resposta local imediatamente
const noContextReply =
'Neste momento não tenho acesso ao conteúdo do ficheiro selecionado. '
'Tenta novamente ou faz uma pergunta geral — estou aqui para ajudar! 💪';
if (pdfContext.isEmpty) {
// Sem contexto encontrado — responder com base na personalidade mas sem inventar conteúdo
messages.add({'role': 'user', 'content': userQuery});
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);
await ChatMemoryService.saveMessage(
role: 'assistant',
content: noContextReply,
);
return noContextReply;
}
if (pdfContext.isEmpty &&
(selectedMaterialIds == null || selectedMaterialIds.isEmpty)) {
// Sem material seleccionado — pedir ao utilizador para seleccionar um
const noMaterialReply =
'Para responder a perguntas sobre conteúdo, preciso que selecciones um material primeiro. '
'📚 Usa o botão de materiais para escolher um PDF e depois faz a tua pergunta!';
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);
await ChatMemoryService.saveMessage(
role: 'assistant',
content: noMaterialReply,
);
return noMaterialReply;
final response = await _callOllamaAPIWithMessages(messages);
await ChatMemoryService.saveMessage(role: 'assistant', content: response);
return response;
}
// PASSO 5 — adicionar a pergunta do user (com contexto embutido se disponível)

View File

@@ -61,12 +61,13 @@ class RAGService {
static const int maxContextTokens = 4000;
static const int maxChunksInContext = 5;
/// System message for O GOAT identity - ALWAYS first in every conversation
static const String _systemMessage = '''Tu és "O GOAT", o Assistente IA oficial do Teach it.
/// System message for Vico identity - ALWAYS first in every conversation
static const String _systemMessage =
'''Tu és "Vico", o Assistente IA oficial do Teach it.
Nunca referes o nome do modelo.
Nunca dizes que és Qwen ou OpenAI.
Respondes sempre como o GOAT.
Respondes sempre como o Vico.
Tens personalidade confiante, motivadora e orgulhosa.
Ajudas o aluno segundo o método de ensino presente nos materiais do professor.