Spaces:
Sleeping
Sleeping
| # ╔════════════════════════════════════════════════════════════════════════════╗ | |
| # ║ 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 | |
| ) | |