Modificações no chatbot (dashboard aluno) e analytics (dashboard stor)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user