caarleexx commited on
Commit
6523287
·
verified ·
1 Parent(s): 9e32e25

Upload ai_studio_code (4).py

Browse files
Files changed (1) hide show
  1. ai_studio_code (4).py +644 -0
ai_studio_code (4).py ADDED
@@ -0,0 +1,644 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Pipeline v10 Refatorada e Comentada - Chatbot com Metacognição Pura.
4
+
5
+ Este arquivo implementa um chatbot avançado utilizando a API Google Gemini.
6
+ A arquitetura se baseia em uma pipeline de múltiplos passos (P0-P8, X1-X2)
7
+ que analisa, raciocina, gera cenários e verifica as respostas antes de
8
+ entregá-las ao usuário.
9
+
10
+ Esta versão inclui a capacidade de pausar the pipeline quando o modelo tem uma
11
+ dúvida, perguntar ao usuário e, em seguida, retomar a execução do ponto de
12
+ parada usando o esclarecimento fornecido.
13
+ """
14
+
15
+ # ============================================================================
16
+ # 1. IMPORTAÇÕES E CONFIGURAÇÃO INICIAL
17
+ # ============================================================================
18
+ import json
19
+ import os
20
+ import base64
21
+ import re
22
+ import warnings
23
+ from datetime import datetime
24
+ from typing import Dict, List, Tuple, Any
25
+
26
+ import gradio as gr
27
+ import google.generativeai as genai
28
+
29
+ warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
30
+
31
+ API_KEY = os.getenv("GOOGLE_API_KEY")
32
+ if not API_KEY:
33
+ raise ValueError("A variável de ambiente GOOGLE_API_KEY não foi configurada.")
34
+
35
+ genai.configure(api_key=API_KEY)
36
+
37
+ COUNSELOR_MODEL = genai.GenerativeModel("gemini-1.5-flash")
38
+ SUPERVISOR_MODEL = genai.GenerativeModel("gemini-1.5-flash")
39
+
40
+ TITLE = "# 🚀 Pipeline v10 | PAUSA & RETOMADA\n**Sistema com gerenciamento de estado para interrupção e continuação do raciocínio.**"
41
+
42
+ # ============================================================================
43
+ # 2. PROMPTS CENTRALIZADOS
44
+ # ============================================================================
45
+ PROMPTS = {
46
+ "P0_ALUNO": """
47
+ Você é um METACOGNITIVO (pensamento interno, NÃO comunicação).
48
+
49
+ TURNO ANTERIOR:
50
+ User: {turno_anterior_user}
51
+ Assistant: {turno_anterior_assistant}
52
+
53
+ NOVA MENSAGEM: {pergunta}
54
+ CONTEXTO VAGO: {historico_compacto}
55
+
56
+ ---
57
+
58
+ Responda EM METACOGNIÇÃO PURA - TELEGRÁFICO
59
+ NÃO use frases completas. APENAS essência semântica com conectores mínimos.
60
+
61
+ EXEMPLO CERTO: entendeu-sim | pergunta-nova | avança-tópico | não-reformulou
62
+
63
+ RETORNE JSON:
64
+ {{
65
+ "usuario_entendeu": "sim|não",
66
+ "evidencias": ["entendeu-pergunta", "pediu-clarificação"],
67
+ "usuario_corrigiu": "sim|não",
68
+ "correcao_detectada": null|"texto-correção",
69
+ "correcao_valida": "sim|não|null",
70
+ "o_que_melhorar": null|"explicar-X-melhor",
71
+ "decisao": "prosseguir-passo1|reexplicar-passo6|atualizar-resposta-anterior",
72
+ "motivo": "texto-curtíssimo"
73
+ }}
74
+ """,
75
+ "P1_TRIAGEM": """
76
+ METACOGNIÇÃO - TRIAGEM INICIAL.
77
+
78
+ CONTEXTO VAGO: {contexto_vago}
79
+ HISTÓRICO RECENTE (últimas 3): {historico_recente}
80
+ P0: {p0}
81
+ PERGUNTA: {pergunta}
82
+
83
+ ---
84
+
85
+ CLASSIFIQUE EM TELEGRÁFICO (sem frases).
86
+
87
+ RETORNE JSON:
88
+ {{
89
+ "tipo": "objetiva|factual|subjetiva|aberta",
90
+ "sinais": ["tem-resposta-única-verificável", "sem-contexto-pessoal"],
91
+ "confianca": "alta|média|baixa",
92
+ "decisao": "responder-direto|analisar-profundamente",
93
+ "razao": "curtíssima",
94
+ "dados_fatuais": ["fato1", "fato2"],
95
+ "divergencias_fatuais": ["possível-ambiguidade-1"],
96
+ "objetivo_principal": "objetivo-primário-identificado",
97
+ "objetivo_secundario": ["objetivo-secundário-1"]
98
+ }}
99
+ """,
100
+ "X1_PERGUNTAS_NECESSARIAS": """
101
+ X1-PERGUNTAS CRÍTICAS - TELEGRÁFICO
102
+ P1: {p1}
103
+ CONTEXTO: {historico_compacto}
104
+ PERGUNTA PRINCIPAL: {pergunta}
105
+
106
+ ---
107
+ Analise as lacunas factuais e subjetivas na pergunta do usuário e no contexto.
108
+ Liste as perguntas essenciais que você precisa responder internamente antes de formular a resposta final.
109
+
110
+ RETORNE JSON:
111
+ {{"perguntas": [
112
+ {{"texto": "pergunta-curta-e-essencial", "necessidade": "alta|média|baixa", "relevancia": "alta|média"}}
113
+ ]}}
114
+ """,
115
+ "X2_RESOLVER_PERGUNTAS": """
116
+ X2-RESOLUÇÃO INTERNA - TELEGRÁFICO
117
+ P1: {p1}
118
+ PERGUNTAS CRÍTICAS (X1): {perguntas_x1}
119
+ CONTEXTO: {historico_compacto}
120
+
121
+ ---
122
+ Para cada pergunta crítica levantada no passo anterior, forneça uma resposta curta e direta baseada no seu conhecimento.
123
+ Avalie sua confiança e o potencial de conflito ou ambiguidade em cada resposta.
124
+
125
+ RETORNE JSON:
126
+ {{"respostas": [
127
+ {{"pergunta": "texto-original-da-pergunta-x1",
128
+ "resposta": "resposta-curta-e-direta",
129
+ "confianca": "alta|média|baixa",
130
+ "conflito": "alto|médio|baixo",
131
+ "razao": "explicação-em-1-2-palavras"}}
132
+ ]}}
133
+ """,
134
+ "P2_CENARIOS": """
135
+ METACOGNIÇÃO - GERAÇÃO DE CENÁRIOS.
136
+ CONTEXTO VAGO: {historico_compacto}
137
+ TRIAGEM P1: {p1}
138
+ X1-PERGUNTAS: {x1}
139
+ X2-RESPOSTAS: {x2}
140
+ PERGUNTA ORIGINAL: {pergunta}
141
+
142
+ ---
143
+
144
+ Mapeie cenários possíveis onde a resposta à pergunta original mudaria significativamente. Pense nas diferentes perspectivas, contextos ou premissas que alteram a conclusão. Use formato telegráfico.
145
+
146
+ RETORNE JSON:
147
+ {{
148
+ "cenarios": {{
149
+ "provaveis": [{{"id": "C1", "desc": "cenário-provável-1-comprimido", "contexto-relevante": "descreva-o-contexto"}}]
150
+ }},
151
+ "total": 1,
152
+ "tipo_resposta": "múltipla|unívoca",
153
+ "confianca": "alta|m��dia|baixa",
154
+ "decisao": "prosseguir|pedir-esclarecimento",
155
+ "pergunta_esclarecimento": null|"texto-da-pergunta-para-o-usuario"
156
+ }}
157
+ """,
158
+ "P3_ISOLAR_CENARIOS": """
159
+ METACOGNIÇÃO - EXPLORAÇÃO DE CENÁRIO ISOLADO.
160
+ CENÁRIO P2: {cenario}
161
+
162
+ ---
163
+ Para este cenário específico, defina a essência da resposta em formato telegráfico. Qual seria a conclusão principal e quais as lacunas de informação restantes?
164
+
165
+ RETORNE JSON:
166
+ {{"id": "{cenario_id}",
167
+ "resposta_essencia": "conclusão-principal-e-razoes-em-palavras-chave",
168
+ "confianca": "alta|média|baixa",
169
+ "lacunas": "contexto-ainda-ausente|null"
170
+ }}
171
+ """,
172
+ "P4_CRUZAR_VALIDACOES": """
173
+ METACOGNIÇÃO - ABSTRAÇÃO DE CONHECIMENTO.
174
+ P1 (Triagem): {p1}
175
+ P2 (Cenários): {p2}
176
+ P3 (Exploração): {p3}
177
+ X2 (Respostas Internas): {x2}
178
+
179
+ ---
180
+
181
+ Identifique os princípios fundamentais, teorias ou símbolos arquetípicos que sustentam as respostas nos cenários explorados. Abstraia o conhecimento para um nível mais alto. Use formato telegráfico.
182
+
183
+ RETORNE JSON:
184
+ {{"principios": [{{"nome": "Custo-Oportunidade", "essencia": "escolher-X-implica-renunciar-Y"}}],
185
+ "simbolos": [{{"nome": "Jornada-do-Herói", "essencia": "transformação-ocorre-através-de-desafios"}}],
186
+ "principio_central": "nome-do-principio-mais-importante",
187
+ "simbolo_dominante": "nome-do-simbolo-mais-relevante"
188
+ }}
189
+ """,
190
+ "P5_LACUNAS_FINAIS": """
191
+ METACOGNIÇÃO - ANÁLISE DE INCERTEZA.
192
+ P1 (Triagem): {p1}
193
+ P4 (Princípios): {p4}
194
+
195
+ ---
196
+
197
+ Avalie o balanço entre certezas e dúvidas com base em toda a análise feita até agora. A informação disponível é suficiente para dar uma resposta confiante? Use formato telegráfico.
198
+
199
+ RETORNE JSON:
200
+ {{"analise_cenarios": [{{"cenario": "C1", "certezas": ["certeza1"], "duvidas": ["dúvida1"]}}],
201
+ "confianca_global": "alta|média|baixa",
202
+ "balanco": "certezas-superam|equilibrado|duvidas-superam",
203
+ "decisao": "responder|questionar",
204
+ "questionamento": null|"texto-da-pergunta-para-o-usuario-se-a-confianca-for-baixa"
205
+ }}
206
+ """,
207
+ "P6_PONDERAR": """
208
+ METACOGNIÇÃO - JULGAMENTO FINAL (JUIZ DA VERDADE).
209
+ P2 (Cenários): {p2}
210
+ P4 (Princípios): {p4}
211
+ P5 (Lacunas): {p5}
212
+
213
+ ---
214
+
215
+ Aja como um árbitro socrático. Com base em toda a metacognição, valide as "verdades" encontradas e decida o nível de consciência sobre a complexidade da resposta. Use formato telegráfico.
216
+
217
+ RETORNE JSON:
218
+ {{"verdade_principal": "a-conclusao-mais-provavel-e-confiavel",
219
+ "nuances_importantes": ["nuance1", "nuance2"],
220
+ "confianca_final": "alta|média|baixa",
221
+ "decisao": "exibir-resposta-completa|exibir-resposta-com-ressalvas|reprocessar",
222
+ "nivel_consciencia": "alto|médio|baixo"
223
+ }}
224
+ """,
225
+ "P7_SINTETIZAR": """
226
+ Você é um SINTETIZADOR especialista em transformar METACOGNIÇÃO CRUA em PROSA HUMANIZADA e empática.
227
+
228
+ DADOS DO JULGAMENTO (P6): {p6}
229
+
230
+ ---
231
+
232
+ TAREFA: Converta a análise telegráfica do 'Juiz da Verdade' em uma resposta textual fluida, natural e útil para o usuário.
233
+
234
+ INSTRUÇÕES:
235
+ 1. Use conectores naturais (ex: "porque", "portanto", "isso significa que").
236
+ 2. Expanda abreviações e jargões para uma linguagem clara.
237
+ 3. Estruture a resposta em parágrafos lógicos (introdução, desenvolvimento, nuances/conclusão).
238
+ 4. Incorpore os princípios e nuances de forma natural na explicação.
239
+ 5. Adote um tom de conselheiro: amigável, empático e empoderador.
240
+ 6. NÃO invente informações. Baseie-se estritamente nos dados do P6.
241
+
242
+ RETORNE A RESPOSTA EM PROSA DENTRO DE UM JSON:
243
+ {{"resposta": "Aqui vai o texto fluido, natural e humano..."}}
244
+ """,
245
+ "P8_VERIFICAR": """
246
+ Você é um VERIFICADOR FINAL, um guardião rigoroso da qualidade da resposta.
247
+
248
+ RESPOSTA SINTETIZADA (P7):
249
+ {resposta_sintetizada}
250
+
251
+ ANÁLISE DO JUIZ (P6):
252
+ {p6}
253
+
254
+ ---
255
+
256
+ Realize uma verificação tripla na resposta sintetizada. Seja crítico.
257
+
258
+ 1. **VERIFICAÇÃO FACTUAL**: A resposta contém fatos incorretos ou não sustentados pela análise do P6?
259
+ 2. **VERIFICAÇÃO LÓGICA**: Existem falácias, saltos de lógica ou contradições? A conclusão segue a linha de raciocínio?
260
+ 3. **VERIFICAÇÃO ÉTICA**: A resposta é apropriada, segura e imparcial? Inclui os avisos ou ressalvas necessários?
261
+
262
+ RETORNE SEU VEREDITO EM JSON:
263
+ {{"verificacao_factual": {{"aprovada": true|false, "problemas": ["descrição do problema se houver"]}},
264
+ "verificacao_logica": {{"aprovada": true|false, "problemas": []}},
265
+ "verificacao_etica": {{"aprovada": true|false, "problemas": []}},
266
+ "todas_aprovadas": true|false,
267
+ "decisao": "exibir-resposta-original|corrigir-e-exibir",
268
+ "resposta_corrigida": null|"texto da versão corrigida e melhorada da resposta"
269
+ }}
270
+ """
271
+ }
272
+
273
+
274
+ # ============================================================================
275
+ # 3. CLASSES E FUNÇÕES HELPERS
276
+ # ============================================================================
277
+
278
+ class Logger:
279
+ def __init__(self, verbose: bool = True):
280
+ self.verbose = verbose
281
+
282
+ def log(self, msg: str, level: str = "INFO"):
283
+ log_msg = f"[{datetime.now().strftime('%H:%M:%S')}] [{level.upper()}] {msg}"
284
+ if self.verbose:
285
+ print(log_msg)
286
+ if level.upper() in ["TASK", "START", "SUCCESS", "ERROR", "WARN"]:
287
+ print("=" * 70)
288
+
289
+ logger = Logger(verbose=True)
290
+
291
+
292
+ def processar_anexo(arquivo: Any) -> Tuple[str, str]:
293
+ if arquivo is None:
294
+ return "", "nenhum"
295
+ caminho_arquivo = arquivo.name
296
+ try:
297
+ if caminho_arquivo.lower().endswith('.pdf'):
298
+ return "[Conteúdo do PDF iria aqui]", "pdf"
299
+ elif any(caminho_arquivo.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.gif']):
300
+ return "[Dados da imagem iriam aqui]", "imagem"
301
+ return "", "nao_suportado"
302
+ except Exception as e:
303
+ logger.log(f"Erro ao processar anexo: {e}", "ERROR")
304
+ return "", "erro"
305
+
306
+
307
+ def construir_prompt_com_anexo(pergunta: str, anexo_conteudo: str, tipo_anexo: str) -> str:
308
+ if not anexo_conteudo or tipo_anexo in ["nenhum", "erro", "nao_suportado"]:
309
+ return pergunta
310
+ if tipo_anexo == "pdf":
311
+ return f"Com base no documento PDF, responda: {pergunta}\nDOCUMENTO:\n{anexo_conteudo}"
312
+ if tipo_anexo == "imagem":
313
+ return f"Com base na imagem, responda: {pergunta}"
314
+ return pergunta
315
+
316
+
317
+ def parse_json_ultra_robusto(texto: str) -> Dict:
318
+ if not texto:
319
+ return {"erro": "Texto de entrada vazio"}
320
+ try:
321
+ match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', texto, re.DOTALL)
322
+ if match:
323
+ json_str = match.group(1)
324
+ else:
325
+ json_str = texto
326
+ return json.loads(json_str)
327
+ except json.JSONDecodeError:
328
+ try:
329
+ inicio = texto.find('{')
330
+ fim = texto.rfind('}') + 1
331
+ if inicio != -1 and fim != 0:
332
+ return json.loads(texto[inicio:fim])
333
+ except json.JSONDecodeError:
334
+ return {"erro": "parse_falhou", "fallback_text": texto[:500]}
335
+
336
+
337
+ def chamar_gemini_json(modelo: genai.GenerativeModel, prompt: str, temperatura: float = 0.5, max_tokens: int = 2000) -> Dict:
338
+ prompt_completo = f"{prompt}\n\n---\n\n**INSTRUÇÃO OBRIGATÓRIA: Sua resposta DEVE ser um único e válido objeto JSON.**"
339
+
340
+ print("\n" + "="*25 + f" 💬 API INPUT PARA [{modelo.model_name}] " + "="*25)
341
+ print(prompt_completo)
342
+ print("="*78 + "\n")
343
+ logger.log(f"Enviando prompt ({len(prompt_completo)} chars) para {modelo.model_name}", "DEBUG")
344
+
345
+ try:
346
+ response = modelo.generate_content(
347
+ prompt_completo,
348
+ generation_config=genai.types.GenerationConfig(
349
+ temperature=temperatura,
350
+ max_output_tokens=max_tokens,
351
+ )
352
+ )
353
+ resposta_bruta = response.text or ""
354
+
355
+ print("\n" + "="*25 + f" 📥 API RAW OUTPUT DE [{modelo.model_name}] " + "="*25)
356
+ print(resposta_bruta)
357
+ print("="*78 + "\n")
358
+ logger.log(f"Gemini RAW ({len(resposta_bruta)} chars): {resposta_bruta[:400]}...", "DEBUG")
359
+
360
+ return parse_json_ultra_robusto(resposta_bruta)
361
+
362
+ except Exception as e:
363
+ logger.log(f"Erro na chamada da API Gemini: {e}", "ERROR")
364
+ return {"erro": f"API_ERROR: {str(e)}"}
365
+
366
+
367
+ def historico_compacto(historico: List[Dict]) -> str:
368
+ if not historico:
369
+ return "Nenhuma conversa anterior."
370
+ compacto = [f"{'User' if m['role'] == 'user' else 'Assistant'}: {m['content'][:80]}" for m in historico[-4:]]
371
+ return "\n".join(compacto)
372
+
373
+
374
+ def criar_dna() -> Dict:
375
+ return {
376
+ "historico_chat": [],
377
+ "meta": {"total_turnos": 0},
378
+ "pipeline_state": {
379
+ "status": "completed",
380
+ "paused_at_step": None,
381
+ "saved_data": {}
382
+ }
383
+ }
384
+
385
+ # ============================================================================
386
+ # 4. PASSOS DA PIPELINE (P0-P8, X1-X2)
387
+ # ============================================================================
388
+ def passo_0_aluno(pergunta: str, historico: List[Dict]) -> Dict:
389
+ logger.log("🧠 P0-ALUNO", "TASK")
390
+ # Simulação para simplificar a demonstração da lógica principal
391
+ return {"p0_data": True}
392
+
393
+ def passo_1_triagem(pergunta: str, p0: Dict, historico: List[Dict]) -> Dict:
394
+ logger.log("📊 P1-TRIAGEM", "TASK")
395
+ return {"p1_data": True}
396
+
397
+ def passo_x1_perguntas_necessarias(pergunta: str, p1: Dict, historico: List[Dict]) -> Dict:
398
+ logger.log("❓ X1-PERGUNTAS", "TASK")
399
+ return {"x1_data": True}
400
+
401
+ def passo_x2_resolver_perguntas(p1: Dict, x1: Dict, historico: List[Dict]) -> Dict:
402
+ logger.log("✅ X2-RESOLVER", "TASK")
403
+ return {"x2_data": True}
404
+
405
+ def passo_2_cenarios(pergunta: str, p1: Dict, x1: Dict, x2: Dict, historico: List[Dict]) -> Dict:
406
+ logger.log("🎯 P2-CENÁRIOS", "TASK")
407
+ # Simulação: Para perguntas sobre "dúvida", ele pausa.
408
+ if "dúvida" in pergunta.lower():
409
+ logger.log("P2 encontrou ambiguidade. Decidindo pedir esclarecimento.", "INFO")
410
+ return {"decisao": "pedir-esclarecimento", "pergunta_esclarecimento": "Você pode esclarecer o ponto X sobre a sua dúvida?"}
411
+ return {"decisao": "prosseguir", "p2_data": True}
412
+
413
+ def passo_3_isolar_cenarios(p2: Dict) -> Dict:
414
+ logger.log("🔍 P3-ISOLAR", "TASK")
415
+ return {"p3_data": True}
416
+
417
+ def passo_4_cruzar_validacoes(p1: Dict, p2: Dict, p3: Dict, x2: Dict) -> Dict:
418
+ logger.log("🔗 P4-VALIDAÇÃO", "TASK")
419
+ # Simulação para o P5
420
+ if p2.get("p2_data"):
421
+ return {"principio_central": "Certeza"}
422
+ return {"principio_central": "Incerteza"}
423
+
424
+ def passo_5_lacunas_finais(p1: Dict, p4: Dict) -> Dict:
425
+ logger.log("🚨 P5-LACUNAS FINAIS", "TASK")
426
+ # Simulação de interrupção
427
+ if p4.get("principio_central") == "Incerteza":
428
+ return {"decisao": "questionar", "questionamento": "A incerteza é muito alta. Preciso de mais dados sobre Y. Você pode fornecer?"}
429
+ return {"decisao": "responder", "p5_data": True}
430
+
431
+ def passo_6_ponderar(p2: Dict, p4: Dict, p5: Dict) -> Dict:
432
+ logger.log("⚖️ P6-PONDERAR", "TASK")
433
+ return {"p6_data": True}
434
+
435
+ def passo_7_sintetizar(p6: Dict) -> Dict:
436
+ logger.log("✍️ P7-SINTETIZAR", "TASK")
437
+ return {"resposta": "Esta é a resposta final, gerada com sucesso."}
438
+
439
+ def passo_8_verificar(p6: Dict, p7: Dict) -> Dict:
440
+ logger.log("🛡️ P8-VERIFICAR", "TASK")
441
+ return {"todas_aprovadas": True, "resposta_corrigida": None}
442
+
443
+ # ============================================================================
444
+ # 5. ORQUESTRADOR PRINCIPAL
445
+ # ============================================================================
446
+
447
+ def iniciar_nova_pipeline(pergunta: str, historico: List[Dict], anexo: Any, dna: Dict) -> Tuple[str, List, Dict]:
448
+ logger.log(f"INICIANDO NOVA PIPELINE: '{pergunta[:50]}...'", "START")
449
+
450
+ conteudo_anexo, tipo_anexo = processar_anexo(anexo)
451
+ pergunta_final = construir_prompt_com_anexo(pergunta, conteudo_anexo, tipo_anexo)
452
+
453
+ p0 = passo_0_aluno(pergunta_final, historico)
454
+ p1 = passo_1_triagem(pergunta_final, p0, historico)
455
+ x1 = passo_x1_perguntas_necessarias(pergunta_final, p1, historico)
456
+ x2 = passo_x2_resolver_perguntas(p1, x1, historico)
457
+ p2 = passo_2_cenarios(pergunta_final, p1, x1, x2, historico)
458
+
459
+ if p2.get("decisao") == "pedir-esclarecimento":
460
+ logger.log("INTERRUPÇÃO no P2. Salvando estado no DNA.", "WARN")
461
+ dna['pipeline_state'] = {
462
+ "status": "paused",
463
+ "paused_at_step": "P2",
464
+ "saved_data": {
465
+ 'p0': p0, 'p1': p1, 'x1': x1, 'x2': x2,
466
+ 'pergunta_original': pergunta,
467
+ 'historico_original': historico
468
+ }
469
+ }
470
+ esclarecimento = p2.get("pergunta_esclarecimento", "Poderia esclarecer?")
471
+ return f"❓ DÚVIDA: {esclarecimento}", historico, dna
472
+
473
+ p3 = passo_3_isolar_cenarios(p2)
474
+ p4 = passo_4_cruzar_validacoes(p1, p2, p3, x2)
475
+ p5 = passo_5_lacunas_finais(p1, p4)
476
+
477
+ if p5.get("decisao") == "questionar":
478
+ logger.log("INTERRUPÇÃO no P5. Salvando estado no DNA.", "WARN")
479
+ dna['pipeline_state'] = {
480
+ "status": "paused",
481
+ "paused_at_step": "P5",
482
+ "saved_data": {
483
+ 'p0': p0, 'p1': p1, 'x1': x1, 'x2': x2, 'p2': p2, 'p3': p3, 'p4': p4,
484
+ 'pergunta_original': pergunta,
485
+ 'historico_original': historico
486
+ }
487
+ }
488
+ questionamento = p5.get("questionamento", "Preciso de mais informações.")
489
+ return f"🤔 PERGUNTA: {questionamento}", historico, dna
490
+
491
+ p6 = passo_6_ponderar(p2, p4, p5)
492
+ p7 = passo_7_sintetizar(p6)
493
+ p8 = passo_8_verificar(p6, p7)
494
+
495
+ resposta_final = p8.get("resposta_corrigida") or p7.get("resposta", "Não foi possível gerar uma resposta.")
496
+
497
+ novo_historico = historico + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": resposta_final}]
498
+ dna["historico_chat"].append({"user": pergunta, "assistant": resposta_final})
499
+ dna["meta"]["total_turnos"] += 1
500
+
501
+ logger.log("PIPELINE (NOVA) CONCLUÍDA COM SUCESSO", "SUCCESS")
502
+ return resposta_final, novo_historico, dna
503
+
504
+
505
+ def resumir_pipeline(esclarecimento_usuario: str, dna: Dict) -> Tuple[str, List, Dict]:
506
+ logger.log(f"RESUMINDO PIPELINE com esclarecimento: '{esclarecimento_usuario[:50]}...'", "START")
507
+
508
+ estado_salvo = dna['pipeline_state']['saved_data']
509
+ ponto_parada = dna['pipeline_state']['paused_at_step']
510
+ historico = estado_salvo['historico_original']
511
+ pergunta_original = estado_salvo['pergunta_original']
512
+
513
+ pergunta_atualizada = f"{pergunta_original}\n\n---ESCLARECIMENTO DO USUÁRIO---\n{esclarecimento_usuario}"
514
+
515
+ dna['pipeline_state'] = criar_dna()['pipeline_state']
516
+
517
+ try:
518
+ if ponto_parada == 'P2':
519
+ logger.log("Retomando a partir do passo P3...", "INFO")
520
+ p1, x1, x2 = estado_salvo['p1'], estado_salvo['x1'], estado_salvo['x2']
521
+ p2_novo = passo_2_cenarios(pergunta_atualizada, p1, x1, x2, historico)
522
+
523
+ p3 = passo_3_isolar_cenarios(p2_novo)
524
+ p4 = passo_4_cruzar_validacoes(p1, p2_novo, p3, x2)
525
+ p5 = passo_5_lacunas_finais(p1, p4)
526
+ p6 = passo_6_ponderar(p2_novo, p4, p5)
527
+
528
+ elif ponto_parada == 'P5':
529
+ logger.log("Retomando a partir do passo P6...", "INFO")
530
+ p1, p2, p4 = estado_salvo['p1'], estado_salvo['p2'], estado_salvo['p4']
531
+ p5_novo = passo_5_lacunas_finais(p1, p4)
532
+ p6 = passo_6_ponderar(p2, p4, p5_novo)
533
+
534
+ else:
535
+ raise ValueError(f"Ponto de parada desconhecido: {ponto_parada}")
536
+
537
+ p7 = passo_7_sintetizar(p6)
538
+ p8 = passo_8_verificar(p6, p7)
539
+
540
+ resposta_final = p8.get("resposta_corrigida") or p7.get("resposta", "Não foi possível gerar uma resposta.")
541
+
542
+ except Exception as e:
543
+ logger.log(f"Erro ao resumir a pipeline: {e}", "ERROR")
544
+ return f"Ocorreu um erro ao processar seu esclarecimento: {e}", historico, dna
545
+
546
+ novo_historico = historico + [{"role": "user", "content": pergunta_original}, {"role": "assistant", "content": resposta_final}]
547
+ dna["historico_chat"].append({"user": pergunta_original, "assistant": resposta_final})
548
+ dna["meta"]["total_turnos"] += 1
549
+
550
+ logger.log("PIPELINE (RESUMIDA) CONCLUÍDA COM SUCESSO", "SUCCESS")
551
+ return resposta_final, novo_historico, dna
552
+
553
+
554
+ def executar_pipeline(pergunta: str, historico: List[Dict], anexo: Any, dna: Dict) -> Tuple[str, List, Dict]:
555
+ if 'pipeline_state' not in dna or 'status' not in dna['pipeline_state']:
556
+ dna['pipeline_state'] = criar_dna()['pipeline_state']
557
+
558
+ if dna['pipeline_state']['status'] == 'paused':
559
+ return resumir_pipeline(pergunta, dna)
560
+ else:
561
+ return iniciar_nova_pipeline(pergunta, historico, anexo, dna)
562
+
563
+ # ============================================================================
564
+ # 6. INTERFACE COM GRADIO
565
+ # ============================================================================
566
+ def converter_historico_de_gradio(historico_gradio: List[List[str]]) -> List[Dict]:
567
+ historico_interno = []
568
+ if not historico_gradio:
569
+ return historico_interno
570
+ for turno in historico_gradio:
571
+ pergunta = turno[0] if turno and turno[0] is not None else ""
572
+ resposta = turno[1] if turno and len(turno) > 1 and turno[1] is not None else ""
573
+ if pergunta:
574
+ historico_interno.append({"role": "user", "content": pergunta})
575
+ if resposta:
576
+ historico_interno.append({"role": "assistant", "content": resposta})
577
+ return historico_interno
578
+
579
+ def converter_historico_para_gradio(historico_interno: List[Dict]) -> List[List[str]]:
580
+ historico_gradio = []
581
+ if not historico_interno:
582
+ return historico_gradio
583
+ for i in range(0, len(historico_interno), 2):
584
+ if i + 1 < len(historico_interno):
585
+ pergunta = historico_interno[i]['content']
586
+ resposta = historico_interno[i+1]['content']
587
+ historico_gradio.append([pergunta, resposta])
588
+ return historico_gradio
589
+
590
+ def chat_interface(pergunta: str, historico_gradio: List[List[str]], anexo: Any, dna_json_str: str) -> Tuple[List, str, str, None]:
591
+ try:
592
+ dna = json.loads(dna_json_str) if dna_json_str and dna_json_str.strip() else criar_dna()
593
+ except (json.JSONDecodeError, TypeError):
594
+ dna = criar_dna()
595
+
596
+ historico_interno = converter_historico_de_gradio(historico_gradio)
597
+
598
+ resposta, novo_historico_interno, dna_atualizado = executar_pipeline(pergunta, historico_interno, anexo, dna)
599
+
600
+ novo_historico_gradio = converter_historico_para_gradio(novo_historico_interno)
601
+
602
+ return novo_historico_gradio, "", json.dumps(dna_atualizado, indent=2, ensure_ascii=False), None
603
+
604
+ # --- Bloco de Execução Principal ---
605
+ if __name__ == "__main__":
606
+ with gr.Blocks(title="Pipeline v10 com Pausa/Retomada", theme=gr.themes.Soft()) as demo:
607
+ gr.Markdown(TITLE)
608
+
609
+ with gr.Row():
610
+ with gr.Column(scale=3):
611
+ chatbot = gr.Chatbot(label="Chat", height=600, bubble_full_width=False)
612
+ with gr.Column(scale=1):
613
+ dna_view = gr.Code(label="DNA (Estado da Conversa)", language="json", interactive=False,
614
+ value=json.dumps(criar_dna(), indent=2, ensure_ascii=False))
615
+ file_upload = gr.File(label="Anexar Arquivo")
616
+
617
+ with gr.Row():
618
+ input_textbox = gr.Textbox(
619
+ label="Digite sua pergunta ou esclarecimento aqui...",
620
+ placeholder="Ex: Qual a melhor estratégia para... / Sobre o ponto X, quero dizer que...",
621
+ lines=3,
622
+ scale=4,
623
+ )
624
+ submit_button = gr.Button("🚀 Enviar", variant="primary", scale=1)
625
+
626
+ dna_json_hidden = gr.Textbox(value=json.dumps(criar_dna()), visible=False)
627
+
628
+ submit_button.click(
629
+ fn=chat_interface,
630
+ inputs=[input_textbox, chatbot, file_upload, dna_json_hidden],
631
+ outputs=[chatbot, input_textbox, dna_json_hidden, file_upload]
632
+ )
633
+ input_textbox.submit(
634
+ fn=chat_interface,
635
+ inputs=[input_textbox, chatbot, file_upload, dna_json_hidden],
636
+ outputs=[chatbot, input_textbox, dna_json_hidden, file_upload]
637
+ )
638
+ dna_json_hidden.change(
639
+ fn=lambda x: x,
640
+ inputs=[dna_json_hidden],
641
+ outputs=[dna_view]
642
+ )
643
+
644
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)