DHEIVER commited on
Commit
2da2923
·
verified ·
1 Parent(s): c1249a6

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -0
app.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import numpy as np
3
+ import faiss
4
+ from sklearn.feature_extraction.text import TfidfVectorizer
5
+ from transformers import AutoTokenizer, AutoModel, pipeline
6
+ from sentence_transformers import SentenceTransformer
7
+ import traceback
8
+ import gradio as gr
9
+
10
+ # --- Classes principais ---
11
+
12
+ class SistemaRecuperacaoAvancado:
13
+ """Classe responsável pela recuperação de chunks relevantes usando embeddings semânticos."""
14
+ def __init__(self, chunks):
15
+ self.chunks = chunks
16
+ self.textos = [chunk["texto"] for chunk in chunks]
17
+
18
+ # Inicialização do modelo de embeddings
19
+ self.embedder = SentenceTransformer('neuralmind/bert-base-portuguese-cased')
20
+ self.embeddings = self.embedder.encode(self.textos)
21
+
22
+ # Índice FAISS para busca eficiente
23
+ self.index = faiss.IndexFlatL2(self.embeddings.shape[1])
24
+ self.index.add(np.array(self.embeddings))
25
+
26
+ def recuperar_chunks_relevantes(self, pergunta, top_k=10):
27
+ """Recupera os top_k chunks mais relevantes para uma pergunta."""
28
+ pergunta_embedding = self.embedder.encode([pergunta])
29
+ _, indices = self.index.search(pergunta_embedding, top_k)
30
+ return [self.chunks[i] for i in indices[0]]
31
+
32
+ def recuperar_chunks_por_itens(self, itens, top_k=10):
33
+ """Filtra chunks que contenham qualquer um dos itens mencionados."""
34
+ return [chunk for chunk in self.chunks
35
+ if any(item in chunk["texto"] for item in itens)][:top_k]
36
+
37
+
38
+ class RAGAvançado:
39
+ """Classe principal do sistema RAG com funcionalidades de análise e geração de relatórios."""
40
+ def __init__(self, chunks):
41
+ self.sistema_recuperacao = SistemaRecuperacaoAvancado(chunks)
42
+
43
+ # Configuração do modelo BERTimbau e sumarizador
44
+ self.modelo_nome = "neuralmind/bert-base-portuguese-cased"
45
+ self.tokenizer = AutoTokenizer.from_pretrained(self.modelo_nome)
46
+ self.model = AutoModel.from_pretrained(self.modelo_nome)
47
+ self.summarizer = pipeline("summarization", model="t5-small")
48
+
49
+ def analisar_divergencias(self, chunks):
50
+ """Analisa divergências em uma lista de chunks."""
51
+ divergencias = []
52
+ divergencias_detalhes = {}
53
+ referencias = {}
54
+
55
+ for chunk in chunks:
56
+ texto, topico = chunk["texto"], chunk["topico"]
57
+ if "Divergência detectada" in texto:
58
+ partes = texto.split("|")
59
+ item_id = partes[0].strip()
60
+ descricao = partes[1].strip() if len(partes) > 1 else "Sem descrição"
61
+ divergencias.append((item_id, descricao, topico))
62
+ divergencias_detalhes[item_id] = texto
63
+
64
+ for div_id, _, _ in divergencias:
65
+ if div_id in texto and "Divergência detectada" not in texto:
66
+ referencias.setdefault(div_id, []).append((topico, texto))
67
+
68
+ return divergencias, divergencias_detalhes, referencias
69
+
70
+ def gerar_resposta_divergencias(self, divergencias, divergencias_detalhes, referencias):
71
+ """Gera uma resposta formatada sobre divergências detectadas."""
72
+ if not divergencias:
73
+ return "Não foram encontradas divergências nos dados analisados."
74
+
75
+ resposta = "## Análise de Divergências Detectadas\n\n"
76
+ resposta += "Foram identificadas as seguintes divergências:\n\n"
77
+
78
+ for i, (item_id, descricao, topico) in enumerate(divergencias, 1):
79
+ resposta += f"### {i}. Divergência no item **{item_id}**\n"
80
+ resposta += f"- **Tópico:** {topico}\n"
81
+ resposta += f"- **Descrição:** {descricao}\n"
82
+ resposta += f"- **Detalhe completo:** {divergencias_detalhes[item_id]}\n\n"
83
+
84
+ if item_id in referencias:
85
+ resposta += "**Informações contextuais relacionadas:**\n"
86
+ for ref_topico, ref_texto in referencias[item_id]:
87
+ resposta += f" - **{ref_topico}:** {ref_texto}\n"
88
+ resposta += "\n"
89
+
90
+ resposta += "## Conclusão\n"
91
+ resposta += f"Foram encontradas **{len(divergencias)} divergências** que precisam ser resolvidas.\n"
92
+ resposta += "Recomenda-se revisar os itens mencionados e consultar as especificações técnicas."
93
+ return resposta
94
+
95
+ def sumarizar_texto(self, texto, max_length=100):
96
+ """Sumariza um texto longo automaticamente."""
97
+ summary = self.summarizer(texto, max_length=max_length, min_length=30, do_sample=False)
98
+ return summary[0]['summary_text']
99
+
100
+ def gerar_relatorio(self, pergunta, top_k=10, exibir_chunks=False):
101
+ """Gera um relatório completo com base em uma pergunta."""
102
+ chunks_relevantes = self.sistema_recuperacao.recuperar_chunks_relevantes(pergunta, top_k)
103
+ try:
104
+ divergencias, detalhes, referencias = self.analisar_divergencias(chunks_relevantes)
105
+ resposta_texto = self.gerar_resposta_divergencias(divergencias, detalhes, referencias)
106
+ except Exception as e:
107
+ resposta_texto = f"Erro ao gerar resposta: {str(e)}\n\n{traceback.format_exc()}"
108
+
109
+ relatorio = f"=== RELATÓRIO GERADO ===\n\n**Pergunta:** {pergunta}\n\n**Resposta:**\n{resposta_texto}\n\n"
110
+ if exibir_chunks:
111
+ relatorio += "=== CHUNKS USADOS COMO CONTEXTO ===\n"
112
+ for i, chunk in enumerate(chunks_relevantes, 1):
113
+ relatorio += f"\n**Chunk {i}:**\n**Tópico:** {chunk['topico']}\n**Texto:** {chunk['texto']}\n"
114
+
115
+ return relatorio, resposta_texto, divergencias
116
+
117
+
118
+ # --- Funções auxiliares ---
119
+
120
+ def carregar_chunks(caminho_arquivo):
121
+ """Carrega chunks de um arquivo JSONL."""
122
+ with open(caminho_arquivo, 'r', encoding='utf-8') as f:
123
+ return [json.loads(linha) for linha in f]
124
+
125
+
126
+ def gerar_resposta_itens_relacionados(rag, itens_divergentes, top_k=10):
127
+ """Gera resposta para itens relacionados às divergências."""
128
+ chunks_relevantes = rag.sistema_recuperacao.recuperar_chunks_por_itens(itens_divergentes, top_k)
129
+
130
+ if not chunks_relevantes:
131
+ return "Não foram encontradas informações relacionadas aos itens mencionados."
132
+
133
+ resposta = "## Informações Relacionadas\n\n"
134
+ for i, chunk in enumerate(chunks_relevantes, 1):
135
+ texto_completo = chunk["texto"]
136
+ sumario = rag.sumarizar_texto(texto_completo)
137
+ resposta += f"### Chunk {i}\n"
138
+ resposta += f"**Tópico:** {chunk['topico']}\n"
139
+ resposta += "**Informações relevantes:**\n"
140
+ resposta += f"- **Sumário:** {sumario}\n"
141
+ resposta += "- **Detalhes completos:**\n"
142
+ for linha in texto_completo.split("\n"):
143
+ if linha.strip():
144
+ resposta += f" - {linha.strip()}\n"
145
+ resposta += "\n"
146
+
147
+ resposta += "## Conclusão\n"
148
+ resposta += "As informações acima estão relacionadas aos itens mencionados.\n"
149
+ return resposta
150
+
151
+
152
+ def calcular_probabilidade_divergencia(chunks):
153
+ """Calcula a probabilidade de um chunk conter 'Divergência detectada'."""
154
+ total_chunks = len(chunks)
155
+ if total_chunks == 0:
156
+ return 0.0, 0, 0
157
+
158
+ # Contar chunks com divergência
159
+ chunks_com_divergencia = sum(1 for chunk in chunks if "Divergência detectada" in chunk["texto"])
160
+
161
+ # Probabilidade
162
+ probabilidade = chunks_com_divergencia / total_chunks
163
+ return probabilidade, chunks_com_divergencia, total_chunks
164
+
165
+
166
+ def encontrar_chunk_mais_representativo(chunks, aba_nome):
167
+ """Encontra o chunk com maior 'relevância' entre os que têm divergência."""
168
+ chunks_divergencia = [chunk for chunk in chunks if "Divergência detectada" in chunk["texto"]]
169
+ if not chunks_divergencia:
170
+ return None, f"Não há chunks com divergência na {aba_nome}."
171
+
172
+ # Escolher o chunk com maior comprimento de texto como proxy para "mais representativo"
173
+ chunk_mais_representativo = max(chunks_divergencia, key=lambda x: len(x["texto"]))
174
+ return chunk_mais_representativo, f"Chunk mais representativo da {aba_nome} (baseado no comprimento do texto)."
175
+
176
+
177
+ # --- Função para interface Gradio ---
178
+
179
+ def processar_pergunta(pergunta, caminho_aba1, caminho_aba2, top_k=10):
180
+ """Processa a pergunta do usuário e retorna relatórios formatados."""
181
+ try:
182
+ # Carregar dados
183
+ chunks_aba1 = carregar_chunks(caminho_aba1)
184
+ chunks_aba2 = carregar_chunks(caminho_aba2)
185
+
186
+ # Inicializar sistemas RAG
187
+ rag_aba1 = RAGAvançado(chunks_aba1)
188
+ rag_aba2 = RAGAvançado(chunks_aba2)
189
+
190
+ # Pergunta 1: Relatório de divergências
191
+ relatorio1, resposta1, divergencias1 = rag_aba1.gerar_relatorio(pergunta, top_k=top_k, exibir_chunks=True)
192
+
193
+ # Análise de probabilidade ABA1
194
+ prob_aba1, num_div_aba1, total_aba1 = calcular_probabilidade_divergencia(chunks_aba1)
195
+ prob_aba1_text = (
196
+ f"### Análise de Probabilidade (ABA1)\n"
197
+ f"- Total de chunks: {total_aba1}\n"
198
+ f"- Chunks com divergência: {num_div_aba1}\n"
199
+ f"- Probabilidade: {prob_aba1:.2%}\n"
200
+ )
201
+
202
+ # Chunk mais representativo ABA1
203
+ chunk_aba1, msg_aba1 = encontrar_chunk_mais_representativo(chunks_aba1, "ABA1")
204
+ chunk_aba1_text = f"### Chunk Mais Representativo (ABA1)\n{msg_aba1}\n"
205
+ if chunk_aba1:
206
+ chunk_aba1_text += f"- Tópico: {chunk_aba1['topico']}\n- Texto: {chunk_aba1['texto']}\n"
207
+
208
+ # Análise de probabilidade ABA2
209
+ prob_aba2, num_div_aba2, total_aba2 = calcular_probabilidade_divergencia(chunks_aba2)
210
+ prob_aba2_text = (
211
+ f"### Análise de Probabilidade (ABA2)\n"
212
+ f"- Total de chunks: {total_aba2}\n"
213
+ f"- Chunks com divergência: {num_div_aba2}\n"
214
+ f"- Probabilidade: {prob_aba2:.2%}\n"
215
+ )
216
+
217
+ # Chunk mais representativo ABA2
218
+ chunk_aba2, msg_aba2 = encontrar_chunk_mais_representativo(chunks_aba2, "ABA2")
219
+ chunk_aba2_text = f"### Chunk Mais Representativo (ABA2)\n{msg_aba2}\n"
220
+ if chunk_aba2:
221
+ chunk_aba2_text += f"- Tópico: {chunk_aba2['topico']}\n- Texto: {chunk_aba2['texto']}\n"
222
+
223
+ # Pergunta 2: Informações relacionadas (se houver divergências)
224
+ resposta2 = ""
225
+ if divergencias1:
226
+ itens_divergentes = [item_id for item_id, _, _ in divergencias1]
227
+ resposta2 = gerar_resposta_itens_relacionados(rag_aba2, itens_divergentes, top_k=top_k)
228
+ else:
229
+ resposta2 = "Nenhuma divergência encontrada para gerar informações relacionadas."
230
+
231
+ # Combinar resultados
232
+ resultado_final = (
233
+ f"{prob_aba1_text}\n{chunk_aba1_text}\n\n"
234
+ f"{prob_aba2_text}\n{chunk_aba2_text}\n\n"
235
+ f"### Resposta à Pergunta\n{resposta1}\n\n"
236
+ f"### Informações Relacionadas\n{resposta2}"
237
+ )
238
+
239
+ return resultado_final
240
+
241
+ except FileNotFoundError as e:
242
+ return f"Erro: Arquivo não encontrado. {e}"
243
+ except Exception as e:
244
+ return f"Erro ao processar a pergunta: {str(e)}\n\n{traceback.format_exc()}"
245
+
246
+ # --- Interface Gradio ---
247
+
248
+ def criar_interface():
249
+ """Cria a interface gráfica com Gradio."""
250
+ with gr.Blocks(title="Sistema RAG Avançado") as interface:
251
+ gr.Markdown("# Sistema RAG Avançado")
252
+ gr.Markdown("Faça uma pergunta e visualize relatórios sobre divergências e informações relacionadas.")
253
+
254
+ with gr.Row():
255
+ with gr.Column():
256
+ pergunta_input = gr.Textbox(label="Sua Pergunta", placeholder="Ex: Quais os itens de Divergência detectada?")
257
+ aba1_input = gr.Textbox(label="Caminho do Arquivo ABA1", value="/content/aba1_rag_estruturado.txt")
258
+ aba2_input = gr.Textbox(label="Caminho do Arquivo ABA2", value="/content/aba2_rag_estruturado.txt")
259
+ top_k_input = gr.Slider(label="Top K Chunks", minimum=1, maximum=20, value=10, step=1)
260
+ submit_btn = gr.Button("Gerar Relatório")
261
+
262
+ with gr.Column():
263
+ output_text = gr.Markdown(label="Relatório Gerado")
264
+
265
+ submit_btn.click(
266
+ fn=processar_pergunta,
267
+ inputs=[pergunta_input, aba1_input, aba2_input, top_k_input],
268
+ outputs=output_text
269
+ )
270
+
271
+ return interface
272
+
273
+ # --- Executar a interface ---
274
+
275
+ if __name__ == "__main__":
276
+ interface = criar_interface()
277
+ interface.launch()