pedrolaskawski commited on
Commit
6b8681b
·
verified ·
1 Parent(s): 2cb9396

Upload 17 files

Browse files
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: 5est
3
+ emoji: 📊
4
+ colorFrom: indigo
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 5.49.1
8
+ app_file: app.py
9
+ pinned: false
10
+ license: agpl-3.0
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
ai_studio_code (12).py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Pipeline v10 - VERSÃO FINAL COM BYPASS INTELIGENTE E ROBUSTEZ.
4
+
5
+ Este script implementa uma pipeline de raciocínio adaptativa.
6
+
7
+ PRINCIPAIS FUNCIONALIDADES:
8
+ - BYPASS INTELIGENTE (FAST PATH): Para perguntas factuais ou técnicas diretas
9
+ identificadas no Passo 1, a pipeline pula a deliberação complexa, gera
10
+ uma resposta direta, justifica a ação e envia para verificação final.
11
+ - ROBUSTEZ COM TRY/EXCEPT: O aplicativo agora trata erros de API e de
12
+ análise de JSON de forma graciosa, exibindo uma mensagem de erro no chat
13
+ em vez de interromper a execução.
14
+ - PIPELINE GENERALIZADA: Todos os prompts foram reescritos para serem
15
+ agnósticos ao domínio, focando em análise crítica geral.
16
+ - NOVO SISTEMA DE CLASSIFICAÇÃO: A escala 'Extremo, Alto, Médio, Baixo,
17
+ Insuficiente' foi implementada para classificar confiança e complexidade.
18
+ - FUNCIONALIDADES ANTERIORES MANTIDAS: Logs de API, lógica de pausa e
19
+ retomada, e análise de cenários extremos continuam funcionais.
20
+ """
21
+
22
+ # ============================================================================
23
+ # 1. IMPORTAÇÕES E CONFIGURAÇÃO INICIAL
24
+ # ============================================================================
25
+ import json
26
+ import os
27
+ import re
28
+ import warnings
29
+ from datetime import datetime
30
+ from typing import Dict, List, Tuple, Any
31
+
32
+ import gradio as gr
33
+ import google.generativeai as genai
34
+
35
+ warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
36
+
37
+ # --- Configuração da API ---
38
+ API_KEY = os.getenv("GOOGLE_API_KEY")
39
+ if not API_KEY:
40
+ raise ValueError("A variável de ambiente GOOGLE_API_KEY não foi configurada.")
41
+
42
+ genai.configure(api_key=API_KEY)
43
+
44
+ # --- Definição dos Modelos ---
45
+ COUNSELOR_MODEL = genai.GenerativeModel("gemini-1.5-flash")
46
+ SUPERVISOR_MODEL = genai.GenerativeModel("gemini-1.5-flash")
47
+
48
+ # --- Título da Interface ---
49
+ TITLE = "# 🚀 Pipeline v10 | Raciocínio Adaptativo\n**Com 'Fast Path' para perguntas simples e análise profunda para complexas.**"
50
+
51
+ # ============================================================================
52
+ # 2. PROMPTS CENTRALIZADOS
53
+ # ============================================================================
54
+ PROMPTS = {
55
+ "P1_TRIAGEM": """
56
+ METACOGNIÇÃO - TRIAGEM INICIAL.
57
+ PERGUNTA: {pergunta}
58
+ ---
59
+ Analise a pergunta do usuário e classifique-a de acordo com o tipo e a sua confiança para respondê-la de forma direta.
60
+
61
+ Use as seguintes categorias:
62
+ - tipo: 'factual' (pede um fato concreto), 'objetiva_tecnica' (pede uma resposta técnica com uma única resposta correta), 'subjetiva_complexa' (envolve opinião, análise, múltiplos fatores).
63
+ - confianca: 'extrema' (certeza absoluta, como "Qual a capital do Brasil?"), 'alta', 'media', 'baixa', 'insuficiente'.
64
+
65
+ RETORNE JSON:
66
+ {{
67
+ "tipo": "...",
68
+ "confianca": "...",
69
+ "decisao": "Com base na classificação, decida se deve 'responder_direto' (para factual/extrema ou objetiva_tecnica) ou 'analisar_profundamente' (para todas as outras)."
70
+ }}
71
+ """,
72
+ "GERAR_RESPOSTA_DIRETA": """
73
+ TAREFA: Resposta Direta (Bypass)
74
+ A pergunta do usuário foi classificada como factual com extrema certeza ou objetiva-técnica.
75
+ PERGUNTA: "{pergunta}"
76
+ ---
77
+ Forneça uma resposta direta, precisa e concisa. Não adicione explicações extras a menos que seja essencial para a resposta.
78
+
79
+ RETORNE JSON:
80
+ {{"resposta_direta": "Sua resposta concisa aqui."}}
81
+ """,
82
+ "JUSTIFICAR_BYPASS": """
83
+ METACOGNIÇÃO - JUSTIFICATIVA DE BYPASS.
84
+ ANÁLISE DA TRIAGEM (P1): {p1}
85
+ ---
86
+ Você está pulando a pipeline de raciocínio profundo. Preencha o formulário abaixo para justificar esta decisão com base na análise da Triagem. Use uma linguagem clara e objetiva.
87
+
88
+ RETORNE JSON:
89
+ {{
90
+ "justificativa_bypass": {{
91
+ "motivo": "A pergunta foi identificada como [tipo da pergunta] para a qual a confiança de uma resposta direta é [nível de confiança].",
92
+ "acao_tomada": "A pipeline de análise profunda (Passos X1-P7) foi pulada para fornecer uma resposta mais rápida e eficiente.",
93
+ "proximo_passo": "A resposta direta será submetida a uma verificação final de qualidade (Passo 8)."
94
+ }}
95
+ }}
96
+ """,
97
+ "P2_CENARIOS": """
98
+ METACOGNIÇÃO - GERAÇÃO DE CENÁRIOS.
99
+ ANÁLISE (P1): {p1}, PERGUNTA: {pergunta}
100
+ ---
101
+ Gere o cenário de interpretação mais provável para a pergunta e um cenário alternativo (improvável, mas plausível) que teste a robustez da análise.
102
+ RETORNE JSON:
103
+ {{
104
+ "cenarios": {{
105
+ "provaveis": [{{"id": "C1_PROVAVEL", "desc": "Descrição do cenário mais provável."}}],
106
+ "improvaveis": [{{"id": "C2_IMPROVAVEL", "desc": "Descrição do cenário alternativo."}}]
107
+ }}, "decisao": "prosseguir"
108
+ }}
109
+ """,
110
+ "P4_CRUZAR_VALIDACOES": """
111
+ METACOGNIÇÃO - ABSTRAÇÃO DE CONHECIMENTO.
112
+ ANÁLISES ANTERIORES: {p1}, {p2}, {p3}
113
+ ---
114
+ Identifique o princípio fundamental ou o conceito central que rege a discussão.
115
+ RETORNE JSON: {{"principio_central": "Descrição do princípio identificado."}}
116
+ """,
117
+ "P5_LACUNAS_FINAIS": """
118
+ METACOGNIÇÃO - ANÁLISE DE INCERTEZA.
119
+ PRINCÍPIO CENTRAL (P4): {p4}
120
+ PERGUNTA ORIGINAL: {pergunta}
121
+ ---
122
+ Com base no princípio central, identifique a principal lacuna de informação que, se preenchida pelo usuário, resolveria a ambiguidade do caso. Formule uma pergunta clara e direta para o usuário.
123
+
124
+ RETORNE JSON:
125
+ {{
126
+ "pontos_de_incerteza": ["A principal dúvida que ainda resta."],
127
+ "decisao_interna": "questionar",
128
+ "pergunta_chave_para_usuario": "A pergunta clara e contextual que você fará ao usuário para obter a informação que falta."
129
+ }}
130
+ """,
131
+ "P7_SINTETIZAR": """
132
+ SINTETIZADOR.
133
+ DADOS DO JULGAMENTO (P6): {p6}
134
+ ---
135
+ Converta a análise técnica em uma resposta final coesa, estruturada e fácil de entender.
136
+ RETORNE JSON: {{"resposta": "O texto da sua resposta final aqui."}}
137
+ """,
138
+ "P8_VERIFICAR": """
139
+ VERIFICADOR FINAL.
140
+ RESPOSTA A SER VERIFICADA: {resposta_a_verificar}
141
+ ---
142
+ Realize uma verificação tripla (factual, lógica, clareza) na resposta. Se houver problemas, corrija-os.
143
+ RETORNE JSON:
144
+ {{
145
+ "todas_aprovadas": true|false,
146
+ "problemas_identificados": ["..."],
147
+ "resposta_corrigida": "O texto da resposta corrigida, se necessário. Senão, null."
148
+ }}
149
+ """
150
+ }
151
+ # Prompts P0, X1, X2, P3, P6 foram omitidos por simplicidade, pois sua lógica interna não muda.
152
+
153
+ # ============================================================================
154
+ # 3. CLASSES E FUNÇÕES HELPERS
155
+ # ============================================================================
156
+
157
+ class Logger:
158
+ # ... (código do Logger sem alterações) ...
159
+ def __init__(self, verbose: bool = True): self.verbose = verbose
160
+ def log(self, msg: str, level: str = "INFO"):
161
+ log_msg = f"[{datetime.now().strftime('%H:%M:%S')}] [{level.upper()}] {msg}"; print(log_msg)
162
+ if level.upper() in ["TASK", "START", "SUCCESS", "ERROR", "WARN"]: print("=" * 70)
163
+
164
+ logger = Logger(verbose=True)
165
+
166
+ def chamar_gemini_json(modelo: genai.GenerativeModel, prompt: str, temperatura: float = 0.4, max_tokens: int = 2048) -> Dict:
167
+ prompt_completo = f"{prompt}\n\n---\n\n**INSTRUÇÃO OBRIGATÓRIA: Sua resposta DEVE ser um único e válido objeto JSON.**"
168
+ print(f"\n{'='*25} 💬 API INPUT PARA [{modelo.model_name}] {'='*25}\n{prompt_completo}\n{'='*78}\n")
169
+ try:
170
+ response = modelo.generate_content(prompt_completo, generation_config=genai.types.GenerationConfig(temperature=temperatura, max_output_tokens=max_tokens))
171
+ resposta_bruta = response.text or ""
172
+ print(f"\n{'='*25} 📥 API RAW OUTPUT DE [{modelo.model_name}] {'='*25}\n{resposta_bruta}\n{'='*78}\n")
173
+
174
+ if not resposta_bruta.strip():
175
+ logger.log("A API Gemini retornou uma resposta vazia. Causa provável: Filtros de segurança.", "WARN")
176
+ return {"erro": "API_EMPTY_RESPONSE", "causa_provavel": "Filtro de segurança do modelo."}
177
+
178
+ return json.loads(re.search(r'```(?:json)?\s*(\{.*?\})\s*```', resposta_bruta, re.DOTALL).group(1) if re.search(r'```', resposta_bruta) else resposta_bruta)
179
+ except Exception as e:
180
+ logger.log(f"Falha na chamada da API ou no parse do JSON: {e}", "ERROR")
181
+ return {"erro": "API_CALL_OR_PARSE_FAILED", "detalhes": str(e)}
182
+
183
+ def criar_dna() -> Dict:
184
+ return { "historico_chat": [], "meta": {"total_turnos": 0}, "pipeline_state": { "status": "completed", "paused_at_step": None, "saved_data": {} } }
185
+
186
+ # ============================================================================
187
+ # 4. PASSOS DA PIPELINE (COM NOVOS PASSOS PARA O BYPASS)
188
+ # ============================================================================
189
+
190
+ def passo_1_triagem(pergunta: str) -> Dict:
191
+ logger.log("📊 PASSO 1: TRIAGEM INICIAL", "TASK")
192
+ prompt = PROMPTS["P1_TRIAGEM"].format(pergunta=pergunta)
193
+ return chamar_gemini_json(COUNSELOR_MODEL, prompt)
194
+
195
+ # --- PASSOS DO CAMINHO RÁPIDO (BYPASS) ---
196
+ def passo_gerar_resposta_direta(pergunta: str) -> Dict:
197
+ logger.log("⚡ FAST PATH: GERANDO RESPOSTA DIRETA", "TASK")
198
+ prompt = PROMPTS["GERAR_RESPOSTA_DIRETA"].format(pergunta=pergunta)
199
+ return chamar_gemini_json(COUNSELOR_MODEL, prompt)
200
+
201
+ def passo_justificar_bypass(p1: Dict) -> Dict:
202
+ logger.log("⚡ FAST PATH: GERANDO JUSTIFICATIVA DE BYPASS", "TASK")
203
+ prompt = PROMPTS["JUSTIFICAR_BYPASS"].format(p1=json.dumps(p1))
204
+ return chamar_gemini_json(COUNSELOR_MODEL, prompt)
205
+
206
+ # --- PASSOS DO CAMINHO COMPLETO ---
207
+ def passo_2_cenarios(pergunta: str, p1: Dict) -> Dict:
208
+ logger.log("🧠 PASSO 2: GERAÇÃO DE CENÁRIOS", "TASK")
209
+ prompt = PROMPTS["P2_CENARIOS"].format(p1=json.dumps(p1), pergunta=pergunta)
210
+ return chamar_gemini_json(COUNSELOR_MODEL, prompt)
211
+
212
+ def passo_4_cruzar_validacoes(p1: Dict, p2: Dict, p3: Dict) -> Dict:
213
+ logger.log("🧠 PASSO 4: IDENTIFICAÇÃO DO PRINCÍPIO CENTRAL", "TASK")
214
+ prompt = PROMPTS["P4_CRUZAR_VALIDACOES"].format(p1=json.dumps(p1), p2=json.dumps(p2), p3=json.dumps(p3))
215
+ return chamar_gemini_json(COUNSELOR_MODEL, prompt)
216
+
217
+ def passo_5_lacunas_finais(pergunta: str, p4: Dict) -> Dict:
218
+ logger.log("🧠 PASSO 5: ANÁLISE DE INCERTEZA", "TASK")
219
+ prompt = PROMPTS["P5_LACUNAS_FINAIS"].format(p4=json.dumps(p4), pergunta=pergunta)
220
+ return chamar_gemini_json(COUNSELOR_MODEL, prompt)
221
+
222
+ def passo_8_verificar(resposta_a_verificar: str) -> Dict:
223
+ logger.log("✅ PASSO 8: VERIFICAÇÃO FINAL (SUPERVISOR)", "TASK")
224
+ prompt = PROMPTS["P8_VERIFICAR"].format(resposta_a_verificar=resposta_a_verificar)
225
+ return chamar_gemini_json(SUPERVISOR_MODEL, prompt)
226
+
227
+ # Funções dummy para outros passos que não mudam
228
+ def passo_3_isolar_cenarios(p2: Dict) -> Dict: return {"simulado": True}
229
+ def passo_6_ponderar(p2: Dict, p4: Dict, p5: Dict) -> Dict: return {"simulado": True}
230
+ def passo_7_sintetizar(p6: Dict) -> Dict: return {"resposta": "Resposta vinda da pipeline completa."}
231
+
232
+ # ============================================================================
233
+ # 5. ORQUESTRADOR PRINCIPAL
234
+ # ============================================================================
235
+
236
+ def iniciar_nova_pipeline(pergunta: str, historico: List[Dict], anexo: Any, dna: Dict) -> Tuple[str, List, Dict]:
237
+ logger.log(f"INICIANDO NOVA PIPELINE: '{pergunta[:50]}...'", "START")
238
+
239
+ # --- PASSO 1: TRIAGEM E DECISÃO DE ROTA ---
240
+ p1 = passo_1_triagem(pergunta)
241
+ if "erro" in p1: return f"Erro na Triagem: {p1['detalhes']}", historico + [{"role": "user", "content": pergunta}], dna
242
+
243
+ decisao = p1.get("decisao")
244
+
245
+ # --- ROTA 1: FAST PATH / BYPASS ---
246
+ if decisao == "responder_direto":
247
+ logger.log("DECISÃO: Tomar o Caminho Rápido (Bypass).", "INFO")
248
+
249
+ resposta_direta_data = passo_gerar_resposta_direta(pergunta)
250
+ justificativa_data = passo_justificar_bypass(p1)
251
+
252
+ if "erro" in resposta_direta_data or "erro" in justificativa_data:
253
+ return "Erro ao gerar a resposta direta.", historico + [{"role": "user", "content": pergunta}], dna
254
+
255
+ resposta_direta = resposta_direta_data.get("resposta_direta", "Não foi possível gerar a resposta.")
256
+ justificativa = justificativa_data.get("justificativa_bypass", {})
257
+
258
+ verificacao = passo_8_verificar(resposta_direta)
259
+ resposta_final = verificacao.get("resposta_corrigida") or resposta_direta
260
+
261
+ # Formata a resposta final com a justificativa
262
+ justificativa_texto = (
263
+ f"**JUSTIFICATIVA DE RESPOSTA DIRETA:**\n"
264
+ f"- **Motivo:** {justificativa.get('motivo', 'N/A')}\n"
265
+ f"- **Ação Tomada:** {justificativa.get('acao_tomada', 'N/A')}\n\n---\n"
266
+ )
267
+ resposta_formatada = justificativa_texto + resposta_final
268
+
269
+ novo_historico = historico + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": resposta_formatada}]
270
+ logger.log("PIPELINE (FAST PATH) CONCLUÍDA COM SUCESSO", "SUCCESS")
271
+ return "PIPELINE_COMPLETED", novo_historico, dna
272
+
273
+ # --- ROTA 2: PIPELINE COMPLETA ---
274
+ else:
275
+ logger.log("DECISÃO: Tomar o Caminho Completo de Análise Profunda.", "INFO")
276
+ p2 = passo_2_cenarios(pergunta, p1)
277
+ p3 = passo_3_isolar_cenarios(p2)
278
+ p4 = passo_4_cruzar_validacoes(p1, p2, p3)
279
+ p5 = passo_5_lacunas_finais(pergunta, p4)
280
+
281
+ if p5.get("decisao_interna") == "questionar":
282
+ logger.log("INTERRUPÇÃO no P5. Salvando estado no DNA.", "WARN")
283
+ dna['pipeline_state'] = {"status": "paused", "paused_at_step": "P5", "saved_data": {'p1':p1, 'p2':p2, 'p3':p3, 'p4':p4, 'pergunta_original': pergunta, 'historico_original': historico}}
284
+ pergunta_do_bot = p5.get('pergunta_chave_para_usuario', 'Preciso de mais informações.')
285
+ historico_atualizado = historico + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": pergunta_do_bot}]
286
+ return "PIPELINE_PAUSED", historico_atualizado, dna
287
+
288
+ p6 = passo_6_ponderar(p2, p4, p5)
289
+ p7 = passo_7_sintetizar(p6)
290
+ p8 = passo_8_verificar(p7.get("resposta", ""))
291
+ resposta_final = p8.get("resposta_corrigida") or p7.get("resposta", "Não foi possível gerar a resposta.")
292
+ novo_historico = historico + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": resposta_final}]
293
+ logger.log("PIPELINE (COMPLETA) CONCLUÍDA COM SUCESSO", "SUCCESS")
294
+ return "PIPELINE_COMPLETED", novo_historico, dna
295
+
296
+
297
+ def executar_pipeline(pergunta: str, historico: List[Dict], anexo: Any, dna: Dict) -> Tuple[str, List, Dict]:
298
+ try:
299
+ if 'pipeline_state' not in dna: dna.update(criar_dna())
300
+ # A lógica de retomada (resumir_pipeline) foi simplificada e pode ser adicionada aqui se necessário.
301
+ # Por enquanto, focamos na nova lógica de bypass.
302
+ if dna['pipeline_state']['status'] == 'paused':
303
+ # Aqui entraria a chamada para 'resumir_pipeline'
304
+ resposta = "A lógica de retomada precisa ser implementada."
305
+ return "PIPELINE_ERROR", historico + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": resposta}], dna
306
+ return iniciar_nova_pipeline(pergunta, historico, anexo, dna)
307
+ except Exception as e:
308
+ logger.log(f"Erro catastrófico no orquestrador: {e}", "ERROR")
309
+ resposta_erro = f"Ocorreu um erro inesperado na pipeline: {e}"
310
+ return "PIPELINE_ERROR", historico + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": resposta_erro}], dna
311
+
312
+ # ============================================================================
313
+ # 6. INTERFACE COM GRADIO
314
+ # ============================================================================
315
+ # ... (código da interface do Gradio sem alterações, pois a lógica de retorno já é robusta) ...
316
+ def chat_interface(pergunta: str, historico_gradio: List[List[str]], anexo: Any, dna_json_str: str) -> Tuple[List, str, str, None]:
317
+ # ... (código da interface sem alterações) ...
318
+ try:
319
+ dna = json.loads(dna_json_str) if dna_json_str and dna_json_str.strip() else criar_dna()
320
+ except:
321
+ dna = criar_dna()
322
+
323
+ historico_interno = [({"role": "user", "content": turno}, {"role": "assistant", "content": turno}) for turno in historico_gradio]
324
+ historico_interno_flat = [item for sublist in historico_interno for item in sublist if item['content']]
325
+
326
+ _ , novo_historico_para_exibir, dna_atualizado = executar_pipeline(pergunta, historico_interno_flat, anexo, dna)
327
+
328
+ novo_historico_gradio = []
329
+ for i in range(0, len(novo_historico_para_exibir), 2):
330
+ if i + 1 < len(novo_historico_para_exibir):
331
+ novo_historico_gradio.append([novo_historico_para_exibir[i]['content'], novo_historico_para_exibir[i+1]['content']])
332
+
333
+ return novo_historico_gradio, "", json.dumps(dna_atualizado, indent=2, ensure_ascii=False), None
334
+
335
+ if __name__ == "__main__":
336
+ with gr.Blocks(title="Pipeline v10 - Raciocínio Adaptativo", theme=gr.themes.Soft()) as demo:
337
+ gr.Markdown(TITLE)
338
+ with gr.Row():
339
+ with gr.Column(scale=3):
340
+ chatbot = gr.Chatbot(label="Chat", height=600, bubble_full_width=False)
341
+ input_textbox = gr.Textbox(label="Digite sua pergunta...", lines=3)
342
+ with gr.Row():
343
+ submit_button = gr.Button("🚀 Enviar", variant="primary", scale=1)
344
+ file_upload = gr.File(label="Anexar Arquivo", scale=1)
345
+ with gr.Column(scale=2):
346
+ dna_view = gr.Code(label="DNA (Estado da Conversa)", language="json", value=json.dumps(criar_dna(), indent=2, ensure_ascii=False))
347
+ dna_json_hidden = gr.Textbox(value=json.dumps(criar_dna()), visible=False)
348
+ submit_button.click(fn=chat_interface, inputs=[input_textbox, chatbot, file_upload, dna_json_hidden], outputs=[chatbot, input_textbox, dna_json_hidden, file_upload])
349
+ input_textbox.submit(fn=chat_interface, inputs=[input_textbox, chatbot, file_upload, dna_json_hidden], outputs=[chatbot, input_textbox, dna_json_hidden, file_upload])
350
+ dna_json_hidden.change(fn=lambda x: x, inputs=[dna_json_hidden], outputs=[dna_view])
351
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
ai_studio_code.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import base64
4
+ import re
5
+ import warnings
6
+ from datetime import datetime
7
+ from typing import Dict, List, Tuple
8
+
9
+ import gradio as gr
10
+ import google.generativeai as genai
11
+
12
+ # ============================================================================
13
+ # CONFIGURAÇÃO
14
+ # ============================================================================
15
+ # Filtra avisos do gRPC para limpar o console
16
+ warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
17
+
18
+ # API KEY
19
+ API_KEY = os.getenv("GOOGLE_API_KEY", "")
20
+ if not API_KEY:
21
+ print("❌ ERRO CRÍTICO: GOOGLE_API_KEY não encontrada nas variáveis de ambiente.")
22
+ print("Defina com: export GOOGLE_API_KEY='sua_chave'")
23
+ # Não paramos o script, mas vai dar erro na chamada
24
+ else:
25
+ print(f"✅ API Key carregada (termina em ...{API_KEY[-4:]})")
26
+
27
+ genai.configure(api_key=API_KEY)
28
+ model = genai.GenerativeModel("gemini-1.5-flash")
29
+
30
+ TITLE = "# 🛠️ Anise v10.2 DEBUG MODE\n**Console mostra SAÍDA BRUTA**"
31
+
32
+ # ============================================================================
33
+ # SISTEMA DE LOGS & DEBUG
34
+ # ============================================================================
35
+ def debug_print(titulo: str, conteudo: any):
36
+ """Imprime no console com formatação visível para debug"""
37
+ print(f"\n{'='*60}")
38
+ print(f"🐛 DEBUG: {titulo}")
39
+ print(f"{'-'*60}")
40
+ if isinstance(conteudo, (dict, list)):
41
+ print(json.dumps(conteudo, indent=2, ensure_ascii=False))
42
+ else:
43
+ print(str(conteudo))
44
+ print(f"{'='*60}\n")
45
+
46
+ # ============================================================================
47
+ # HELPERS (SEM TRY/EXCEPT PARA DEBUG)
48
+ # ============================================================================
49
+ def processar_anexo(arquivo) -> Tuple[str, str]:
50
+ if arquivo is None:
51
+ return "", "nenhum"
52
+
53
+ caminho = str(arquivo)
54
+ print(f"📂 Processando arquivo: {caminho}")
55
+
56
+ if caminho.lower().endswith('.pdf'):
57
+ import PyPDF2 # Se falhar aqui, queremos ver o erro de importação
58
+ with open(caminho, 'rb') as f:
59
+ leitor = PyPDF2.PdfReader(f)
60
+ texto = "".join(pagina.extract_text() + "\n" for pagina in leitor.pages[:5])
61
+ print(f"📄 PDF extraído: {len(texto)} caracteres")
62
+ return texto[:5000], "pdf"
63
+
64
+ elif any(caminho.lower().endswith(ext) for ext in ['.png','.jpg','.jpeg','.gif','.webp']):
65
+ with open(caminho, 'rb') as f:
66
+ encoded = base64.b64encode(f.read()).decode()
67
+ print(f"🖼️ Imagem codificada: {len(encoded)} bytes")
68
+ return encoded, "imagem"
69
+
70
+ return "", "nao_suportado"
71
+
72
+ def limpar_json_raw(texto: str) -> str:
73
+ """Limpa markdown ```json ... ``` para tentar parsear"""
74
+ texto = re.sub(r'^```json\s*', '', texto, flags=re.MULTILINE)
75
+ texto = re.sub(r'^```\s*', '', texto, flags=re.MULTILINE)
76
+ texto = re.sub(r'```$', '', texto, flags=re.MULTILINE)
77
+ return texto.strip()
78
+
79
+ def chamar_gemini_json(prompt_base: str, etapa: str, temperatura=0.2, max_tokens=2000) -> Dict:
80
+ """
81
+ Chama o Gemini e imprime o RAW OUTPUT.
82
+ Se falhar o JSON, explode o erro para análise.
83
+ """
84
+ full_prompt = f"""{prompt_base}
85
+
86
+ ---
87
+ **INSTRUÇÃO DE SISTEMA OBRIGATÓRIA:**
88
+ 1. Responda APENAS com um JSON válido.
89
+ 2. NÃO use blocos de código markdown (```json).
90
+ 3. NÃO escreva texto antes ou depois do JSON.
91
+ """
92
+
93
+ # 1. Chamada API
94
+ print(f"📡 Enviando requisição para {etapa}...")
95
+ response = model.generate_content(
96
+ full_prompt,
97
+ generation_config=genai.types.GenerationConfig(
98
+ temperature=temperatura,
99
+ max_output_tokens=max_tokens,
100
+ # response_mime_type="application/json" # Opcional: força modo JSON estrito do 1.5
101
+ )
102
+ )
103
+
104
+ raw_text = response.text
105
+
106
+ # 2. PRINT DA SAÍDA BRUTA (O que você pediu)
107
+ print(f"\n🛑 SAÍDA BRUTA GEMINI [{etapa}]:")
108
+ print(f">>> INICIO RAW <<<\n{raw_text}\n>>> FIM RAW <<<")
109
+
110
+ # 3. Tentativa de Parse (sem try/except silencioso)
111
+ texto_limpo = limpar_json_raw(raw_text)
112
+
113
+ if not texto_limpo:
114
+ raise ValueError(f"[{etapa}] Gemini retornou texto vazio!")
115
+
116
+ try:
117
+ dados_json = json.loads(texto_limpo)
118
+ return dados_json
119
+ except json.JSONDecodeError as e:
120
+ print(f"❌ FALHA NO PARSE JSON [{etapa}]")
121
+ print(f"Erro: {e}")
122
+ print("Tentando corrigir manualmente strings...")
123
+ # Fallback simples se for apenas texto solto (opcional)
124
+ raise e # Relança o erro para parar o script e ver o traceback
125
+
126
+ def historico_compacto(historico: List) -> str:
127
+ if not historico: return "Nenhum."
128
+ return "\n".join([f"{m['role']}: {str(m['content'])[:100]}..." for m in historico[-4:]])
129
+
130
+ def criar_dna() -> Dict:
131
+ return {"historico": [], "meta": {"turnos": 0}}
132
+
133
+ # ============================================================================
134
+ # PIPELINE - PASSOS (COM LOGS EXPLÍCITOS)
135
+ # ============================================================================
136
+
137
+ def passo_0_aluno(pergunta: str, historico: List) -> Dict:
138
+ prompt = f"""ETAPA: P0-INTENÇÃO
139
+ HISTÓRICO: {historico_compacto(historico)}
140
+ PERGUNTA: {pergunta}
141
+
142
+ Analise a intenção do usuário.
143
+ JSON: {{"relacao": "continua|nova", "intent": "resumo"}}"""
144
+ return chamar_gemini_json(prompt, "P0")
145
+
146
+ def passo_1_triagem(pergunta: str, p0: Dict) -> Dict:
147
+ prompt = f"""ETAPA: P1-TRIAGEM
148
+ P0: {json.dumps(p0)}
149
+ PERGUNTA: {pergunta}
150
+
151
+ Classifique a pergunta.
152
+ JSON: {{"tipo": "factual|analitica", "complexidade": "alta|baixa"}}"""
153
+ return chamar_gemini_json(prompt, "P1")
154
+
155
+ def passo_x1_lacunas(pergunta: str, p1: Dict) -> Dict:
156
+ prompt = f"""ETAPA: X1-LACUNAS
157
+ P1: {json.dumps(p1)}
158
+ PERGUNTA: {pergunta}
159
+
160
+ Quais perguntas o assistente deve fazer a si mesmo para responder isso perfeitamente?
161
+ JSON: {{"perguntas_internas": ["pergunta1", "pergunta2"]}}"""
162
+ return chamar_gemini_json(prompt, "X1")
163
+
164
+ def passo_x2_resolver(x1: Dict, historico: List) -> Dict:
165
+ perguntas = x1.get("perguntas_internas", [])
166
+ if not perguntas: return {"respostas": []}
167
+
168
+ prompt = f"""ETAPA: X2-RESOLUÇÃO
169
+ PERGUNTAS: {json.dumps(perguntas)}
170
+ CONTEXTO: {historico_compacto(historico)}
171
+
172
+ Responda as perguntas internas.
173
+ JSON: {{"respostas": [{{"p": "pergunta", "r": "resposta", "confianca": "alta|baixa"}}]}}"""
174
+ return chamar_gemini_json(prompt, "X2")
175
+
176
+ def passo_2_cenarios(pergunta: str, x2: Dict) -> Dict:
177
+ prompt = f"""ETAPA: P2-CENÁRIOS
178
+ DADOS X2: {json.dumps(x2)}
179
+ PERGUNTA ORIGINAL: {pergunta}
180
+
181
+ Crie cenários de resposta ou decida parar se faltar informação crítica.
182
+ JSON: {{"decisao": "continuar|parar", "cenarios": ["C1: ...", "C2: ..."], "motivo_parada": "..."}}"""
183
+ return chamar_gemini_json(prompt, "P2")
184
+
185
+ def passo_7_sintese(p2: Dict, pergunta: str) -> Dict:
186
+ prompt = f"""ETAPA: P7-FINAL
187
+ CENÁRIOS: {json.dumps(p2)}
188
+ PERGUNTA: {pergunta}
189
+
190
+ Escreva a resposta final para o usuário.
191
+ JSON: {{"resposta_final": "texto aqui"}}"""
192
+ return chamar_gemini_json(prompt, "P7", temperatura=0.7)
193
+
194
+ # ============================================================================
195
+ # ORQUESTADOR (SEM REDE DE SEGURANÇA)
196
+ # ============================================================================
197
+ def processar_pipeline(pergunta: str, historico: List, arquivo_anexo=None, dna=None) -> Tuple[str, List, Dict]:
198
+ # Debug inicial
199
+ debug_print("INICIO PIPELINE", f"Pergunta: {pergunta}\nAnexo: {arquivo_anexo}")
200
+
201
+ if dna is None: dna = criar_dna()
202
+
203
+ # 1. Processar Anexo
204
+ conteudo_anexo, tipo_anexo = processar_anexo(arquivo_anexo)
205
+
206
+ # 2. Montar Prompt com Contexto Visual/Documental
207
+ if tipo_anexo == "pdf":
208
+ prompt_final = f"CONTEXTO DO PDF:\n{conteudo_anexo}\n---\nPERGUNTA: {pergunta}"
209
+ elif tipo_anexo == "imagem":
210
+ prompt_final = f"[IMAGEM BASE64: {conteudo_anexo}]\nAnalise a imagem.\nPERGUNTA: {pergunta}"
211
+ else:
212
+ prompt_final = pergunta
213
+
214
+ # 3. Execução Linear (Se falhar, o erro aparece no console)
215
+ p0 = passo_0_aluno(prompt_final, historico)
216
+ debug_print("P0 Resultado", p0)
217
+
218
+ p1 = passo_1_triagem(prompt_final, p0)
219
+ debug_print("P1 Resultado", p1)
220
+
221
+ x1 = passo_x1_lacunas(prompt_final, p1)
222
+ debug_print("X1 Resultado", x1)
223
+
224
+ x2 = passo_x2_resolver(x1, historico)
225
+ debug_print("X2 Resultado", x2)
226
+
227
+ p2 = passo_2_cenarios(prompt_final, x2)
228
+ debug_print("P2 Resultado", p2)
229
+
230
+ # Lógica de Parada
231
+ if p2.get("decisao") == "parar":
232
+ resposta = f"⚠️ Não consigo responder com certeza.\nMotivo: {p2.get('motivo_parada')}"
233
+ else:
234
+ p7 = passo_7_sintese(p2, prompt_final)
235
+ debug_print("P7 Resultado", p7)
236
+ resposta = p7.get("resposta_final", "Erro na síntese P7")
237
+
238
+ # Atualiza Histórico
239
+ novo_hist = historico + [
240
+ {"role": "user", "content": pergunta},
241
+ {"role": "assistant", "content": resposta}
242
+ ]
243
+
244
+ dna["historico"].append({"turn": dna["meta"]["turnos"], "p": pergunta, "r": resposta[:20]})
245
+ dna["meta"]["turnos"] += 1
246
+
247
+ return resposta, novo_hist, dna
248
+
249
+ # ============================================================================
250
+ # INTERFACE
251
+ # ============================================================================
252
+ def chat_interface(msg, hist, anexo, dna_json):
253
+ # Converte string JSON do DNA de volta para dict
254
+ dna = json.loads(dna_json) if dna_json else {}
255
+ if hist is None: hist = []
256
+
257
+ # Chama o pipeline (Erros vão aparecer no console do servidor)
258
+ resp, novo_hist, dna_new = processar_pipeline(msg, hist, anexo, dna)
259
+
260
+ return novo_hist, "", json.dumps(dna_new, indent=2), None
261
+
262
+ if __name__ == "__main__":
263
+ with gr.Blocks(title="Anise Debug", theme=gr.themes.Base()) as demo:
264
+ gr.Markdown(TITLE)
265
+
266
+ with gr.Row():
267
+ chat = gr.Chatbot(height=500, type="messages", label="Chat")
268
+ dna_box = gr.Code(label="DNA (JSON State)", language="json")
269
+
270
+ with gr.Row():
271
+ txt_in = gr.Textbox(label="Pergunta", scale=2)
272
+ file_in = gr.File(label="Anexo")
273
+ btn = gr.Button("Enviar", variant="primary")
274
+
275
+ btn.click(chat_interface, [txt_in, chat, file_in, dna_box], [chat, txt_in, dna_box, file_in])
276
+ txt_in.submit(chat_interface, [txt_in, chat, file_in, dna_box], [chat, txt_in, dna_box, file_in])
277
+
278
+ print("🚀 Servidor Iniciado! Verifique este console para logs.")
279
+ demo.launch(server_name="0.0.0.0", server_port=7860)
anuse-em-portugues-a-cadeia-ca-dC8Wv2_jQSmXmtP7T2wzDA.html ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ <!doctype html><html lang="en"><head><meta name="min-version" content="1764729605530"><meta name="version" content="c86ce28"><meta charset="utf-8"/><meta name="theme-color" media="(prefers-color-scheme: light)" content="#FCFCF9"/><meta name="theme-color" media="(prefers-color-scheme: dark)" content="#100E12"/><style>html:not(.todesktop){background-color:#fcfcf9}@media (prefers-color-scheme:light){html:not([data-color-scheme]){background-color:#fcfcf9}}@media (prefers-color-scheme:dark){html:not([data-color-scheme]){background-color:#100e12}}html[data-color-scheme=light]:not(.todesktop){background-color:#fcfcf9!important}html[data-color-scheme=dark]:not(.todesktop){background-color:#100e12!important}</style><script>!function(){"use strict";
2
+ /*! js-cookie v3.0.5 | MIT */function e(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)e[n]=r[n]}return e}var t=function t(r,n){function o(t,o,c){if(!(typeof document>"u")){"number"==typeof(c=e({},n,c)).expires&&(c.expires=new Date(Date.now()+864e5*c.expires)),c.expires&&(c.expires=c.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var i="";for(var a in c)c[a]&&(i+="; "+a,!0!==c[a]&&(i+="="+c[a].split(";")[0]));return document.cookie=t+"="+r.write(o,t)+i}}return Object.create({set:o,get:function(e){if(!(typeof document>"u"||arguments.length&&!e)){for(var t=document.cookie?document.cookie.split("; "):[],n={},o=0;o<t.length;o++){var c=t[o].split("="),i=c.slice(1).join("=");try{var a=decodeURIComponent(c[0]);if(n[a]=r.read(i,a),e===a)break}catch{}}return e?n[e]:n}},remove:function(t,r){o(t,"",e({},r,{expires:-1}))},withAttributes:function(r){return t(this.converter,e({},this.attributes,r))},withConverter:function(r){return t(e({},this.converter,r),this.attributes)}},{attributes:{value:Object.freeze(n)},converter:{value:Object.freeze(r)}})}({read:function(e){return'"'===e[0]&&(e=e.slice(1,-1)),e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}},{path:"/"});const r="pplx.visitor-id",n="pplx.session-id";const o=10/1440;(function(){var e,r;if(!document.documentElement.dataset.colorScheme){let n=t.get("colorScheme");n||(n=null!=(r=null==(e=window.matchMedia)?void 0:e.call(window,"(prefers-color-scheme: dark)"))&&r.matches?"dark":"light"),document.documentElement.setAttribute("data-color-scheme",n)}})(),function(){try{t.get(r)||t.set(r,crypto.randomUUID(),{expires:365})}catch{}try{t.get(n)||t.set(n,crypto.randomUUID(),{expires:o})}catch{}}()}()</script><title>Perplexity</title><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="canonical" href="https://www.perplexity.ai"/><meta name="description" content="Perplexity is a free AI-powered answer engine that provides accurate, trusted, and real-time answers to any question."/><meta property="og:title" content="Perplexity"/><meta property="og:description" content="Perplexity is a free AI-powered answer engine that provides accurate, trusted, and real-time answers to any question."/><meta property="og:url" content="https://www.perplexity.ai/"/><meta property="og:site_name" content="Perplexity AI"/><meta property="og:image" content="https://ppl-ai-public.s3.amazonaws.com/static/img/pplx-default-preview.png"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:site" content="@perplexity_ai"/><meta name="twitter:title" content="Perplexity"/><meta name="twitter:description" content="Perplexity is a free AI-powered answer engine that provides accurate, trusted, and real-time answers to any question."/><meta name="twitter:image" content="https://ppl-ai-public.s3.amazonaws.com/static/img/pplx-default-preview.png"/><script type="module" crossorigin src="https://pplx-next-static-public.perplexity.ai/_spa/assets/index.html-CFBHWED3.js"></script><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/vite-TauYPkP6.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/vendors--B3cNBEr.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/platform-CNL9dx63.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/i18n-BJ3eRUdR.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/icons-DkIdSA0L.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/platform-components-9aIOb7K0.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/spa-shell-BJYDHLRR.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/bootstrap-C9ntdNuf.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/queries-DwLWO9XD.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/QueryPrimerHomepageWidget-Cetxlkcm.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/LocationPermission-3MFuMEqO.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useHasMounted-D_IbjtGk.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/AnimateHeight-WcbwJqQe.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/three-C7cB06Ao.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/HomepageWidgets-DMwG5JHg.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/lexicalNodeToQuery-B2nIRwj_.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/IncognitoIcon-B2K_KCId.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useShortcutsTypeaheadQuery-D2nqSlnA.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/domHelpers-CZ5ovM57.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useAskInputCore-CCTYmofj.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useSubmitHandlerWithSBS-Cw0zyiM4.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useHomeAskInput-BCvchg_G.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/shortcut-BMGYsgm7.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useMentionTypeaheadOptions-BQ_GWc5c.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/enterpriseTiers-MKUN7G6O.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/sheerid-Btb-Gh63.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useUrlModalHandler-DGVqG0Fb.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/SharepointSiteSelector-CZfPEI0q.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/MicrosoftFilePickerModal-MxIBwYfi.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useGetImageDownloadUrl-Bt3IRk4-.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/ConditionalWrapper-bLzXBZfr.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useSourceMeta-BsAng6mS.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/connectorDetails-Dk0aWFbD.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useConnectorDetails-DOXDPgzH.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/Popover-uNiQBw9l.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useSourceActivity-C4-ihGmW.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/SearchModelSelector-D9URfRSm.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/DropZone-CszhSoEJ.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/AskInput-D78V0sGt.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/useAllowedFloatingUpsellPath-m8wh8JQ8.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/LearnMoreButton-Db1iJo1j.js"><link rel="modulepreload" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/home-Ct9GUH_v.js"><link rel="stylesheet" crossorigin href="https://pplx-next-static-public.perplexity.ai/_spa/assets/spa-shell-DiIfLJCt.css"></head><body class="md:bg-underlay bg-transparent"><main id="root"></main></body></html>
ap_mrnomp.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-flash-latest")
20
+ model_pro = genai.GenerativeModel("gemini-pro-latest")
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
+ )
app-gg.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ╔════════════════════════════════════════════════════════════════════════════╗
2
+ # ║ PIPELINE v27: UI LIMPA & ESTRUTURA DE ABAS ║
3
+ # ║ Layout: Chat (Aba 1) | Debug (Aba 2) | Config (Aba 3) ║
4
+ # ╚════════════════════════════════════════════════════════════════════════════╝
5
+
6
+ import os
7
+ import json
8
+ import re
9
+ import time
10
+ from datetime import datetime
11
+ import gradio as gr
12
+ import google.generativeai as genai
13
+
14
+ # ==================== 1. CONFIGURAÇÃO ====================
15
+ api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
16
+ if api_key: genai.configure(api_key=api_key)
17
+
18
+ model_flash = genai.GenerativeModel("gemini-flash-latest")
19
+ model_pro = genai.GenerativeModel("gemini-pro-latest")
20
+
21
+ ARQUIVO_CONFIG = "protocolo.json"
22
+
23
+ # ==================== 2. UTILIDADES ====================
24
+
25
+ def carregar_protocolo():
26
+ try:
27
+ with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f: return f.read()
28
+ except: return "[]"
29
+
30
+ def salvar_protocolo(conteudo):
31
+ try:
32
+ json.loads(conteudo)
33
+ with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f: f.write(conteudo)
34
+ return "✅ Salvo"
35
+ except: return "❌ Erro JSON"
36
+
37
+ def ler_anexo(arquivo):
38
+ if arquivo is None: return ""
39
+ try:
40
+ with open(arquivo.name, "r", encoding="utf-8") as f:
41
+ return f"\n\n[ANEXO SISTEMA: {os.path.basename(arquivo.name)}]\n{f.read()}\n[FIM ANEXO]\n"
42
+ except: return ""
43
+
44
+ # ==================== 3. ENGINE DE EXECUÇÃO ====================
45
+
46
+ def executar_no(timeline, config):
47
+ modelo = model_pro if config.get("modelo") == "pro" else model_flash
48
+ contexto = json.dumps(timeline, ensure_ascii=False, indent=2)
49
+ prompt = f"--- TIMELINE ---\n{contexto}\n----------------\nAGENTE: {config['nome']}\nMISSÃO: {config['missao']}"
50
+
51
+ log = f"\n🔸 {config['nome']}..."
52
+ try:
53
+ inicio = time.time()
54
+ resp = modelo.generate_content(prompt)
55
+ out = resp.text
56
+ tempo = time.time() - inicio
57
+
58
+ content = json.loads(out.strip().replace('```json','').replace('```','')) if config['tipo_saida']=='json' else out
59
+ log += f" (OK - {tempo:.2f}s)"
60
+ return {"role": "assistant", "agent": config['nome'], "content": content}, log, out
61
+ except Exception as e:
62
+ return {"role": "system", "error": str(e)}, f" (ERRO: {e})", str(e)
63
+
64
+ # ==================== 4. ORQUESTRADOR ====================
65
+
66
+ def orquestrador(texto, arquivo, history, json_config):
67
+ # 1. Input Check
68
+ anexo = ler_anexo(arquivo)
69
+ full_input = f"{texto}\n{anexo}".strip()
70
+
71
+ if not full_input:
72
+ yield history, {}, "Sem input."
73
+ return
74
+
75
+ # 2. Setup
76
+ history = history + [[texto + (" 📎" if arquivo else ""), None]]
77
+ try: protocolo = json.loads(json_config)
78
+ except:
79
+ history[-1][1] = "❌ Erro no JSON de Configuração."
80
+ yield history, {}, "Erro JSON"
81
+ return
82
+
83
+ timeline = [{"role": "user", "content": full_input}]
84
+ logs = f"🚀 START: {datetime.now().strftime('%H:%M:%S')}\n"
85
+ history[-1][1] = "⏳ Iniciando análise..."
86
+ yield history, timeline, logs
87
+
88
+ # 3. Loop
89
+ final_response = ""
90
+ for cfg in protocolo:
91
+ history[-1][1] = f"⚙️ {cfg['nome']} trabalhando..."
92
+ yield history, timeline, logs
93
+
94
+ res, log_add, raw = executar_no(timeline, cfg)
95
+ timeline.append(res)
96
+ logs += log_add + "\n"
97
+
98
+ if cfg['tipo_saida'] == 'texto':
99
+ final_response = res['content']
100
+ history[-1][1] = final_response # Mostra texto progressivo se fosse stream, aqui mostra final
101
+
102
+ yield history, timeline, logs
103
+
104
+ logs += "✅ FIM."
105
+ yield history, timeline, logs
106
+
107
+ # ==================== 5. UI LIMPA (v27) ====================
108
+
109
+ def ui_clean():
110
+ css = """
111
+ footer {display: none !important;}
112
+ .contain {border: none !important;}
113
+ """
114
+
115
+ config_init = carregar_protocolo()
116
+
117
+ with gr.Blocks(title="AI Forensics", css=css, theme=gr.themes.Soft()) as app:
118
+
119
+ with gr.Tabs():
120
+
121
+ # === ABA 1: CHAT (LIMPO) ===
122
+ with gr.Tab("💬 Investigador"):
123
+ chatbot = gr.Chatbot(
124
+ label="",
125
+ show_label=False,
126
+ height=600,
127
+ show_copy_button=True,
128
+ render_markdown=True
129
+ )
130
+
131
+ with gr.Row():
132
+ with gr.Column(scale=10):
133
+ txt_in = gr.Textbox(
134
+ show_label=False,
135
+ placeholder="Descreva o caso ou instrução...",
136
+ lines=1,
137
+ max_lines=5,
138
+ container=False
139
+ )
140
+ with gr.Column(scale=1, min_width=50):
141
+ file_in = gr.UploadButton(
142
+ "📎",
143
+ file_types=[".txt", ".md", ".csv", ".json"],
144
+ size="sm"
145
+ )
146
+ with gr.Column(scale=1, min_width=80):
147
+ btn_send = gr.Button("Enviar", variant="primary", size="sm")
148
+
149
+ # Feedback visual sutil do arquivo
150
+ file_status = gr.Markdown("", visible=True)
151
+ file_in.upload(lambda x: f"📎 Anexo: {os.path.basename(x.name)}", file_in, file_status)
152
+
153
+ # === ABA 2: DEPURAÇÃO (ESCONDIDO) ===
154
+ with gr.Tab("🕵️ Depuração"):
155
+ with gr.Row():
156
+ out_dna = gr.JSON(label="DNA (Timeline)")
157
+ out_logs = gr.Textbox(label="Logs do Sistema", lines=20)
158
+
159
+ # === ABA 3: CONFIG (TÉCNICO) ===
160
+ with gr.Tab("⚙️ Config"):
161
+ with gr.Row():
162
+ btn_save = gr.Button("Salvar Config")
163
+ lbl_save = gr.Label(show_label=False)
164
+ code_json = gr.Code(value=config_init, language="json", label="protocolo.json")
165
+ btn_save.click(salvar_protocolo, code_json, lbl_save)
166
+
167
+ # === TRIGGERS ===
168
+ # Enter ou Botão Enviar
169
+ triggers = [btn_send.click, txt_in.submit]
170
+
171
+ for trig in triggers:
172
+ trig(
173
+ orquestrador,
174
+ inputs=[txt_in, file_in, chatbot, code_json],
175
+ outputs=[chatbot, out_dna, out_logs]
176
+ ).then(
177
+ lambda: (None, ""), # Limpa input e label do arquivo após envio
178
+ outputs=[txt_in, file_status]
179
+ )
180
+
181
+ return app
182
+
183
+ if __name__ == "__main__":
184
+ ui_clean().launch()
app.4py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ╔════════════════════════════════════════════════════════════════════════════╗
2
+ # ║ PIPELINE v43: FRAG + VISÃO PAGINADA + CONFEXT_UPLOAD + PARSE ROBUSTO ║
3
+ # ╚════════════════════════════════════════════════════════════════════════════╝
4
+
5
+ import os
6
+ import json
7
+ import time
8
+ from datetime import datetime
9
+
10
+ import gradio as gr
11
+ import google.generativeai as genai
12
+ import pypdf # pip install pypdf
13
+
14
+ # ==================== 1. CONFIGURAÇÃO ====================
15
+
16
+ api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
17
+ if api_key:
18
+ genai.configure(api_key=api_key)
19
+
20
+ model_flash = genai.GenerativeModel("gemini-flash-latest")
21
+ model_pro = genai.GenerativeModel("gemini-pro-latest")
22
+
23
+ ARQUIVO_CONFIG = "protocolo_fragmentacao_visao-3.json"
24
+
25
+ # ==================== 2. UTILIDADES ====================
26
+
27
+ def log_point(msg, logs):
28
+ ts = datetime.now().strftime("%H:%M:%S")
29
+ return logs + f"[{ts}] {msg}\n"
30
+
31
+ def carregar_protocolo():
32
+ try:
33
+ with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
34
+ return f.read()
35
+ except:
36
+ # fallback mínimo válido
37
+ proto = [
38
+ {
39
+ "nome": "PAGINADOR_VISUAL",
40
+ "missao": (
41
+ "Você recebe o texto bruto de um conjunto de páginas de um PDF. "
42
+ "Separe por página e devolva uma lista JSON com objetos "
43
+ "{'pagina','transcricao_fiel','descricao_visual'}."
44
+ "Retorne APENAS essa lista JSON, sem texto extra."
45
+ ),
46
+ "tipo_saida": "json",
47
+ "modelo": "flash",
48
+ }
49
+ ]
50
+ return json.dumps(proto, ensure_ascii=False, indent=2)
51
+
52
+ def salvar_protocolo(conteudo):
53
+ try:
54
+ json.loads(conteudo)
55
+ with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
56
+ f.write(conteudo)
57
+ return "✅ Salvo"
58
+ except:
59
+ return "❌ Erro JSON"
60
+
61
+ # --------- DIVISÃO BURRA COM TEXTO REAL + LOGS ---------
62
+
63
+ def ler_anexo_e_fragmentar(arquivo, paginas_por_fragmento=5, logs=""):
64
+ logs = log_point("ler_anexo_e_fragmentar() chamado", logs)
65
+
66
+ if arquivo is None:
67
+ logs = log_point("Nenhum arquivo recebido", logs)
68
+ return [], "", logs
69
+
70
+ filename = getattr(arquivo, "name", arquivo)
71
+ logs = log_point(f"Arquivo recebido: {filename}", logs)
72
+
73
+ if not os.path.exists(filename):
74
+ msg = f"Arquivo não encontrado: {filename}"
75
+ logs = log_point(msg, logs)
76
+ return [], f"[ERRO: {msg}]", logs
77
+
78
+ anexo_info = f"[PDF: {os.path.basename(filename)}]"
79
+
80
+ if not filename.lower().endswith(".pdf"):
81
+ logs = log_point("Arquivo não é PDF; tratado como texto simples", logs)
82
+ return [f"[ARQUIVO_TEXTO: {os.path.basename(filename)}]"], anexo_info, logs
83
+
84
+ try:
85
+ reader = pypdf.PdfReader(filename)
86
+ total_pages = len(reader.pages)
87
+ logs = log_point(f"PDF com {total_pages} páginas", logs)
88
+
89
+ fragments = []
90
+ for i in range(0, total_pages, paginas_por_fragmento):
91
+ start = i + 1
92
+ end = min(i + paginas_por_fragmento, total_pages)
93
+
94
+ bloco_texto = ""
95
+ for p in range(i, end):
96
+ try:
97
+ t = reader.pages[p].extract_text() or ""
98
+ except Exception as e:
99
+ t = f"\n[ERRO_EXTRACT_PAG_{p+1}: {e}]\n"
100
+ bloco_texto += f"\n=== PAGINA {p+1}/{total_pages} ===\n{t}\n"
101
+
102
+ fragment = (
103
+ f"=== FRAG {i//paginas_por_fragmento + 1} "
104
+ f"(PÁGS {start}-{end}/{total_pages}) ===\n"
105
+ f"{bloco_texto.strip()}"
106
+ )
107
+ fragments.append(fragment)
108
+ logs = log_point(
109
+ f"Fragmento {i//paginas_por_fragmento + 1} criado (pags {start}-{end})",
110
+ logs,
111
+ )
112
+
113
+ logs = log_point(f"Total de fragmentos: {len(fragments)}", logs)
114
+ return fragments, anexo_info, logs
115
+ except Exception as e:
116
+ logs = log_point(f"ERRO PDF: {e}", logs)
117
+ return [f"[ERRO PDF: {str(e)}]"], anexo_info, logs
118
+
119
+ # ==================== 3. ENGINE DE EXECUÇÃO ====================
120
+
121
+ def _extrair_json_possivel(out_raw: str) -> str:
122
+ """
123
+ Tenta isolar só o bloco JSON de uma resposta que pode ter texto extra.
124
+ Procura o primeiro 'json.
125
+ """
126
+ cleaned = out_raw.strip()
127
+ idx_abre_col = cleaned.find("
128
+
129
+ # menor índice válido
130
+ candidatos = [i for i in [idx_abre_col, idx_abre_obj] if i != -1]
131
+ if candidatos:
132
+ start = min(candidatos)
133
+ cleaned = cleaned[start:]
134
+
135
+ cleaned = cleaned.replace("```json", "").replace("```")
136
+ return cleaned
137
+
138
+ def executar_no(timeline, config, fragmento_input=None, logs=""):
139
+ logs = log_point(f"executar_no({config['nome']}) chamado", logs)
140
+ modo = "input_fragmento" if fragmento_input is not None else "timeline"
141
+ logs = log_point(f"Modo de entrada: {modo}", logs)
142
+
143
+ modelo = model_pro if config.get("modelo") == "pro" else model_flash
144
+
145
+ if fragmento_input is not None:
146
+ input_para_prompt = fragmento_input
147
+ else:
148
+ input_para_prompt = json.dumps(timeline, ensure_ascii=False, indent=2)
149
+
150
+ prompt = (
151
+ "--- INPUT PARA O AGENTE ---\n"
152
+ f"{input_para_prompt}\n"
153
+ "----------------\n"
154
+ f"AGENTE: {config['nome']}\n"
155
+ f"MISSÃO: {config['missao']}"
156
+ )
157
+
158
+ try:
159
+ inicio = time.time()
160
+ logs = log_point("Chamando modelo.generate_content()", logs)
161
+ resp = modelo.generate_content(prompt)
162
+ out = resp.text or ""
163
+ tempo = time.time() - inicio
164
+ logs = log_point(f"Tempo de geração: {tempo:.2f}s", logs)
165
+ logs = log_point(f"Saída bruta (120 chars): {out[:120]!r}", logs)
166
+
167
+ if config["tipo_saida"] == "json":
168
+ cleaned = _extrair_json_possivel(out)
169
+ logs = log_point(f"Trecho candidato a JSON (120): {cleaned[:120]!r}", logs)
170
+ try:
171
+ content = json.loads(cleaned)
172
+ except Exception as e:
173
+ content = []
174
+ logs = log_point(f"ERRO JSON parse: {e}", logs)
175
+ else:
176
+ content = out
177
+
178
+ logs = log_point("executar_no() concluído com sucesso", logs)
179
+ return {"role": "assistant", "agent": config["nome"], "content": content}, logs, out
180
+ except Exception as e:
181
+ logs = log_point(f"ERRO em executar_no: {e}", logs)
182
+ return {"role": "system", "error": str(e)}, logs, str(e)
183
+
184
+ # ==================== 4. ORQUESTRADOR ====================
185
+
186
+ def orquestrador(texto, arquivo, history, json_config, confext_state):
187
+ logs = f"🚀 START: {datetime.now().strftime('%H:%M:%S')}\n"
188
+ logs = log_point("orquestrador() iniciado", logs)
189
+ logs = log_point(f"Texto len={len(texto or '')}", logs)
190
+
191
+ fragmentos, anexo_info, logs = ler_anexo_e_fragmentar(
192
+ arquivo, paginas_por_fragmento=5, logs=logs
193
+ )
194
+ logs = log_point(f"Qtd fragmentos após leitura: {len(fragmentos)}", logs)
195
+
196
+ if not texto and not fragmentos:
197
+ logs = log_point("Sem texto e sem fragmentos; encerrando", logs)
198
+ yield history, {}, logs, confext_state
199
+ return
200
+
201
+ history = history + [[texto + (" 📎" if arquivo else ""), None]]
202
+
203
+ try:
204
+ protocolo = json.loads(json_config)
205
+ logs = log_point("Protocolo JSON carregado", logs)
206
+ except Exception as e:
207
+ history[-1] = "❌ Erro no JSON de Configuração."[3]
208
+ logs = log_point(f"ERRO carregando protocolo: {e}", logs)
209
+ yield history, {}, logs, confext_state
210
+ return
211
+
212
+ timeline = [{"role": "user", "content": texto}]
213
+ confext_upload = {
214
+ "arquivo": os.path.basename(getattr(arquivo, "name", "sem_arquivo"))
215
+ if arquivo else None,
216
+ "meta": anexo_info,
217
+ "paginas": []
218
+ }
219
+ logs = log_point(
220
+ f"confext_upload inicializado para arquivo={confext_upload['arquivo']}",
221
+ logs,
222
+ )
223
+
224
+ if fragmentos:
225
+ history[-1] = "⏳ Fragmentando + visão paginada..."[3]
226
+ logs = log_point("Fragmentos disponíveis; iniciando visão paginada", logs)
227
+ yield history, timeline, logs, confext_upload
228
+
229
+ # PASSO PAGINADOR_VISUAL (primeiro agente, se existir)
230
+ if protocolo and fragmentos:
231
+ cfg_visao = protocolo
232
+ logs = log_point(f"Agente de visão selecionado: {cfg_visao['nome']}", logs)
233
+
234
+ for i, fragmento in enumerate(fragmentos):
235
+ history[-1] = f"👁️ {cfg_visao['nome']} frag {i+1}/{len(fragmentos)}..."[3]
236
+ logs = log_point(f"Enviando frag {i+1}", logs)
237
+ yield history, timeline, logs, confext_upload
238
+
239
+ res, logs, raw = executar_no(
240
+ timeline, cfg_visao, fragmento_input=fragmento, logs=logs
241
+ )
242
+
243
+ if "error" in res:
244
+ logs = log_point(f"Erro no frag {i+1}: {res['error']}", logs)
245
+ continue
246
+
247
+ try:
248
+ paginas_res = res["content"]
249
+ if isinstance(paginas_res, dict):
250
+ paginas_res = [paginas_res]
251
+ antes = len(confext_upload["paginas"])
252
+ for p in paginas_res:
253
+ confext_upload["paginas"].append(p)
254
+ depois = len(confext_upload["paginas"])
255
+ logs = log_point(
256
+ f"Frag {i+1} adicionou {depois-antes} páginas; total={depois}",
257
+ logs,
258
+ )
259
+ except Exception as e:
260
+ logs = log_point(f"Falha ao anexar páginas do frag {i+1}: {e}", logs)
261
+
262
+ logs = log_point(
263
+ f"Visão paginada concluída; paginas={len(confext_upload['paginas'])}",
264
+ logs,
265
+ )
266
+
267
+ timeline.append({
268
+ "role": "system",
269
+ "agent": "CONFEXT_UPLOAD",
270
+ "content": confext_upload
271
+ })
272
+ logs = log_point("CONFEXT_UPLOAD injetado na timeline", logs)
273
+
274
+ restante = protocolo[1:] if protocolo else []
275
+ final_response = ""
276
+
277
+ for cfg in restante:
278
+ history[-1] = f"⚙️ {cfg['nome']}..."[3]
279
+ logs = log_point(f"Iniciando passo adicional: {cfg['nome']}", logs)
280
+ yield history, timeline, logs, confext_upload
281
+
282
+ res, logs, raw = executar_no(timeline, cfg, fragmento_input=None, logs=logs)
283
+ timeline.append(res)
284
+
285
+ if cfg["tipo_saida"] == "texto":
286
+ final_response = res["content"]
287
+ history[-1] = final_response[3]
288
+ logs = log_point(f"Passo {cfg['nome']} produziu texto final", logs)
289
+
290
+ yield history, timeline, logs, confext_upload
291
+
292
+ if not restante and not texto:
293
+ history[-1] = "✅ PDF processado. Pronto para perguntas usando confext_upload."[3]
294
+ final_response = history[-1][3]
295
+ logs = log_point("Nenhum passo adicional; apenas pré-processamento", logs)
296
+
297
+ logs = log_point("FIM orquestrador()", logs)
298
+ yield history, timeline, logs, confext_upload
299
+
300
+ # ==================== 5. UI ====================
301
+
302
+ def ui_clean():
303
+ css = """
304
+ footer {display: none !important;}
305
+ .contain {border: none !important;}
306
+ """
307
+
308
+ config_init = carregar_protocolo()
309
+
310
+ with gr.Blocks(title="AI Forensics – Visão Paginada", css=css, theme=gr.themes.Soft()) as app:
311
+ confext_state = gr.State(value=None)
312
+
313
+ with gr.Tabs():
314
+ with gr.Tab("💬 Investigador"):
315
+ chatbot = gr.Chatbot(
316
+ label="",
317
+ show_label=False,
318
+ height=600,
319
+ show_copy_button=True,
320
+ render_markdown=True,
321
+ )
322
+
323
+ with gr.Row():
324
+ with gr.Column(scale=10):
325
+ txt_in = gr.Textbox(
326
+ show_label=False,
327
+ placeholder="Descreva o caso ou faça perguntas (opcional após upload)...",
328
+ lines=1,
329
+ max_lines=5,
330
+ container=False,
331
+ )
332
+ with gr.Column(scale=1, min_width=50):
333
+ file_in = gr.UploadButton(
334
+ "📎",
335
+ file_types=[".txt", ".md", ".csv", ".json", ".pdf"],
336
+ size="sm",
337
+ )
338
+ with gr.Column(scale=1, min_width=80):
339
+ btn_send = gr.Button("Enviar", variant="primary", size="sm")
340
+
341
+ file_status = gr.Markdown("", visible=True)
342
+
343
+ def _on_upload(x):
344
+ nome = os.path.basename(getattr(x, "name", x))
345
+ print("[DEBUG] upload arquivo:", nome)
346
+ return f"📎 Anexo recebido: {nome}"
347
+
348
+ file_in.upload(
349
+ _on_upload,
350
+ inputs=file_in,
351
+ outputs=file_status,
352
+ )
353
+
354
+ with gr.Tab("🕵️ Depuração"):
355
+ with gr.Row():
356
+ out_dna = gr.JSON(label="DNA (Timeline)")
357
+ out_logs = gr.Textbox(label="Logs do Sistema", lines=20)
358
+ confext_view = gr.JSON(label="confext_upload")
359
+
360
+ with gr.Tab("⚙️ Config"):
361
+ with gr.Row():
362
+ btn_save = gr.Button("Salvar Config")
363
+ lbl_save = gr.Label(show_label=False)
364
+ code_json = gr.Code(value=config_init, language="json", label=ARQUIVO_CONFIG)
365
+ btn_save.click(salvar_protocolo, code_json, lbl_save)
366
+
367
+ def _orq_wrapper(texto, arquivo, history, json_cfg, confext_old):
368
+ print(
369
+ "[DEBUG] _orq_wrapper disparado",
370
+ "len_texto=", len(texto or ""),
371
+ "arquivo=", getattr(arquivo, "name", None),
372
+ )
373
+ for h, dna, logs, confext_new in orquestrador(
374
+ texto, arquivo, history, json_cfg, confext_old
375
+ ):
376
+ yield h, dna, logs, confext_new
377
+
378
+ triggers = [btn_send.click, txt_in.submit]
379
+
380
+ for trig in triggers:
381
+ trig(
382
+ _orq_wrapper,
383
+ inputs=[txt_in, file_in, chatbot, code_json, confext_state],
384
+ outputs=[chatbot, out_dna, out_logs, confext_state],
385
+ ).then(
386
+ lambda c: (
387
+ print(
388
+ "[DEBUG] pós-envio; paginas_confext=",
389
+ 0 if not c else len(c.get("paginas", [])),
390
+ ),
391
+ None,
392
+ None,
393
+ "",
394
+ c,
395
+ )[1:],
396
+ inputs=confext_state,
397
+ outputs=[txt_in, file_in, file_status, confext_state],
398
+ ).then(
399
+ lambda c: c,
400
+ inputs=confext_state,
401
+ outputs=confext_view,
402
+ )
403
+
404
+ return app
405
+
406
+
407
+ if __name__ == "__main__":
408
+ ui_clean().launch()
app.py ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ import json
4
+ import time
5
+ import hashlib
6
+ from datetime import datetime
7
+ from concurrent.futures import ThreadPoolExecutor, as_completed
8
+
9
+ import gradio as gr
10
+ import google.generativeai as genai
11
+
12
+ # Dependências para PDF
13
+ try:
14
+ import PyPDF2
15
+ PDF_SUPPORT = True
16
+ except ImportError:
17
+ PDF_SUPPORT = False
18
+ print("⚠️ PyPDF2 não instalado. Instale com: pip install PyPDF2")
19
+
20
+ # ==================== 1. CONFIGURAÇÃO ====================
21
+
22
+ api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
23
+ if api_key and api_key != "SUA_API_KEY_AQUI":
24
+ genai.configure(api_key=api_key)
25
+
26
+ # Modelos do Gemini
27
+ model_flash = genai.GenerativeModel("gemini-flash-latest")
28
+ model_pro = genai.GenerativeModel("gemini-pro-latest")
29
+
30
+ ARQUIVO_CONFIG = "protocolo.json"
31
+ PASTA_TRANSCRICOES = "transcricoes"
32
+ PAGES_PER_CHUNK = 10
33
+ MAX_WORKERS = 5 # Limite de chamadas paralelas
34
+
35
+ os.makedirs(PASTA_TRANSCRICOES, exist_ok=True)
36
+
37
+ # ==================== 2. UTILIDADES ====================
38
+
39
+ def carregar_protocolo():
40
+ """ Carrega o protocolo. Se não existir, cria um com exemplo de STOP. """
41
+ try:
42
+ with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
43
+ return f.read()
44
+ except FileNotFoundError:
45
+ # Protocolo padrão que inclui um agente com a lógica de pergunta ao usuário
46
+ protocolo_padrao = [
47
+ {"nome": "Leitor Inicial", "modelo": "flash", "missao": "Leia o contexto e resuma os fatos principais em 3 a 5 pontos."},
48
+ {
49
+ "nome": "Advogado de Acusação",
50
+ "modelo": "pro",
51
+ "missao": "Com base nos fatos, formule uma pergunta crucial para o usuário para fortalecer um caso. Sua resposta DEVE ser APENAS um JSON no formato: {\"tipo\": \"pergunta_usuario\", \"pergunta\": \"Sua pergunta aqui\"}"
52
+ },
53
+ {"nome": "Analista Final", "modelo": "pro", "missao": "Considere a resposta do usuário e os fatos iniciais para dar um parecer final sobre o caso."}
54
+ ]
55
+ return json.dumps(protocolo_padrao, indent=2)
56
+
57
+ def salvar_protocolo(conteudo):
58
+ try:
59
+ json.loads(conteudo)
60
+ with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
61
+ f.write(conteudo)
62
+ return "✅ Protocolo salvo com sucesso!"
63
+ except Exception as e:
64
+ return f"❌ Erro ao salvar: {str(e)}"
65
+
66
+ def limpar_nome_arquivo(nome):
67
+ nome_base = os.path.basename(nome)
68
+ nome_limpo = "".join([c for c in nome_base if c.isalnum() or c in (' ', '.', '_', '-')]).strip()
69
+ return nome_limpo + ".json"
70
+
71
+ def extrair_texto_pdf(caminho_pdf):
72
+ # (Implementação existente, sem alterações)
73
+ try:
74
+ with open(caminho_pdf, 'rb') as f:
75
+ reader = PyPDF2.PdfReader(f)
76
+ paginas = []
77
+ for i, page in enumerate(reader.pages):
78
+ texto = page.extract_text()
79
+ paginas.append({
80
+ "numero": i + 1,
81
+ "texto": texto,
82
+ "metadata": str(page)[:200]
83
+ })
84
+ return paginas, None
85
+ except Exception as e:
86
+ return None, str(e)
87
+
88
+
89
+ def fragmentar_pdf(paginas, tamanho_chunk=PAGES_PER_CHUNK):
90
+ # (Implementação existente, sem alterações)
91
+ chunks = []
92
+ for i in range(0, len(paginas), tamanho_chunk):
93
+ chunk = paginas[i:i + tamanho_chunk]
94
+ num_inicio = chunk[0]["numero"]
95
+ num_fim = chunk[-1]["numero"]
96
+
97
+ texto_consolidado = "\n---QUEBRA DE PÁGINA---\n".join(
98
+ [f"[PÁGINA {p['numero']}]\n{p['texto']}" for p in chunk]
99
+ )
100
+
101
+ chunks.append({
102
+ "id": f"chunk_{num_inicio}_{num_fim}",
103
+ "paginas": f"{num_inicio}-{num_fim}",
104
+ "num_paginas": len(chunk),
105
+ "texto": texto_consolidado,
106
+ "metadata": [p["metadata"] for p in chunk]
107
+ })
108
+ return chunks
109
+
110
+ def processar_pdf_completo(arquivo_pdf):
111
+ # (Implementação existente, sem alterações)
112
+ if not PDF_SUPPORT:
113
+ return None, "❌ PyPDF2 não disponível"
114
+
115
+ try:
116
+ paginas, erro = extrair_texto_pdf(arquivo_pdf.name if hasattr(arquivo_pdf, 'name') else arquivo_pdf)
117
+ if erro:
118
+ return None, f"❌ Erro ao ler PDF: {erro}"
119
+
120
+ chunks = fragmentar_pdf(paginas)
121
+ nome_arquivo = os.path.basename(arquivo_pdf.name if hasattr(arquivo_pdf, 'name') else arquivo_pdf)
122
+
123
+ return {
124
+ "arquivo": nome_arquivo,
125
+ "total_paginas": len(paginas),
126
+ "total_chunks": len(chunks),
127
+ "chunks": chunks,
128
+ "tipo": "pdf"
129
+ }, None
130
+ except Exception as e:
131
+ return None, f"❌ Erro no processamento: {str(e)}"
132
+
133
+ def ler_arquivo_texto(arquivo):
134
+ # (Implementação existente, sem alterações)
135
+ if arquivo is None: return None
136
+ try:
137
+ with open(arquivo.name, "r", encoding="utf-8") as f:
138
+ conteudo = f.read()
139
+ return {
140
+ "arquivo": os.path.basename(arquivo.name),
141
+ "conteudo": conteudo,
142
+ "tipo": "texto"
143
+ }
144
+ except: return None
145
+
146
+ # ==================== 3. PIPELINE DE IA ====================
147
+
148
+ def transcrever_chunk(chunk_data, config_agentes):
149
+ # (Implementação existente, sem alterações)
150
+ modelo = model_flash
151
+ try:
152
+ if config_agentes and isinstance(config_agentes, list):
153
+ if config_agentes[0].get("modelo") == "pro":
154
+ modelo = model_pro
155
+ except:
156
+ pass
157
+
158
+ prompt = f"""
159
+ ANÁLISE DE DOCUMENTO (OCR/LEITURA):
160
+ Transcreva e estruture o conteúdo das páginas {chunk_data['paginas']}.
161
+ Texto extraído:
162
+ {chunk_data['texto']}
163
+
164
+ Retorne JSON: {{ "transcricao": "...", "objetos": ["..."], "resumo": "..." }}
165
+ """
166
+ try:
167
+ for tentativa in range(3):
168
+ try:
169
+ resposta = modelo.generate_content(prompt)
170
+ texto_resp = resposta.text.replace("```json", "").replace("```", "")
171
+ return json.loads(texto_resp.strip()), None
172
+ except Exception as inner_e:
173
+ if "429" in str(inner_e):
174
+ time.sleep(2 * (tentativa + 1))
175
+ continue
176
+ raise inner_e
177
+ except Exception as e:
178
+ return None, str(e)
179
+
180
+ # ==================== 4. GERENCIADOR DE ARQUIVOS ====================
181
+
182
+ class GerenciadorArquivos:
183
+ # (Implementação existente, sem alterações)
184
+ def __init__(self):
185
+ self.arquivos = {}
186
+
187
+ def adicionar(self, arquivo, arquivo_id):
188
+ self.arquivos[arquivo_id] = {
189
+ "arquivo": arquivo,
190
+ "nome": os.path.basename(arquivo.name),
191
+ "status": "adicionado",
192
+ "processado": None,
193
+ "transcricao": None
194
+ }
195
+
196
+ def gerar_prompt_com_transcricoes(self, texto_usuario):
197
+ prompt = texto_usuario + "\n\n--- CONTEXTO DOS ARQUIVOS ---\n"
198
+ count = 0
199
+ for _, item in self.arquivos.items():
200
+ if item["status"] == "processado" and item["transcricao"]:
201
+ count += 1
202
+ trans = item["transcricao"]
203
+ nome = item["nome"]
204
+ prompt += f"\n[ARQUIVO: {nome}]\n"
205
+
206
+ if isinstance(trans, dict) and "chunks_processados" in trans:
207
+ for chunk in trans["chunks_processados"]:
208
+ if chunk.get("status") == "OK":
209
+ resumo = chunk.get('resumo', '')
210
+ resumo = str(resumo) if resumo else ""
211
+ prompt += f"Páginas {chunk['paginas']}: {resumo}\n"
212
+
213
+ texto_full = chunk.get('transcricao', '')
214
+ if texto_full:
215
+ texto_seguro = str(texto_full)
216
+ prompt += f"Trecho: {texto_seguro[:400]}...\n"
217
+ else:
218
+ prompt += "Trecho: (vazio)\n"
219
+
220
+ elif isinstance(trans, dict) and "conteudo" in trans:
221
+ conteudo = str(trans['conteudo'])
222
+ prompt += f"Conteúdo: {conteudo[:1000]}...\n"
223
+
224
+ if count == 0:
225
+ prompt += "(Nenhum arquivo processado ainda)"
226
+ return prompt
227
+
228
+ # Instância Global
229
+ gerenciador = GerenciadorArquivos()
230
+
231
+ # ==================== 5. FUNÇÕES DE ORQUESTRAÇÃO ====================
232
+
233
+ def automacao_upload_processamento(files, history, config_json):
234
+ # (Implementação existente, sem alterações)
235
+ if not files:
236
+ return history
237
+
238
+ try:
239
+ config_agentes = json.loads(config_json)
240
+ except:
241
+ config_agentes = []
242
+
243
+ if history is None:
244
+ history = []
245
+
246
+ history.append([None, f"📂 **SISTEMA:** Recebi {len(files)} arquivo(s). Verificando cache e processando..."])
247
+ yield history
248
+
249
+ ids_para_processar = []
250
+
251
+ for f in files:
252
+ arquivo_id = f"arq_{int(time.time()*1000)}_{f.name}"
253
+ gerenciador.adicionar(f, arquivo_id)
254
+ ids_para_processar.append(arquivo_id)
255
+
256
+ for arq_id in ids_para_processar:
257
+ item = gerenciador.arquivos[arq_id]
258
+ nome = item["nome"]
259
+
260
+ nome_cache = limpar_nome_arquivo(nome)
261
+ caminho_cache = os.path.join(PASTA_TRANSCRICOES, nome_cache)
262
+
263
+ if os.path.exists(caminho_cache):
264
+ try:
265
+ with open(caminho_cache, "r", encoding="utf-8") as cache_file:
266
+ dados_cache = json.load(cache_file)
267
+ item["transcricao"] = dados_cache
268
+ item["status"] = "processado"
269
+ if nome.lower().endswith('.pdf') and "chunks_processados" in dados_cache:
270
+ item["processado"] = {"tipo": "pdf", "chunks": []}
271
+ history.append([None, f"♻️ **Cache Encontrado:** `{nome}` já foi processado. Carregando..."])
272
+ yield history
273
+ continue
274
+ except Exception as e:
275
+ history.append([None, f"⚠️ Erro cache `{nome}`: {e}. Reprocessando..."])
276
+
277
+ history.append([None, f"⚙️ **Processando:** `{nome}`..."])
278
+ yield history
279
+
280
+ if nome.lower().endswith('.pdf'):
281
+ if not PDF_SUPPORT:
282
+ history.append([None, f"❌ Erro em `{nome}`: Biblioteca PDF ausente."])
283
+ yield history
284
+ continue
285
+
286
+ pdf_proc, erro = processar_pdf_completo(item["arquivo"])
287
+ if erro:
288
+ history.append([None, f"❌ Erro em `{nome}`: {erro}"])
289
+ yield history
290
+ continue
291
+
292
+ item["processado"] = pdf_proc
293
+ chunks = pdf_proc["chunks"]
294
+ total_chunks = len(chunks)
295
+
296
+ chunks_ordenados = [None] * total_chunks
297
+
298
+ history.append([None, f"📄 `{nome}` fragmentado em {total_chunks} partes. Iniciando IA (Paralelo: {MAX_WORKERS} threads)..."])
299
+ yield history
300
+
301
+ with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
302
+ futures_map = {}
303
+ for i, chunk in enumerate(chunks):
304
+ future = executor.submit(transcrever_chunk, chunk, config_agentes)
305
+ futures_map[future] = i
306
+
307
+ concluidos = 0
308
+ for future in as_completed(futures_map):
309
+ index_original = futures_map[future]
310
+ res, err = future.result()
311
+
312
+ if err:
313
+ chunks_ordenados[index_original] = {"status": "ERRO", "paginas": chunks[index_original]["paginas"]}
314
+ else:
315
+ chunks_ordenados[index_original] = {
316
+ "status": "OK",
317
+ "paginas": chunks[index_original]["paginas"],
318
+ "transcricao": res.get("transcricao"),
319
+ "resumo": res.get("resumo")
320
+ }
321
+
322
+ concluidos += 1
323
+ if concluidos % 2 == 0 or concluidos == total_chunks:
324
+ msg_base = f"📄 `{nome}`: Processando partes... ({concluidos}/{total_chunks})"
325
+ history[-1][1] = msg_base
326
+ yield history
327
+
328
+ dados_finais = {
329
+ "arquivo": nome,
330
+ "data_processamento": str(datetime.now()),
331
+ "chunks_processados": chunks_ordenados
332
+ }
333
+
334
+ item["transcricao"] = dados_finais
335
+ item["status"] = "processado"
336
+
337
+ try:
338
+ with open(caminho_cache, "w", encoding="utf-8") as f_out:
339
+ json.dump(dados_finais, f_out, indent=2, ensure_ascii=False)
340
+ history.append([None, f"💾 `{nome}` processado e salvo no cache."])
341
+ except Exception as e:
342
+ history.append([None, f"⚠️ Erro ao salvar cache: {e}"])
343
+
344
+ yield history
345
+
346
+ else:
347
+ res = ler_arquivo_texto(item["arquivo"])
348
+ if res:
349
+ item["processado"] = res
350
+ dados_finais = {"conteudo": res["conteudo"], "data_processamento": str(datetime.now())}
351
+ item["transcricao"] = dados_finais
352
+ item["status"] = "processado"
353
+
354
+ with open(caminho_cache, "w", encoding="utf-8") as f_out:
355
+ json.dump(dados_finais, f_out, indent=2, ensure_ascii=False)
356
+
357
+ history.append([None, f"✅ `{nome}` (Texto) lido e salvo."])
358
+ else:
359
+ history.append([None, f"❌ Falha ao ler `{nome}`."])
360
+ yield history
361
+
362
+ history.append([None, "🏁 **Processamento de lote finalizado.** Os arquivos estão prontos para análise."])
363
+ yield history
364
+
365
+
366
+ def chat_orquestrador(message, history, config_json, pipeline_state):
367
+ """
368
+ Orquestra a conversa. Pode iniciar uma nova pipeline ou continuar uma que foi pausada.
369
+ """
370
+
371
+ # --- LÓGICA DE CONTINUAÇÃO ---
372
+ if pipeline_state.get("is_paused"):
373
+ history.append([message, None])
374
+
375
+ # Recupera o estado
376
+ timeline_execucao = pipeline_state["timeline"]
377
+ agentes_restantes = pipeline_state["remaining_agents"]
378
+
379
+ # Adiciona a resposta do usuário à trilha de auditoria
380
+ timeline_execucao.append({
381
+ "passo": len(timeline_execucao) + 1,
382
+ "tipo": "resposta_usuario",
383
+ "conteudo": message
384
+ })
385
+
386
+ # Reseta o estado para evitar loops
387
+ pipeline_state["is_paused"] = False
388
+
389
+ # Continua a execução do ponto onde parou
390
+ yield from executar_pipeline(history, timeline_execucao, agentes_restantes, pipeline_state)
391
+ return
392
+
393
+ # --- LÓGICA DE INÍCIO DE UMA NOVA CONVERSA ---
394
+ try:
395
+ prompt_contexto = gerenciador.gerar_prompt_com_transcricoes(message)
396
+ except Exception as e:
397
+ history.append([message, f"❌ Erro ao gerar contexto: {str(e)}"])
398
+ yield history, [], pipeline_state
399
+ return
400
+
401
+ try:
402
+ protocolo = json.loads(config_json)
403
+ except:
404
+ history.append([message, "❌ Erro no JSON de Configuração do Protocolo."])
405
+ yield history, [], pipeline_state
406
+ return
407
+
408
+ history.append([message, None])
409
+
410
+ # Inicia uma nova trilha de auditoria
411
+ timeline_execucao = [{"passo": 1, "tipo": "prompt_usuario", "conteudo": prompt_contexto}]
412
+ yield history, timeline_execucao, pipeline_state
413
+
414
+ # Inicia a execução com todos os agentes do protocolo
415
+ yield from executar_pipeline(history, timeline_execucao, protocolo, pipeline_state)
416
+
417
+
418
+ def executar_pipeline(history, timeline_execucao, agentes_a_executar, pipeline_state):
419
+ """
420
+ Função core que executa a lista de agentes em sequência.
421
+ Pode ser pausada se um agente pedir input do usuário.
422
+ """
423
+ passo_atual = len(timeline_execucao) + 1
424
+
425
+ for i, cfg in enumerate(agentes_a_executar):
426
+ nome_agente = cfg.get("nome", "Agente")
427
+ modelo_agente = model_pro if cfg.get("modelo") == "pro" else model_flash
428
+
429
+ msg_atual = history[-1][1] or ""
430
+ history[-1][1] = msg_atual + f"⏳ **{nome_agente}** está analisando...\n"
431
+ yield history, timeline_execucao, pipeline_state
432
+
433
+ prompt_agente = f"""
434
+ --- HISTÓRICO DA CONVERSA ATÉ AGORA ---
435
+ {json.dumps(timeline_execucao, ensure_ascii=False, indent=2)}
436
+ -----------------
437
+ Sua Identidade: {nome_agente}
438
+ Sua Missão Específica Agora: {cfg['missao']}
439
+ Responda de forma concisa e direta, focando apenas na sua missão.
440
+ """
441
+ try:
442
+ inicio = time.time()
443
+ resp = modelo_agente.generate_content(prompt_agente)
444
+ texto_resp = resp.text
445
+ duracao = time.time() - inicio
446
+
447
+ # --- LÓGICA DE PAUSA (STOP) ---
448
+ try:
449
+ # Tenta interpretar a resposta como JSON para verificar se é uma pergunta
450
+ resposta_json = json.loads(texto_resp)
451
+ if resposta_json.get("tipo") == "pergunta_usuario":
452
+ pergunta = resposta_json.get("pergunta", "Não foi possível extrair a pergunta.")
453
+
454
+ # Salva o estado atual da pipeline
455
+ pipeline_state["is_paused"] = True
456
+ pipeline_state["timeline"] = timeline_execucao
457
+ # Salva os agentes que AINDA NÃO rodaram
458
+ pipeline_state["remaining_agents"] = agentes_a_executar[i+1:]
459
+
460
+ # Adiciona a pergunta à auditoria e ao chat
461
+ timeline_execucao.append({"passo": passo_atual, "tipo": "pergunta_agente", "agente": nome_agente, "pergunta": pergunta})
462
+ msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
463
+ history[-1][1] = msg_atual + f"**{nome_agente}** precisa de mais informações:\n\n> *{pergunta}*\n\nAguardando sua resposta na caixa de texto abaixo..."
464
+
465
+ # Encerra a execução atual e aguarda o usuário
466
+ yield history, timeline_execucao, pipeline_state
467
+ return # Sai da função
468
+ except (json.JSONDecodeError, TypeError):
469
+ # Se não for um JSON de pergunta, é uma resposta normal
470
+ pass
471
+ # ---------------------------
472
+
473
+ timeline_execucao.append({"passo": passo_atual, "tipo": "resposta_agente", "agente": nome_agente, "resposta": texto_resp})
474
+
475
+ msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
476
+
477
+ # Não exibe o conteúdo da resposta do modelo no chat, apenas a confirmação
478
+ novo_trecho = f"✅ **[{nome_agente}]** concluiu sua análise em ({duracao:.1f}s).\n"
479
+ history[-1][1] = msg_atual + novo_trecho
480
+ yield history, timeline_execucao, pipeline_state
481
+
482
+ except Exception as e:
483
+ timeline_execucao.append({"passo": passo_atual, "tipo": "erro_agente", "agente": nome_agente, "erro": str(e)})
484
+ msg_atual = history[-1][1]
485
+ history[-1][1] = msg_atual.replace(f"⏳ **{nome_agente}** está analisando...\n", "") + f"\n❌ Erro em {nome_agente}: {str(e)}\n"
486
+ yield history, timeline_execucao, pipeline_state
487
+
488
+ passo_atual += 1
489
+
490
+ # ==================== 6. UI (Gradio) ====================
491
+
492
+ def ui_v29_stop_logic():
493
+ css = """
494
+ footer {display: none !important;}
495
+ .contain {border: none !important;}
496
+ """
497
+
498
+ config_inicial = carregar_protocolo()
499
+
500
+ with gr.Blocks(title="AI Forensics Auto", css=css, theme=gr.themes.Soft()) as app:
501
+
502
+ # Estado da configuração dos agentes
503
+ state_config = gr.State(config_inicial)
504
+ # NOVO: Estado para controlar a pausa/continuação da pipeline
505
+ pipeline_state = gr.State({"is_paused": False, "timeline": [], "remaining_agents": []})
506
+
507
+ with gr.Tabs():
508
+ with gr.Tab("💬 Investigação"):
509
+
510
+ chatbot = gr.Chatbot(
511
+ height=400,
512
+ show_label=False,
513
+ show_copy_button=True,
514
+ render_markdown=True,
515
+ label="Chat de Investigação"
516
+ )
517
+
518
+ with gr.Row():
519
+ txt_input = gr.Textbox(
520
+ scale=8,
521
+ show_label=False,
522
+ placeholder="Digite sua instrução ou responda à pergunta do agente...",
523
+ lines=1
524
+ )
525
+ btn_enviar = gr.Button("Enviar 📨", variant="primary", scale=1)
526
+
527
+ with gr.Accordion("📂 Adicionar Arquivos para Análise", open=False):
528
+ gr.Markdown("Selecione arquivos (PDF, TXT). A transcrição iniciará **automaticamente**.")
529
+ file_uploader = gr.File(
530
+ file_count="multiple",
531
+ file_types=[".pdf", ".txt", ".json", ".md"],
532
+ label="Arraste arquivos aqui ou clique para selecionar"
533
+ )
534
+
535
+ with gr.Tab("🕵️ Auditoria"):
536
+ gr.Markdown("### Trilha de Auditoria\nExibe o histórico completo de prompts e respostas de cada agente na última execução.")
537
+ json_audit = gr.JSON(label="Timeline da Execução da Última Mensagem")
538
+
539
+ with gr.Tab("⚙️ Contexto & Config"):
540
+ gr.Markdown("### Protocolo dos Agentes\nDefina a sequência e as missões dos agentes de IA. Para pausar e pedir input, use a missão de exemplo para o agente retornar um JSON específico.")
541
+ with gr.Row():
542
+ btn_save_cfg = gr.Button("💾 Salvar Alterações")
543
+ lbl_cfg_status = gr.Label(show_label=False)
544
+
545
+ code_config = gr.Code(value=config_inicial, language="json", label="protocolo.json")
546
+
547
+ btn_save_cfg.click(salvar_protocolo, inputs=[code_config], outputs=[lbl_cfg_status])
548
+ # Atualiza o state_config em memória após salvar
549
+ btn_save_cfg.click(lambda x: x, inputs=[code_config], outputs=[state_config])
550
+
551
+ # Ação de clique agora passa o pipeline_state para o orquestrador
552
+ btn_enviar.click(
553
+ chat_orquestrador,
554
+ inputs=[txt_input, chatbot, state_config, pipeline_state],
555
+ outputs=[chatbot, json_audit, pipeline_state] # Atualiza o estado da pipeline
556
+ ).then(
557
+ lambda: "", outputs=[txt_input]
558
+ )
559
+
560
+ file_uploader.upload(
561
+ automacao_upload_processamento,
562
+ inputs=[file_uploader, chatbot, state_config],
563
+ outputs=[chatbot]
564
+ )
565
+
566
+ return app
567
+
568
+ if __name__ == "__main__":
569
+ ui_v29_stop_logic().launch()
epct0.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ | Id | Resumo | Porquês | Tags |
2
+ | :-- | :-- | :-- | :-- |
3
+ | I | Distinguir o que depende e não depende de nós; focar nos encargos próprios | Evitar sofrimento desnecessário; alcançar liberdade interior; não culpar outros; manter tranquilidade mesmo com adversidades | \#controle-interno \#liberdade \#dicotomia-controle \#autodominio \#tranquilidade \#poder-pessoal \#responsabilidade \#escolhas \#foco \#sabedoria-pratica \#limites \#aceitacao |
4
+ | II | Redirecionar desejos e repulsas apenas ao que controlamos | Prevenir frustração; garantir satisfação; não depender de fatores externos; desenvolver autocontrole gradual | \#desejo \#repulsa \#controle-emocional \#expectativas \#moderacao \#disciplina \#autoconhecimento \#prudencia \#paciencia \#desenvolvimento-gradual |
5
+ | III | Lembrar a natureza impermanente das coisas que amamos | Reduzir angústia com perdas; aceitar finitude humana; não se apegar excessivamente; manter equilíbrio emocional | \#impermanencia \#desapego \#perda \#mortalidade \#aceitacao \#preparacao-mental \#realismo \#equilibrio \#amor-consciente \#fragilidade |
6
+ | IV | Antecipar obstáculos antes de agir; manter propósito alinhado à natureza | Evitar irritação e frustração; preparar-se mentalmente; manter serenidade diante de adversidades; agir conscientemente | \#preparacao \#antecipacao \#objetivos \#resiliencia \#expectativas-realistas \#serenidade \#proposito \#consciencia \#planejamento \#adaptacao |
7
+ | Va | Reconhecer que opiniões, não eventos, nos perturbam | Assumir responsabilidade pelas emoções; modificar interpretações; desenvolver autonomia emocional; eliminar vitimização | \#opiniao \#interpretacao \#perspectiva \#responsabilidade-emocional \#autogoverno \#percepção \#pensamento \#liberdade-interior \#consciencia \#transformacao |
8
+ | Vb | Progressão na educação filosófica: não culpar outros nem a si mesmo | Amadurecer eticamente; desenvolver autocrítica saudável; superar acusações; alcançar equanimidade; evoluir espiritualmente | \#educacao \#progresso \#autocritic \#maturidade \#evolucao \#culpa \#responsabilidade \#desenvolvimento-pessoal \#equilibrio \#sabedoria |
9
+ | VI | Orgulhar-se apenas do que é verdadeiramente nosso | Cultivar autenticidade; evitar vaidade vazia; reconhecer valores reais; desenvolver caráter próprio; usar bem as representações | \#autenticidade \#orgulho \#valores \#carater \#representacoes \#ego \#discernimento \#merito-proprio \#autoestima \#realidade |
10
+ | VII | Manter foco no essencial; desprender-se quando chamado | Priorizar o fundamental; não se distrair com secundário; estar pronto para mudanças; viver conscientemente; preparar-se para morte | \#prioridades \#essencial \#foco \#desapego \#morte \#prontidao \#vida-consciente \#proposito \#finalidade \#temporalidade |
11
+ | VIII | Desejar que aconteça o que acontece, não o contrário | Alcançar serenidade; aceitar realidade; eliminar resistência inútil; fluir com eventos; viver em harmonia | \#aceitacao \#serenidade \#harmonia \#realidade \#resistencia \#fluxo \#paz-interior \#contentamento \#sabedoria \#alinhamento |
12
+ | IX | Distinguir entraves físicos de escolhas internas | Preservar liberdade interior; reconhecer poder pessoal; não confundir limitações externas com internas; manter autonomia | \#liberdade-interior \#escolha \#limitacoes \#corpo \#mente \#autonomia \#poder-interno \#discernimento \#separacao \#resiliencia |
13
+ | X | Desenvolver capacidades específicas para cada desafio | Fortalecer virtudes; treinar autodomínio; cultivar perseverança e paciência; não ser dominado por impressões; crescer com adversidades | \#virtudes \#autodominio \#perseveranca \#paciencia \#desafios \#crescimento \#capacidades \#treinamento \#habito \#fortalecimento |
14
+ | XI | Ver perdas como restituições temporárias | Suavizar dor da perda; reconhecer posse temporária; cultivar desapego; aceitar transitoriedade; cuidar sem possuir | \#perda \#luto \#desapego \#temporalidade \#restituicao \#aceitacao \#morte \#posse \#hospedagem \#impermanencia |
15
+ | XII | Priorizar tranquilidade sobre bens materiais | Valorizar paz interior acima de riqueza; começar com pequenas coisas; não depender de servos; pagar preço por serenidade | \#tranquilidade \#paz-interior \#prioridades \#materialismo \#desapego-material \#serenidade \#valores \#progresso \#simplicidade \#custo |
16
+ | XIII | Aceitar parecer tolo; não buscar aprovação externa | Focar no caráter interno; evitar vaidade; não dividir atenção; progredir genuinamente; escolher entre interno e externo | \#aparencia \#reputacao \#opiniao-alheia \#humildade \#carater \#progresso \#foco-interno \#autenticidade \#prioridades \#escolhas |
17
+ | XIVa | Não querer controlar o incontrolável (vida de outros, perfeição alheia) | Aceitar natureza humana; reconhecer limites do controle; focar no possível; evitar expectativas irreais; exercitar o viável | \#controle \#limites \#expectativas-realistas \#natureza-humana \#aceitacao \#possibilidades \#foco \#exercicio \#realismo \#vicio |
18
+ | XIVb | Ser livre não querendo nem evitando o que depende de outros | Conquistar liberdade verdadeira; evitar escravidão emocional; desapegar de dependências externas; manter autonomia total | \#liberdade \#escravidao \#dependencia \#autonomia \#independencia \#poder-externo \#desapego \#autogoverno \#senhorio \#escolha |
19
+ | XV | Comportar-se em banquete da vida com disciplina e desapego | Receber com gratidão sem apego; não perseguir; esperar pacientemente; desdenhar quando necessário; tornar-se divino | \#disciplina \#paciencia \#desapego \#gratidao \#espera \#contentamento \#divindade \#moderacao \#banquete-vida \#sabedoria |
20
+ | XVI | Solidarizar-se sem ser arrebatado por opiniões alheias | Oferecer compaixão genuína; não internalizar sofrimento alheio; manter clareza mental; consolar sem perturbar-se; distinguir evento de opinião | \#compaixao \#empatia \#solidariedade \#fronteiras-emocionais \#equilibrio \#consolacao \#clareza \#opiniao \#sofrimento \#discernimento |
21
+ | XVII | Aceitar papel atribuído e desempenhá-lo bem | Cumprir função com excelência; não escolher papel; interpretar bem destino; aceitar circunstâncias; focar na execução | \#papel-vida \#destino \#aceitacao \#excelencia \#desempenho \#teatro-vida \#circunstancias \#funcao \#talento \#cumprimento |
22
+ | XVIII | Não ser arrebatado por presságios e superstições | Distinguir o relevante; manter controle interpretativo; beneficiar-se de qualquer evento; preservar poder pessoal; descartar crenças limitantes | \#supersticao \#presagios \#interpretacao \#controle-mental \#beneficio \#poder-pessoal \#crencas \#representacoes \#discernimento \#autonomia |
23
+ | XIXa | Engajar-se apenas em lutas onde vitória depende de você | Ser invencível estrategicamente; escolher batalhas certas; preservar energia; focar no controlável; evitar derrotas desnecessárias | \#invencibilidade \#estrategia \#escolha-batalhas \#sabedoria \#controle \#vitoria \#foco \#energia \#discernimento \#poder |
24
+ | XIXb | Não invejar quem tem honras externas; buscar liberdade interior | Evitar inveja e ciúme; valorizar bem verdadeiro; desprezar o que não controla; buscar homem livre, não cargos | \#inveja \#ciume \#honras \#liberdade \#valores \#bem-verdadeiro \#poder \#cargo \#felicidade \#desprezar-externo |
25
+ | XX | Reconhecer que ofensa vem de juízo próprio, não do ofensor | Ganhar tempo antes de reagir; manter autocontrole; não ser provocado; responsabilizar-se por reações; dominar-se | \#ofensa \#provocacao \#autocontrole \#reacao \#juizo \#tempo \#pausa \#dominio-proprio \#responsabilidade \#controle-emocional |
26
+ | XXI | Meditar diariamente sobre morte e coisas terríveis | Manter perspectiva; não valorizar coisas abjetas; não aspirar excessivamente; preparar-se mentalmente; cultivar equilíbrio | \#morte \#memento-mori \#meditacao \#perspectiva \#exilio \#medo \#preparacao \#equilibrio \#moderacao \#reflexao |
27
+ | XXII | Preparar-se para críticas ao aspirar filosofia | Perseverar diante de zombaria; manter firmeza; agarrar-se ao propósito; conquistar admiração futura; não ser vencido por críticos | \#filosofia \#critica \#zombaria \#perseveranca \#firmeza \#proposito \#ridiculo \#admiracao \#vencer \#designacao-divina |
28
+ | XXIII | Não buscar agradar externamente; ser filósofo internamente | Manter rumo; evitar perder-se; bastar-se a si mesmo; não exibir-se; focar em ser, não parecer | \#autenticidade \#exibicionismo \#agradar \#aparencia \#ser-parecer \#rumo \#foco-interno \#vaidade \#consistencia \#integridade |
29
+ | XXIV | Não temer desonra; focar no que está sob controle | Reconhecer que desonra não vem de outros; valorizar o que controla; beneficiar amigos pelo caráter; servir pátria sendo digno e leal | \#honra \#desonra \#reputacao \#amigos \#caracter \#patria \#valores \#utilidade \#dignidade \#lealdade \#servico |
30
+ | XXV | Não invejar honras alheias; entender preço das coisas | Reconhecer que tudo tem preço; não pagar para obter certos "bens"; preservar liberdade não adulando; manter dignidade; compreender trocas | \#honras \#inveja \#preco \#troca \#liberdade \#adulacao \#dignidade \#elogios \#obsequios \#sabedoria-pratica \#economia-moral |
31
+ | XXVI | Aplicar a si mesmo a compaixão que tem por outros | Manter consistência emocional; aceitar perdas próprias como de outros; transferir perspectiva; lembrar condição humana universal | \#compaixao \#consistencia \#perda \#perspectiva \#humanidade \#morte \#aceitacao \#igualdade \#reflexao \#universalidade |
32
+ | XXVII | Reconhecer que mal não existe no cosmos por natureza | Compreender ordem universal; não existir mal absoluto; aceitar propósito maior; ver harmonia cósmica; confiar em ordem natural | \#mal \#cosmos \#ordem \#natureza \#proposito \#harmonia \#aceitacao \#filosofia \#visao-cosmica \#confianca |
33
+ | XXVIII | Não entregar pensamento a quem insulta | Proteger mente; não permitir perturbação externa; envergonhar-se de vulnerabilidade mental; manter soberania interior; controlar reações mentais | \#pensamento \#insulto \#protecao-mental \#soberania \#mente \#controle \#vergonha \#perturbacao \#reacao \#dominio |
34
+ | XXIX | Examinar consequências antes de agir; comprometer-se plenamente | Evitar arrependimento; ponderar antes; não desistir vergonhosamente; escolher com maturidade; dedicar-se inteiramente ou não começar | \#exame \#consequencias \#compromisso \#planejamento \#filosofia \#atleta \#dedicacao \#disciplina \#arrependimento \#maturidade \#ponderacao |
35
+ | XXX | Cumprir deveres conforme relações sociais | Respeitar papéis sociais; manter postura adequada; focar no próprio dever; não julgar outros; considerar relações; agir convenientemente | \#deveres \#relacoes \#familia \#papeis-sociais \#responsabilidade \#irmao \#pai \#vizinho \#cidadao \#conveniencia \#postura |
36
+ | XXXI | Cultivar piedade correta aos deuses; aceitar eventos como inteligência divina | Ter juízos corretos; obedecer divindade; aceitar acontecimentos; não censurar deuses; colocar bem/mal no controlável; fazer ritos adequadamente | \#piedade \#deuses \#religiosidade \#aceitacao \#providencia \#juizo \#obediencia \#ritos \#interesse \#bem-mal \#inteligencia-divina |
37
+ | XXXII | Consultar oráculos reconhecendo indiferença de eventos futuros | Ir confiante sem desejo/repulsa; fazer bom uso de qualquer resultado; consultar apenas quando razão não basta; seguir razão sobre presságios | \#divinacao \#oraculo \#indiferenca \#futuro \#confianca \#razao \#consequencias \#socrates \#apolo \#amizade \#patria |
38
+ | XXXIII | Fixar caráter e padrão consistente; silenciar; viver com frugalidade | Manter conduta consistente; evitar conversas ordinárias; não ostentar; preservar pureza; não abraçar vulgaridade; agir, não falar | \#carater \#silencio \#consistencia \#frugalidade \#conduta \#simplicidade \#pureza \#ostentacao \#acao \#palavras \#padrão |
39
+ | XXXIV | Não ser arrebatado por prazeres; ponderar antes de agir | Ganhar tempo para decisão; comparar momentos; prever arrependimento; escolher abstenção consciente; preparar-se para tentação | \#prazer \#tentacao \#ponderacao \#tempo \#arrependimento \#abstencao \#comparacao \#decisao \#autocontrole \#reflexao |
40
+ | XXXV | Fazer o correto sem temer juízo alheio | Agir conforme discernimento; não evitar por opinião alheia; evitar ação errada, não olhares; ter coragem de fazer certo | \#coragem \#acao-correta \#opiniao-alheia \#juizo \#medo \#discernimento \#fazer \#integridade \#repreensao \#firmeza |
41
+ | XXXVII | Não aceitar papel além da capacidade | Reconhecer limites; manter compostura; desempenhar bem o possível; não falhar por ambição excessiva; conhecer-se | \#capacidade \#limites \#autoconhecimento \#compostura \#papel \#ambicao \#desempenho \#possivel \#prudencia \#realismo |
42
+ | XXXVIII | Proteger faculdade diretriz como pés ao caminhar | Preservar razão; agir com segurança; proteger mente de danos; manter atenção; empreender com cautela; zelar pelo essencial | \#faculdade-diretriz \#razao \#protecao \#cuidado \#atencao \#seguranca \#mente \#cautela \#zelar \#preservacao |
43
+ | XXXIX | Manter posses proporcionais às necessidades do corpo | Evitar excesso; respeitar medida; não cair em luxo; manter simplicidade; reconhecer que sem limite há abismo | \#posses \#medida \#moderacao \#simplicidade \#corpo \#luxo \#necessidade \#excesso \#limite \#abismo \#proporcao |
44
+ | XL | Educar mulheres sobre dignidade além da aparência | Valorizar disciplina e dignidade; não reduzir a sexualidade; honrar caráter; educar para valores; perceber verdadeira honra | \#mulheres \#dignidade \#educacao \#valores \#aparencia \#carater \#disciplina \#honra \#reducao \#sociedade |
45
+ | XLI | Priorizar atenção ao pensamento, não ao corpo | Focar em desenvolvimento mental; tratar corpo como secundário; não exagerar cuidados físicos; cultivar mente; evitar incapacidade | \#corpo \#mente \#pensamento \#prioridades \#equilibrio \#desenvolvimento \#foco-mental \#secundario \#capacidade \#cultivo |
46
+ | XLII | Ser gentil com quem insulta, reconhecendo perspectiva dele | Compreender motivação alheia; não levar para si; reconhecer que erro é dele; ter compaixão; responder "assim lhe parece" | \#insulto \#gentileza \#perspectiva \#compaixao \#erro \#motivacao \#tolerancia \#compreensao \#reacao \#sabedoria-relacional |
47
+ | XLIII | Ver coisas pelo lado suportável, não pelo insuportável | Escolher perspectiva útil; ver irmão como irmão, não como injusto; tornar suportável o que acontece; mudar ângulo | \#perspectiva \#suportavel \#angulo \#irmao \#injustica \#escolha \#interpretacao \#relacionamento \#tolerancia \#flexibilidade |
48
+ | XLIV | Distinguir ser de ter; não confundir identidade com posses | Reconhecer que não é posses nem eloquência; valorizar essência; não superioridade por externos; argumentar consistentemente; separar identidade | \#identidade \#posses \#essencia \#eloquencia \#superioridade \#argumentacao \#consistencia \#ser-ter \#confusao \#clareza |
49
+ | XLV | Não julgar antes de discernir opinião interna do outro | Suspender julgamento; não assumir má ação; distinguir aparência de intenção; evitar assentimento precipitado; manter representações corretas | \#julgamento \#opiniao \#discernimento \#suspensao \#intencao \#aparencia \#precipitacao \#representacoes \#tolerancia \#paciencia |
50
+ | XLVI | Não declarar-se filósofo; agir conforme princípios silenciosamente | Viver filosofia, não falar dela; digerir princípios internamente; mostrar através de ações; não demonstrar ostensivamente; aceitar ser desprezado | \#filosofia \#acao \#silencio \#principios \#ostentacao \#digestao \#demonstracao \#viver \#socrates \#humildade \#pratica |
51
+ | XLVII | Não ostentar frugalidade ou disciplinas ascéticas | Praticar para si, não para outros; não gabar-se; manter exercício privado; não anunciar abstinências; agir sem exibição | \#frugalidade \#ostentacao \#exibicao \#ascetismo \#privacidade \#pratica \#disciplina \#silencio \#humildade \#simplicidade |
52
+ | XLVIIIa | Esperar benefício e dano de si mesmo, não de externos | Cultivar postura filosófica; depender de si; não de externos; assumir responsabilidade total; diferenciar-se do homem comum | \#filosofo \#responsabilidade \#interno-externo \#beneficio \#dano \#autonomia \#postura \#dependencia \#diferenciacao \#maturidade |
53
+ | XLVIIIb | Não recriminar, elogiar ou acusar; recriminar-se quando impedido | Progredir genuinamente; aceitar responsabilidade; não fazer alarde; receber elogios sem vaidade; ser modesto; não considerar-se alguém | \#progresso \#recriminacao \#elogio \#acusacao \#modestia \#responsabilidade \#autocritic \#humildade \#vaidade \#sinais-progresso |
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ poppler-utils
prompts_pipeline.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- START OF FILE prompts_pipeline.json ---
2
+ {
3
+ "P1_TRIAGEM": "METACOGNIÇÃO - TRIAGEM INICIAL.\nPERGUNTA: {pergunta}\n---\nAnalise a PERGUNTA do usuário e o histórico de conversa. Sua função é CLASSIFICAR e TOMAR UMA DECISÃO.\n\n### CRITÉRIOS DE CLASSIFICAÇÃO\n1. **tipo**: 'factual' (Respostas rápidas baseadas em fatos ou informações comuns), 'objetiva_tecnica' (Respostas que exigem um cálculo, definição precisa ou contexto especializado), ou 'subjetiva_complexa' (Respostas que envolvem ética, opinião, cenário hipotético ou muita incerteza).\n2. **confianca**: 'extrema' (Resposta óbvia/conhecida), 'alta', 'media', 'baixa', ou 'insuficiente' (Exige raciocínio profundo ou mais contexto).\n\n### DECISÃO\n- 'responder_direto': Use para 'factual' com confiança 'extrema' ou 'alta'. Seguir o caminho mais rápido.\n- 'analisar_profundamente': Use para todas as outras combinações. Seguir a pipeline de raciocínio completo.\n\nRETORNE JSON: {{\"tipo\": \"...\", \"confianca\": \"...\", \"decisao\": \"responder_direto|analisar_profundamente\"}}",
4
+ "GERAR_RESPOSTA_DIRETA": "TAREFA: Resposta Direta (Bypass).\nPERGUNTA: \"{pergunta}\"\n---\nConsiderando o histórico de conversa (memória), forneça uma resposta completa, precisa e concisa para a última PERGUNTA do usuário. Utilize a ferramenta de Pesquisa Google se o conhecimento precisar de atualização ou checagem factual rápida.\n\nRETORNE JSON: {{\"resposta_direta\": \"Sua resposta concisa e precisa aqui.\"}}",
5
+ "JUSTIFICAR_BYPASS": "METACOGNIÇÃO - JUSTIFICATIVA DE BYPASS.\nANÁLISE (P1): {p1}\n---\nJustifique, para a governança interna (NÃO é a resposta final), por que a pipeline de raciocínio profundo foi pulada com base na Triagem P1 (JSON fornecido).\n\nRETORNE JSON: {{\"justificativa_bypass\": {{\"motivo\": \"A pergunta foi classificada como ... e a confiança era ....\", \"acao_tomada\": \"O modelo gerou a resposta e a enviará para verificação tripla de qualidade.\", \"proximo_passo\": \"Não há necessidade de mais análise profunda neste turno.\", \"p1_decisao\": {p1}}}}}",
6
+ "P2_CENARIOS": "METACOGNIÇÃO - GERAÇÃO DE CENÁRIOS.\nANÁLISE (P1): {p1}, PERGUNTA: {pergunta}\n---\nA tarefa é a 'geração de antagonismo controlado' para fortalecer a análise.\n\n1. Crie pelo menos 2 CENÁRIOS/ARGUMENTOS PROVÁVEIS que VALIDAM (dão suporte) ao princípio ou implicações diretas da PERGUNTA. Esses cenários devem ser fortes e contextuais.\n2. Crie pelo menos 2 CENÁRIOS/ARGUMENTOS IMPROVÁVEIS que REFUTAM ou introduzem forte incerteza ao princípio da PERGUNTA. Esses cenários devem forçar a barra do raciocínio. \n\nSeja estritamente um gerador de CENÁRIOS. Não tome decisões ou conclusões aqui.\n\nRETORNE JSON: {{\"cenarios\": {{\"provaveis\": [\"Fato/Cenário que Valida 1\", \"Fato/Cenário que Valida 2\"], \"improvaveis\": [\"Fato/Cenário que Refuta 1\", \"Fato/Cenário que Refuta 2\"]}}, \"decisao\": \"prosseguir para as próximas validações.\"}}",
7
+ "P4_CRUZAR_VALIDACOES": "METACOGNIÇÃO - ABSTRAÇÃO E FUNDAMENTO.\nANÁLISES ANTERIORES: P1(Triagem): {p1}, P2(Cenários): {p2}, P3(Isolamento Simulado): {p3}\n---\nCruze as análises P1, P2 e P3. Seu objetivo é:\n\n1. **Princípio Central:** Identificar o fundamento, conceito, ou princípio maior que está subjacente à PERGUNTA do usuário.\n2. **Verificação de Inconsistência:** Verificar se os cenários de P2 e a triagem de P1 apontam para inconsistências sérias no raciocínio atual.\n\nRETORNE JSON: {{\"principio_central\": \"O Princípio Central da discussão é ...\", \"analise_de_inconsistencia\": \"Nenhuma inconsistência séria ou Há uma clara falha na validade dos cenários em relação a ...\", \"status_analise\": \"FUNDAMENTO_OK\"}}",
8
+ "P5_LACUNAS_FINAIS": "METACOGNIÇÃO - ANÁLISE DE INCERTEZA (Ações Lógicas de Raciocínio).\nPRINCÍPIO CENTRAL (P4): {p4}, PERGUNTA: {pergunta}\n---\nCompare a PERGUNTA do usuário com o PRINCÍPIO CENTRAL identificado. O que falta? Identifique os pontos críticos que ainda são desconhecidos ou ambíguos.\n\n### DECISÃO FINAL DA ETAPA\n- **'continuar'**: Se você puder resolver a incerteza com seu conhecimento e a ferramenta de Pesquisa Google. Isso significa que o processo pode avançar para a ponderação.\n- **'questionar'**: Se a lacuna for grande demais ou depender do dado ou preferência exclusiva do usuário. Nesse caso, pare o processamento.\n\nFormule uma **pergunta_chave_para_usuario** clara e direcionada SE a decisão for 'questionar'.\n\nRETORNE JSON: {{\"pontos_de_incerteza\": [\"Detalhe de Contexto/Escopo 1 Faltante\", \"Dado de Preferência Faltante\"], \"decisao_interna\": \"questionar|continuar\", \"pergunta_chave_para_usuario\": \"Sua pergunta para o usuário (ou null se for 'continuar').\"}}",
9
+ "P7_SINTETIZAR": "SINTETIZADOR. (Etapa P7, recebe o JSON P6, simulado, ou real).\nDADOS CRÍTICOS (P6: Ponderação Final): {p6}\n---\nConverta todo o raciocínio profundo da pipeline em uma **RESPOSTA FINAL** coesa, completa e fácil de entender para o usuário. Garanta que todas as premissas identificadas nos passos anteriores foram consideradas no P6 e estão refletidas aqui no tom da resposta. Sua resposta final será revisada em P8.\n\nRETORNE JSON: {{\"resposta\": \"Sua resposta completa aqui. Deve ser direta e refletir a profundidade da análise executada.\"}}",
10
+ "P8_VERIFICAR": "VERIFICADOR FINAL (SUPERVISOR DE QUALIDADE).\nRESPOSTA A VERIFICAR: {resposta_a_verificar}\n---\nAtue como um Supervisor Cético de Nível Extremo.\n\n### CHECKLIST DE VERIFICAÇÃO\n1. **Factual:** Há algum dado verificável incorreto (Use a Pesquisa Google)?\n2. **Lógica:** A estrutura do argumento está sã, coerente com a pergunta e com o raciocínio abstrato de P4?\n3. **Clareza/Tom:** A resposta é fácil de entender, completa e mantive o tom profissional?\n\n- Se todos os pontos estiverem corretos, **todas_aprovadas**: true, **resposta_corrigida**: null.\n- Se um ou mais pontos exigirem correção, **todas_aprovadas**: false, forneça o texto FINAL E CORRIGIDO em **resposta_corrigida**.\n\nRETORNE JSON: {{\"todas_aprovadas\": true|false, \"erros_encontrados\": \"Nenhum ou Descreva brevemente o erro e a correção feita.\", \"resposta_corrigida\": null}} (Se true), {{\"todas_aprovadas\": false, \"erros_encontrados\": \"...\", \"resposta_corrigida\": \"[Novo Texto Final e Corrigido].\"}} (Se false)"
11
+ }
12
+ --- END OF FILE prompts_pipeline.json ---
protocolo.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "fase": 0,
4
+ "nome": "CONTEXTO_INICIAL_VALOR_VIDA",
5
+ "modelo": "flash",
6
+ "tipo_saida": "json",
7
+ "missao": "INICIAR PELO RACIOCÍNIO DO USUÁRIO + CAPTURA BRUTA.\n\nObjetivos:\n- Ajudar a pessoa a organizar, com calma e clareza, o cenário de lesão, violência, acidente ou morte.\n- Capturar fatos básicos, histórico mínimo de vida, contexto do dano e nexo causal percebido.\n- Forçar o usuário a pensar se já tem alguma ideia de valor justo antes de acionar os outros agentes.\n- Detectar dúvida relevante e, se necessário, disparar STOP em vez de JSON.\n\nRegras STOP (fora de escopo):\n- Se a pergunta do usuário não tiver relação com: (a) casos de dano/indenização, (b) as missões/agentes, ou (c) o histórico mínimo do caso atual,\n ENTÃO o modelo NÃO responde ao conteúdo da pergunta.\n- Em vez disso, responde apenas:\n\n STOP: infelizmente não posso responder a essa pergunta,\n pois meu objetivo aqui é apenas ajudar a analisar fatos,\n nexo causal e valor de indenizações em casos de lesão,\n violência, acidente ou morte, seguindo as missões do protocolo.\n\n- Nessa situação, NUNCA retornar JSON; apenas o texto acima (ou uma versão equivalente, breve).\n\nRegras STOP (dúvida crítica):\n- Se a pergunta estiver dentro do contexto do caso, mas houver dúvida alta sobre ponto crítico (nexo causal, gravidade da lesão/morte, valor-base, identidade das partes),\n ENTÃO o agente pode responder diretamente com STOP + até 3 perguntas objetivas para esclarecimento, SEM JSON.\n- Exemplo de formato:\n\n STOP: preciso que você esclareça até 3 pontos antes de continuar:\n\n 1) ...\n 2) ...\n 3) ... (opcional)\n\nCampos de saída (quando NÃO for caso de STOP):\n1. PERGUNTA_NORMALIZADA\n2. CONTEXTO_IDENTIFICADO {\n tipo_caso: \"LESAO_LEVE|GRAVE|GRAVISSIMA|MORTE|OUTRO\",\n foro: \"civel|criminal|trabalhista|outro\"\n }\n3. RESUMO_FATOS_INICIAIS\n4. EXPECTATIVA_VALOR_INICIAL {\n sabe_valor: true/false,\n faixa_sugerida: { min: null, max: null },\n justificativa_intuitiva: \"...\"\n }\n5. NIVEL_CERTEZA_USUARIO (0-10)\n6. DADOS_MINIMOS_NEXO {\n houve_evento: true/false,\n houve_dano: true/false,\n sente_relacao_causa_efeito: true/false\n }\n7. AFETO_RAW { amor: 0.0, medo: 0.0, paixa: 0.0 }\n8. FELICIDADE_LATENTE\n9. SINAIS_DUVIDA { contradicoes: [], lacunas_obvias: [] }\n10. DUVIDA_DETECTADA { true|false }\n11. TESTE_REFLEXAO {\n perguntas: [\n \"O que exatamente aconteceu, em ordem temporal?\",\n \"Se você fosse juiz, que valor consideraria minimamente justo e por quê?\",\n \"Existe algum caso parecido que você conhece? O que aconteceu lá?\"\n ],\n instrucoes_ao_usuario: \"Responda com calma a cada pergunta. O sistema só seguirá quando houver clareza mínima sobre fatos e sua própria expectativa.\"\n }\n12. PROXIMA_ACAO { \"PERGUNTAR_USUARIO\" | \"AVANCAR_FASE_1\" }\n\nRegras gerais:\n- Se DUVIDA_DETECTADA == true em ponto crítico → considerar usar STOP em vez de JSON, conforme regras acima.\n- Se for possível responder com JSON estruturado sem comprometer segurança/claridade, retornar o JSON completo."
8
+ },
9
+ {
10
+ "fase": 1,
11
+ "nome": "HISTORICO_VIDA_E_REDE_AFETIVA",
12
+ "modelo": "flash",
13
+ "tipo_saida": "json",
14
+ "missao": "MAPEAR VIDA, PAPÉIS E REDE AFETIVA PARA VALOR_DA_VIDA/DIGNIDADE.\n\nObjetivos:\n- Clarificar quem é/era a vítima na sua biografia: trabalho, família, sonhos, vulnerabilidades.\n- Entender rede de dependência e afeto (cônjuge, filhos, pais, etc.).\n- Preparar terreno para valorar a perda/lesão na dimensão existencial.\n\nJSON:\n1. PERFIL_VITIMA { idade, genero, profissao, renda_media, estado_civil }\n2. PAPEL_SOCIAL_CENTRAL { provedor_familiar, cuidador, estudante, aposentado, outro }\n3. DEPENDENTES_DIRETOS { quantidade, tipos: [filhos, pais, conjuges, outros] }\n4. PROJETOS_DE_VIDA { curto_prazo, longo_prazo }\n5. VULNERABILIDADE_PREVIA { pobreza, doenca_preexistente, deficiencia, nenhum }\n6. REDE_AFETIVA { lista_pessoas_chave, grau_dependencia_emocional: 0-10 }\n7. IMPACTO_POTENCIAL_PERDA { descricao_curta, intensidade: 0-10 }\n8. COERENCIA_COM_CONTEXTO_INICIAL { coerente|parcial|incoerente }\n9. LACUNAS_HISTORICO_VIDA [lista]\n10. SUGESTAO_PERGUNTAS_ADICIONAIS_USUARIO [lista]"
15
+ },
16
+ {
17
+ "fase": 2,
18
+ "nome": "FATO_DANO_E_NEXO_CAUSAL",
19
+ "modelo": "flash",
20
+ "tipo_saida": "json",
21
+ "missao": "CLAREAR FATO, DANO E NEXO CAUSAL DE FORMA ESTRUTURADA.\n\nObjetivos:\n- Organizar, de forma lógica, o que aconteceu, que dano houve e qual nexo alegado.\n- Separar fato objetivo de percepção subjetiva.\n\nJSON:\n1. FATO_GERADOR_LINEAR { linha_do_tempo: [eventos_em_ordem] }\n2. TIPO_EVENTO { acidente_transito, erro_medico, violencia_domestica, crime_intencional, outro }\n3. DANO_CORPORAL_CLASSIFICACAO { LESAO_LEVE|GRAVE|GRAVISSIMA|MORTE|SEM_INFORMACAO }\n4. DANO_CONCRETO_DESCRITO { lesoes, sequelas, morte, dano_estetico }\n5. PROVAS_DISPONIVEIS { laudos_medicos, boletim_ocorrencia, fotos, videos, testemunhas }\n6. NARRATIVA_NEXO_CAUSAL { em_ate_500_caracteres }\n7. GRAU_CONFIANCA_NEXO_DECLARADO (0-10)\n8. AMBIGUIDADES_IDENTIFICADAS { sim|nao, detalhes }\n9. ITENS_QUE_EXIGEM_PERICIA { lista }\n10. COERENCIA_FATO_NEXO { alta|media|baixa }"
22
+ },
23
+ {
24
+ "fase": 3,
25
+ "nome": "CONTEXTO_E_CONSEQUENCIAS_DO_DANO",
26
+ "modelo": "flash",
27
+ "tipo_saida": "json",
28
+ "missao": "MAPEAR CONTEXTO E CONSEQUÊNCIAS MATERIAIS, MORAIS E EXISTENCIAIS.\n\nJSON:\n1. CONSEQUENCIAS_SAUDE { dor_cronica, limitacao_fisica, dependencia_terceiros, tratamento_longo_prazo }\n2. CONSEQUENCIAS_TRABALHO { dias_afastamento, perda_emprego, rebaixamento_funcao, incapacidade_parcial, incapacidade_total }\n3. CONSEQUENCIAS_FAMILIA { rompimento_relacoes, sobrecarga_cuidador, impacto_filhos }\n4. CONSEQUENCIAS_PSICOLOGICAS { ansiedade, depressao, TEPT, medo_constante }\n5. PERDA_QUALIDADE_VIDA { nota: 0-10, justificativa }\n6. DESCRICAO_DANO_MORAL_SUBJETIVO { humilhacao, medo_de_morrer, perda_dignidade, luto }\n7. SINTONIA_COM_HISTORICO_VIDA { sim|parcial|nao }\n8. PONTOS_FORTES_DANO_MORAL { lista }\n9. PONTOS_FRACOS_DANO_MORAL { lista }"
29
+ },
30
+ {
31
+ "fase": 4,
32
+ "nome": "GRAVIDADE_DANO_E_FAIXA_STJ",
33
+ "modelo": "flash",
34
+ "tipo_saida": "json",
35
+ "missao": "CONECTAR GRAVIDADE DA LESÃO ÀS FAIXAS JURISPRUDENCIAIS DE DANO MORAL.\n\nJSON:\n1. CLASSIFICACAO_JURIDICA_LESAO { LEVE|GRAVE|GRAVISSIMA|MORTE }\n2. CRITERIOS_USADOS { dias_incapacidade, risco_vida, sequela_permanente, incapacidade_trabalho, deformidade }\n3. FAIXA_REFERENCIA_STJ_SM { min_sm, max_sm, mediana_sm }\n4. FAIXA_REFERENCIA_STJ_RS { min_rs, max_rs, mediana_rs }\n5. AJUSTES_POR_CASO_CONCRETO { fatores_agravantes, fatores_atenuantes }\n6. FAIXA_AJUSTADA_RS { min_rs, max_rs }\n7. NOTA_SOBRE_TETOS_E_PISOS { comentario_curto }"
36
+ },
37
+ {
38
+ "fase": 5,
39
+ "nome": "CENARIOS_VALOR_VIDA",
40
+ "modelo": "flash",
41
+ "tipo_saida": "json",
42
+ "missao": "GERAR CENÁRIOS DE VALOR (PRINCIPAL/ALTERNATIVO/IMPROVÁVEL) PARA O DANO À VIDA/DIGNIDADE.\n\nJSON (lista de 3 cenários):\n[\n {\n \"ID\": \"PRINCIPAL\",\n \"DESCRICAO\": \"cenário mais provável com base nas provas e na jurisprudência\",\n \"PRIOR\": 0.6,\n \"VALOR_SUGERIDO_RS\": 0,\n \"SUPOSICOES_CHAVE\": [\"nexo_causal_reconhecido\"],\n \"COMPATIBILIDADE_FATOS\": \"alta\",\n \"DANO_MORAL_RS\": { \"min\": 0, \"max\": 0, \"mediano\": 0 },\n \"DANO_MATERIAL_RS\": 0,\n \"VALOR_TOTAL_RS\": { \"min\": 0, \"max\": 0, \"mediano\": 0 }\n },\n {\n \"ID\": \"ALTERNATIVO\",\n \"PRIOR\": 0.3,\n \"...\": \"estrutura_análoga\"\n },\n {\n \"ID\": \"IMPROVAVEL\",\n \"PRIOR\": 0.1,\n \"...\": \"estrutura_análoga\"\n }\n]\n\nRegra: soma dos PRIORES = 1.0."
43
+ },
44
+ {
45
+ "fase": 6,
46
+ "nome": "TESTE_JUSTICA_DO_VALOR",
47
+ "modelo": "flash",
48
+ "tipo_saida": "json",
49
+ "missao": "EXPLICAR POR QUE O VALOR ESCOLHIDO É JUSTO E POR QUE OUTROS NÃO SÃO.\n\nJSON:\n1. VALOR_RECOMENDADO_RS\n2. CENARIO_BASE { PRINCIPAL|ALTERNATIVO|IMPROVAVEL }\n3. POR_QUE_ESTE_VALOR\n4. POR_QUE_NAO_VALORES_MENORES [lista]\n5. POR_QUE_NAO_VALORES_MAIORES [lista]\n6. TESTE_PROPORCIONALIDADE { passou|falhou, justificativa }\n7. TESTE_ENRIQUECIMENTO_SEM_CAUSA { passou|falhou, justificativa }\n8. TESTE_REPARACAO_MINIMA_DIGNA { passou|falhou, justificativa }\n9. MARGEM_DISCRICIONARIEDADE_RS { min, max }"
50
+ },
51
+ {
52
+ "fase": 7,
53
+ "nome": "RELATORIO_VALOR_VIDA",
54
+ "modelo": "pro",
55
+ "tipo_saida": "texto",
56
+ "missao": "GERAR TEXTO FINAL EXPLICANDO O VALOR DA INDENIZAÇÃO À LUZ DA VIDA, HISTÓRIA E GRAVIDADE DO DANO.\n\nEstrutura mínima:\n1. RESUMO_FATOS_E_NEXO\n2. QUEM_ERA_A_VITIMA_E_O_QUE_PERDEU\n3. GRAVIDADE_DA_LESÃO_E_FAIXA_JURISPRUDENCIAL\n4. CENARIO_ESCOLHIDO_E_VALOR_RECOMENDADO\n5. POR_QUE_ESTE_VALOR\n6. POR_QUE_NAO_MENOS\n7. POR_QUE_NAO_MAIS\n8. OBSERVACOES_SOBRE_DUVIDAS_E_LIMITES_DA_ANALISE"
57
+ }
58
+ ]
59
+
protocolo_bibliotecario_final.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "nome": "VISÃO_FRAGMENTO (PASSO 0)",
4
+ "missao": "Você é OLHO MECÂNICO. Descreva APENAS o que vê nestas páginas: imagens, texto visível, layout, objetos, cores, diagramas. SEM interpretações, julgamentos, omissões ou criações. Liste tudo fielmente.",
5
+ "tipo_saida": "texto",
6
+ "modelo": "flash"
7
+ },
8
+ {
9
+ "nome": "CATALOGADOR",
10
+ "missao": "Analise todas as descrições de visão acumuladas. Crie índices temáticos e estrutura sem alterar fatos visuais.",
11
+ "tipo_saida": "json",
12
+ "modelo": "pro"
13
+ },
14
+ {
15
+ "nome": "CONCATENADOR_FINAL",
16
+ "missao": "UNA TODAS descrições de visão em TEXTO ÚNICO CONTÍNUO. Mantenha ordem original. Sem resumos, só o retrato fiel completo do documento.",
17
+ "tipo_saida": "texto",
18
+ "modelo": "pro"
19
+ }
20
+ ]
protocolo_fragmentacao_visao.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "nome": "PAGINADOR_VISUAL",
4
+ "missao": "Você recebe o texto bruto de um conjunto de páginas de um PDF. Separe o conteúdo por PÁGINA, na ordem original. Para cada página, produza um objeto com: 'pagina', 'transcricao_fiel' (texto integral, sem resumo) e 'descricao_visual' (imagens, tabelas, diagramas, layout, sem julgamentos). Se o fragmento tiver 5 páginas, devolva uma lista JSON com EXATAMENTE 5 objetos, um por página.",
5
+ "tipo_saida": "json",
6
+ "modelo": "flash"
7
+ }
8
+ ]
protocoloforence.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "nome": "ESCRIVAO_FORENSE",
4
+ "modelo": "flash",
5
+ "tipo_saida": "json",
6
+ "missao": "ATUE COMO ESCRIVÃO DE POLÍCIA METÓDICO. Leia todo o input (Texto + Anexos). Sua tarefa é extrair APENAS dados objetivos, sem julgamento.\n\nExtraia e retorne um JSON com:\n1. PERFIL_PARTES: Quem é Vítima e Acusado.\n2. CRONOLOGIA: Linha do tempo dos eventos (Hora a hora, se disponível).\n3. EVIDENCIAS_FISICAS: Lista de provas citadas (Áudios, Vídeos, Testemunhas, BO).\n4. RESUMO_VERSOES: Resumo neutro do que a Vítima alega vs. o que o Acusado alega."
7
+ },
8
+ {
9
+ "nome": "PERITO_PSICOLOGO",
10
+ "modelo": "flash",
11
+ "tipo_saida": "json",
12
+ "missao": "ATUE COMO PSICÓLOGO FORENSE ESPECIALISTA EM VIOLÊNCIA DOMÉSTICA. Analise a transcrição e os depoimentos focando no SUBTEXTO e na PSICOLOGIA.\n\nAnalise e retorne JSON:\n1. DINAMICA_PODER: Quem domina o diálogo?\n2. INDICADORES_ABUSO: Procure sinais de 'Gaslighting' (fazer a vítima duvidar da realidade), Inversão de Culpa ou Chantagem Emocional. Cite as frases exatas.\n3. ANALISE_TOM: Compare a frieza/calma de um vs. o desespero do outro. Explique se a calma do acusado é um sinal de controle ou inocência."
13
+ },
14
+ {
15
+ "nome": "INVESTIGADOR_CHEFE",
16
+ "modelo": "flash",
17
+ "tipo_saida": "json",
18
+ "missao": "ATUE COMO INVESTIGADOR SÊNIOR. Sua tarefa é o CRUZAMENTO DE DADOS (Fact-Checking).\n\nCompare os Fatos (Nó 1) com a Psicologia (Nó 2). Onde a versão do acusado contradiz a realidade do áudio/texto?\n\nRetorne JSON com:\n1. CONTRADICOES_FATAIS: Pontos onde a mentira é óbvia (Ex: Ele diz que foi atacado, mas o áudio mostra ele coagindo).\n2. VERACIDADE: Qual versão é mais sustentável pelas provas?"
19
+ },
20
+ {
21
+ "nome": "PROMOTOR_GERAL",
22
+ "modelo": "pro",
23
+ "tipo_saida": "texto",
24
+ "missao": "ATUE COMO PROMOTOR DE JUSTIÇA. Escreva o PARECER TÉCNICO FINAL em Markdown rico e bem formatado.\nUse todos os dados acumulados (Fatos, Psicologia, Contradições).\n\nEstrutura Obrigatória:\n# PARECER TÉCNICO MINISTERIAL\n## 1. SUMÁRIO DOS FATOS\n(Resumo narrativo cronológico)\n\n## 2. ANÁLISE FORENSE E COMPORTAMENTAL\n(Explique o 'Gaslighting' e a manipulação identificada pelo psicólogo. Cite frases do áudio).\n\n## 3. O CONFRONTO PROBATÓRIO\n(Exponha as contradições do acusado de forma lista e direta).\n\n## 4. FUNDAMENTAÇÃO JURÍDICA\n(Tipifique os crimes: Ameaça (Art. 147), Violência Psicológica (147-B), Lei Maria da Penha. Explique por que se enquadra).\n\n## 5. CONCLUSÃO E PEDIDOS\n(Solicite Medida Protetiva de Urgência ou Prisão Preventiva. Justifique a periculosidade do réu).\n\nSeja eloquente, técnico e incisivo na defesa da vítima."
25
+ }
26
+ ]
protocolosocrates.json ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "fase": 0,
4
+ "nome": "ESTADO_INICIAL",
5
+ "modelo": "flash",
6
+ "tipo_saida": "json",
7
+ "missao": "ATUE COMO ANALISTA DE ENTRADA DE DADOS.\n\nSua tarefa é registrar e normalizar o input inicial.\n\nExtraia e retorne JSON:\n1. PERGUNTA_NORMALIZADA: A questão principal a investigar\n2. CONTEXTO_IDENTIFICADO: Resumo do contexto fornecido\n3. TIPO_CASO: Classificação (civil, criminal, família, etc)\n4. PARTES_ENVOLVIDAS: Lista inicial de pessoas/entidades mencionadas\n5. INCERTEZA_INICIAL: Avalie de 0.0 a 1.0 quão incerto é o caso\n6. EVIDENCIAS_DISPONIVEIS: Lista do que foi fornecido (textos, áudios, etc)\n\nSeja objetivo e neutro. Apenas organize o input."
8
+ },
9
+ {
10
+ "fase": 1,
11
+ "nome": "MAPEAMENTO_OBJETIVO",
12
+ "modelo": "flash",
13
+ "tipo_saida": "json",
14
+ "missao": "ATUE COMO ESTRATEGISTA INVESTIGATIVO.\n\nAnalise o estado inicial e decomponha a investigação.\n\nRetorne JSON:\n1. OBJETIVO_CLARIFICADO: O que exatamente precisa ser respondido?\n2. TIPO_RESPOSTA_ESPERADA: Qual formato de resposta satisfaria?\n3. CRITERIOS_VALIDACAO: Como saberemos se a resposta é boa? (3-5 critérios)\n4. PRESSUPOSTOS_IMPLICITOS: Quais suposições a pergunta carrega?\n5. PERGUNTAS_DERIVADAS: Que sub-perguntas precisam ser respondidas?\n6. METODOLOGIA_SUGERIDA: Que tipo de análise é necessária?\n\nUse o ESTADO_INICIAL da timeline."
15
+ },
16
+ {
17
+ "fase": 2,
18
+ "nome": "INVENTARIO_EPISTEMICO",
19
+ "modelo": "flash",
20
+ "tipo_saida": "json",
21
+ "missao": "ATUE COMO AUDITOR DE CONHECIMENTO.\n\nMapeie O QUE SABEMOS e O QUE NÃO SABEMOS.\n\nRetorne JSON com 4 categorias de conhecimento:\n\n1. CONHECIMENTO_CERTO (P≈1.0): Fatos confirmados, evidências irrefutáveis\n - Array: [{fato, probabilidade, evidencia_fonte}]\n\n2. CONHECIMENTO_PROVAVEL (P≈0.7-0.9): Muito provável mas não 100%\n - Array: [{fato, probabilidade, evidencia_fonte}]\n\n3. CONHECIMENTO_INCERTO (P≈0.3-0.6): Hipóteses com algum suporte\n - Array: [{hipotese, probabilidade, suposicoes}]\n\n4. DESCONHECIMENTO_TOTAL (P≈0.0): O que ainda não sabemos\n - Array: [string]\n\n5. PERCENTUAL_COBERTURA: De 0 a 100, quanto % do caso entendemos?\n6. GAPS_CRITICOS: Que informações faltam e são cruciais?\n\nUse toda a timeline anterior."
22
+ },
23
+ {
24
+ "fase": 3,
25
+ "nome": "GERACAO_CENARIOS",
26
+ "modelo": "flash",
27
+ "tipo_saida": "json",
28
+ "missao": "ATUE COMO CONSTRUTOR DE HIPÓTESES.\n\nGere CENÁRIOS PLAUSÍVEIS baseados no conhecimento mapeado.\n\nRetorne JSON com array de cenários (mínimo 3):\n\nPara cada cenário:\n1. ID: \"C1\", \"C2\", \"C3\"...\n2. DESCRICAO: Narrativa do cenário\n3. PROBABILIDADE_PRIOR: 0.0 a 1.0 (soma deve dar 1.0)\n4. SUPOSICOES: Array de premissas necessárias\n5. COMPATIBILIDADE: {conhecimento_certo: 0-1, conhecimento_provavel: 0-1}\n6. EVIDENCIAS_SUPORTE: Array de evidências que sustentam\n7. EVIDENCIAS_CONTRA: Array de evidências contrárias\n8. PLAUSIBILIDADE_SCORE: 0.0 a 1.0\n\nInclua: 1 cenário principal (>50%), 1 alternativo (20-40%), 1 improvável (<20%).\nUse INVENTARIO_EPISTEMICO."
29
+ },
30
+ {
31
+ "fase": 4,
32
+ "nome": "ANALISE_CONTRAFACTUAL",
33
+ "modelo": "flash",
34
+ "tipo_saida": "json",
35
+ "missao": "ATUE COMO TESTE DE SENSIBILIDADE.\n\nPara cada cenário, pergunte: \"E SE essa suposição for FALSA?\"\n\nRetorne JSON:\n1. VARIAVEIS_CRITICAS: Array de variáveis que mudam significativamente os cenários\n - [{variavel, cenarios_afetados, impacto_delta, tipo}]\n\n2. MAPA_CAUSAL: Grafo de dependências\n - {dependencias: {cenario_id: [suposicoes]}}\n\n3. PONTOS_INFLEXAO: Suposições que se falsas invertem conclusões\n - Array: [string]\n\n4. TESTES_RECOMENDADOS: Que evidências poderiam confirmar/refutar?\n - Array: [{teste, cenario_alvo, impacto_esperado}]\n\nIdentifique onde o caso é FRÁGIL.\nUse GERACAO_CENARIOS."
36
+ },
37
+ {
38
+ "fase": 5,
39
+ "nome": "CADEIA_INVESTIGATIVA",
40
+ "modelo": "flash",
41
+ "tipo_saida": "json",
42
+ "missao": "ATUE COMO PLANEJADOR DE INVESTIGAÇÃO.\n\nConstrua cadeia de perguntas PRIORIZADAS por information gain.\n\nRetorne JSON:\n1. CADEIA_PERGUNTAS: Array ordenado por prioridade\n - [{\n id: \"Q1\",\n pergunta: \"string\",\n justificativa: \"Por que é importante?\",\n reducao_entropia_estimada: 0.0-1.0,\n metodo_busca: [string],\n proximos_passos: {se_sim: \"Q2\", se_nao: \"Q3\", se_inconclusivo: \"Q4\"},\n prioridade: int\n }]\n\n2. ARVORE_DECISAO: Estrutura condicional\n - {raiz: \"Q1\", nos: {...}}\n\n3. EVIDENCIAS_NECESSARIAS: Lista crítica do que buscar\n - Array: [string]\n\nMaximize redução de incerteza.\nUse ANALISE_CONTRAFACTUAL."
43
+ },
44
+ {
45
+ "fase": 6,
46
+ "nome": "COLETA_ATUALIZACAO_BAYESIANA",
47
+ "modelo": "pro",
48
+ "tipo_saida": "json",
49
+ "missao": "ATUE COMO ANALISTA BAYESIANO.\n\nPara cada evidência disponível, ATUALIZE as probabilidades dos cenários.\n\nFórmula: P(Cenário|Evidência) = P(Evidência|Cenário) × P(Cenário) / P(Evidência)\n\nRetorne JSON:\n1. EVIDENCIAS_ANALISADAS: Array de evidências processadas\n - [{evidencia_id, conteudo, fonte, confiabilidade: 0-1, \n likelihoods: {C1: 0.8, C2: 0.3, C3: 0.1}}]\n\n2. CENARIOS_ATUALIZADOS: Cenários com probabilidades posteriores\n - [{id, probabilidade_prior, probabilidade_posterior, delta, \n evidencias_chave}]\n\n3. GANHO_INFORMACAO: KL divergence (quanto aprendemos?)\n - Float\n\n4. CONVERGENCIA: As probabilidades estabilizaram?\n - Boolean\n\n5. CENARIO_DOMINANTE: Qual cenário emergiu como mais provável?\n - {id, probabilidade, confianca}\n\nUse CADEIA_INVESTIGATIVA e todos os dados da timeline."
50
+ },
51
+ {
52
+ "fase": 7,
53
+ "nome": "TESTE_CRUCIALIDADE",
54
+ "modelo": "pro",
55
+ "tipo_saida": "json",
56
+ "missao": "ATUE COMO VALIDADOR RIGOROSO.\n\nTeste a conclusão tentativa contra 7 CRITÉRIOS:\n\n1. CONSISTENCIA_INTERNA: Sem contradições lógicas? (0.0-1.0)\n2. COMPATIBILIDADE_EVIDENCIAS: Todas evidências se encaixam? (0.0-1.0)\n3. EXPLICACOES_ALTERNATIVAS: Cenários alternativos foram considerados? (0.0-1.0)\n4. FALSIFICABILIDADE: A conclusão é testável? (0.0-1.0)\n5. PARSIMONIA: É a explicação mais simples? (0.0-1.0)\n6. ESCOPO: Explica TODOS os dados? (0.0-1.0)\n7. PREDICOES_VERIFICAVEIS: Gera predições testáveis? (0.0-1.0)\n\nRetorne JSON:\n- TESTES: {criterio: score}\n- SCORE_CRUCIALIDADE: Média dos 7 scores\n- LIMIAR_CONFIANCA: 0.70 (padrão)\n- PASSOU: Boolean (score >= limiar?)\n- ACAO: \"PROSSEGUIR\" ou \"VOLTAR_FASE_3\"\n- FRAGILIDADES_IDENTIFICADAS: Array de pontos fracos\n\nSe PASSOU = false, o sistema iterará.\nUse COLETA_ATUALIZACAO_BAYESIANA."
57
+ },
58
+ {
59
+ "fase": 8,
60
+ "nome": "GERACAO_RESPOSTA",
61
+ "modelo": "pro",
62
+ "tipo_saida": "json",
63
+ "missao": "ATUE COMO SINTETIZADOR FINAL.\n\nGere resposta estruturada e CALIBRADA epistemicamente.\n\nRetorne JSON:\n1. AFIRMACAO_PRINCIPAL: Conclusão clara e direta\n2. NIVEL_CONFIANCA: 0.0 a 1.0 (probabilidade posterior do cenário dominante)\n3. EVIDENCIAS_SUPORTE: Array com peso de cada evidência\n4. SUPOSICOES_CRITICAS: Premissas que SE FALSAS mudam tudo\n5. CENARIOS_ALTERNATIVOS: Outros cenários com P>0.1\n6. PROXIMOS_PASSOS: Recomendações para fortalecer o caso\n7. FRONTEIRAS_CONHECIMENTO: O que ainda não sabemos\n8. CALIBRACAO_EPISTEMICA: \"honest\" | \"overconfident\" | \"underconfident\"\n9. QUALIFICADORES: Nuances, ressalvas, contexto importante\n\nUse TESTE_CRUCIALIDADE e toda a timeline."
64
+ },
65
+ {
66
+ "fase": 9,
67
+ "nome": "RELATORIO_FINAL",
68
+ "modelo": "pro",
69
+ "tipo_saida": "texto",
70
+ "missao": "ATUE COMO REDATOR TÉCNICO FORENSE.\n\nEscreva o RELATÓRIO FINAL em Markdown estruturado e profissional.\n\nEstrutura OBRIGATÓRIA:\n\n# RELATÓRIO DE ANÁLISE EPISTÊMICA\n*Protocolo de Investigação Causal - 10 Fases*\n\n---\n\n## 📋 SUMÁRIO EXECUTIVO\n- **Caso:** [descrição breve]\n- **Conclusão Principal:** [afirmação clara]\n- **Nível de Confiança:** [X.XX%]\n- **Calibração Epistêmica:** [honest/over/under]\n\n---\n\n## 🔍 1. ESTADO INICIAL DO CASO\n[Descrição do input, partes, contexto]\n\n## 🎯 2. OBJETIVO DA INVESTIGAÇÃO\n[O que foi investigado e por quê]\n\n## 📊 3. INVENTÁRIO DE CONHECIMENTO\n### 3.1 Conhecimento Certo (P≈1.0)\n- [lista]\n\n### 3.2 Conhecimento Provável (P≈0.7-0.9)\n- [lista]\n\n### 3.3 Conhecimento Incerto (P≈0.3-0.6)\n- [lista]\n\n### 3.4 Gaps Críticos\n- [lista]\n\n**Cobertura Epistêmica:** XX%\n\n## 🌳 4. CENÁRIOS CONSIDERADOS\n### Cenário C1 (Principal) - P=XX%\n[Descrição, suposições, evidências]\n\n### Cenário C2 (Alternativo) - P=XX%\n[Descrição, suposições, evidências]\n\n### Cenário C3 (Improvável) - P=XX%\n[Descrição, suposições, evidências]\n\n## 🔬 5. ANÁLISE BAYESIANA\n### Atualização de Probabilidades\n| Cenário | Prior | Posterior | Δ |\n|---------|-------|-----------|---|\n| C1 | XX% | XX% | +XX% |\n| C2 | XX% | XX% | -XX% |\n| C3 | XX% | XX% | -XX% |\n\n**Ganho de Informação:** X.XXX\n**Convergência:** [Sim/Não]\n\n## ✅ 6. VALIDAÇÃO (7 Critérios)\n| Critério | Score |\n|----------|-------|\n| Consistência Interna | X.XX |\n| Compatibilidade Evidências | X.XX |\n| Explicações Alternativas | X.XX |\n| Falsificabilidade | X.XX |\n| Parcimônia | X.XX |\n| Escopo | X.XX |\n| Predições Verificáveis | X.XX |\n\n**Score de Crucialidade:** X.XX ✓\n\n## 💡 7. CONCLUSÃO E RECOMENDAÇÕES\n\n### 7.1 Afirmação Principal\n[Conclusão clara]\n\n### 7.2 Nível de Confiança\n**XX.X%** - Baseado em [justificativa]\n\n### 7.3 Evidências-Chave\n1. [Evidência 1] - Peso: XX%\n2. [Evidência 2] - Peso: XX%\n3. [Evidência 3] - Peso: XX%\n\n### 7.4 Suposições Críticas\n⚠️ **Atenção:** Se as seguintes premissas forem falsas, a conclusão muda:\n- [Suposição 1]\n- [Suposição 2]\n\n### 7.5 Cenários Alternativos Viáveis\n- **C2** (XX%): [Breve descrição]\n\n### 7.6 Próximos Passos Recomendados\n1. [Recomendação 1]\n2. [Recomendação 2]\n3. [Recomendação 3]\n\n## 🎓 8. FRONTEIRAS DO CONHECIMENTO\nO que ainda NÃO sabemos:\n- [Questão aberta 1]\n- [Questão aberta 2]\n\n---\n\n## ⚖️ 9. CALIBRAÇÃO EPISTÊMICA\n**Status:** [honest/overconfident/underconfident]\n\n[Explicação da calibração]\n\n---\n\n## 📎 10. METADADOS\n- **Fases Executadas:** 10/10\n- **Iterações Necessárias:** X\n- **Redução de Incerteza:** X.XXX\n- **Timestamp:** [data/hora]\n\n---\n\n*Este relatório foi gerado através do Protocolo Epistêmico Causal, que resolve o Paradoxo de Ménon operando no espaço de conhecimento parcial (0% < K < 100%).*\n\n**USE TODA A TIMELINE. Seja técnico, preciso e epistemicamente honesto.**"
71
+ }
72
+ ]
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ google-generativeai>=0.3.0
2
+ gradio>=4.0.0
3
+ python-dotenv>=1.0.0
4
+ google-genai
5
+ pypdf
6
+ PyPDF2
7
+ pdf2image
8
+ pillow
9
+ fastapi
10
+ uviconr