busca.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { BancoVetorial, type DocumentoComEmbedding } from './database';
  2. import { gerarEmbedding } from './insert-embeddings';
  3. import { calcularSimilaridadeCosseno } from './utils';
  4. import type { ResultadoBusca } from './types';
  5. /**
  6. * Busca os documentos mais similares a uma pergunta
  7. *
  8. * Este é o coração do sistema RAG!
  9. *
  10. * Processo:
  11. * 1. Converte a pergunta em embedding
  12. * 2. Busca todos os documentos do banco
  13. * 3. Calcula similaridade entre pergunta e cada documento
  14. * 4. Ordena por similaridade (maior primeiro)
  15. * 5. Retorna os N mais relevantes
  16. *
  17. * @param pergunta - A pergunta do usuário
  18. * @param topK - Quantos documentos retornar (padrão: 3)
  19. * @param limiarSimilaridade - Similaridade mínima para considerar relevante (padrão: 0.5)
  20. * @returns Array com os documentos mais similares
  21. */
  22. async function buscarDocumentosSimilares(
  23. pergunta: string,
  24. topK: number = 3,
  25. limiarSimilaridade: number = 0.5
  26. ): Promise<ResultadoBusca[]> {
  27. console.log(`\nBuscando documentos similares a: "${pergunta}"`);
  28. console.log(`Parametros: topK=${topK}, limiar=${limiarSimilaridade}\n`);
  29. try {
  30. // Passo 1: Gerar embedding da pergunta
  31. console.log('Gerando embedding da pergunta...');
  32. const embeddingPergunta = await gerarEmbedding(pergunta);
  33. console.log(` - Embedding gerado: ${embeddingPergunta.length} dimensoes`);
  34. // Passo 2: Buscar todos os documentos do banco
  35. console.log('\nBuscando documentos no banco...');
  36. const banco = new BancoVetorial();
  37. const todosDocumentos = banco.buscarTodosDocumentos();
  38. console.log(` - Encontrados ${todosDocumentos.length} documentos`);
  39. // Passo 3: Calcular similaridade para cada documento
  40. console.log('\nCalculando similaridades...');
  41. const resultados: ResultadoBusca[] = [];
  42. for (const documento of todosDocumentos) {
  43. const similaridade = calcularSimilaridadeCosseno(
  44. embeddingPergunta,
  45. documento.embedding
  46. );
  47. console.log(` - ${documento.nome}: ${similaridade.toFixed(4)}`);
  48. // Só adiciona se passar no limiar de similaridade
  49. if (similaridade >= limiarSimilaridade) {
  50. resultados.push({
  51. documento,
  52. similaridade
  53. });
  54. }
  55. }
  56. // Passo 4: Ordenar por similaridade (maior primeiro)
  57. // Sort é in-place, modifica o array original
  58. resultados.sort((a, b) => b.similaridade - a.similaridade);
  59. // Passo 5: Retornar apenas os topK resultados
  60. const resultadosFinais = resultados.slice(0, topK);
  61. console.log(`\nDocumentos relevantes encontrados: ${resultadosFinais.length}`);
  62. // Fecha a conexão com o banco
  63. banco.fechar();
  64. return resultadosFinais;
  65. } catch (erro) {
  66. console.error('Erro na busca:', erro);
  67. throw new Error('Falha ao buscar documentos similares');
  68. }
  69. }
  70. /**
  71. * Monta um contexto concatenando os documentos mais relevantes
  72. *
  73. * Por que montar um contexto?
  74. * - A LLM precisa receber os documentos relevantes junto com a pergunta
  75. * - Isso permite que ela responda baseada em informações reais
  76. * - Reduz alucinações e aumenta a precisão
  77. *
  78. * @param resultados - Array com os resultados da busca
  79. * @returns String com o contexto formatado
  80. */
  81. function montarContexto(resultados: ResultadoBusca[]): string {
  82. if (resultados.length === 0) {
  83. return 'Nenhum documento relevante encontrado.';
  84. }
  85. // Construímos o contexto formatado
  86. let contexto = 'Documentos relevantes:\n\n';
  87. resultados.forEach((resultado, index) => {
  88. contexto += `--- Documento ${index + 1}: ${resultado.documento.nome} (similaridade: ${resultado.similaridade.toFixed(2)}) ---\n`;
  89. contexto += `${resultado.documento.conteudo}\n\n`;
  90. });
  91. return contexto;
  92. }
  93. // Exporta para ser usado em outros arquivos
  94. export {
  95. buscarDocumentosSimilares,
  96. montarContexto,
  97. calcularSimilaridadeCosseno,
  98. type ResultadoBusca
  99. };