rag / app.py
FabioSantos's picture
Upload 4 files
2f8b4e1 verified
"""
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> &nbsp;|&nbsp;
LLM: <code>Llama-3.1-8B (Cerebras)</code> &nbsp;|&nbsp;
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()