# ╔════════════════════════════════════════════════════════════════════════════╗ # ║ PIPELINE EPISTÊMICO v28: Protocolo Causal de 10 Fases ║ # ║ Resolução do Paradoxo de Ménon aplicado à investigação forense ║ # ╚════════════════════════════════════════════════════════════════════════════╝ import os import json import time from datetime import datetime import gradio as gr import google.generativeai as genai # ==================== 1. CONFIGURAÇÃO ==================== api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI") if api_key and api_key != "SUA_API_KEY_AQUI": genai.configure(api_key=api_key) model_flash = genai.GenerativeModel("gemini-flash-latest") model_pro = genai.GenerativeModel("gemini-pro-latest") ARQUIVO_CONFIG = "protocolo_epistemico_forense.json" MAX_ITERACOES = 3 # Máximo de voltas Fase 7 → Fase 3 # ==================== 2. UTILIDADES ==================== def carregar_protocolo(): """Carrega configuração do protocolo""" try: with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f: conteudo = f.read() # Validar se é JSON válido json.loads(conteudo) return conteudo except FileNotFoundError: print(f"⚠️ Arquivo {ARQUIVO_CONFIG} não encontrado. Criando protocolo padrão...") return criar_protocolo_padrao() except json.JSONDecodeError as e: print(f"⚠️ JSON inválido: {e}") return criar_protocolo_padrao() except Exception as e: print(f"⚠️ Erro ao carregar protocolo: {e}") return "[]" def criar_protocolo_padrao(): """Cria protocolo padrão se não existir""" protocolo_minimo = [ {"fase": 0, "nome": "ESTADO_INICIAL", "modelo": "flash", "tipo_saida": "json", "missao": "Registre o input inicial em JSON com: PERGUNTA_NORMALIZADA, CONTEXTO_IDENTIFICADO, TIPO_CASO, PARTES_ENVOLVIDAS, INCERTEZA_INICIAL (0-1), EVIDENCIAS_DISPONIVEIS."}, {"fase": 1, "nome": "MAPEAMENTO_OBJETIVO", "modelo": "flash", "tipo_saida": "json", "missao": "Analise o estado inicial e retorne JSON com: OBJETIVO_CLARIFICADO, TIPO_RESPOSTA_ESPERADA, CRITERIOS_VALIDACAO (3-5), PRESSUPOSTOS_IMPLICITOS, PERGUNTAS_DERIVADAS, METODOLOGIA_SUGERIDA."}, {"fase": 2, "nome": "INVENTARIO_EPISTEMICO", "modelo": "flash", "tipo_saida": "json", "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."}, {"fase": 3, "nome": "GERACAO_CENARIOS", "modelo": "flash", "tipo_saida": "json", "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."}, {"fase": 4, "nome": "ANALISE_CONTRAFACTUAL", "modelo": "flash", "tipo_saida": "json", "missao": "Teste 'E SE?' para cada suposição. Retorne JSON: VARIAVEIS_CRITICAS, MAPA_CAUSAL, PONTOS_INFLEXAO (que se falsos invertem conclusões), TESTES_RECOMENDADOS."}, {"fase": 5, "nome": "CADEIA_INVESTIGATIVA", "modelo": "flash", "tipo_saida": "json", "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."}, {"fase": 6, "nome": "COLETA_ATUALIZACAO_BAYESIANA", "modelo": "pro", "tipo_saida": "json", "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."}, {"fase": 7, "nome": "TESTE_CRUCIALIDADE", "modelo": "pro", "tipo_saida": "json", "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)."}, {"fase": 8, "nome": "GERACAO_RESPOSTA", "modelo": "pro", "tipo_saida": "json", "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."}, {"fase": 9, "nome": "RELATORIO_FINAL", "modelo": "pro", "tipo_saida": "texto", "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)"} ] protocolo_json = json.dumps(protocolo_minimo, ensure_ascii=False, indent=2) try: with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f: f.write(protocolo_json) print(f"✅ Protocolo padrão criado: {ARQUIVO_CONFIG}") except Exception as e: print(f"⚠️ Não foi possível salvar protocolo: {e}") return protocolo_json def salvar_protocolo(conteudo): """Salva e valida JSON do protocolo""" try: protocolo = json.loads(conteudo) # Validar estrutura if not isinstance(protocolo, list): return "❌ Erro: Protocolo deve ser um array JSON" fases = {f.get('fase') for f in protocolo if isinstance(f, dict) and 'fase' in f} if len(fases) < 10: return f"⚠️ Aviso: Protocolo tem apenas {len(fases)} fases (esperado: 10)" with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f: f.write(conteudo) return f"✅ Protocolo salvo com sucesso ({len(fases)} fases)" except json.JSONDecodeError as e: return f"❌ Erro no JSON: {str(e)}" except Exception as e: return f"❌ Erro: {str(e)}" def ler_anexo(arquivo): """Lê arquivo anexado""" if arquivo is None: return "" try: with open(arquivo.name, "r", encoding="utf-8") as f: conteudo = f.read() return f"\n\n--- ANEXO: {os.path.basename(arquivo.name)} ---\n{conteudo}\n--- FIM ANEXO ---\n" except Exception as e: return f"\n[ERRO ao ler anexo: {e}]\n" # ==================== 3. ENGINE DE EXECUÇÃO ==================== def executar_fase(timeline, config, fase_atual, total_fases): """Executa uma fase do protocolo""" # Selecionar modelo modelo = model_pro if config.get("modelo") == "pro" else model_flash # Construir contexto com toda a timeline contexto = json.dumps(timeline, ensure_ascii=False, indent=2) # Prompt estruturado prompt = f"""--- TIMELINE COMPLETA --- {contexto} --- FASE ATUAL --- AGENTE: {config['nome']} FASE: {fase_atual}/{total_fases} TIPO_SAIDA: {config['tipo_saida']} --- MISSÃO --- {config['missao']} --- INSTRUÇÕES --- 1. Analise TODA a timeline acima 2. Execute sua missão com rigor 3. {"Retorne APENAS JSON válido" if config['tipo_saida'] == 'json' else "Retorne texto em Markdown"} 4. Seja epistemicamente honesto sobre incertezas """ log = f"\n🔸 Fase {fase_atual}: {config['nome']}" try: inicio = time.time() # Executar com retry max_tentativas = 2 for tentativa in range(max_tentativas): try: resp = modelo.generate_content( prompt, generation_config={ "temperature": 0.3 if config['tipo_saida'] == 'json' else 0.7, "max_output_tokens": 8000 } ) output_raw = resp.text break except Exception as e: if tentativa == max_tentativas - 1: raise e time.sleep(2) tempo = time.time() - inicio # Processar output if config['tipo_saida'] == 'json': # Limpar markdown output_limpo = output_raw.strip() output_limpo = output_limpo.replace('```json', '').replace('```', '') content = json.loads(output_limpo) else: content = output_raw log += f" ✓ ({tempo:.1f}s)" return { "role": "assistant", "agent": config['nome'], "fase": config['fase'], "content": content }, log, True except Exception as e: log += f" ✗ ERRO: {str(e)[:100]}" return { "role": "system", "agent": config['nome'], "fase": config['fase'], "error": str(e) }, log, False # ==================== 4. ORQUESTRADOR COM LOOP ITERATIVO ==================== def orquestrador(texto, arquivo, history, json_config): """Orquestra execução do protocolo de 10 fases com loop iterativo""" # 1. Validar input anexo = ler_anexo(arquivo) full_input = f"{texto}\n{anexo}".strip() if not full_input: yield history, {}, "⚠️ Nenhum input fornecido." return # 2. Setup history = history + [[texto + (" 📎" if arquivo else ""), None]] try: protocolo = json.loads(json_config) # Validar que protocolo tem todas as fases necessárias fases_disponiveis = {f['fase'] for f in protocolo if isinstance(f, dict) and 'fase' in f} if 7 not in fases_disponiveis: history[-1][1] = "❌ **Erro: Fase 7 não encontrada no protocolo**\n\nO protocolo deve ter 10 fases (0-9)." yield history, {}, "Erro: Fase 7 ausente" return except Exception as e: history[-1][1] = f"❌ **Erro no JSON de Configuração**\n\n```\n{str(e)}\n```" yield history, {}, f"Erro JSON: {e}" return # 3. Inicializar timeline timeline = [{ "role": "user", "content": full_input, "timestamp": datetime.now().isoformat() }] logs = f"🚀 INÍCIO: {datetime.now().strftime('%H:%M:%S')}\n" logs += f"📋 Protocolo: {len(protocolo)} fases\n" logs += f"🔄 Max iterações: {MAX_ITERACOES}\n" logs += "=" * 60 + "\n" history[-1][1] = "⏳ **Iniciando Protocolo Epistêmico...**\n\nFase 0/10: Estado Inicial" yield history, timeline, logs # 4. Executar fases 0-6 (sequenciais) fases_sequenciais = [f for f in protocolo if f['fase'] < 7] for cfg in fases_sequenciais: fase_num = cfg['fase'] history[-1][1] = f"⚙️ **Fase {fase_num}/10: {cfg['nome']}**\n\nProcessando..." yield history, timeline, logs resultado, log_add, sucesso = executar_fase(timeline, cfg, fase_num, 10) timeline.append(resultado) logs += log_add + "\n" if not sucesso: history[-1][1] = f"❌ **Erro na Fase {fase_num}**\n\n{resultado.get('error', 'Erro desconhecido')}" yield history, timeline, logs return yield history, timeline, logs # 5. Loop iterativo: Fases 7 (teste) ↔ Fases 3-6 (refinamento) iteracao = 0 fase_7_passou = False # Buscar configuração da fase 7 com segurança cfg_fase7 = None for f in protocolo: if f.get('fase') == 7: cfg_fase7 = f break if cfg_fase7 is None: history[-1][1] = "❌ **Erro: Fase 7 (TESTE_CRUCIALIDADE) não encontrada**" yield history, timeline, logs return while iteracao < MAX_ITERACOES and not fase_7_passou: iteracao += 1 logs += f"\n{'='*60}\n🔄 ITERAÇÃO {iteracao}/{MAX_ITERACOES}\n{'='*60}\n" # Se não é primeira vez, re-executar fases 3-6 if iteracao > 1: history[-1][1] = f"🔄 **Iteração {iteracao}**: Refinando cenários (Fases 3-6)..." yield history, timeline, logs fases_refinamento = [f for f in protocolo if 3 <= f['fase'] < 7] for cfg in fases_refinamento: resultado, log_add, sucesso = executar_fase(timeline, cfg, cfg['fase'], 10) timeline.append(resultado) logs += log_add + "\n" if not sucesso: history[-1][1] = f"❌ **Erro no refinamento**" yield history, timeline, logs return yield history, timeline, logs # Executar Fase 7: TESTE_CRUCIALIDADE history[-1][1] = f"🧪 **Fase 7/10: Teste de Crucialidade** (Iteração {iteracao})\n\nValidando..." yield history, timeline, logs resultado, log_add, sucesso = executar_fase(timeline, cfg_fase7, 7, 10) timeline.append(resultado) logs += log_add + "\n" if not sucesso: history[-1][1] = f"❌ **Erro na Fase 7**" yield history, timeline, logs return # Verificar se passou no teste teste_resultado = resultado.get('content', {}) fase_7_passou = teste_resultado.get('PASSOU', False) score = teste_resultado.get('SCORE_CRUCIALIDADE', 0) if fase_7_passou: logs += f"✅ TESTE PASSOU (score: {score:.3f})\n" history[-1][1] = f"✅ **Validação Aprovada!**\n\nScore: {score:.3f}" else: logs += f"⚠️ TESTE FALHOU (score: {score:.3f})\n" fragilidades = teste_resultado.get('FRAGILIDADES_IDENTIFICADAS', []) history[-1][1] = f"⚠️ **Validação Reprovada** (Score: {score:.3f})\n\nFragilidades:\n" + "\n".join(f"- {f}" for f in fragilidades[:5]) yield history, timeline, logs if not fase_7_passou: logs += f"\n⚠️ Limite de iterações atingido ({MAX_ITERACOES}). Prosseguindo com melhor resultado.\n" # 6. Fases finais 8-9 (sempre executam) fases_finais = [f for f in protocolo if f['fase'] >= 8] for cfg in fases_finais: fase_num = cfg['fase'] history[-1][1] = f"📝 **Fase {fase_num}/10: {cfg['nome']}**\n\nGerando..." yield history, timeline, logs resultado, log_add, sucesso = executar_fase(timeline, cfg, fase_num, 10) timeline.append(resultado) logs += log_add + "\n" if not sucesso: history[-1][1] = f"❌ **Erro na Fase {fase_num}**" yield history, timeline, logs return # Se for a fase final (9 - RELATORIO_FINAL), mostrar resultado if cfg['fase'] == 9 and cfg['tipo_saida'] == 'texto': final_response = resultado.get('content', 'Erro ao gerar relatório') history[-1][1] = final_response yield history, timeline, logs # 7. Finalizar logs += "\n" + "=" * 60 + "\n" logs += f"✅ CONCLUÍDO: {datetime.now().strftime('%H:%M:%S')}\n" logs += f"📊 Total de fases executadas: {len([t for t in timeline if t.get('fase') is not None])}\n" logs += f"🔄 Iterações necessárias: {iteracao}\n" yield history, timeline, logs # ==================== 5. UI COM GRADIO ==================== def criar_ui(): """Cria interface Gradio""" css = """ footer {display: none !important;} .contain {border: none !important;} """ config_inicial = carregar_protocolo() with gr.Blocks( title="🔬 Investigador Epistêmico", css=css, theme=gr.themes.Soft() ) as app: gr.Markdown(""" # 🔬 Investigador Epistêmico ### Protocolo Causal de 10 Fases | Resolução do Paradoxo de Ménon """) with gr.Tabs(): # === ABA 1: INVESTIGAÇÃO === with gr.Tab("💬 Investigação"): chatbot = gr.Chatbot( label="", show_label=False, height=650, show_copy_button=True, render_markdown=True, type="tuples" # Explicitamente definido ) with gr.Row(): with gr.Column(scale=10): txt_input = gr.Textbox( show_label=False, placeholder="Descreva o caso, pergunta ou situação a investigar...", lines=2, max_lines=8, container=False ) with gr.Column(scale=1, min_width=60): file_input = gr.UploadButton( "📎", file_types=[".txt", ".md", ".csv", ".json", ".pdf"], size="sm" ) with gr.Column(scale=1, min_width=100): btn_enviar = gr.Button("🚀 Investigar", variant="primary", size="sm") file_status = gr.Markdown("", visible=True) file_input.upload( lambda x: f"📎 **Anexo:** {os.path.basename(x.name)}", file_input, file_status ) # === ABA 2: DEPURAÇÃO === with gr.Tab("🕵️ Depuração"): gr.Markdown("### Timeline Completa (DNA da Investigação)") output_timeline = gr.JSON(label="Timeline") gr.Markdown("### Logs do Sistema") output_logs = gr.Textbox(label="Logs", lines=25, max_lines=50) # === ABA 3: CONFIGURAÇÃO === with gr.Tab("⚙️ Configuração"): gr.Markdown(""" ### Protocolo Epistêmico (JSON) **Estrutura de cada fase:** - `fase`: Número da fase (0-9) - `nome`: Identificador da fase - `modelo`: "flash" ou "pro" - `tipo_saida`: "json" ou "texto" - `missao`: Instruções detalhadas **Fluxo:** 0→1→2→3→4→5→6→7(teste)→[volta 3 se falhar]→8→9 """) with gr.Row(): btn_salvar = gr.Button("💾 Salvar Protocolo", variant="primary") label_salvar = gr.Label(show_label=False) code_protocolo = gr.Code( value=config_inicial, language="json", label="protocolo_epistemico_forense.json", lines=30 ) btn_salvar.click(salvar_protocolo, code_protocolo, label_salvar) # === EVENTOS === triggers = [btn_enviar.click, txt_input.submit] for trigger in triggers: trigger( orquestrador, inputs=[txt_input, file_input, chatbot, code_protocolo], outputs=[chatbot, output_timeline, output_logs] ).then( lambda: (None, ""), outputs=[txt_input, file_status] ) return app # ==================== 6. MAIN ==================== if __name__ == "__main__": print("=" * 80) print("🔬 INVESTIGADOR EPISTÊMICO - Protocolo Causal v28") print("=" * 80) print(f"📋 Arquivo de configuração: {ARQUIVO_CONFIG}") print(f"🔄 Máximo de iterações: {MAX_ITERACOES}") print("=" * 80) app = criar_ui() app.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True )