206 lines
7.3 KiB
C#
206 lines
7.3 KiB
C#
#pragma warning disable SKEXP0001
|
|
|
|
using ChatApi.Models;
|
|
using ChatRAG.Contracts.VectorSearch;
|
|
using ChatRAG.Models;
|
|
using ChatRAG.Services.Contracts;
|
|
using Microsoft.SemanticKernel;
|
|
using Microsoft.SemanticKernel.ChatCompletion;
|
|
using Microsoft.SemanticKernel.Embeddings;
|
|
|
|
namespace ChatRAG.Services.ResponseService
|
|
{
|
|
public class QdrantResponseService : IResponseService
|
|
{
|
|
private readonly IVectorSearchService _vectorSearchService;
|
|
private readonly ITextEmbeddingGenerationService _embeddingService;
|
|
private readonly IChatCompletionService _chatService;
|
|
private readonly ILogger<QdrantResponseService> _logger;
|
|
|
|
public QdrantResponseService(
|
|
IVectorSearchService vectorSearchService,
|
|
ITextEmbeddingGenerationService embeddingService,
|
|
IChatCompletionService chatService,
|
|
ILogger<QdrantResponseService> logger)
|
|
{
|
|
_vectorSearchService = vectorSearchService;
|
|
_embeddingService = embeddingService;
|
|
_chatService = chatService;
|
|
_logger = logger;
|
|
}
|
|
|
|
public string ProviderName => "Qdrant";
|
|
|
|
public async Task<string> GetResponse(
|
|
UserData userData,
|
|
string projectId,
|
|
string sessionId,
|
|
string userMessage)
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation("Processando consulta RAG com Qdrant para projeto {ProjectId}", projectId);
|
|
|
|
// 1. Gerar embedding da pergunta do usuário
|
|
var questionEmbedding = await _embeddingService.GenerateEmbeddingAsync(userMessage);
|
|
var embeddingArray = questionEmbedding.ToArray().Select(e => (double)e).ToArray();
|
|
|
|
// 2. Buscar documentos similares no Qdrant
|
|
var searchResults = await _vectorSearchService.SearchSimilarDynamicAsync(
|
|
queryEmbedding: embeddingArray,
|
|
projectId: projectId,
|
|
minThreshold: 0.5,
|
|
limit: 5
|
|
);
|
|
|
|
// 3. Construir contexto a partir dos resultados
|
|
var context = BuildContextFromResults(searchResults);
|
|
|
|
// 4. Criar prompt com contexto
|
|
var prompt = BuildRagPrompt(userMessage, context);
|
|
|
|
// 5. Gerar resposta usando LLM
|
|
var response = await _chatService.GetChatMessageContentAsync(prompt);
|
|
|
|
_logger.LogDebug("Resposta RAG gerada com {ResultCount} documentos do Qdrant",
|
|
searchResults.Count);
|
|
|
|
return response.Content ?? "Desculpe, não foi possível gerar uma resposta.";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao processar consulta RAG com Qdrant");
|
|
return "Ocorreu um erro ao processar sua consulta. Tente novamente.";
|
|
}
|
|
}
|
|
|
|
public async Task<string> GetResponseWithHistory(
|
|
UserData userData,
|
|
string projectId,
|
|
string sessionId,
|
|
string userMessage,
|
|
List<string> conversationHistory)
|
|
{
|
|
try
|
|
{
|
|
// Combina histórico com mensagem atual para melhor contexto
|
|
var enhancedMessage = BuildEnhancedMessageWithHistory(userMessage, conversationHistory);
|
|
|
|
return await GetResponse(userData, projectId, sessionId, enhancedMessage);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao processar consulta RAG com histórico");
|
|
return "Ocorreu um erro ao processar sua consulta. Tente novamente.";
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// MÉTODOS AUXILIARES PRIVADOS
|
|
// ========================================
|
|
|
|
private string BuildContextFromResults(List<VectorSearchResult> results)
|
|
{
|
|
if (!results.Any())
|
|
{
|
|
return "Nenhum documento relevante encontrado.";
|
|
}
|
|
|
|
var contextBuilder = new System.Text.StringBuilder();
|
|
contextBuilder.AppendLine("=== CONTEXTO DOS DOCUMENTOS ===");
|
|
|
|
foreach (var result in results.Take(5)) // Limita a 5 documentos
|
|
{
|
|
contextBuilder.AppendLine($"\n--- Documento: {result.Title} (Relevância: {result.GetScorePercentage()}) ---");
|
|
contextBuilder.AppendLine(result.Content);
|
|
contextBuilder.AppendLine();
|
|
}
|
|
|
|
return contextBuilder.ToString();
|
|
}
|
|
|
|
private string BuildRagPrompt(string userQuestion, string context)
|
|
{
|
|
return $@"
|
|
Você é um assistente especializado que responde perguntas baseado nos documentos fornecidos.
|
|
|
|
CONTEXTO DOS DOCUMENTOS:
|
|
{context}
|
|
|
|
PERGUNTA DO USUÁRIO:
|
|
{userQuestion}
|
|
|
|
INSTRUÇÕES:
|
|
- Responda baseado APENAS nas informações dos documentos fornecidos
|
|
- Se a informação não estiver nos documentos, diga que não encontrou a informação
|
|
- Seja preciso e cite trechos relevantes quando possível
|
|
- Mantenha um tom profissional e prestativo
|
|
- Se houver múltiplas informações relevantes, organize-as de forma clara
|
|
|
|
RESPOSTA:
|
|
";
|
|
}
|
|
|
|
private string BuildEnhancedMessageWithHistory(string currentMessage, List<string> history)
|
|
{
|
|
if (!history.Any())
|
|
return currentMessage;
|
|
|
|
var enhancedMessage = new System.Text.StringBuilder();
|
|
|
|
enhancedMessage.AppendLine("HISTÓRICO DA CONVERSA:");
|
|
foreach (var message in history.TakeLast(3)) // Últimas 3 mensagens para contexto
|
|
{
|
|
enhancedMessage.AppendLine($"- {message}");
|
|
}
|
|
|
|
enhancedMessage.AppendLine($"\nPERGUNTA ATUAL: {currentMessage}");
|
|
|
|
return enhancedMessage.ToString();
|
|
}
|
|
|
|
// ========================================
|
|
// MÉTODOS DE ESTATÍSTICAS
|
|
// ========================================
|
|
|
|
public async Task<Dictionary<string, object>> GetProviderStatsAsync()
|
|
{
|
|
try
|
|
{
|
|
var vectorStats = await _vectorSearchService.GetStatsAsync();
|
|
|
|
return new Dictionary<string, object>(vectorStats)
|
|
{
|
|
["response_service_provider"] = "Qdrant",
|
|
["supports_history"] = true,
|
|
["supports_dynamic_threshold"] = true,
|
|
["last_check"] = DateTime.UtcNow
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new Dictionary<string, object>
|
|
{
|
|
["response_service_provider"] = "Qdrant",
|
|
["health"] = "error",
|
|
["error"] = ex.Message,
|
|
["last_check"] = DateTime.UtcNow
|
|
};
|
|
}
|
|
}
|
|
|
|
public async Task<bool> IsHealthyAsync()
|
|
{
|
|
try
|
|
{
|
|
return await _vectorSearchService.IsHealthyAsync();
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma warning restore SKEXP0001 |