133 lines
5.5 KiB
C#
133 lines
5.5 KiB
C#
|
|
using ChatApi;
|
|
using ChatApi.Models;
|
|
using ChatRAG.Models;
|
|
using ChatRAG.Repositories;
|
|
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 TextDataService _textDataService;
|
|
private readonly IChatCompletionService _chatCompletionService;
|
|
|
|
public ResponseRAGService(
|
|
ChatHistoryService chatHistoryService,
|
|
Kernel kernel,
|
|
TextFilter textFilter,
|
|
TextDataService textDataService,
|
|
IChatCompletionService chatCompletionService)
|
|
{
|
|
this._chatHistoryService = chatHistoryService;
|
|
this._kernel = kernel;
|
|
this._textFilter = textFilter;
|
|
this._textDataService = textDataService;
|
|
this._chatCompletionService = chatCompletionService;
|
|
}
|
|
public async Task<string> GetResponse(UserData userData, string sessionId, string question)
|
|
{
|
|
var stopWatch = new System.Diagnostics.Stopwatch();
|
|
stopWatch.Start();
|
|
|
|
//var resposta = await BuscarTextoRelacionado(question);
|
|
var resposta = await BuscarTopTextosRelacionados(question);
|
|
|
|
question = "Para responder à solicitação/pergunta: \"" + question + "\" por favor, considere um resumo com 3 linhas baseado exclusivamente no texto: \"" + 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<string> BuscarTextoRelacionado(string pergunta)
|
|
{
|
|
var embeddingService = _kernel.GetRequiredService<ITextEmbeddingGenerationService>();
|
|
var embeddingPergunta = await embeddingService.GenerateEmbeddingAsync(_textFilter.ToLowerAndWithoutAccents(pergunta));
|
|
var embeddingArrayPergunta = embeddingPergunta.ToArray().Select(e => (double)e).ToArray();
|
|
|
|
var textos = await _textDataService.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<string> BuscarTopTextosRelacionados(string pergunta, int size = 3)
|
|
{
|
|
var embeddingService = _kernel.GetRequiredService<ITextEmbeddingGenerationService>();
|
|
var embeddingPergunta = await embeddingService.GenerateEmbeddingAsync(_textFilter.ToLowerAndWithoutAccents(pergunta));
|
|
var embeddingArrayPergunta = embeddingPergunta.ToArray().Select(e => (double)e).ToArray();
|
|
|
|
var textos = await _textDataService.GetAsync();
|
|
|
|
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.
|