Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| } | |