Algoritmo de busca
[ 16 de Abril, 2025 ]
Algumas semanas atrás, eu estava testando o chatbot da nossa plataforma e reparei em um detalhe curioso: mesmo quando a pergunta era parecida com um problema já cadastrado, o resultado retornado era… diferente do esperado. Inicialmente, os resultados de buscas retornavam soluções priorizando os problemas e as tags. Mas ainda assim, era uma busca que poderia conter falhas. Afinal, os logs indicavam que a correspondência de palavras-chave foi reconhecida corretamente, e a pontuação estava sendo calculada com os pesos ajustados. Os valores pareciam coerentes, não havendo necessidade de ajustes. Posteriormente, notei que algumas buscas não retornavam resultados desejados, exigindo maior refinamento.
O chatbot utiliza a função CalculateSimilarity, que normaliza os textos (remove acentos, stopwords etc.), tokeniza a query e os problemas do banco de dados, e compara os tokens, calculando a similaridade baseada na proporção de palavras coincidentes.
Mas existe um problema: o método não considera a importância das palavras na base. Algumas palavras podem ter mais peso que outras, e nossa aplicação estava tratando todas igualmente.
> O que melhorar?
Cheguei a três melhorias principais no cálculo:
- 1. Maior peso para tags e categorias: se uma palavra da query estiver presente nesses campos, ela valerá mais pontos.
- 2. Filtro por frequência: palavras muito comuns (como "o", "de", "com") não devem ter tanto peso.
- 3. Similaridade mínima: resultados com uma pontuação muito baixa (ex: 10%) devem ser descartados.
Atualizei a função CalculateSimilarity com a seguinte lógica:
foreach (var token in inputTokens)
{
if (problemTokens.Contains(token)) matches++;
if (tagTokens.Contains(token)) tagMatches++;
}
double score = ((matches * 1.0) + (tagMatches * 1.5)) / inputTokens.Count;
Esse código faz com que as palavras que aparecem nas tags ganhem um peso 1.5x maior que as do problema principal, e a similaridade agora reflete melhor os tópicos principais.
Também criei uma lista de stopwords para ignorar palavras irrelevantes, e apliquei um limite mínimo de similaridade. Assim, garantimos que só resultados realmente relevantes sejam exibidos.
> E quanto à performance?
Mesmo com a busca refinada, percebi que ainda dava para melhorar o tempo de resposta. A solução: cache com Redis.
Sem cache, o fluxo era assim:
- 1. Usuário envia pergunta ao chatbot.
- 2. A API consulta o banco de dados.
- 3. SQL Server processa e retorna os dados.
- 4. API responde ao usuário.
Esse processo, com muitos acessos simultâneos, poderia sobrecarregar o banco. A solução foi usar o Redis para armazenar as respostas frequentes e evitar consultas desnecessárias.
Agora, a API verifica primeiro no Redis. Se a resposta estiver no cache, ela é retornada imediatamente. Caso contrário, busca no banco, salva no Redis e responde.
O resultado? Menos chamadas ao banco, respostas em milissegundos e maior escalabilidade sem comprometer o desempenho.