caarleexx commited on
Commit
0d05da0
·
verified ·
1 Parent(s): 649be2c

Upload app_epistemico_v28 (1).py

Browse files
Files changed (1) hide show
  1. app_epistemico_v28 (1).py +501 -0
app_epistemico_v28 (1).py ADDED
@@ -0,0 +1,501 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ╔════════════════════════════════════════════════════════════════════════════╗
2
+ # ║ PIPELINE EPISTÊMICO v28: Protocolo Causal de 10 Fases ║
3
+ # ║ Resolução do Paradoxo de Ménon aplicado à investigação forense ║
4
+ # ╚════════════════════════════════════════════════════════════════════════════╝
5
+
6
+ import os
7
+ import json
8
+ import time
9
+ from datetime import datetime
10
+ import gradio as gr
11
+ import google.generativeai as genai
12
+
13
+ # ==================== 1. CONFIGURAÇÃO ====================
14
+
15
+ api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
16
+ if api_key and api_key != "SUA_API_KEY_AQUI":
17
+ genai.configure(api_key=api_key)
18
+
19
+ model_flash = genai.GenerativeModel("gemini-2.0-flash-exp")
20
+ model_pro = genai.GenerativeModel("gemini-1.5-pro-002")
21
+
22
+ ARQUIVO_CONFIG = "protocolo_epistemico_forense.json"
23
+ MAX_ITERACOES = 3 # Máximo de voltas Fase 7 → Fase 3
24
+
25
+ # ==================== 2. UTILIDADES ====================
26
+
27
+ def carregar_protocolo():
28
+ """Carrega configuração do protocolo"""
29
+ try:
30
+ with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
31
+ conteudo = f.read()
32
+ # Validar se é JSON válido
33
+ json.loads(conteudo)
34
+ return conteudo
35
+ except FileNotFoundError:
36
+ print(f"⚠️ Arquivo {ARQUIVO_CONFIG} não encontrado. Criando protocolo padrão...")
37
+ return criar_protocolo_padrao()
38
+ except json.JSONDecodeError as e:
39
+ print(f"⚠️ JSON inválido: {e}")
40
+ return criar_protocolo_padrao()
41
+ except Exception as e:
42
+ print(f"⚠️ Erro ao carregar protocolo: {e}")
43
+ return "[]"
44
+
45
+ def criar_protocolo_padrao():
46
+ """Cria protocolo padrão se não existir"""
47
+ protocolo_minimo = [
48
+ {"fase": 0, "nome": "ESTADO_INICIAL", "modelo": "flash", "tipo_saida": "json",
49
+ "missao": "Registre o input inicial em JSON com: PERGUNTA_NORMALIZADA, CONTEXTO_IDENTIFICADO, TIPO_CASO, PARTES_ENVOLVIDAS, INCERTEZA_INICIAL (0-1), EVIDENCIAS_DISPONIVEIS."},
50
+
51
+ {"fase": 1, "nome": "MAPEAMENTO_OBJETIVO", "modelo": "flash", "tipo_saida": "json",
52
+ "missao": "Analise o estado inicial e retorne JSON com: OBJETIVO_CLARIFICADO, TIPO_RESPOSTA_ESPERADA, CRITERIOS_VALIDACAO (3-5), PRESSUPOSTOS_IMPLICITOS, PERGUNTAS_DERIVADAS, METODOLOGIA_SUGERIDA."},
53
+
54
+ {"fase": 2, "nome": "INVENTARIO_EPISTEMICO", "modelo": "flash", "tipo_saida": "json",
55
+ "missao": "Mapeie conhecimento em 4 categorias JSON: CONHECIMENTO_CERTO (P≈1.0), CONHECIMENTO_PROVAVEL (P≈0.7-0.9), CONHECIMENTO_INCERTO (P≈0.3-0.6), DESCONHECIMENTO_TOTAL, PERCENTUAL_COBERTURA (0-100), GAPS_CRITICOS."},
56
+
57
+ {"fase": 3, "nome": "GERACAO_CENARIOS", "modelo": "flash", "tipo_saida": "json",
58
+ "missao": "Gere array de 3+ cenários em JSON. Cada um com: ID (C1, C2...), DESCRICAO, PROBABILIDADE_PRIOR (soma=1.0), SUPOSICOES, COMPATIBILIDADE, EVIDENCIAS_SUPORTE, EVIDENCIAS_CONTRA, PLAUSIBILIDADE_SCORE."},
59
+
60
+ {"fase": 4, "nome": "ANALISE_CONTRAFACTUAL", "modelo": "flash", "tipo_saida": "json",
61
+ "missao": "Teste 'E SE?' para cada suposição. Retorne JSON: VARIAVEIS_CRITICAS, MAPA_CAUSAL, PONTOS_INFLEXAO (que se falsos invertem conclusões), TESTES_RECOMENDADOS."},
62
+
63
+ {"fase": 5, "nome": "CADEIA_INVESTIGATIVA", "modelo": "flash", "tipo_saida": "json",
64
+ "missao": "Construa cadeia de perguntas priorizadas. JSON: CADEIA_PERGUNTAS (array ordenado com id, pergunta, justificativa, reducao_entropia_estimada, metodo_busca, proximos_passos, prioridade), ARVORE_DECISAO, EVIDENCIAS_NECESSARIAS."},
65
+
66
+ {"fase": 6, "nome": "COLETA_ATUALIZACAO_BAYESIANA", "modelo": "pro", "tipo_saida": "json",
67
+ "missao": "Aplique P(C|E) = P(E|C) × P(C) / P(E). JSON: EVIDENCIAS_ANALISADAS (com likelihoods), CENARIOS_ATUALIZADOS (prior→posterior), GANHO_INFORMACAO (KL divergence), CONVERGENCIA (bool), CENARIO_DOMINANTE."},
68
+
69
+ {"fase": 7, "nome": "TESTE_CRUCIALIDADE", "modelo": "pro", "tipo_saida": "json",
70
+ "missao": "Valide contra 7 critérios (cada 0.0-1.0): CONSISTENCIA_INTERNA, COMPATIBILIDADE_EVIDENCIAS, EXPLICACOES_ALTERNATIVAS, FALSIFICABILIDADE, PARSIMONIA, ESCOPO, PREDICOES_VERIFICAVEIS. JSON: TESTES (dict), SCORE_CRUCIALIDADE (média), LIMIAR_CONFIANCA (0.70), PASSOU (bool), ACAO (PROSSEGUIR ou VOLTAR_FASE_3), FRAGILIDADES_IDENTIFICADAS (array)."},
71
+
72
+ {"fase": 8, "nome": "GERACAO_RESPOSTA", "modelo": "pro", "tipo_saida": "json",
73
+ "missao": "Sintetize em JSON: AFIRMACAO_PRINCIPAL, NIVEL_CONFIANCA (0-1), EVIDENCIAS_SUPORTE, SUPOSICOES_CRITICAS, CENARIOS_ALTERNATIVOS (P>0.1), PROXIMOS_PASSOS, FRONTEIRAS_CONHECIMENTO, CALIBRACAO_EPISTEMICA (honest/overconfident/underconfident), QUALIFICADORES."},
74
+
75
+ {"fase": 9, "nome": "RELATORIO_FINAL", "modelo": "pro", "tipo_saida": "texto",
76
+ "missao": "Escreva relatório completo em Markdown com estrutura: # RELATÓRIO DE ANÁLISE EPISTÊMICA\n\n## SUMÁRIO EXECUTIVO\n(Caso, Conclusão, Confiança, Calibração)\n\n## 1. ESTADO INICIAL\n## 2. OBJETIVO\n## 3. INVENTÁRIO DE CONHECIMENTO\n(Certo, Provável, Incerto, Gaps)\n\n## 4. CENÁRIOS CONSIDERADOS\n## 5. ANÁLISE BAYESIANA\n(Tabela Prior→Posterior)\n\n## 6. VALIDAÇÃO\n(Tabela 7 critérios)\n\n## 7. CONCLUSÃO E RECOMENDAÇÕES\n(Afirmação, Evidências-chave, Suposições críticas, Próximos passos)\n\n## 8. FRONTEIRAS DO CONHECIMENTO\n## 9. CALIBRAÇÃO EPISTÊMICA\n## 10. METADADOS\n(Fases, Iterações, Timestamp)"}
77
+ ]
78
+
79
+ protocolo_json = json.dumps(protocolo_minimo, ensure_ascii=False, indent=2)
80
+
81
+ try:
82
+ with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
83
+ f.write(protocolo_json)
84
+ print(f"✅ Protocolo padrão criado: {ARQUIVO_CONFIG}")
85
+ except Exception as e:
86
+ print(f"⚠️ Não foi possível salvar protocolo: {e}")
87
+
88
+ return protocolo_json
89
+
90
+ def salvar_protocolo(conteudo):
91
+ """Salva e valida JSON do protocolo"""
92
+ try:
93
+ protocolo = json.loads(conteudo)
94
+
95
+ # Validar estrutura
96
+ if not isinstance(protocolo, list):
97
+ return "❌ Erro: Protocolo deve ser um array JSON"
98
+
99
+ fases = {f.get('fase') for f in protocolo if isinstance(f, dict) and 'fase' in f}
100
+ if len(fases) < 10:
101
+ return f"⚠️ Aviso: Protocolo tem apenas {len(fases)} fases (esperado: 10)"
102
+
103
+ with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
104
+ f.write(conteudo)
105
+
106
+ return f"✅ Protocolo salvo com sucesso ({len(fases)} fases)"
107
+ except json.JSONDecodeError as e:
108
+ return f"❌ Erro no JSON: {str(e)}"
109
+ except Exception as e:
110
+ return f"❌ Erro: {str(e)}"
111
+
112
+ def ler_anexo(arquivo):
113
+ """Lê arquivo anexado"""
114
+ if arquivo is None:
115
+ return ""
116
+ try:
117
+ with open(arquivo.name, "r", encoding="utf-8") as f:
118
+ conteudo = f.read()
119
+ return f"\n\n--- ANEXO: {os.path.basename(arquivo.name)} ---\n{conteudo}\n--- FIM ANEXO ---\n"
120
+ except Exception as e:
121
+ return f"\n[ERRO ao ler anexo: {e}]\n"
122
+
123
+ # ==================== 3. ENGINE DE EXECUÇÃO ====================
124
+
125
+ def executar_fase(timeline, config, fase_atual, total_fases):
126
+ """Executa uma fase do protocolo"""
127
+
128
+ # Selecionar modelo
129
+ modelo = model_pro if config.get("modelo") == "pro" else model_flash
130
+
131
+ # Construir contexto com toda a timeline
132
+ contexto = json.dumps(timeline, ensure_ascii=False, indent=2)
133
+
134
+ # Prompt estruturado
135
+ prompt = f"""--- TIMELINE COMPLETA ---
136
+ {contexto}
137
+
138
+ --- FASE ATUAL ---
139
+ AGENTE: {config['nome']}
140
+ FASE: {fase_atual}/{total_fases}
141
+ TIPO_SAIDA: {config['tipo_saida']}
142
+
143
+ --- MISSÃO ---
144
+ {config['missao']}
145
+
146
+ --- INSTRUÇÕES ---
147
+ 1. Analise TODA a timeline acima
148
+ 2. Execute sua missão com rigor
149
+ 3. {"Retorne APENAS JSON válido" if config['tipo_saida'] == 'json' else "Retorne texto em Markdown"}
150
+ 4. Seja epistemicamente honesto sobre incertezas
151
+ """
152
+
153
+ log = f"\n🔸 Fase {fase_atual}: {config['nome']}"
154
+
155
+ try:
156
+ inicio = time.time()
157
+
158
+ # Executar com retry
159
+ max_tentativas = 2
160
+ for tentativa in range(max_tentativas):
161
+ try:
162
+ resp = modelo.generate_content(
163
+ prompt,
164
+ generation_config={
165
+ "temperature": 0.3 if config['tipo_saida'] == 'json' else 0.7,
166
+ "max_output_tokens": 8000
167
+ }
168
+ )
169
+ output_raw = resp.text
170
+ break
171
+ except Exception as e:
172
+ if tentativa == max_tentativas - 1:
173
+ raise e
174
+ time.sleep(2)
175
+
176
+ tempo = time.time() - inicio
177
+
178
+ # Processar output
179
+ if config['tipo_saida'] == 'json':
180
+ # Limpar markdown
181
+ output_limpo = output_raw.strip()
182
+ output_limpo = output_limpo.replace('```json', '').replace('```', '')
183
+ content = json.loads(output_limpo)
184
+ else:
185
+ content = output_raw
186
+
187
+ log += f" ✓ ({tempo:.1f}s)"
188
+
189
+ return {
190
+ "role": "assistant",
191
+ "agent": config['nome'],
192
+ "fase": config['fase'],
193
+ "content": content
194
+ }, log, True
195
+
196
+ except Exception as e:
197
+ log += f" ✗ ERRO: {str(e)[:100]}"
198
+ return {
199
+ "role": "system",
200
+ "agent": config['nome'],
201
+ "fase": config['fase'],
202
+ "error": str(e)
203
+ }, log, False
204
+
205
+ # ==================== 4. ORQUESTRADOR COM LOOP ITERATIVO ====================
206
+
207
+ def orquestrador(texto, arquivo, history, json_config):
208
+ """Orquestra execução do protocolo de 10 fases com loop iterativo"""
209
+
210
+ # 1. Validar input
211
+ anexo = ler_anexo(arquivo)
212
+ full_input = f"{texto}\n{anexo}".strip()
213
+
214
+ if not full_input:
215
+ yield history, {}, "⚠️ Nenhum input fornecido."
216
+ return
217
+
218
+ # 2. Setup
219
+ history = history + [[texto + (" 📎" if arquivo else ""), None]]
220
+
221
+ try:
222
+ protocolo = json.loads(json_config)
223
+
224
+ # Validar que protocolo tem todas as fases necessárias
225
+ fases_disponiveis = {f['fase'] for f in protocolo if isinstance(f, dict) and 'fase' in f}
226
+ if 7 not in fases_disponiveis:
227
+ history[-1][1] = "❌ **Erro: Fase 7 não encontrada no protocolo**\n\nO protocolo deve ter 10 fases (0-9)."
228
+ yield history, {}, "Erro: Fase 7 ausente"
229
+ return
230
+
231
+ except Exception as e:
232
+ history[-1][1] = f"❌ **Erro no JSON de Configuração**\n\n```\n{str(e)}\n```"
233
+ yield history, {}, f"Erro JSON: {e}"
234
+ return
235
+
236
+ # 3. Inicializar timeline
237
+ timeline = [{
238
+ "role": "user",
239
+ "content": full_input,
240
+ "timestamp": datetime.now().isoformat()
241
+ }]
242
+
243
+ logs = f"🚀 INÍCIO: {datetime.now().strftime('%H:%M:%S')}\n"
244
+ logs += f"📋 Protocolo: {len(protocolo)} fases\n"
245
+ logs += f"🔄 Max iterações: {MAX_ITERACOES}\n"
246
+ logs += "=" * 60 + "\n"
247
+
248
+ history[-1][1] = "⏳ **Iniciando Protocolo Epistêmico...**\n\nFase 0/10: Estado Inicial"
249
+ yield history, timeline, logs
250
+
251
+ # 4. Executar fases 0-6 (sequenciais)
252
+ fases_sequenciais = [f for f in protocolo if f['fase'] < 7]
253
+
254
+ for cfg in fases_sequenciais:
255
+ fase_num = cfg['fase']
256
+ history[-1][1] = f"⚙️ **Fase {fase_num}/10: {cfg['nome']}**\n\nProcessando..."
257
+ yield history, timeline, logs
258
+
259
+ resultado, log_add, sucesso = executar_fase(timeline, cfg, fase_num, 10)
260
+ timeline.append(resultado)
261
+ logs += log_add + "\n"
262
+
263
+ if not sucesso:
264
+ history[-1][1] = f"❌ **Erro na Fase {fase_num}**\n\n{resultado.get('error', 'Erro desconhecido')}"
265
+ yield history, timeline, logs
266
+ return
267
+
268
+ yield history, timeline, logs
269
+
270
+ # 5. Loop iterativo: Fases 7 (teste) ↔ Fases 3-6 (refinamento)
271
+ iteracao = 0
272
+ fase_7_passou = False
273
+
274
+ # Buscar configuração da fase 7 com segurança
275
+ cfg_fase7 = None
276
+ for f in protocolo:
277
+ if f.get('fase') == 7:
278
+ cfg_fase7 = f
279
+ break
280
+
281
+ if cfg_fase7 is None:
282
+ history[-1][1] = "❌ **Erro: Fase 7 (TESTE_CRUCIALIDADE) não encontrada**"
283
+ yield history, timeline, logs
284
+ return
285
+
286
+ while iteracao < MAX_ITERACOES and not fase_7_passou:
287
+ iteracao += 1
288
+ logs += f"\n{'='*60}\n🔄 ITERAÇÃO {iteracao}/{MAX_ITERACOES}\n{'='*60}\n"
289
+
290
+ # Se não é primeira vez, re-executar fases 3-6
291
+ if iteracao > 1:
292
+ history[-1][1] = f"🔄 **Iteração {iteracao}**: Refinando cenários (Fases 3-6)..."
293
+ yield history, timeline, logs
294
+
295
+ fases_refinamento = [f for f in protocolo if 3 <= f['fase'] < 7]
296
+ for cfg in fases_refinamento:
297
+ resultado, log_add, sucesso = executar_fase(timeline, cfg, cfg['fase'], 10)
298
+ timeline.append(resultado)
299
+ logs += log_add + "\n"
300
+
301
+ if not sucesso:
302
+ history[-1][1] = f"❌ **Erro no refinamento**"
303
+ yield history, timeline, logs
304
+ return
305
+
306
+ yield history, timeline, logs
307
+
308
+ # Executar Fase 7: TESTE_CRUCIALIDADE
309
+ history[-1][1] = f"🧪 **Fase 7/10: Teste de Crucialidade** (Iteração {iteracao})\n\nValidando..."
310
+ yield history, timeline, logs
311
+
312
+ resultado, log_add, sucesso = executar_fase(timeline, cfg_fase7, 7, 10)
313
+ timeline.append(resultado)
314
+ logs += log_add + "\n"
315
+
316
+ if not sucesso:
317
+ history[-1][1] = f"❌ **Erro na Fase 7**"
318
+ yield history, timeline, logs
319
+ return
320
+
321
+ # Verificar se passou no teste
322
+ teste_resultado = resultado.get('content', {})
323
+ fase_7_passou = teste_resultado.get('PASSOU', False)
324
+ score = teste_resultado.get('SCORE_CRUCIALIDADE', 0)
325
+
326
+ if fase_7_passou:
327
+ logs += f"✅ TESTE PASSOU (score: {score:.3f})\n"
328
+ history[-1][1] = f"✅ **Validação Aprovada!**\n\nScore: {score:.3f}"
329
+ else:
330
+ logs += f"⚠️ TESTE FALHOU (score: {score:.3f})\n"
331
+ fragilidades = teste_resultado.get('FRAGILIDADES_IDENTIFICADAS', [])
332
+ history[-1][1] = f"⚠️ **Validação Reprovada** (Score: {score:.3f})\n\nFragilidades:\n" + "\n".join(f"- {f}" for f in fragilidades[:5])
333
+
334
+ yield history, timeline, logs
335
+
336
+ if not fase_7_passou:
337
+ logs += f"\n⚠️ Limite de iterações atingido ({MAX_ITERACOES}). Prosseguindo com melhor resultado.\n"
338
+
339
+ # 6. Fases finais 8-9 (sempre executam)
340
+ fases_finais = [f for f in protocolo if f['fase'] >= 8]
341
+
342
+ for cfg in fases_finais:
343
+ fase_num = cfg['fase']
344
+ history[-1][1] = f"📝 **Fase {fase_num}/10: {cfg['nome']}**\n\nGerando..."
345
+ yield history, timeline, logs
346
+
347
+ resultado, log_add, sucesso = executar_fase(timeline, cfg, fase_num, 10)
348
+ timeline.append(resultado)
349
+ logs += log_add + "\n"
350
+
351
+ if not sucesso:
352
+ history[-1][1] = f"❌ **Erro na Fase {fase_num}**"
353
+ yield history, timeline, logs
354
+ return
355
+
356
+ # Se for a fase final (9 - RELATORIO_FINAL), mostrar resultado
357
+ if cfg['fase'] == 9 and cfg['tipo_saida'] == 'texto':
358
+ final_response = resultado.get('content', 'Erro ao gerar relatório')
359
+ history[-1][1] = final_response
360
+
361
+ yield history, timeline, logs
362
+
363
+ # 7. Finalizar
364
+ logs += "\n" + "=" * 60 + "\n"
365
+ logs += f"✅ CONCLUÍDO: {datetime.now().strftime('%H:%M:%S')}\n"
366
+ logs += f"📊 Total de fases executadas: {len([t for t in timeline if t.get('fase') is not None])}\n"
367
+ logs += f"🔄 Iterações necessárias: {iteracao}\n"
368
+
369
+ yield history, timeline, logs
370
+
371
+ # ==================== 5. UI COM GRADIO ====================
372
+
373
+ def criar_ui():
374
+ """Cria interface Gradio"""
375
+
376
+ css = """
377
+ footer {display: none !important;}
378
+ .contain {border: none !important;}
379
+ """
380
+
381
+ config_inicial = carregar_protocolo()
382
+
383
+ with gr.Blocks(
384
+ title="🔬 Investigador Epistêmico",
385
+ css=css,
386
+ theme=gr.themes.Soft()
387
+ ) as app:
388
+
389
+ gr.Markdown("""
390
+ # 🔬 Investigador Epistêmico
391
+ ### Protocolo Causal de 10 Fases | Resolução do Paradoxo de Ménon
392
+ """)
393
+
394
+ with gr.Tabs():
395
+
396
+ # === ABA 1: INVESTIGAÇÃO ===
397
+ with gr.Tab("💬 Investigação"):
398
+ chatbot = gr.Chatbot(
399
+ label="",
400
+ show_label=False,
401
+ height=650,
402
+ show_copy_button=True,
403
+ render_markdown=True,
404
+ type="tuples" # Explicitamente definido
405
+ )
406
+
407
+ with gr.Row():
408
+ with gr.Column(scale=10):
409
+ txt_input = gr.Textbox(
410
+ show_label=False,
411
+ placeholder="Descreva o caso, pergunta ou situação a investigar...",
412
+ lines=2,
413
+ max_lines=8,
414
+ container=False
415
+ )
416
+
417
+ with gr.Column(scale=1, min_width=60):
418
+ file_input = gr.UploadButton(
419
+ "📎",
420
+ file_types=[".txt", ".md", ".csv", ".json", ".pdf"],
421
+ size="sm"
422
+ )
423
+
424
+ with gr.Column(scale=1, min_width=100):
425
+ btn_enviar = gr.Button("🚀 Investigar", variant="primary", size="sm")
426
+
427
+ file_status = gr.Markdown("", visible=True)
428
+ file_input.upload(
429
+ lambda x: f"📎 **Anexo:** {os.path.basename(x.name)}",
430
+ file_input,
431
+ file_status
432
+ )
433
+
434
+ # === ABA 2: DEPURAÇÃO ===
435
+ with gr.Tab("🕵️ Depuração"):
436
+ gr.Markdown("### Timeline Completa (DNA da Investigação)")
437
+ output_timeline = gr.JSON(label="Timeline")
438
+
439
+ gr.Markdown("### Logs do Sistema")
440
+ output_logs = gr.Textbox(label="Logs", lines=25, max_lines=50)
441
+
442
+ # === ABA 3: CONFIGURAÇÃO ===
443
+ with gr.Tab("⚙️ Configuração"):
444
+ gr.Markdown("""
445
+ ### Protocolo Epistêmico (JSON)
446
+
447
+ **Estrutura de cada fase:**
448
+ - `fase`: Número da fase (0-9)
449
+ - `nome`: Identificador da fase
450
+ - `modelo`: "flash" ou "pro"
451
+ - `tipo_saida`: "json" ou "texto"
452
+ - `missao`: Instruções detalhadas
453
+
454
+ **Fluxo:** 0→1→2→3→4→5→6→7(teste)→[volta 3 se falhar]→8→9
455
+ """)
456
+
457
+ with gr.Row():
458
+ btn_salvar = gr.Button("💾 Salvar Protocolo", variant="primary")
459
+ label_salvar = gr.Label(show_label=False)
460
+
461
+ code_protocolo = gr.Code(
462
+ value=config_inicial,
463
+ language="json",
464
+ label="protocolo_epistemico_forense.json",
465
+ lines=30
466
+ )
467
+
468
+ btn_salvar.click(salvar_protocolo, code_protocolo, label_salvar)
469
+
470
+ # === EVENTOS ===
471
+ triggers = [btn_enviar.click, txt_input.submit]
472
+
473
+ for trigger in triggers:
474
+ trigger(
475
+ orquestrador,
476
+ inputs=[txt_input, file_input, chatbot, code_protocolo],
477
+ outputs=[chatbot, output_timeline, output_logs]
478
+ ).then(
479
+ lambda: (None, ""),
480
+ outputs=[txt_input, file_status]
481
+ )
482
+
483
+ return app
484
+
485
+ # ==================== 6. MAIN ====================
486
+
487
+ if __name__ == "__main__":
488
+ print("=" * 80)
489
+ print("🔬 INVESTIGADOR EPISTÊMICO - Protocolo Causal v28")
490
+ print("=" * 80)
491
+ print(f"📋 Arquivo de configuração: {ARQUIVO_CONFIG}")
492
+ print(f"🔄 Máximo de iterações: {MAX_ITERACOES}")
493
+ print("=" * 80)
494
+
495
+ app = criar_ui()
496
+ app.launch(
497
+ server_name="0.0.0.0",
498
+ server_port=7860,
499
+ share=False,
500
+ show_error=True
501
+ )