melhoramento do fall back da ia

This commit is contained in:
2026-05-16 17:41:29 +01:00
parent 321df8bb1d
commit 728368b040
2 changed files with 49 additions and 10 deletions

View File

@@ -492,6 +492,33 @@ Usas formatação clara e organizada.''';
}
}
/// Cache do último contexto PDF enviado ao modelo — reutilizado em follow-ups
static String _lastPdfContext = '';
/// Limpar contexto cacheado — chamar ao mudar de material
static void clearLastContext() {
_lastPdfContext = '';
Logger.info('Last PDF context cleared');
}
/// 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();
// Menos de 6 palavras E começa com pronome/advérbio de follow-up
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',
];
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
/// [selectedMaterialIds] — se fornecido, limita o RAG apenas aos materiais escolhidos pelo aluno
static Future<String> ask(
@@ -537,15 +564,24 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
}
// PASSO 4 — BUSCAR PDFs DO PROFESSOR NO Firebase Storage (RAG CHUNK RETRIEVAL)
final pdfContext = await MaterialsRAGService.getRelevantChunks(
// Detectar follow-up e reutilizar contexto anterior se disponível
String pdfContext;
if (_isFollowUp(userQuery) && _lastPdfContext.isNotEmpty) {
pdfContext = _lastPdfContext;
Logger.info('Follow-up detected — reusing last PDF context (${pdfContext.length} chars)');
} else {
pdfContext = await MaterialsRAGService.getRelevantChunks(
userQuery: userQuery,
maxMaterials: 5,
maxChunks: 5,
selectedMaterialIds: selectedMaterialIds,
);
if (pdfContext.isNotEmpty) {
_lastPdfContext = pdfContext;
Logger.info('PDF context sent to model (${pdfContext.length} chars): ${pdfContext.length > 300 ? pdfContext.substring(0, 300) : pdfContext}');
} else if (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. '
@@ -553,7 +589,8 @@ REGRAS CRÍTICAS SOBRE O CONTEXTO:
await ChatMemoryService.saveMessage(role: 'user', content: userQuery);
await ChatMemoryService.saveMessage(role: 'assistant', content: noContextReply);
return noContextReply;
} else {
}
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. '

View File

@@ -621,6 +621,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
_messages.clear();
});
ChatMemoryService.clearHistory();
RAGAIService.clearLastContext();
Navigator.of(dialogContext).pop();
},
child: const Text('Limpar'),
@@ -632,6 +633,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
_messages.clear();
});
ChatMemoryService.clearHistory();
RAGAIService.clearLastContext();
Navigator.of(dialogContext).pop();
},
style: ElevatedButton.styleFrom(