""" Aba de Exploração da Base de Conhecimento Permite visualizar documentos armazenados e testar busca semântica """ import time import gradio as gr from src.database import DatabaseManager from src.embeddings import EmbeddingManager def create_exploration_tab(db_manager: DatabaseManager, embedding_manager: EmbeddingManager, session_id: str): """Cria aba de exploração da base de conhecimento""" with gr.Tab(" Exploração da Base"): gr.Markdown(""" ## Exploração da Base de Conhecimento Visualize documentos armazenados e teste a busca semântica: - **Documentos**: Veja todos os chunks armazenados - **Busca Semântica**: Teste queries e veja resultados ranqueados - **Scores de Similaridade**: Entenda a relevância dos resultados """) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Estatísticas do Banco") refresh_stats_btn = gr.Button(" Atualizar Estatísticas", size="sm") db_stats_display = gr.JSON(label="Métricas da Base de Dados") gr.Markdown("### Busca Semântica") search_query = gr.Textbox( label="Query de Busca", placeholder="Digite sua pergunta ou termo de busca...", lines=2 ) top_k_search = gr.Slider( minimum=1, maximum=20, value=5, step=1, label="Top K (quantidade de resultados)" ) search_btn = gr.Button(" Buscar", variant="primary") with gr.Column(scale=2): gr.Markdown("### Resultados da Busca") search_status = gr.Markdown("Aguardando query de busca...") with gr.Accordion(" Resultados Detalhados", open=True): search_results = gr.Dataframe( headers=["Rank", "Score", "Título", "Conteúdo (preview)"], label="Documentos Recuperados", wrap=True ) with gr.Accordion(" Análise de Scores", open=False): scores_analysis = gr.Markdown("Sem dados") with gr.Accordion(" Conteúdo Completo", open=False): full_content_display = gr.Textbox( label="Conteúdo dos Resultados", lines=15, max_lines=30, interactive=False ) with gr.Row(): gr.Markdown("### 📚 Documentos Armazenados") with gr.Row(): docs_limit = gr.Slider( minimum=10, maximum=200, value=50, step=10, label="Limite de documentos a exibir" ) refresh_docs_btn = gr.Button(" Carregar Documentos") documents_display = gr.Dataframe( headers=["ID", "Título", "Preview", "Data"], label="Base de Conhecimento", wrap=True ) # Função para atualizar estatísticas def refresh_stats(): stats = db_manager.get_database_stats() if not stats: return {"erro": "Não foi possível conectar ao banco"} return stats # Função para busca semântica def semantic_search(query, k): if not query or not query.strip(): return ( " Digite uma query para buscar", [], "Nenhuma análise disponível", "" ) start_time = time.time() # Gera embedding da query query_embedding = embedding_manager.encode_single(query, normalize=True) # Busca no banco (apenas na sessão do usuário) results = db_manager.search_similar(query_embedding, k=int(k), session_id=session_id) search_time = (time.time() - start_time) * 1000 if not results: return ( f" Nenhum resultado encontrado (tempo: {search_time:.0f}ms)", [], "Sem resultados para análise", "" ) # Prepara dados para tabela table_data = [] full_contents = [] for i, result in enumerate(results, 1): preview = result['content'][:100] + "..." if len(result['content']) > 100 else result['content'] table_data.append([ i, f"{result['score']:.4f}", result['title'], preview ]) full_contents.append(f"--- Resultado {i} (Score: {result['score']:.4f}) ---") full_contents.append(f"Título: {result['title']}") full_contents.append(f"Conteúdo:\n{result['content']}\n") # Análise de scores scores = [r['score'] for r in results] avg_score = sum(scores) / len(scores) max_score = max(scores) min_score = min(scores) analysis_md = f""" **Análise dos Scores de Similaridade** - **Tempo de busca**: {search_time:.0f}ms - **Resultados encontrados**: {len(results)} - **Score máximo**: {max_score:.4f} (melhor match) - **Score mínimo**: {min_score:.4f} (pior match) - **Score médio**: {avg_score:.4f} **Interpretação**: - Scores próximos de 1.0 = alta similaridade - Scores próximos de 0.5 = similaridade moderada - Scores abaixo de 0.3 = baixa similaridade """ status_md = f" **Busca concluída** em {search_time:.0f}ms | {len(results)} resultados" full_text = "\n".join(full_contents) return status_md, table_data, analysis_md, full_text # Função para carregar documentos def load_documents(limit): docs = db_manager.get_all_documents(limit=int(limit), session_id=session_id) if not docs: return [] table_data = [] for doc in docs: preview = doc['content'][:80] + "..." if len(doc['content']) > 80 else doc['content'] created_str = str(doc['created_at']) if doc['created_at'] else "N/A" table_data.append([ doc['id'], doc['title'], preview, created_str ]) return table_data # Conecta eventos refresh_stats_btn.click( fn=refresh_stats, outputs=[db_stats_display] ) search_btn.click( fn=semantic_search, inputs=[search_query, top_k_search], outputs=[search_status, search_results, scores_analysis, full_content_display] ) refresh_docs_btn.click( fn=load_documents, inputs=[docs_limit], outputs=[documents_display] ) return { "search_query": search_query, "search_btn": search_btn }