ToM / ap_mrnomp.py
caarleexx's picture
Rename app.py to ap_mrnomp.py
6e64067 verified
# ╔════════════════════════════════════════════════════════════════════════════╗
# ║ 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
)