Spaces:
Sleeping
Sleeping
| """ | |
| app.py — Interface Gradio para o RAG didático (compatível com Gradio 5) | |
| Execute localmente: python app.py | |
| No HF Space: o Spaces detecta automaticamente o app.py com gr.Blocks() | |
| """ | |
| import gradio as gr | |
| from rag import SimpleRAG | |
| # --------------------------------------------------------------------------- | |
| # Texto de exemplo embutido | |
| # --------------------------------------------------------------------------- | |
| SAMPLE_TEXT = """ | |
| Inteligência Artificial (IA) é um campo da ciência da computação dedicado a criar sistemas | |
| capazes de realizar tarefas que normalmente exigiriam inteligência humana. Essas tarefas incluem | |
| reconhecimento de fala, visão computacional, tradução de idiomas, tomada de decisões e muito mais. | |
| A história da IA começa na década de 1950, quando Alan Turing propôs o famoso "Teste de Turing" | |
| para avaliar se uma máquina poderia exibir comportamento inteligente indistinguível do humano. | |
| Em 1956, John McCarthy cunhou o termo "Inteligência Artificial" na Conferência de Dartmouth, | |
| considerada o marco oficial do nascimento da área. | |
| O aprendizado de máquina (Machine Learning) é um subcampo da IA que permite que sistemas | |
| aprendam automaticamente a partir de dados, sem serem explicitamente programados. Os principais | |
| paradigmas são: aprendizado supervisionado, não supervisionado e por reforço. | |
| O aprendizado profundo (Deep Learning) utiliza redes neurais com múltiplas camadas (por isso | |
| "profundo") para aprender representações hierárquicas dos dados. Essa abordagem revolucionou | |
| áreas como visão computacional e processamento de linguagem natural a partir de 2012, com | |
| a vitória da rede AlexNet na competição ImageNet. | |
| Os Modelos de Linguagem de Grande Escala (LLMs) são treinados em enormes quantidades de texto | |
| e conseguem gerar, resumir e responder perguntas em linguagem natural. Exemplos incluem | |
| GPT-4 da OpenAI, Gemini do Google e Claude da Anthropic. Esses modelos funcionam prevendo | |
| o próximo token (pedaço de palavra) com base no contexto anterior. | |
| O RAG (Retrieval-Augmented Generation) combina a busca de informações relevantes em uma base | |
| de conhecimento com a capacidade de geração de texto dos LLMs. Em vez de depender apenas do | |
| conhecimento memorizado durante o treinamento, o modelo recebe trechos do documento como | |
| contexto adicional, tornando as respostas mais precisas e verificáveis. | |
| Aplicações práticas de IA incluem: assistentes virtuais (Siri, Alexa), carros autônomos, | |
| diagnóstico médico por imagem, recomendação de conteúdo em plataformas de streaming, | |
| detecção de fraudes em bancos e tradução automática de textos. | |
| Os desafios éticos da IA envolvem preocupações com vieses algorítmicos, privacidade de dados, | |
| desemprego tecnológico e o risco de sistemas autônomos tomarem decisões críticas sem supervisão | |
| humana adequada. A área de IA Responsável busca desenvolver frameworks e regulamentações para | |
| garantir que sistemas de IA sejam seguros, justos e transparentes. | |
| """ | |
| # --------------------------------------------------------------------------- | |
| # Estado global | |
| # --------------------------------------------------------------------------- | |
| rag_instance = None | |
| is_indexed = False | |
| def setup_rag(hf_token, document, chunk_size, overlap): | |
| global rag_instance, is_indexed | |
| if not hf_token.strip(): | |
| return "❌ Insira um token válido do Hugging Face.", gr.update(interactive=False) | |
| if not document.strip(): | |
| return "❌ O documento está vazio.", gr.update(interactive=False) | |
| try: | |
| rag_instance = SimpleRAG(hf_token=hf_token.strip()) | |
| n_chunks = rag_instance.index(document, chunk_size=chunk_size, overlap=overlap) | |
| is_indexed = True | |
| return ( | |
| f"✅ Documento indexado com sucesso!\n" | |
| f" • Chunks gerados : {n_chunks}\n" | |
| f" • Chunk size : {chunk_size} palavras\n" | |
| f" • Overlap : {overlap} palavras\n\n" | |
| f"Agora faça uma pergunta sobre o documento!", | |
| gr.update(interactive=True), | |
| ) | |
| except Exception as e: | |
| is_indexed = False | |
| return f"❌ Erro ao indexar: {str(e)}", gr.update(interactive=False) | |
| def answer_question(question, top_k, history): | |
| """Gradio 5: history é lista de dicts {role, content}.""" | |
| global rag_instance, is_indexed | |
| if not is_indexed or rag_instance is None: | |
| history = history or [] | |
| history.append({"role": "user", "content": question}) | |
| history.append({"role": "assistant", "content": "⚠️ Indexe um documento primeiro na aba **Configuração**."}) | |
| return history, "_Faça uma pergunta para ver os trechos recuperados._", "" | |
| if not question.strip(): | |
| return history or [], "_Faça uma pergunta para ver os trechos recuperados._", "" | |
| try: | |
| answer, retrieved = rag_instance.query(question, top_k=top_k) | |
| context_display = "" | |
| for i, (chunk, score) in enumerate(retrieved, 1): | |
| bar = "█" * int(score * 20) | |
| context_display += ( | |
| f"**Trecho {i}** — similaridade: `{score:.3f}` {bar}\n\n" | |
| f"```\n{chunk[:400]}{'...' if len(chunk) > 400 else ''}\n```\n\n---\n\n" | |
| ) | |
| history = history or [] | |
| history.append({"role": "user", "content": question}) | |
| history.append({"role": "assistant", "content": answer}) | |
| return history, context_display, "" | |
| except Exception as e: | |
| history = history or [] | |
| history.append({"role": "user", "content": question}) | |
| history.append({"role": "assistant", "content": f"❌ Erro: {str(e)}"}) | |
| return history, "", "" | |
| # --------------------------------------------------------------------------- | |
| # Interface | |
| # --------------------------------------------------------------------------- | |
| CSS = """ | |
| .gradio-container { max-width: 1100px !important; margin: auto; } | |
| #title-block { text-align: center; padding: 24px 0 8px; } | |
| #title-block h1 { font-size: 2rem; font-weight: 700; margin-bottom: 4px; } | |
| #title-block p { color: #9ca3af; font-size: 0.95rem; } | |
| #status-box textarea { font-family: monospace; font-size: 0.85rem; } | |
| """ | |
| with gr.Blocks(css=CSS, title="RAG Didático — HF Spaces") as demo: | |
| gr.HTML(""" | |
| <div id="title-block"> | |
| <h1>🔍 RAG Didático</h1> | |
| <p>Retrieval-Augmented Generation com Hugging Face Inference API</p> | |
| <p style="font-size:0.8rem; color:#6b7280;"> | |
| Embeddings: <code>all-MiniLM-L6-v2</code> | | |
| LLM: <code>Llama-3.1-8B (Cerebras)</code> | | |
| Busca: Similaridade Cosseno | |
| </p> | |
| </div> | |
| """) | |
| with gr.Tabs(): | |
| with gr.Tab("⚙️ Configuração"): | |
| gr.Markdown( | |
| "### 1. Token do Hugging Face\n" | |
| "Crie um token gratuito em [hf.co/settings/tokens](https://huggingface.co/settings/tokens) " | |
| "(tipo **Read** é suficiente)." | |
| ) | |
| hf_token_input = gr.Textbox( | |
| label="HF Token", placeholder="hf_...", type="password", max_lines=1 | |
| ) | |
| gr.Markdown("### 2. Documento a ser indexado") | |
| document_input = gr.Textbox( | |
| label="Texto do documento", value=SAMPLE_TEXT, lines=12, | |
| placeholder="Cole aqui o texto que deseja consultar...", | |
| ) | |
| with gr.Row(): | |
| chunk_size_slider = gr.Slider( | |
| 50, 600, value=300, step=50, | |
| label="Tamanho do chunk (palavras)", | |
| info="Chunks menores = mais precisão; maiores = mais contexto", | |
| ) | |
| overlap_slider = gr.Slider( | |
| 0, 150, value=50, step=10, | |
| label="Overlap entre chunks (palavras)", | |
| info="Sobreposição evita cortar informações no meio", | |
| ) | |
| index_btn = gr.Button("🚀 Indexar Documento", variant="primary") | |
| status_output = gr.Textbox( | |
| label="Status da indexação", elem_id="status-box", | |
| lines=5, interactive=False, | |
| ) | |
| with gr.Tab("💬 Chat"): | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| chatbot = gr.Chatbot( | |
| label="Conversa", height=420, | |
| type="messages", # formato Gradio 5 | |
| ) | |
| with gr.Row(): | |
| question_input = gr.Textbox( | |
| label="Pergunta", | |
| placeholder="Faça uma pergunta sobre o documento...", | |
| scale=5, interactive=False, | |
| ) | |
| ask_btn = gr.Button("Perguntar", variant="primary", scale=1) | |
| top_k_slider = gr.Slider( | |
| 1, 6, value=3, step=1, | |
| label="Número de trechos recuperados (top-k)", | |
| info="Quantos chunks serão enviados como contexto ao LLM", | |
| ) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 📄 Trechos Recuperados") | |
| gr.Markdown("*O coração do RAG — veja exatamente o que o modelo usa para responder!*") | |
| context_output = gr.Markdown( | |
| value="_Faça uma pergunta para ver os trechos recuperados._" | |
| ) | |
| with gr.Tab("📚 Como Funciona"): | |
| gr.Markdown(""" | |
| ## Pipeline RAG — Passo a Passo | |
| ``` | |
| INDEXAÇÃO (feita uma vez) | |
| ────────────────────────────────────────────── | |
| Documento → [Chunking] → [Embedding] → Índice | |
| CONSULTA (a cada pergunta) | |
| ────────────────────────────────────────────── | |
| Pergunta → [Embedding] → [Similaridade Cosseno] | |
| → Top-K chunks → [Prompt RAG] → LLM → Resposta | |
| ``` | |
| ## Por que RAG? | |
| | Problema do LLM puro | Solução com RAG | | |
| |---|---| | |
| | Conhecimento desatualizado | Contexto sempre atual | | |
| | Alucina fatos | Resposta baseada no documento | | |
| | Não conhece seus dados | Funciona com qualquer texto | | |
| | Caixa-preta | Trechos recuperados são visíveis | | |
| """) | |
| index_btn.click( | |
| fn=setup_rag, | |
| inputs=[hf_token_input, document_input, chunk_size_slider, overlap_slider], | |
| outputs=[status_output, question_input], | |
| ) | |
| ask_btn.click( | |
| fn=answer_question, | |
| inputs=[question_input, top_k_slider, chatbot], | |
| outputs=[chatbot, context_output, question_input], | |
| ) | |
| question_input.submit( | |
| fn=answer_question, | |
| inputs=[question_input, top_k_slider, chatbot], | |
| outputs=[chatbot, context_output, question_input], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |