using ChatApi; using ChatApi.Models; using ChatRAG.Data; using ChatRAG.Models; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Embeddings; #pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. namespace ChatRAG.Services.ResponseService { public class ResponseRAGService : IResponseService { private readonly ChatHistoryService _chatHistoryService; private readonly Kernel _kernel; private readonly TextFilter _textFilter; private readonly TextDataRepository _textDataRepository; private readonly ProjectDataRepository _projectDataRepository; private readonly IChatCompletionService _chatCompletionService; public ResponseRAGService( ChatHistoryService chatHistoryService, Kernel kernel, TextFilter textFilter, TextDataRepository textDataRepository, ProjectDataRepository projectDataRepository, IChatCompletionService chatCompletionService) { this._chatHistoryService = chatHistoryService; this._kernel = kernel; this._textFilter = textFilter; this._textDataRepository = textDataRepository; this._projectDataRepository = projectDataRepository; this._chatCompletionService = chatCompletionService; } public async Task GetResponse(UserData userData, string projectId, string sessionId, string question) { var stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); //var resposta = await BuscarTextoRelacionado(question); var resposta = await BuscarTopTextosRelacionados(question, projectId); var projectData = (await _projectDataRepository.GetAsync()).FirstOrDefault(); var project = $"Nome: {projectData.Nome} \n\n Descrição:{projectData.Descricao}"; question = $"Para responder à solicitação/pergunta: \"{question }\" por favor, considere o projeto: \"{project}\" e os requisitos: \"{resposta}\""; ChatHistory history = _chatHistoryService.GetSumarizer(sessionId); history.AddUserMessage(question); var response = await _chatCompletionService.GetChatMessageContentAsync(history); history.AddMessage(response.Role, response.Content ?? ""); _chatHistoryService.UpdateHistory(sessionId, history); stopWatch.Stop(); return $"{response.Content ?? ""}\n\nTempo: {stopWatch.ElapsedMilliseconds / 1000}s"; } async Task BuscarTextoRelacionado(string pergunta) { var embeddingService = _kernel.GetRequiredService(); var embeddingPergunta = await embeddingService.GenerateEmbeddingAsync(_textFilter.ToLowerAndWithoutAccents(pergunta)); var embeddingArrayPergunta = embeddingPergunta.ToArray().Select(e => (double)e).ToArray(); var textos = await _textDataRepository.GetAsync(); TextoComEmbedding melhorTexto = null; double melhorSimilaridade = -1.0; foreach (var texto in textos) { double similaridade = CalcularSimilaridadeCoseno(embeddingArrayPergunta, texto.Embedding); if (similaridade > melhorSimilaridade) { melhorSimilaridade = similaridade; melhorTexto = texto; } } return melhorTexto != null ? melhorTexto.Conteudo : "Não encontrei uma resposta adequada."; } async Task BuscarTopTextosRelacionados(string pergunta, string projectId, int size = 3) { var embeddingService = _kernel.GetRequiredService(); var embeddingPergunta = await embeddingService.GenerateEmbeddingAsync(_textFilter.ToLowerAndWithoutAccents(pergunta)); var embeddingArrayPergunta = embeddingPergunta.ToArray().Select(e => (double)e).ToArray(); var textos = await _textDataRepository.GetByProjectIdAsync(projectId); var melhoresTextos = textos .Select(texto => new { Conteudo = texto.Conteudo, Similaridade = CalcularSimilaridadeCoseno(embeddingArrayPergunta, texto.Embedding) }) .Where(x => x.Similaridade > 0.3) .OrderByDescending(x => x.Similaridade) .Take(3) .ToList(); if (!melhoresTextos.Any()) return "Não encontrei respostas adequadas para a pergunta fornecida."; var cabecalho = $"Contexto encontrado para: '{pergunta}' ({melhoresTextos.Count} resultado(s)):\n\n"; var resultadosFormatados = melhoresTextos .Select((item, index) => $"=== CONTEXTO {index + 1} ===\n" + $"Relevância: {item.Similaridade:P1}\n" + $"Conteúdo:\n{item.Conteudo}") .ToList(); return cabecalho + string.Join("\n\n", resultadosFormatados); } double CalcularSimilaridadeCoseno(double[] embedding1, double[] embedding2) { double dotProduct = 0.0; double normA = 0.0; double normB = 0.0; for (int i = 0; i < embedding1.Length; i++) { dotProduct += embedding1[i] * embedding2[i]; normA += Math.Pow(embedding1[i], 2); normB += Math.Pow(embedding2[i], 2); } return dotProduct / (Math.Sqrt(normA) * Math.Sqrt(normB)); } } } #pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.