caarleexx commited on
Commit
bcd3e5e
·
verified ·
1 Parent(s): b0c8126

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +490 -127
app.py CHANGED
@@ -9,192 +9,555 @@ import warnings
9
  import re
10
  warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
11
 
 
 
12
  # ============================================================================
13
  API_KEY = os.getenv("GOOGLE_API_KEY", "sua-chave-aqui")
14
  genai.configure(api_key=API_KEY)
15
- model = genai.GenerativeModel("gemini-2.0-flash")
 
 
 
16
 
 
 
17
  # ============================================================================
18
  class Logger:
19
- def __init__(self, verbose=True): self.verbose = verbose; self.logs = []
 
 
20
  def log(self, msg: str, level="INFO"):
21
  timestamp = datetime.now().strftime("%H:%M:%S")
22
- print(f"[{timestamp}] [{level}] {msg}")
 
 
 
23
 
24
  logger = Logger(verbose=True)
25
 
26
  # ============================================================================
27
- def processar_anexo(arquivo):
28
- if not arquivo: return "", "nenhum"
 
 
29
  try:
30
  caminho = str(arquivo)
31
- if caminho.endswith('.pdf'):
32
  try:
33
  import PyPDF2
34
  with open(caminho, 'rb') as f:
35
  leitor = PyPDF2.PdfReader(f)
36
- return "".join(p.extract_text()+"\n" for p in leitor.pages[:3])[:2000], "pdf"
37
- except: return "[PDF]", "pdf"
38
- if any(caminho.endswith(x) for x in ['.png','.jpg','.jpeg']):
 
39
  with open(caminho, 'rb') as f:
40
  return base64.b64encode(f.read()).decode()[:500], "imagem"
41
- except: pass
42
- return "", "erro"
 
 
43
 
44
- def construir_prompt(pergunta: str, anexo: str, tipo: str):
45
- if not anexo: return pergunta
46
- return f"ANEXO {tipo.upper()}:\n{anexo}\n\nPERGUNTA: {pergunta}"
 
 
 
 
 
47
 
48
- # ============================================================================
49
- def PARSER_SIMPLES(texto: str) -> Dict:
50
- """PARSER ULTRA SIMPLES - PEGA PRIMEIRO JSON"""
 
51
  if not texto: return {"erro": "vazio"}
52
 
53
- # 1. Regex simples
54
- match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', texto)
55
- if match:
56
- try: return json.loads(match.group(0))
57
- except: pass
58
-
59
- # 2. Primeiro { até último }
60
- ini = texto.find('{')
61
- fim = texto.rfind('}')
62
- if ini >= 0 and fim > ini:
63
- try: return json.loads(texto[ini:fim+1])
64
- except: pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- return {"erro": texto[:100]}
 
 
 
67
 
68
- def gemini_json(prompt: str, temp=0.4, tokens=1500) -> Dict:
69
- prompt += "\n\n**JSON PURO. SEM TEXTO.**"
 
 
 
 
 
 
 
70
  try:
71
- resp = model.generate_content(prompt,
72
  generation_config=genai.types.GenerationConfig(
73
- temperature=temp, max_output_tokens=tokens))
74
- raw = resp.text or ""
75
- logger.log(f"RAW({len(raw)}): {raw[:200]}...", "RAW")
76
- return PARSER_SIMPLES(raw)
 
 
 
 
 
 
 
77
  except Exception as e:
78
- logger.log(f"API ERRO: {e}", "ERROR")
 
79
  return {"erro": str(e)}
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  # ============================================================================
82
- def criar_dna(): return {
83
- "chat": [],
84
- "passos": {f"p{i}": [] for i in range(10)},
85
- "meta": {"turnos": 0}
86
- }
 
 
 
 
 
 
 
 
87
 
88
- def hist_compacto(hist: List):
89
- return "\n".join([str(h).split("\n")[0][:60] for h in hist[-2:]] or ["PRIMEIRO"])
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  # ============================================================================
92
- # PASSOS
93
- def p0_aluno(pergunta: str, hist: List):
94
- logger.log("🧠 P0", "TASK")
95
- return gemini_json(f"P0\nHIST: {hist_compacto(hist)}\nQ: {pergunta}\n{{\"relacao\": \"nova|continua\", \"peso\": \"alto|medio\"}}")
 
 
 
 
 
96
 
97
- def p1_triagem(pergunta: str, p0: Dict, hist: List):
98
- logger.log("📊 P1", "TASK")
99
- return gemini_json(f"P1\nP0: {json.dumps(p0)}\nQ: {pergunta}\n{{\"tipo\": \"factual|subjetiva\", \"conf\": \"alta|baixa\"}}")
100
 
101
- def x1_perguntas(pergunta: str, p1: Dict, hist: List):
102
- logger.log(" X1", "TASK")
103
- return gemini_json(f"X1\nP1: {json.dumps(p1)}\nQ: {pergunta}\n{{\"perguntas\": [{{\"t\": \"pergunta\", \"nec\": \"alta|baixa\"}}]}}")
 
 
104
 
105
- def x2_respostas(pergunta: str, p1: Dict, x1: Dict, hist: List):
106
- logger.log("X2", "TASK")
107
- return gemini_json(f"X2\nP1: {json.dumps(p1)}\nX1: {json.dumps(x1)}\n{{\"respostas\": [{{\"p\": \"pergunta\", \"r\": \"resposta\", \"conf\": \"alta\"}}]}}")
 
 
 
 
 
 
 
108
 
109
- def p2_cenarios(pergunta: str, p1: Dict, x1: Dict, x2: Dict, hist: List):
110
- logger.log("🎯 P2", "TASK")
111
- return gemini_json(f"P2\nP1: {json.dumps(p1)}\nX1: {json.dumps(x1)}\nX2: {json.dumps(x2)}\n{{\"cen\": {{\"prov\": [{{\"id\": \"C1\"}}], \"imp\": [{{\"id\": \"C2\"}}]}}}}")
 
 
 
 
 
 
 
112
 
113
- def p3_isolar(p2: Dict):
114
- logger.log("🔍 P3", "TASK")
115
- res = []
116
- for tipo in ['prov', 'imp']:
117
- for c in p2.get('cen', {}).get(tipo, [])[:1]:
118
- res.append(gemini_json(f"P3-{c.get('id')}\n{{\"id\": \"{c.get('id')}\", \"sol\": \"direta\"}}"))
119
- return {"isol": res}
 
 
 
 
 
 
 
120
 
121
- def p4_validar(p1: Dict, p2: Dict, p3: Dict):
122
- logger.log("✅ P4", "TASK")
123
- return gemini_json(f"P4\n{{\"conf\": [{{\"f\": \"fato\", \"n\": \"alta\"}}]}}")
124
 
125
- def p5_lacunas(p1: Dict, p4: Dict):
126
- logger.log("🚨 P5", "TASK")
127
- return gemini_json(f"P5\n{{\"lac\": [{{\"t\": \"lacuna\", \"c\": \"sim\"}}], \"dec\": \"cont|par\"}}")
128
 
129
- def p6_ponderar(p2: Dict, p4: Dict, p5: Dict):
130
- logger.log("⚖️ P6", "TASK")
131
- return gemini_json(f"P6\n{{\"win\": \"C1\", \"peso\": 0.8}}")
 
 
 
 
 
 
 
 
 
 
132
 
133
- def p7_sintetizar(p6: Dict):
134
- logger.log("✍️ P7", "TASK")
135
- return gemini_json(f"P7\nP6: {json.dumps(p6)}\n{{\"resp\": \"texto final\"}}", temp=0.7, tokens=2000)
 
 
 
 
 
 
 
136
 
137
- def p8_verificar(p7: Dict):
138
- logger.log("🔍 P8", "TASK")
139
- return gemini_json(f"P8\n{{\"ok\": true, \"final\": \"aprovado\"}}")
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  # ============================================================================
142
- def processar(pergunta: str, hist: List, anexo=None, dna=None):
143
- if dna is None: dna = criar_dna()
144
- logger.log(f"🚀 {pergunta[:40]}", "START")
145
 
146
- if not pergunta.strip(): return "Pergunta inválida", hist, dna
 
 
 
147
 
148
- a_c, a_t = processar_anexo(anexo)
149
- q = construir_prompt(pergunta, a_c, a_t)
150
 
151
- # PIPELINE
152
- p0 = p0_aluno(q, hist)
153
- p1 = p1_triagem(q, p0, hist)
154
- x1 = x1_perguntas(q, p1, hist)
155
- x2 = x2_respostas(q, p1, x1, hist)
156
 
157
- p2 = p2_cenarios(q, p1, x1, x2, hist)
158
- if p2.get("dec") == "par": return "Esclareça", hist, dna
 
159
 
160
- p3 = p3_isolar(p2)
161
- p4 = p4_validar(p1, p2, p3)
162
- p5 = p5_lacunas(p1, p4)
163
 
164
- if p5.get("dec") == "par": return "Lacuna", hist, dna
 
 
165
 
166
- p6 = p6_ponderar(p2, p4, p5)
167
- p7 = p7_sintetizar(p6)
168
- p8 = p8_verificar(p7)
 
 
169
 
170
- resp = p8.get("final") or p7.get("resp") or "Erro"
171
- novo_hist = hist + [{"role": "user", "content": pergunta}, {"role": "assistant", "content": resp}]
 
172
 
173
- dna["chat"].append({"u": pergunta, "a": resp})
174
- dna["meta"]["turnos"] += 1
 
175
 
176
- return resp, novo_hist, dna
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
  # ============================================================================
179
- def chat(msg: str, hist: List, anexo=None, dna_j: str="{}"):
180
- try: dna = json.loads(dna_j)
181
- except: dna = {}
182
- r, nh, nd = processar(msg, hist, anexo, dna)
183
- return nh, "", json.dumps(nd, indent=2)
 
 
 
 
 
 
 
 
184
 
185
- if __name__ == "__main__":
186
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
187
- gr.Markdown("# 🚀 v10 X1-X2")
188
- with gr.Row():
189
- with gr.Column(2):
190
- c = gr.Chatbot(height=500)
191
- dj = gr.Code(label="DNA", language="json")
192
- with gr.Column(1):
193
- i = gr.Textbox(label="Pergunta", lines=3)
194
- f = gr.File(file_types=[".pdf", ".png", ".jpg"])
195
- b = gr.Button("GO", variant="primary")
196
 
197
- b.click(chat, [i,c,f,dj], [c,i,dj,f])
198
- i.submit(chat, [i,c,f,dj], [c,i,dj,f])
 
 
 
 
 
 
 
 
 
 
 
199
 
200
- demo.launch(server_name="0.0.0.0", port=7860)
 
9
  import re
10
  warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
11
 
12
+ # ============================================================================
13
+ # CONFIGURAÇÃO
14
  # ============================================================================
15
  API_KEY = os.getenv("GOOGLE_API_KEY", "sua-chave-aqui")
16
  genai.configure(api_key=API_KEY)
17
+ counselor_model = genai.GenerativeModel("gemini-2.0-flash")
18
+ supervisor_model = genai.GenerativeModel("gemini-2.0-flash")
19
+
20
+ TITLE = "# 🚀 Pipeline v10 ATUALIZADA\n**P0-P1 → X1-X2 → P2-P8 (com Metacognição Pura)**"
21
 
22
+ # ============================================================================
23
+ # LOGGER MELHORADO
24
  # ============================================================================
25
  class Logger:
26
+ def __init__(self, verbose=True):
27
+ self.verbose = verbose
28
+ self.logs = []
29
  def log(self, msg: str, level="INFO"):
30
  timestamp = datetime.now().strftime("%H:%M:%S")
31
+ log_msg = f"[{timestamp}] [{level}] {msg}"
32
+ self.logs.append(log_msg)
33
+ if self.verbose: print(log_msg)
34
+ print("="*70)
35
 
36
  logger = Logger(verbose=True)
37
 
38
  # ============================================================================
39
+ # HELPERS ULTRA ROBUSTOS (DEPURAÇÃO)
40
+ # ============================================================================
41
+ def processar_anexo(arquivo) -> Tuple[str, str]:
42
+ if arquivo is None: return "", "nenhum"
43
  try:
44
  caminho = str(arquivo)
45
+ if caminho.lower().endswith('.pdf'):
46
  try:
47
  import PyPDF2
48
  with open(caminho, 'rb') as f:
49
  leitor = PyPDF2.PdfReader(f)
50
+ texto = "".join(pagina.extract_text() + "\n" for pagina in leitor.pages[:3])
51
+ return texto[:2000], "pdf"
52
+ except: return f"[PDF detectado]", "pdf"
53
+ elif any(caminho.lower().endswith(ext) for ext in ['.png','.jpg','.jpeg','.gif']):
54
  with open(caminho, 'rb') as f:
55
  return base64.b64encode(f.read()).decode()[:500], "imagem"
56
+ return "", "nao_suportado"
57
+ except Exception as e:
58
+ logger.log(f"Erro processar_anexo: {str(e)}", "ERROR")
59
+ return "", "erro"
60
 
61
+ def construir_prompt_com_anexo(pergunta: str, anexo_conteudo: str, tipo_anexo: str) -> str:
62
+ if not anexo_conteudo or tipo_anexo == "nenhum": return pergunta
63
+ if tipo_anexo == "pdf": return f"""DOCUMENTO:
64
+ {anexo_conteudo[:1800]}
65
+ ---
66
+ PERGUNTA: {pergunta}"""
67
+ return f"""ANEXO VISUAL:
68
+ PERGUNTA: {pergunta}"""
69
 
70
+ def parse_json_ultra_robusto(texto: str) -> Dict:
71
+ """Extrai QUALQUER JSON de texto bagunçado"""
72
+ logger.log(f"Parse: {len(texto)} chars", "DEBUG")
73
+ print(f"DEBUG PARSE INPUT: {texto[:300]}...")
74
  if not texto: return {"erro": "vazio"}
75
 
76
+ try:
77
+ # Regex múltiplos JSONs
78
+ matches = re.findall(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', texto, re.DOTALL)
79
+ for match in matches:
80
+ try:
81
+ resultado = json.loads(match)
82
+ logger.log(f"Parse OK via regex: {json.dumps(resultado, indent=2)[:200]}", "SUCCESS")
83
+ return resultado
84
+ except: continue
85
+
86
+ # JSON balanceado primeiro
87
+ inicio = texto.find('{')
88
+ if inicio != -1:
89
+ count, i = 1, inicio + 1
90
+ while i < len(texto) and count > 0:
91
+ if texto[i] == '{': count += 1
92
+ elif texto[i] == '}': count -= 1
93
+ i += 1
94
+ if count == 0:
95
+ resultado = json.loads(texto[inicio:i])
96
+ logger.log(f"Parse OK balanceado", "SUCCESS")
97
+ return resultado
98
+
99
+ # Fallback último JSON
100
+ fim = texto.rfind('}')
101
+ if fim != -1:
102
+ count, i = 1, fim - 1
103
+ while i >= 0 and count > 0:
104
+ if texto[i] == '}': count += 1
105
+ elif texto[i] == '{': count -= 1
106
+ i -= 1
107
+ if count == 0:
108
+ resultado = json.loads(texto[i+1:fim+1])
109
+ logger.log(f"Parse OK fallback", "SUCCESS")
110
+ return resultado
111
+
112
+ except Exception as e:
113
+ logger.log(f"Parse falhou: {str(e)}", "ERROR")
114
 
115
+ return {"erro": "parse_falhou", "fallback": texto[:200]}
116
+
117
+ def chamar_gemini_json(modelo, prompt_base: str, temperatura=0.4, max_tokens=1500) -> Dict:
118
+ prompt = f"""{prompt_base}
119
 
120
+ ---
121
+
122
+ **JSON PURO OBRIGATÓRIO**
123
+ SEM TEXTO EXTRA. APENAS:
124
+ {{"chave": "valor"}}"""
125
+
126
+ logger.log(f"Enviando prompt ({len(prompt)} chars): {prompt[:300]}...", "DEBUG")
127
+ print(f"DEBUG PROMPT GEMINI:\n{prompt}")
128
+
129
  try:
130
+ response = modelo.generate_content(prompt,
131
  generation_config=genai.types.GenerationConfig(
132
+ temperature=temperatura,
133
+ max_output_tokens=max_tokens))
134
+
135
+ resposta_bruta = response.text or ""
136
+ logger.log(f"Gemini RAW ({len(resposta_bruta)} chars): {resposta_bruta[:500]}...", "DEBUG")
137
+ print(f"DEBUG - RESPOSTA BRUTA GEMINI:\n{resposta_bruta}")
138
+
139
+ resultado = parse_json_ultra_robusto(resposta_bruta)
140
+ logger.log(f"Gemini PARSE OK: {json.dumps(resultado, indent=2)[:300]}", "SUCCESS")
141
+ return resultado
142
+
143
  except Exception as e:
144
+ logger.log(f"API erro: {str(e)}", "ERROR")
145
+ print(f"ERRO GEMINI: {str(e)}")
146
  return {"erro": str(e)}
147
 
148
+ def historico_compacto(historico: List) -> str:
149
+ if not historico: return "PRIMEIRO"
150
+ return "\n".join([str(msg).split("\n")[0][:80] for msg in historico[-2:]])
151
+
152
+ def criar_dna() -> Dict:
153
+ return {
154
+ "historico_chat": [],
155
+ "historico_passos": {f"passo{i}": [] for i in range(10)},
156
+ "memoria": {"resumo": ""},
157
+ "meta": {"total_turnos": 0, "ultimo_turno_limpeza": 0}
158
+ }
159
+
160
+ # ============================================================================
161
+ # PASSOS P0-P1 (COPIADOS DA VERSÃO ANTIGA - METACOGNIÇÃO PURA)
162
  # ============================================================================
163
+ def passo_0_aluno(pergunta: str, historico: List) -> Dict:
164
+ logger.log("🧠 P0-ALUNO - METACOGNIÇÃO PURA", "TASK")
165
+ print("="*70)
166
+ print("PASSO 0 ALUNO analisando feedback...")
167
+
168
+ turno_anterior = historico[-1] if len(historico) > 0 else None
169
+ memoria_contextual = "NA" # Pode ser expandido depois
170
+
171
+ prompt = f"""Você é um METACOGNITIVO (pensamento interno, NÃO comunicação).
172
+
173
+ TURNO ANTERIOR:
174
+ User: {turno_anterior['content'] if turno_anterior else 'NA'}
175
+ Assistant: {historico[-2]['content'] if len(historico) >= 2 else 'NA'}
176
 
177
+ NOVA MENSAGEM: {pergunta}
178
+ CONTEXTO VAGO: {historico_compacto(historico)}
179
 
180
+ ---
181
+
182
+ RESponda EM METACOGNIÇÃO PURA - TELEGRÁFICO
183
+ NÃO use frases completas. APENAS essência semântica com conectores mínimos.
184
+
185
+ EXEMPLO CERTO: entendeu-sim | pergunta-nova | avança-tópico | não-reformulou
186
+
187
+ RETORNE JSON:
188
+ {{
189
+ "usuario_entendeu": "sim|não",
190
+ "evidencias": ["entendeu-pergunta", "pediu-clarificação"],
191
+ "usuario_corrigiu": "sim|não",
192
+ "correcao_detectada": null|"texto-correção",
193
+ "correcao_valida": "sim|não|null",
194
+ "o_que_melhorar": null|"explicar-X-melhor",
195
+ "decisao": "prosseguir-passo1|reexplicar-passo6|atualizar-resposta-anterior",
196
+ "motivo": "texto-curtíssimo"
197
+ }}"""
198
+
199
+ return chamar_gemini_json(counselor_model, prompt, temperatura=0.5)
200
+
201
+ def passo_1_triagem(pergunta: str, p0: Dict, historico: List) -> Dict:
202
+ logger.log("📊 P1-TRIAGEM - METACOGNIÇÃO PURA", "TASK")
203
+ print("="*70)
204
+ print("PASSO 1 TRIAGEM...")
205
+
206
+ contexto_vago = historico_compacto(historico)
207
+ historico_recente = [{"user": msg["content"], "assistant": historico[i+1]["content"] if i+1 < len(historico) else "NA"}
208
+ for i, msg in enumerate(historico[-4:]) if msg["role"] == "user"]
209
+
210
+ prompt = f"""METACOGNIÇÃO - TRIAGEM INICIAL.
211
+
212
+ CONTEXTO VAGO: {contexto_vago}
213
+ HISTÓRICO RECENTE (últimas 3): {json.dumps(historico_recente[-3:], indent=2, ensure_ascii=False)}
214
+ P0: {json.dumps(p0, indent=2, ensure_ascii=False)}
215
+ PERGUNTA: {pergunta}
216
+
217
+ ---
218
+
219
+ CLASSIFIQUE EM TELEGRÁFICO (sem frases).
220
+
221
+ RETORNE JSON:
222
+ {{
223
+ "tipo": "objetiva|factual|subjetiva|aberta",
224
+ "sinais": ["tem-resposta-única-verificável", "sem-contexto-pessoal"],
225
+ "confianca": "alta|média|baixa",
226
+ "decisao": "responder-direto|analisar-profundamente",
227
+ "razao": "curtíssima",
228
+ "dados_fatuais": ["fato1", "fato2"],
229
+ "divergencias_fatuais": ["possível-ambiguidade-1"],
230
+ "objetivo_principal": "objetivo-primário-identificado",
231
+ "objetivo_secundario": ["objetivo-secundário-1"]
232
+ }}"""
233
+
234
+ return chamar_gemini_json(counselor_model, prompt, temperatura=0.5)
235
+
236
+ # ============================================================================
237
+ # NOVOS PASSOS X1-X2 (MELHORADOS)
238
  # ============================================================================
239
+ def passo_x1_perguntas_necessarias(pergunta: str, p1: Dict, historico: List) -> Dict:
240
+ """X1: Quais perguntas precisamos responder antes dos cenários?"""
241
+ logger.log(" X1-PERGUNTAS NECESSÁRIAS", "TASK")
242
+ print("="*70)
243
+
244
+ prompt = f"""X1-PERGUNTAS CRÍTICAS - TELEGRÁFICO
245
+ P1: {json.dumps(p1, indent=2)}
246
+ CONTEXTO: {historico_compacto(historico)}
247
+ PERGUNTA PRINCIPAL: {pergunta}
248
 
249
+ LACUNAS FACTUAIS/SUBJETIVAS:
 
 
250
 
251
+ {{"perguntas": [
252
+ {{"texto": "pergunta-curta", "necessidade": "alta|média|baixa", "relevancia": "alta|média"}}
253
+ ]}}"""
254
+
255
+ return chamar_gemini_json(counselor_model, prompt, max_tokens=2000)
256
 
257
+ def passo_x2_resolver_perguntas(pergunta: str, p1: Dict, x1: Dict, historico: List) -> Dict:
258
+ """X2: Responde as perguntas levantadas com confiança/conflito"""
259
+ logger.log(" X2-RESOLVER PERGUNTAS", "TASK")
260
+ print("="*70)
261
+
262
+ perguntas = x1.get("perguntas", [])
263
+ prompt = f"""X2-RESOLUÇÃO - TELEGRÁFICO
264
+ P1: {json.dumps(p1, indent=2)}
265
+ PERGUNTAS X1: {json.dumps(perguntas, indent=2)}
266
+ CONTEXTO: {historico_compacto(historico)}
267
 
268
+ PARA CADA PERGUNTA:
269
+ {{"respostas": [
270
+ {{"pergunta": "texto-original",
271
+ "resposta": "curta-direta",
272
+ "confianca": "alta|média|baixa",
273
+ "conflito": "alto|médio|baixo",
274
+ "razao": "1-2-palavras"}}
275
+ ]}}"""
276
+
277
+ return chamar_gemini_json(counselor_model, prompt, max_tokens=2000)
278
 
279
+ # ============================================================================
280
+ # PASSOS P2-P8 (COPIADOS E ADAPTADOS DA VERSÃO ANTIGA)
281
+ # ============================================================================
282
+ def passo_2_cenarios(pergunta: str, p1: Dict, x1: Dict, x2: Dict, historico: List) -> Dict:
283
+ logger.log("🎯 P2-CENÁRIOS (com X1-X2)", "TASK")
284
+ print("="*70)
285
+ print("PASSO 2 CENÁRIOS...")
286
+
287
+ prompt = f"""METACOGNIÇÃO - CENÁRIOS.
288
+ CONTEXTO VAGO: {historico_compacto(historico)}
289
+ TRIAGEM P1: {json.dumps(p1, indent=2)}
290
+ X1-PERGUNTAS: {json.dumps(x1, indent=2)}
291
+ X2-RESPOSTAS: {json.dumps(x2, indent=2)}
292
+ PERGUNTA: {pergunta}
293
 
294
+ ---
 
 
295
 
296
+ MAPEIE CENÁRIOS ONDE RESPOSTA MUDA - TELEGRÁFICO
 
 
297
 
298
+ RETORNE JSON:
299
+ {{"cenarios": {{"provaveis": [{{"id": "C1", "desc": "cenário-1-comprimido", "contexto-relevante": "X"}}],
300
+ "improvaveis": [{{"id": "C2", "desc": "cenário-2-comprimido"}}]},
301
+ "total": 2,
302
+ "tipo_resposta": "múltipla|unívoca",
303
+ "confianca": "alta|média|baixa",
304
+ "decisao": "prosseguir|pedir-esclarecimento",
305
+ "pergunta_esclarecimento": null|"texto-pergunta"
306
+ }}"""
307
+
308
+ resultado = chamar_gemini_json(counselor_model, prompt, temperatura=0.6)
309
+ logger.log(f"Cenários: {resultado.get('cenarios', {}).get('total', 0)}", "INFO")
310
+ return resultado
311
 
312
+ def passo_3_isolar_cenarios(p2: Dict) -> Dict:
313
+ logger.log("🔍 P3-ISOLAR CENÁRIOS", "TASK")
314
+ print("="*70)
315
+ print("PASSO 3 EXPLORAÇÃO...")
316
+
317
+ exploracoes = []
318
+ for tipo in ['provaveis', 'improvaveis']:
319
+ for c in p2.get('cenarios', {}).get(tipo, [])[:2]:
320
+ prompt = f"""METACOGNIÇÃO - EXPLORAÇÃO.
321
+ CENÁRIO P2: {json.dumps(c, indent=2)}
322
 
323
+ PARA ESTE CENÁRIO, RESPONDA ESSÊNCIA - TELEGRÁFICO
 
 
324
 
325
+ {{"id": "{c.get('id')}",
326
+ "resposta_essencia": "palavra-chave-razoes",
327
+ "confianca": "alta|média|baixa",
328
+ "lacunas": "contexto-ausente|null"
329
+ }}"""
330
+ exploracoes.append(chamar_gemini_json(counselor_model, prompt, temperatura=0.6))
331
+
332
+ logger.log(f"P3: {len(exploracoes)} explorações isoladas", "INFO")
333
+ return {"isolados": exploracoes}
334
+
335
+ def passo_4_cruzar_validacoes(p1: Dict, p2: Dict, p3: Dict, x2: Dict) -> Dict:
336
+ logger.log("✅ P4-VALIDAR (Princípios/Símbolos)", "TASK")
337
+ print("="*70)
338
+ print("PASSO 4 PRINCÍPIOS...")
339
+
340
+ prompt = f"""METACOGNIÇÃO - CONHECIMENTO FUNDAMENTAL.
341
+ P1: {json.dumps(p1, indent=2)}
342
+ P2: {json.dumps(p2, indent=2)}
343
+ P3: {json.dumps(p3, indent=2)}
344
+ X2: {json.dumps(x2, indent=2)}
345
+
346
+ ---
347
+
348
+ IDENTIFIQUE PRINCÍPIOS E SÍMBOLOS - TELEGRÁFICO
349
+
350
+ RETORNE JSON:
351
+ {{"principios": [{{"nome": "Custo-Oportunidade", "essencia": "escolher-X-renunciar-Y"}}],
352
+ "simbolos": [{{"nome": "Jornada-Herói", "essencia": "transformação-iminente"}}],
353
+ "principio_central": "nome",
354
+ "simbolo_dominante": "nome"
355
+ }}"""
356
+
357
+ return chamar_gemini_json(counselor_model, prompt, temperatura=0.6)
358
+
359
+ def passo_5_lacunas_finais(p1: Dict, p4: Dict) -> Dict:
360
+ logger.log("🚨 P5-LACUNAS FINAIS (Metacognição)", "TASK")
361
+ print("="*70)
362
+ print("PASSO 5 METACOGNIÇÃO...")
363
+
364
+ prompt = f"""METACOGNIÇÃO - CERTEZAS vs DÚVIDAS.
365
+ P1: {json.dumps(p1, indent=2)}
366
+ P4: {json.dumps(p4, indent=2)}
367
+
368
+ ---
369
+
370
+ AVALIE CERTEZAS vs DÚVIDAS - TELEGRÁFICO
371
+
372
+ RETORNE JSON:
373
+ {{"analise_cenarios": [{{"cenario": "C1", "certezas": ["certeza1"], "duvidas": ["dúvida1"]}}],
374
+ "confianca_global": "alta|média|baixa",
375
+ "balanco": "certezas-superam|equilibrado",
376
+ "decisao": "responder|questionar",
377
+ "questionamento": null|"texto"
378
+ }}"""
379
+
380
+ return chamar_gemini_json(counselor_model, prompt, temperatura=0.5)
381
+
382
+ def passo_6_ponderar(p2: Dict, p4: Dict, p5: Dict) -> Dict:
383
+ logger.log("⚖️ P6-PONDERAR (Juiz da Verdade)", "TASK")
384
+ print("="*70)
385
+ print("PASSO 6 JUIZ DA VERDADE...")
386
+
387
+ prompt = f"""METACOGNIÇÃO - ARBITRAGEM SOCRÁTICA.
388
+ P2: {json.dumps(p2, indent=2)}
389
+ P4: {json.dumps(p4, indent=2)}
390
+ P5: {json.dumps(p5, indent=2)}
391
+
392
+ ---
393
+
394
+ VALIDE VERDADES SOCRÁTICAS - TELEGRÁFICO
395
+
396
+ RETORNE JSON:
397
+ {{"verdade1_nao_sei": "validada-true|false",
398
+ "evidencias": "P3-respostas-P4-principios",
399
+ "confianca": "alta|média|baixa",
400
+ "decisao": "exibir-resposta|exibir-falhas|reprocessar",
401
+ "nivel_consciencia": "alto|médio|baixo"
402
+ }}"""
403
+
404
+ return chamar_gemini_json(counselor_model, prompt, temperatura=0.5)
405
+
406
+ def passo_7_sintetizar(p6: Dict) -> Dict:
407
+ logger.log("✍️ P7-SINTETIZAR (Prosa Humanizada)", "TASK")
408
+ print("="*70)
409
+ print("SINTETIZADOR - Transformando metacognição em prosa...")
410
+
411
+ prompt = f"""Você é um SINTETIZADOR - transforma METACOGNIÇÃO CRUA em PROSA HUMANIZADA.
412
+
413
+ JUIZ P6: {json.dumps(p6, indent=2)}
414
+
415
+ ---
416
+
417
+ TAREFA: TRANSFORMAR METACOGNIÇÃO → PROSA HUMANIZADA
418
+
419
+ INSTRUÇÕES:
420
+ 1. Adicione conectores naturais (porque, portanto, isso significa)
421
+ 2. Expanda abreviações (curva-aprendizado → curva de aprendizado)
422
+ 3. Humanize números (3-6m → 3 a 6 meses)
423
+ 4. Estruture em parágrafos (introdução-desenvolvimento-nuances)
424
+ 5. Integre princípios naturalmente
425
+ 6. Tom: Amigo, empático, empoderador
426
+
427
+ GENRE RESPOSTA FINAL (texto fluido, natural, humano):"""
428
+
429
+ resposta_raw = chamar_gemini_json(counselor_model, prompt, temperatura=0.8, max_tokens=2500)
430
+ logger.log(f"P7: Resposta gerada ({len(resposta_raw.get('resposta', ''))} chars)", "INFO")
431
+ return resposta_raw
432
+
433
+ def passo_8_verificar(p7: Dict) -> Dict:
434
+ logger.log("🔍 P8-FINAL (Verificador)", "TASK")
435
+ print("="*70)
436
+ print("PASSO 8 VERIFICAÇÃO FINAL...")
437
+
438
+ resposta_sintetizada = p7.get("resposta", "")
439
+
440
+ prompt = f"""Você é um VERIFICADOR FINAL - guardião de qualidade.
441
+
442
+ RESPOSTA SINTETIZADA: {resposta_sintetizada}
443
+ ANÁLISE P6: {json.dumps(p7, indent=2)}
444
+
445
+ ---
446
+
447
+ VERIFICAÇÃO TRIPLA (devolver JSON):
448
+ 1. VERIFICAÇÃO FACTUAL: Fatos incorretos?
449
+ 2. VERIFICAÇÃO LÓGICA: Falácias? Saltos injustificados?
450
+ 3. VERIFICAÇÃO ÉTICA: Apropriada? Disclaimers?
451
+
452
+ RETORNE JSON:
453
+ {{"verificacao_factual": {{"aprovada": "true|false", "problemas": []}},
454
+ "verificacao_logica": {{"aprovada": "true|false", "problemas": []}},
455
+ "verificacao_etica": {{"aprovada": "true|false", "problemas": []}},
456
+ "todas_aprovadas": "true|false",
457
+ "decisao": "exibir-resposta|corrigir|reexibir",
458
+ "resposta_corrigida": null|"versão-corrigida"
459
+ }}"""
460
+
461
+ return chamar_gemini_json(supervisor_model, prompt, temperatura=0.5)
462
+
463
+ # ============================================================================
464
+ # ORQUESTADOR PRINCIPAL v10 (SEM TRY-EXCEPT PARA DEPURAÇÃO)
465
  # ============================================================================
466
+ def processar_pipeline(pergunta: str, historico: List, arquivo_anexo=None, dna=None) -> Tuple[str, List, Dict]:
467
+ if dna is None or not isinstance(dna, dict):
468
+ dna = criar_dna()
469
 
470
+ logger.log(f"🚀 v10 ATUALIZADA: {pergunta[:50]}", "START")
471
+ print("="*70)
472
+ print("PIPELINE PRINCIPAL v10 ATUALIZADA")
473
+ print("="*70)
474
 
475
+ if not pergunta.strip():
476
+ return "Pergunta inválida", historico, dna
477
 
478
+ anexo_c, tipo_a = processar_anexo(arquivo_anexo)
479
+ pergunta_final = construir_prompt_com_anexo(pergunta, anexo_c, tipo_a)
 
 
 
480
 
481
+ # P0-P1 (Metacognição Pura)
482
+ p0 = passo_0_aluno(pergunta_final, historico)
483
+ logger.log(f"P0 Decisão: {p0.get('decisao', 'NA')}", "INFO")
484
 
485
+ p1 = passo_1_triagem(pergunta_final, p0, historico)
486
+ logger.log(f"P1 Tipo: {p1.get('tipo', 'NA')} | Decisão: {p1.get('decisao', 'NA')}", "INFO")
 
487
 
488
+ # X1-X2 (Perguntas Críticas)
489
+ x1 = passo_x1_perguntas_necessarias(pergunta_final, p1, historico)
490
+ x2 = passo_x2_resolver_perguntas(pergunta_final, p1, x1, historico)
491
 
492
+ # P2-P8 (Fluxo Principal com Metacognição)
493
+ p2 = passo_2_cenarios(pergunta_final, p1, x1, x2, historico)
494
+ if p2.get("decisao") == "pedir-esclarecimento":
495
+ esclarecimento = p2.get("pergunta_esclarecimento", "Esclareça")
496
+ return esclarecimento, historico, dna
497
 
498
+ p3 = passo_3_isolar_cenarios(p2)
499
+ p4 = passo_4_cruzar_validacoes(p1, p2, p3, x2)
500
+ p5 = passo_5_lacunas_finais(p1, p4)
501
 
502
+ if p5.get("decisao") == "questionar":
503
+ questionamento = p5.get("questionamento", "Lacuna crítica")
504
+ return questionamento, historico, dna
505
 
506
+ p6 = passo_6_ponderar(p2, p4, p5)
507
+ p7 = passo_7_sintetizar(p6)
508
+ p8 = passo_8_verificar(p7)
509
+
510
+ # Decisão Final P8
511
+ if p8.get("todas_aprovadas") == "true" or p8.get("decisao") == "exibir-resposta":
512
+ resposta = p7.get("resposta", "Resposta gerada")
513
+ else:
514
+ resposta = p8.get("resposta_corrigida", p7.get("resposta", "Erro na verificação"))
515
+
516
+ novo_hist = historico + [{"role": "user", "content": pergunta},
517
+ {"role": "assistant", "content": resposta}]
518
+
519
+ dna["historico_chat"].append({"u": pergunta, "a": resposta})
520
+ dna["meta"]["total_turnos"] += 1
521
+
522
+ logger.log("✅ PIPELINE CONCLUÍDA", "SUCCESS")
523
+ print("="*70)
524
+ print("RESPOSTA FINAL:")
525
+ print(resposta)
526
+ print("="*70)
527
+
528
+ return resposta, novo_hist, dna
529
 
530
  # ============================================================================
531
+ # INTERFACE (DEPURAÇÃO)
532
+ # ============================================================================
533
+ def chat_interface(msg: str, hist: List, anexo=None, dna_json: str="{}") -> Tuple[List, str, str, None]:
534
+ print(f"\n🎯 NOVA MENSAGEM: {msg[:100]}...")
535
+ try:
536
+ dna = json.loads(dna_json) if dna_json else {}
537
+ except Exception as e:
538
+ print(f"Erro DNA: {e}")
539
+ dna = {}
540
+
541
+ resp, novo_hist, dna_new = processar_pipeline(msg, hist, anexo, dna)
542
+ print(f"✅ RESPOSTA ENVIADA: {len(resp)} chars")
543
+ return novo_hist, "", json.dumps(dna_new, indent=2), None
544
 
545
+ __name__ == "__main__":
546
+ with gr.Blocks(title="Pipeline v10 ATUALIZADA", theme=gr.themes.Soft()) as demo:
547
+ gr.Markdown(TITLE + "\n**Metacognição Pura + X1-X2 + Verificação Final**")
 
 
 
 
 
 
 
 
548
 
549
+ with gr.Row():
550
+ with gr.Column(scale=2):
551
+ chat = gr.Chatbot(height=550, type="messages")
552
+ dna_view = gr.Code(label="DNA", language="json", lines=12)
553
+ with gr.Column(scale=1):
554
+ input_txt = gr.Textbox(label="Pergunta", lines=3, scale=2)
555
+ file_up = gr.File(label="Anexo", file_types=[".pdf",".png",".jpg"])
556
+ btn_go = gr.Button("🚀 v10 ATUALIZADA", variant="primary")
557
+
558
+ btn_go.click(chat_interface, [input_txt, chat, file_up, dna_view],
559
+ [chat, input_txt, dna_view, file_up])
560
+ input_txt.submit(chat_interface, [input_txt, chat, file_up, dna_view],
561
+ [chat, input_txt, dna_view, file_up])
562
 
563
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)