Quiz e tutor chat modificações

This commit is contained in:
2026-05-18 14:27:30 +01:00
parent ad825f47d7
commit 9b53eb06b6
3 changed files with 811 additions and 497 deletions

View File

@@ -29,11 +29,11 @@ class RAGAIService {
// PASSO 2 — ADICIONAR SYSTEM MESSAGE DO GOAT (SEMPRE PRIMEIRO)
messages.add({
'role': 'system',
'content': '''Tu és "O GOAT", o Assistente IA oficial do Teach it.
'content': '''Tu és "Alt", 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 a Alt.
Tens personalidade confiante, motivadora e orgulhosa.
Ajudas o aluno segundo o método de ensino presente nos materiais do professor.
@@ -41,7 +41,9 @@ Usas formatação Markdown clara e organizada.''',
});
// PASSO 3 — BUSCAR MEMÓRIA DA CONVERSA NA Cloud Firestore
final conversationHistory = await ChatMemoryService.getRecentMessages(limit: 20);
final conversationHistory = await ChatMemoryService.getRecentMessages(
limit: 20,
);
for (final msg in conversationHistory) {
messages.add({
'role': msg['role'] as String,
@@ -58,33 +60,27 @@ Usas formatação Markdown clara e organizada.''',
if (pdfContext.isNotEmpty) {
messages.add({
'role': 'system',
'content': pdfContext, // Já vem formatado com [CHUNK 1], [CHUNK 2], etc.
'content':
pdfContext, // Já vem formatado com [CHUNK 1], [CHUNK 2], etc.
});
}
// PASSO 5 — SÓ AGORA adicionar a pergunta do user
messages.add({
'role': 'user',
'content': userQuery,
});
messages.add({'role': 'user', 'content': userQuery});
// Log do tamanho do array para verificação
Logger.info('Built messages array with ${messages.length} messages for API');
Logger.info(
'Built messages array with ${messages.length} messages for API',
);
// Save user message to Firestore (after building the messages array)
await ChatMemoryService.saveMessage(
role: 'user',
content: userQuery,
);
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);
// Call Ollama API with complete messages array
final response = await _callOllamaAPIWithMessages(messages);
// Save AI response to memory
await ChatMemoryService.saveMessage(
role: 'assistant',
content: response,
);
await ChatMemoryService.saveMessage(role: 'assistant', content: response);
// Process response and create RAGResponse
final ragResponse = _createRAGResponse(
@@ -181,11 +177,12 @@ Usas formatação Markdown clara e organizada.''',
}
/// System message for O GOAT identity (for legacy calls)
static const String _systemMessage = '''Tu és "O GOAT", o Assistente IA oficial do Teach it.
static const String _systemMessage =
'''Tu és "Alt", 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 a Alt.
Tens personalidade confiante, motivadora e orgulhosa.
Ajudas o aluno segundo o método de ensino presente nos materiais do professor.
@@ -485,13 +482,11 @@ Usas formatação clara e organizada.''';
final messages = <Map<String, String>>[
{
'role': 'system',
'content': 'És um assistente educativo especializado em criar quizzes pedagógicos. '
'content':
'És um assistente educativo especializado em criar quizzes pedagógicos. '
'Cria sempre perguntas claras, baseadas exclusivamente no contexto fornecido.',
},
{
'role': 'user',
'content': prompt,
},
{'role': 'user', 'content': prompt},
];
return await _callOllamaAPIWithMessages(messages);
}
@@ -524,13 +519,39 @@ Usas formatação clara e organizada.''';
final words = q.split(RegExp(r'\s+'));
if (words.length > 8) return false;
const followUpStarters = [
'e ', 'e o', 'e a', 'e os', 'e as', 'mas ', 'então ',
'explica', 'explique', 'explica melhor', 'melhor', 'mais detalhes',
'podes', 'pode ', 'consegues', 'e se ', 'e quando',
'dá um exemplo', 'da um exemplo', 'um exemplo', 'exemplo',
'como assim', 'o que significa', 'porquê', 'porque isso',
'e o ponto', 'e a regra', 'continua', 'continua',
'o que mais', 'mais algum', 'e depois', 'e agora',
'e ',
'e o',
'e a',
'e os',
'e as',
'mas ',
'então ',
'explica',
'explique',
'explica melhor',
'melhor',
'mais detalhes',
'podes',
'pode ',
'consegues',
'e se ',
'e quando',
'dá um exemplo',
'da um exemplo',
'um exemplo',
'exemplo',
'como assim',
'o que significa',
'porquê',
'porque isso',
'e o ponto',
'e a regra',
'continua',
'continua',
'o que mais',
'mais algum',
'e depois',
'e agora',
];
return followUpStarters.any((s) => q.startsWith(s) || q == s.trim());
}
@@ -549,11 +570,11 @@ Usas formatação clara e organizada.''';
// PASSO 2 — ADICIONAR SYSTEM MESSAGE DO GOAT (SEMPRE PRIMEIRO)
messages.add({
'role': 'system',
'content': '''Tu és "O GOAT", o Assistente IA oficial do Teach it.
'content': '''Tu és "Alt", 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 Alt.
Tens personalidade confiante, motivadora e orgulhosa.
Usas formatação Markdown clara e organizada.
@@ -566,7 +587,9 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
});
// PASSO 3 — BUSCAR MEMÓRIA DA CONVERSA NA Cloud Firestore (máx 4 para poupar heap)
final conversationHistory = await ChatMemoryService.getRecentMessages(limit: 4);
final conversationHistory = await ChatMemoryService.getRecentMessages(
limit: 4,
);
for (final msg in conversationHistory) {
messages.add({
'role': msg['role'] as String,
@@ -584,7 +607,9 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
String pdfContext;
if (_isFollowUp(userQuery) && _lastPdfContext.isNotEmpty) {
pdfContext = _lastPdfContext;
Logger.info('Follow-up detected — reusing last PDF context (${pdfContext.length} chars)');
Logger.info(
'Follow-up detected — reusing last PDF context (${pdfContext.length} chars)',
);
} else {
pdfContext = await MaterialsRAGService.getRelevantChunks(
userQuery: userQuery,
@@ -594,25 +619,36 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
);
if (pdfContext.isNotEmpty) {
_lastPdfContext = pdfContext;
Logger.info('PDF context sent to model (${pdfContext.length} chars): ${pdfContext.length > 300 ? pdfContext.substring(0, 300) : pdfContext}');
Logger.info(
'PDF context sent to model (${pdfContext.length} chars): ${pdfContext.length > 300 ? pdfContext.substring(0, 300) : pdfContext}',
);
}
}
if (pdfContext.isEmpty && selectedMaterialIds != null && selectedMaterialIds.isNotEmpty) {
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! 💪';
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);
await ChatMemoryService.saveMessage(role: 'assistant', content: noContextReply);
await ChatMemoryService.saveMessage(
role: 'assistant',
content: noContextReply,
);
return noContextReply;
}
if (pdfContext.isEmpty && (selectedMaterialIds == null || selectedMaterialIds.isEmpty)) {
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);
await ChatMemoryService.saveMessage(
role: 'assistant',
content: noMaterialReply,
);
return noMaterialReply;
}
@@ -625,12 +661,11 @@ $pdfContext
Pergunta: $userQuery'''
: userQuery;
messages.add({
'role': 'user',
'content': userContent,
});
messages.add({'role': 'user', 'content': userContent});
Logger.info('USING RAG AI SERVICE - Built messages array with ${messages.length} messages');
Logger.info(
'USING RAG AI SERVICE - Built messages array with ${messages.length} messages',
);
// Save user message to Firestore
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);