Busca mais eficiente
This commit is contained in:
parent
fdb4fc6996
commit
8f4c8ad1af
@ -58,7 +58,7 @@ namespace ChatApi.Controllers
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[Route("saveproject")]
|
||||
[Route("savegroup")]
|
||||
public async Task SaveSingleProject([FromBody] ProjectRequest project)
|
||||
{
|
||||
var projectSave = project.MapTo<ProjectRequest, Project>();
|
||||
@ -66,7 +66,7 @@ namespace ChatApi.Controllers
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("projects")]
|
||||
[Route("groups")]
|
||||
public async Task<IEnumerable<ProjectRequest>> GetProjects()
|
||||
{
|
||||
var projects = await _projectDataRepository.GetAsync();
|
||||
|
||||
118
Data/SearchTextData.cs
Normal file
118
Data/SearchTextData.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using ChatRAG.Models;
|
||||
using MongoDB.Driver;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ChatRAG.Data
|
||||
{
|
||||
public class SearchTextData
|
||||
{
|
||||
private readonly IMongoCollection<TextoComEmbedding> _textsCollection;
|
||||
|
||||
public SearchTextData(IMongoCollection<TextoComEmbedding> textsCollection)
|
||||
{
|
||||
_textsCollection = textsCollection;
|
||||
}
|
||||
|
||||
// Busca otimizada com filtros pré-aplicados
|
||||
public async Task<List<ResultadoSimilaridade>> BuscarSimilaridadeOtimizada(
|
||||
double[] queryEmbedding,
|
||||
string projectId = null,
|
||||
string tipoDocumento = null,
|
||||
double similaridadeMinima = 0.3,
|
||||
int? limite = 3)
|
||||
{
|
||||
// 1. Aplica filtros primeiro para reduzir dataset
|
||||
var filterBuilder = Builders<TextoComEmbedding>.Filter;
|
||||
var filters = new List<FilterDefinition<TextoComEmbedding>>();
|
||||
|
||||
if (!string.IsNullOrEmpty(projectId))
|
||||
filters.Add(filterBuilder.Eq(x => x.ProjetoId, projectId));
|
||||
|
||||
if (!string.IsNullOrEmpty(tipoDocumento))
|
||||
filters.Add(filterBuilder.Eq(x => x.TipoDocumento, tipoDocumento));
|
||||
|
||||
var finalFilter = filters.Any()
|
||||
? filterBuilder.And(filters)
|
||||
: filterBuilder.Empty;
|
||||
|
||||
// 2. Busca apenas os documentos filtrados
|
||||
var documentosFiltrados = await _textsCollection
|
||||
.Find(finalFilter)
|
||||
.ToListAsync();
|
||||
|
||||
// 3. Calcula similaridade apenas nos documentos filtrados
|
||||
var resultados = documentosFiltrados
|
||||
.AsParallel() // Paralelização para performance
|
||||
.Select(doc => new ResultadoSimilaridade
|
||||
{
|
||||
Documento = doc,
|
||||
Similaridade = CalcularSimilaridadeCoseno(queryEmbedding, doc.Embedding)
|
||||
})
|
||||
.Where(r => r.Similaridade >= similaridadeMinima)
|
||||
.OrderByDescending(r => r.Similaridade)
|
||||
.ToList();
|
||||
|
||||
// 4. Aplica limite se especificado
|
||||
if (limite.HasValue)
|
||||
resultados = resultados.Take(limite.Value).ToList();
|
||||
|
||||
return resultados;
|
||||
}
|
||||
|
||||
// Busca dinâmica baseada em threshold de similaridade
|
||||
public async Task<List<ResultadoSimilaridade>> BuscarPorSimilaridadeDinamica(
|
||||
double[] queryEmbedding,
|
||||
string projectId,
|
||||
double similaridadeMinima = 0.5,
|
||||
int limiteSuperior = 50) // Limite máximo para evitar sobrecarga
|
||||
{
|
||||
var resultados = await BuscarSimilaridadeOtimizada(
|
||||
queryEmbedding,
|
||||
projectId,
|
||||
similaridadeMinima: similaridadeMinima,
|
||||
limite: limiteSuperior
|
||||
);
|
||||
|
||||
// Se não encontrar resultados suficientes, relaxa o threshold
|
||||
if (resultados.Count < 3)
|
||||
{
|
||||
resultados = await BuscarSimilaridadeOtimizada(
|
||||
queryEmbedding,
|
||||
projectId,
|
||||
similaridadeMinima: similaridadeMinima * 0.7, // Reduz 30%
|
||||
limite: limiteSuperior
|
||||
);
|
||||
}
|
||||
|
||||
return resultados;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ResultadoSimilaridade
|
||||
{
|
||||
public TextoComEmbedding Documento { get; set; }
|
||||
public double Similaridade { get; set; }
|
||||
}
|
||||
|
||||
public class GrupoSimilaridade
|
||||
{
|
||||
public ResultadoSimilaridade Representante { get; set; }
|
||||
public List<ResultadoSimilaridade> Membros { get; set; }
|
||||
public int TotalMembros => Membros.Count;
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,11 @@ namespace ChatRAG.Data
|
||||
bookStoreDatabaseSettings.Value.TextCollectionName);
|
||||
}
|
||||
|
||||
public IMongoCollection<TextoComEmbedding> GetCollection()
|
||||
{
|
||||
return _textsCollection;
|
||||
}
|
||||
|
||||
public async Task<List<TextoComEmbedding>> GetAsync() =>
|
||||
await _textsCollection.Find(_ => true).ToListAsync();
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ builder.Services.AddSingleton<CryptUtil>();
|
||||
//var apiClient = new OllamaApiClient(new Uri("http://localhost:11435"), "tinydolphin");
|
||||
|
||||
//Olllama
|
||||
builder.Services.AddOllamaChatCompletion("llama3.1", new Uri("http://localhost:11434"));
|
||||
builder.Services.AddOllamaChatCompletion("llama3.2", new Uri("http://localhost:11434"));
|
||||
|
||||
//builder.Services.AddOllamaChatCompletion("tinydolphin", new Uri("http://localhost:11435"));
|
||||
//builder.Services.AddOllamaChatCompletion("tinyllama", new Uri("http://localhost:11435"));
|
||||
|
||||
@ -42,7 +42,8 @@ namespace ChatRAG.Services.ResponseService
|
||||
stopWatch.Start();
|
||||
|
||||
//var resposta = await BuscarTextoRelacionado(question);
|
||||
var resposta = await BuscarTopTextosRelacionados(question, projectId);
|
||||
//var resposta = await BuscarTopTextosRelacionados(question, projectId);
|
||||
var resposta = await BuscarTopTextosRelacionadosDinamico(question, projectId);
|
||||
|
||||
var projectData = (await _projectDataRepository.GetAsync()).FirstOrDefault();
|
||||
|
||||
@ -87,6 +88,92 @@ namespace ChatRAG.Services.ResponseService
|
||||
return melhorTexto != null ? melhorTexto.Conteudo : "Não encontrei uma resposta adequada.";
|
||||
}
|
||||
|
||||
// Adicione esta nova rotina no seu ResponseRAGService
|
||||
|
||||
async Task<string> BuscarTopTextosRelacionadosDinamico(string pergunta, string projectId, 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();
|
||||
|
||||
// Cria instância da classe de busca otimizada
|
||||
var searchTextData = new SearchTextData(_textDataRepository.GetCollection()); // Você precisará expor a collection
|
||||
|
||||
// Busca dinâmica com threshold adaptativo
|
||||
var resultados = await BuscarSimilaridadeHibridaAdaptativa(
|
||||
searchTextData,
|
||||
embeddingArrayPergunta,
|
||||
projectId,
|
||||
size
|
||||
);
|
||||
|
||||
if (!resultados.Any())
|
||||
return "Não encontrei respostas adequadas para a pergunta fornecida.";
|
||||
|
||||
var cabecalho = $"Contexto encontrado para: '{pergunta}' ({resultados.Count} resultado(s)):\n\n";
|
||||
|
||||
var resultadosFormatados = resultados
|
||||
.Select((item, index) =>
|
||||
$"=== CONTEXTO {index + 1} ===\n" +
|
||||
$"Relevância: {item.Similaridade:P1}\n" +
|
||||
$"Conteúdo:\n{item.Documento.Conteudo}")
|
||||
.ToList();
|
||||
|
||||
return cabecalho + string.Join("\n\n", resultadosFormatados);
|
||||
}
|
||||
|
||||
// Método auxiliar para busca híbrida adaptativa
|
||||
private async Task<List<ResultadoSimilaridade>> BuscarSimilaridadeHibridaAdaptativa(
|
||||
SearchTextData searchTextData,
|
||||
double[] embeddingArray,
|
||||
string projectId,
|
||||
int quantidadeDesejada)
|
||||
{
|
||||
// Estratégia 1: Busca com threshold alto (0.5) limitado à quantidade desejada
|
||||
var resultados = await searchTextData.BuscarSimilaridadeOtimizada(
|
||||
embeddingArray,
|
||||
projectId,
|
||||
similaridadeMinima: 0.5,
|
||||
limite: quantidadeDesejada
|
||||
);
|
||||
|
||||
// Se conseguiu a quantidade desejada com qualidade alta, retorna
|
||||
if (resultados.Count >= quantidadeDesejada)
|
||||
{
|
||||
return resultados.Take(quantidadeDesejada).ToList();
|
||||
}
|
||||
|
||||
// Estratégia 2: Se não conseguiu o suficiente, busca com threshold médio
|
||||
if (resultados.Count < quantidadeDesejada)
|
||||
{
|
||||
resultados = await searchTextData.BuscarSimilaridadeOtimizada(
|
||||
embeddingArray,
|
||||
projectId,
|
||||
similaridadeMinima: 0.35,
|
||||
limite: quantidadeDesejada * 2 // Busca mais para ter opções
|
||||
);
|
||||
|
||||
if (resultados.Count >= quantidadeDesejada)
|
||||
{
|
||||
return resultados.Take(quantidadeDesejada).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
// Estratégia 3: Se ainda não conseguiu, busca com threshold baixo
|
||||
if (resultados.Count < quantidadeDesejada)
|
||||
{
|
||||
resultados = await searchTextData.BuscarSimilaridadeOtimizada(
|
||||
embeddingArray,
|
||||
projectId,
|
||||
similaridadeMinima: 0.2,
|
||||
limite: quantidadeDesejada * 3
|
||||
);
|
||||
}
|
||||
|
||||
// Retorna o que conseguiu encontrar, no máximo a quantidade desejada
|
||||
return resultados.Take(quantidadeDesejada).ToList();
|
||||
}
|
||||
|
||||
async Task<string> BuscarTopTextosRelacionados(string pergunta, string projectId, int size = 3)
|
||||
{
|
||||
var embeddingService = _kernel.GetRequiredService<ITextEmbeddingGenerationService>();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user