melhoramento do fall back da ia
This commit is contained in:
@@ -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
|
/// 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
|
/// [selectedMaterialIds] — se fornecido, limita o RAG apenas aos materiais escolhidos pelo aluno
|
||||||
static Future<String> ask(
|
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)
|
// 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,
|
userQuery: userQuery,
|
||||||
maxMaterials: 5,
|
maxMaterials: 5,
|
||||||
maxChunks: 5,
|
maxChunks: 5,
|
||||||
selectedMaterialIds: selectedMaterialIds,
|
selectedMaterialIds: selectedMaterialIds,
|
||||||
);
|
);
|
||||||
if (pdfContext.isNotEmpty) {
|
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}');
|
||||||
} else if (selectedMaterialIds != null && selectedMaterialIds.isNotEmpty) {
|
}
|
||||||
|
}
|
||||||
|
if (pdfContext.isEmpty && selectedMaterialIds != null && selectedMaterialIds.isNotEmpty) {
|
||||||
// Contexto vazio com materiais seleccionados — retornar resposta local imediatamente
|
// Contexto vazio com materiais seleccionados — retornar resposta local imediatamente
|
||||||
const noContextReply =
|
const noContextReply =
|
||||||
'Neste momento não tenho acesso ao conteúdo do ficheiro selecionado. '
|
'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: 'user', content: userQuery);
|
||||||
await ChatMemoryService.saveMessage(role: 'assistant', content: noContextReply);
|
await ChatMemoryService.saveMessage(role: 'assistant', content: noContextReply);
|
||||||
return noContextReply;
|
return noContextReply;
|
||||||
} else {
|
}
|
||||||
|
if (pdfContext.isEmpty && (selectedMaterialIds == null || selectedMaterialIds.isEmpty)) {
|
||||||
// Sem material seleccionado — pedir ao utilizador para seleccionar um
|
// Sem material seleccionado — pedir ao utilizador para seleccionar um
|
||||||
const noMaterialReply =
|
const noMaterialReply =
|
||||||
'Para responder a perguntas sobre conteúdo, preciso que selecciones um material primeiro. '
|
'Para responder a perguntas sobre conteúdo, preciso que selecciones um material primeiro. '
|
||||||
|
|||||||
@@ -621,6 +621,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
|
|||||||
_messages.clear();
|
_messages.clear();
|
||||||
});
|
});
|
||||||
ChatMemoryService.clearHistory();
|
ChatMemoryService.clearHistory();
|
||||||
|
RAGAIService.clearLastContext();
|
||||||
Navigator.of(dialogContext).pop();
|
Navigator.of(dialogContext).pop();
|
||||||
},
|
},
|
||||||
child: const Text('Limpar'),
|
child: const Text('Limpar'),
|
||||||
@@ -632,6 +633,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
|
|||||||
_messages.clear();
|
_messages.clear();
|
||||||
});
|
});
|
||||||
ChatMemoryService.clearHistory();
|
ChatMemoryService.clearHistory();
|
||||||
|
RAGAIService.clearLastContext();
|
||||||
Navigator.of(dialogContext).pop();
|
Navigator.of(dialogContext).pop();
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
|
|||||||
Reference in New Issue
Block a user