caarleexx commited on
Commit
b8ad8b1
·
verified ·
1 Parent(s): 118bb26

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +325 -177
app.py CHANGED
@@ -1,8 +1,13 @@
1
  import os
2
  import json
 
3
  import gradio as gr
4
  import google.generativeai as genai
5
  from typing import Dict, List, Optional
 
 
 
 
6
 
7
  # ==================== CONFIGURAÇÃO DA API GEMINI ====================
8
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI"))
@@ -11,7 +16,7 @@ supervisor_model = genai.GenerativeModel("gemini-flash-latest")
11
 
12
  # ==================== HISTÓRICOS GLOBAIS ====================
13
  historico_dialogo: List[Dict] = []
14
- historico_passo1_clareza: List[Dict] = []
15
  historico_passo2_proposito: List[Dict] = []
16
  historico_passo3_motivacao: List[Dict] = []
17
  historico_passo4_ambiguidade: List[Dict] = []
@@ -31,37 +36,90 @@ CONTEXTO_FILOSOFICO = carregar_contexto_filosofico()
31
 
32
  # ==================== PROMPTS PARA CADA PASSO ====================
33
 
34
- def prompt_passo1_clareza(historico_dialogo: List[Dict], historico_passo1_completo: List[Dict], input_usuario: str) -> str:
35
- """Prompt para análise de CLAREZA: Eu entendi o que o usuário quer?"""
36
  return f"""
37
- Você é um analisador de CLAREZA em uma pipeline de raciocínio multi-etapa.
38
 
39
- Sua única tarefa: avaliar se você entendeu claramente o que o usuário está perguntando ou expressando.
40
 
41
  HISTÓRICO DO CHAT (perguntas e respostas ao usuário):
42
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
43
 
44
- HISTÓRICO COMPLETO DO PASSO 1 (todas as suas análises anteriores de clareza com justificativas):
45
  {json.dumps(historico_passo1_completo, indent=2, ensure_ascii=False)}
46
 
47
  NOVO INPUT DO USUÁRIO:
48
  "{input_usuario}"
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  INSTRUÇÕES:
51
- 1. Analise o histórico completo do PASSO 1 para entender como a clareza evoluiu ao longo da conversa.
52
- 2. Se este é o primeiro turno ou se o usuário mudou completamente de assunto, analise a clareza do novo input isoladamente.
53
- 3. Se o usuário está respondendo a uma pergunta sua anterior, reavalie: a clareza aumentou? Ele esclareceu o que você precisava?
54
- 4. Se o input é ambíguo, vago ou tem múltiplas interpretações possíveis, classifique como confiança "baixa".
55
- 5. Se você entendeu perfeitamente, classifique como "alta".
56
 
57
- RETORNE APENAS UM JSON VÁLIDO NO FORMATO:
58
  {{
59
- "justificativa": "Explique detalhadamente POR QUE você classificou assim, referenciando o histórico e evolução da clareza",
60
- "analise": "Resumo da sua análise sobre a clareza do input",
61
- "confianca": "baixa|media|alta",
62
- "decisao": "prosseguir|pedir_esclarecimento",
63
- "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, coloque aqui uma pergunta natural e conversacional. Caso contrário, null."
64
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  """
66
 
67
  def prompt_passo2_proposito(historico_dialogo: List[Dict], historico_passo2_completo: List[Dict], ultimo_p1: Dict) -> str:
@@ -74,28 +132,30 @@ Sua única tarefa: identificar PARA QUE o usuário está fazendo essa pergunta o
74
  HISTÓRICO DO CHAT:
75
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
76
 
77
- ÚLTIMO REGISTRO DO PASSO 1 (Clareza):
78
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
79
 
80
- HISTÓRICO COMPLETO DO PASSO 2 (todas as suas análises anteriores de propósito com justificativas):
81
  {json.dumps(historico_passo2_completo, indent=2, ensure_ascii=False)}
82
 
83
  INSTRUÇÕES:
84
- 1. Analise o histórico completo do PASSO 2 para entender como o propósito foi refinado ao longo da conversa.
85
- 2. Use a última análise de CLAREZA (Passo 1) para informar sua decisão.
86
  3. Tente inferir o propósito do usuário. Exemplos: "tomar uma decisão", "aprender algo novo", "validar uma crença", "resolver um problema prático", "desabafar".
87
  4. Se você tem alta confiança sobre o propósito, classifique como "alta".
88
- 5. Se não consegue determinar com certeza, classifique como "baixa" ou "media" e considere pedir esclarecimento.
89
 
90
- RETORNE APENAS UM JSON VÁLIDO NO FORMATO:
91
  {{
92
- "justificativa": "Explique detalhadamente POR QUE você inferiu esse propósito, referenciando o histórico e a análise de clareza",
93
  "analise": "Resumo da sua análise sobre o propósito",
94
  "proposito_inferido": "Descrição do propósito ou null se incerto",
95
  "confianca": "baixa|media|alta",
96
  "decisao": "prosseguir|pedir_esclarecimento",
97
  "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, coloque aqui uma pergunta natural. Caso contrário, null."
98
  }}
 
 
99
  """
100
 
101
  def prompt_passo3_motivacao(historico_dialogo: List[Dict], historico_passo3_completo: List[Dict], ultimo_p1: Dict, ultimo_p2: Dict) -> str:
@@ -108,45 +168,45 @@ Sua única tarefa: identificar POR QUE o usuário está interessado nisso agora.
108
  HISTÓRICO DO CHAT:
109
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
110
 
111
- ÚLTIMO REGISTRO DO PASSO 1 (Clareza):
112
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
113
 
114
  ÚLTIMO REGISTRO DO PASSO 2 (Propósito):
115
  {json.dumps(ultimo_p2, indent=2, ensure_ascii=False)}
116
 
117
- HISTÓRICO COMPLETO DO PASSO 3 (todas as suas análises anteriores de motivação com justificativas):
118
  {json.dumps(historico_passo3_completo, indent=2, ensure_ascii=False)}
119
 
120
  INSTRUÇÕES:
121
- 1. Analise o histórico completo do PASSO 3 para entender como a motivação foi identificada ao longo da conversa.
122
- 2. Use as últimas análises de CLAREZA e PROPÓSITO para informar sua decisão.
123
- 3. Tente identificar o gatilho emocional, contextual ou situacional que levou o usuário a fazer essa pergunta.
124
- 4. Exemplos de motivação: "frustração com situação atual", "curiosidade intelectual", "urgência de decisão", "busca por validação", "experiência recente".
125
- 5. Se você tem alta confiança sobre a motivação, classifique como "alta".
126
- 6. Se não consegue determinar, classifique como "baixa" ou "media".
127
-
128
- RETORNE APENAS UM JSON VÁLIDO NO FORMATO:
129
  {{
130
- "justificativa": "Explique detalhadamente POR QUE você identificou essa motivação, referenciando o histórico e análises anteriores",
131
  "analise": "Resumo da sua análise sobre a motivação",
132
  "motivacao_inferida": "Descrição da motivação ou null se incerto",
133
  "confianca": "baixa|media|alta",
134
  "decisao": "prosseguir|pedir_esclarecimento",
135
  "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, coloque aqui uma pergunta natural. Caso contrário, null."
136
  }}
 
 
137
  """
138
 
139
  def prompt_passo4_ambiguidade(historico_dialogo: List[Dict], historico_passo4_completo: List[Dict], ultimo_p1: Dict, ultimo_p2: Dict, ultimo_p3: Dict) -> str:
140
- """Prompt para análise de AMBIGUIDADE: Existem múltiplos cenários válidos que precisam ser desambiguados?"""
141
  return f"""
142
  Você é um analisador de AMBIGUIDADE DE CENÁRIO em uma pipeline de raciocínio multi-etapa.
143
 
144
- Sua única tarefa: detectar se a pergunta/situação do usuário tem múltiplas interpretações ou cenários válidos que precisam ser esclarecidos antes de dar uma resposta final.
145
 
146
  HISTÓRICO DO CHAT:
147
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
148
 
149
- ÚLTIMO REGISTRO DO PASSO 1 (Clareza):
150
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
151
 
152
  ÚLTIMO REGISTRO DO PASSO 2 (Propósito):
@@ -155,32 +215,29 @@ HISTÓRICO DO CHAT:
155
  ÚLTIMO REGISTRO DO PASSO 3 (Motivação):
156
  {json.dumps(ultimo_p3, indent=2, ensure_ascii=False)}
157
 
158
- HISTÓRICO COMPLETO DO PASSO 4 (todas as suas análises anteriores de ambiguidade com justificativas):
159
  {json.dumps(historico_passo4_completo, indent=2, ensure_ascii=False)}
160
 
161
  INSTRUÇÕES:
162
- 1. Analise o histórico completo do PASSO 4 para entender como a ambiguidade foi tratada ao longo da conversa.
163
- 2. Use as últimas análises de CLAREZA, PROPÓSITO e MOTIVAÇÃO para informar sua decisão.
164
- 3. Analise se a resposta pode variar significativamente dependendo de uma perspectiva, contexto ou premissa não explicitada.
165
- 4. Exemplos de ambiguidade:
166
- - "Me fale sobre carros" pode ser engenharia, história, compra, manutenção, esportes...
167
- - "Como lidar com ansiedade?" → pode ser clínica, filosófica, técnicas práticas, contexto profissional...
168
- 5. Se há UMA ÚNICA resposta clara e direta possível, classifique confiança como "alta" e decisao "prosseguir_resposta_final".
169
- 6. Se há múltiplos cenários válidos, classifique confiança como "baixa" e decisao "pedir_esclarecimento".
170
-
171
- RETORNE APENAS UM JSON VÁLIDO NO FORMATO:
172
  {{
173
- "justificativa": "Explique detalhadamente POR QUE você identificou (ou não) ambiguidade, referenciando o histórico e análises anteriores",
174
- "analise": "Resumo da sua análise sobre possíveis ambiguidades",
175
- "cenarios_possiveis": ["cenário 1", "cenário 2", ...] ou null se não há ambiguidade,
176
  "confianca": "baixa|media|alta",
177
  "decisao": "prosseguir_resposta_final|pedir_esclarecimento",
178
- "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, coloque aqui uma pergunta aberta e natural. Caso contrário, null."
179
  }}
 
 
180
  """
181
 
182
  def prompt_sintetizador(historico_dialogo: List[Dict], ultimo_p1: Dict, ultimo_p2: Dict, ultimo_p3: Dict, ultimo_p4: Dict, modo_sabio: bool) -> str:
183
- """Prompt para SÍNTESE FINAL: gera a resposta textual ao usuário."""
184
  contexto_filosofico_str = ""
185
  if modo_sabio and CONTEXTO_FILOSOFICO:
186
  contexto_filosofico_str = f"""
@@ -189,18 +246,18 @@ def prompt_sintetizador(historico_dialogo: List[Dict], ultimo_p1: Dict, ultimo_p
189
 
190
  IMPORTANTE: Use este contexto como sua bússola interna. NÃO cite Epicteto explicitamente.
191
  Sua resposta deve soar como um amigo sábio, não como um professor de filosofia.
192
- Seja conversacional, empático, use humor leve quando apropriado, e empodere o usuário a encontrar suas próprias respostas.
193
  """
194
 
195
  return f"""
196
  Você é um SINTETIZADOR FINAL de uma pipeline de análise conversacional.
197
 
198
- Sua tarefa: gerar uma resposta textual natural, completa e útil ao usuário, baseada nas análises consolidadas.
199
 
200
  HISTÓRICO DO CHAT COMPLETO:
201
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
202
 
203
- ÚLTIMO REGISTRO DO PASSO 1 (Clareza):
204
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
205
 
206
  ÚLTIMO REGISTRO DO PASSO 2 (Propósito):
@@ -217,10 +274,10 @@ HISTÓRICO DO CHAT COMPLETO:
217
  INSTRUÇÕES:
218
  1. Analise as justificativas e decisões dos 4 passos.
219
  2. Se algum passo indicou "pedir_esclarecimento", RETORNE APENAS A PERGUNTA DE ESCLARECIMENTO (priorize o passo com menor confiança).
220
- 3. Se todos os passos indicaram "prosseguir" ou "prosseguir_resposta_final", gere uma resposta completa e bem fundamentada.
221
- 4. {"Se Modo Sábio estiver ativo, aplique a persona de Amigo Sábio conforme o contexto filosófico acima." if modo_sabio else "Use tom informativo e objetivo."}
222
- 5. Sua resposta deve ser APENAS o texto final para o usuário. NÃO retorne JSON aqui.
223
- 6. Use as justificativas dos passos para entender profundamente o contexto e intenção do usuário.
224
 
225
  RESPOSTA FINAL:
226
  """
@@ -237,95 +294,204 @@ def chamada_gemini(model, prompt: str) -> str:
237
 
238
  # ==================== FUNÇÃO AUXILIAR: PARSE SEGURO DE JSON ====================
239
  def parse_json_seguro(texto: str) -> Dict:
240
- """Tenta extrair JSON de resposta do LLM, mesmo se vier com markdown ou ruído."""
 
 
241
  try:
242
- # Remove possíveis blocos markdown ``````
243
- texto = texto.replace("``````", "").strip()
 
 
 
244
  return json.loads(texto)
 
245
  except json.JSONDecodeError as e:
246
- print(f"⚠ Erro ao parsear JSON: {e}")
247
- print(f"Texto recebido: {texto[:300]}...")
248
- return {"erro": "Resposta inválida do modelo", "texto_original": texto}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
  # ==================== PIPELINE PRINCIPAL ====================
251
  def processar_turno(mensagem_usuario: str, modo_sabio: bool = False, modo_supervisor: bool = True) -> str:
252
- """
253
- Processa um turno completo do usuário através da pipeline fragmentada.
254
- Retorna a resposta textual final para exibição.
255
- """
256
- global historico_dialogo, historico_passo1_clareza, historico_passo2_proposito
257
  global historico_passo3_motivacao, historico_passo4_ambiguidade
258
 
259
- # 1. Registrar input do usuário no histórico de diálogo
260
  historico_dialogo.append({"user": mensagem_usuario, "assistant": None})
261
 
262
  print("\n" + "="*70)
263
  print(f"📩 NOVO INPUT DO USUÁRIO: {mensagem_usuario}")
264
  print("="*70)
265
 
266
- # 2. PASSO 1: Análise de CLAREZA
267
- print("\n🔍 PASSO 1: Analisando CLAREZA...")
268
- prompt1 = prompt_passo1_clareza(historico_dialogo[:-1], historico_passo1_clareza, mensagem_usuario)
269
  resposta_p1 = chamada_gemini(counselor_model, prompt1)
270
  analise_p1 = parse_json_seguro(resposta_p1)
271
- historico_passo1_clareza.append(analise_p1)
272
- print(f" Confiança: {analise_p1.get('confianca', 'N/A')}")
273
- print(f" Decisão: {analise_p1.get('decisao', 'N/A')}")
 
 
 
 
 
274
  print(f" Justificativa: {analise_p1.get('justificativa', 'N/A')[:100]}...")
275
 
276
- # 3. PASSO 2: Análise de PROPÓSITO
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  print("\n🎯 PASSO 2: Analisando PROPÓSITO...")
278
- ultimo_p1 = historico_passo1_clareza[-1]
279
- prompt2 = prompt_passo2_proposito(historico_dialogo[:-1], historico_passo2_proposito, ultimo_p1)
280
  resposta_p2 = chamada_gemini(counselor_model, prompt2)
281
  analise_p2 = parse_json_seguro(resposta_p2)
 
 
282
  historico_passo2_proposito.append(analise_p2)
283
  print(f" Confiança: {analise_p2.get('confianca', 'N/A')}")
284
  print(f" Decisão: {analise_p2.get('decisao', 'N/A')}")
285
  print(f" Justificativa: {analise_p2.get('justificativa', 'N/A')[:100]}...")
286
 
287
- # 4. PASSO 3: Análise de MOTIVAÇÃO
288
  print("\n💡 PASSO 3: Analisando MOTIVAÇÃO...")
289
- ultimo_p2 = historico_passo2_proposito[-1]
290
- prompt3 = prompt_passo3_motivacao(historico_dialogo[:-1], historico_passo3_motivacao, ultimo_p1, ultimo_p2)
291
  resposta_p3 = chamada_gemini(counselor_model, prompt3)
292
  analise_p3 = parse_json_seguro(resposta_p3)
 
 
293
  historico_passo3_motivacao.append(analise_p3)
294
  print(f" Confiança: {analise_p3.get('confianca', 'N/A')}")
295
  print(f" Decisão: {analise_p3.get('decisao', 'N/A')}")
296
  print(f" Justificativa: {analise_p3.get('justificativa', 'N/A')[:100]}...")
297
 
298
- # 5. PASSO 4: Análise de AMBIGUIDADE
299
  print("\n🔀 PASSO 4: Analisando AMBIGUIDADE...")
300
- ultimo_p3 = historico_passo3_motivacao[-1]
301
- prompt4 = prompt_passo4_ambiguidade(historico_dialogo[:-1], historico_passo4_ambiguidade, ultimo_p1, ultimo_p2, ultimo_p3)
302
  resposta_p4 = chamada_gemini(counselor_model, prompt4)
303
  analise_p4 = parse_json_seguro(resposta_p4)
 
 
304
  historico_passo4_ambiguidade.append(analise_p4)
305
  print(f" Confiança: {analise_p4.get('confianca', 'N/A')}")
306
  print(f" Decisão: {analise_p4.get('decisao', 'N/A')}")
307
  print(f" Justificativa: {analise_p4.get('justificativa', 'N/A')[:100]}...")
308
 
309
- # 6. SÍNTESE FINAL
310
  print("\n✨ SINTETIZANDO RESPOSTA FINAL...")
311
- ultimo_p4 = historico_passo4_ambiguidade[-1]
312
-
313
- prompt_final = prompt_sintetizador(historico_dialogo[:-1], ultimo_p1, ultimo_p2, ultimo_p3, ultimo_p4, modo_sabio)
314
  resposta_final = chamada_gemini(counselor_model, prompt_final)
315
 
316
- # 7. SUPERVISÃO (se ativada)
317
- if modo_supervisor and ultimo_p4.get('decisao') == 'prosseguir_resposta_final':
318
  print("\n🔍 MODO SUPERVISOR: Verificando fatos...")
319
  feedback_supervisor = verificar_fatos(resposta_final)
320
-
321
  if feedback_supervisor:
322
  print(" ⚠ Divergências encontradas. Iniciando autocorreção...")
323
  resposta_final = autocorrigir(resposta_final, feedback_supervisor)
 
 
324
 
325
- # 8. Atualizar histórico de diálogo
326
  historico_dialogo[-1]["assistant"] = resposta_final
327
-
328
- print("\n✅ RESPOSTA ENVIADA AO USUÁRIO")
329
  print("="*70 + "\n")
330
 
331
  return resposta_final
@@ -336,25 +502,23 @@ def verificar_fatos(texto: str) -> Optional[str]:
336
  prompt_supervisor = f"""
337
  Você é um Supervisor de IA, um fact-checker rigoroso e objetivo.
338
 
339
- Sua única tarefa: analisar o texto abaixo em busca de INCORREÇÕES FACTUAIS.
340
 
341
  - Analise apenas FATOS verificáveis (datas, nomes, estatísticas, conceitos científicos).
342
  - NÃO analise opiniões, conselhos filosóficos ou estrutura da resposta.
343
- - Se encontrar divergências factuais com confiança média ou alta, liste-as claramente.
344
- - Se NÃO encontrar nenhuma divergência factual, responda APENAS: "NODIVERGENCE"
345
 
346
- TEXTO PARA ANÁLISE:
347
  {texto}
348
 
349
- SUA RESPOSTA:
350
  """
351
  try:
352
  resposta = supervisor_model.generate_content(prompt_supervisor)
353
- if "NODIVERGENCE" in resposta.text:
354
- print(" ✓ Nenhuma divergência factual encontrada.")
355
  return None
356
  else:
357
- print(f" ⚠ Divergências: {resposta.text[:200]}...")
358
  return resposta.text
359
  except Exception as e:
360
  print(f"❌ Erro no Supervisor: {e}")
@@ -363,9 +527,9 @@ SUA RESPOSTA:
363
  def autocorrigir(resposta_original: str, feedback: str) -> str:
364
  """Solicita autocorreção ao modelo Conselheiro."""
365
  prompt_correcao = f"""
366
- Você é um Conselheiro em processo de refinamento.
367
 
368
- Sua resposta anterior continha uma imprecisão factual identificada pelo Supervisor.
369
 
370
  SUA RESPOSTA ORIGINAL:
371
  {resposta_original}
@@ -373,10 +537,10 @@ SUA RESPOSTA ORIGINAL:
373
  FEEDBACK DO SUPERVISOR (CORREÇÃO):
374
  {feedback}
375
 
376
- SUA TAREFA:
377
  1. Aceite a correção sem ser defensivo.
378
- 2. Reescreva sua resposta integrando a correção de forma natural.
379
- 3. Ao final, adicione uma breve "Nota de Refinamento:" explicando a correção.
380
 
381
  RESPOSTA CORRIGIDA:
382
  """
@@ -389,12 +553,12 @@ RESPOSTA CORRIGIDA:
389
 
390
  # ==================== FUNÇÃO PARA LIMPAR HISTÓRICOS ====================
391
  def resetar_conversa():
392
- """Reseta todos os históricos para iniciar nova conversa."""
393
- global historico_dialogo, historico_passo1_clareza, historico_passo2_proposito
394
  global historico_passo3_motivacao, historico_passo4_ambiguidade
395
 
396
  historico_dialogo = []
397
- historico_passo1_clareza = []
398
  historico_passo2_proposito = []
399
  historico_passo3_motivacao = []
400
  historico_passo4_ambiguidade = []
@@ -403,86 +567,70 @@ def resetar_conversa():
403
 
404
  # ==================== WRAPPER PARA GRADIO ====================
405
  def handle_chat(mensagem: str, historico_chat_gradio: List, modo_sabio: bool, modo_supervisor: bool):
406
- """Wrapper para integração com Gradio ChatInterface."""
407
-
408
- # Se é nova conversa (histórico vazio), reseta estados internos
409
  if not historico_chat_gradio:
410
  resetar_conversa()
411
 
412
- # Processa turno
413
  resposta = processar_turno(mensagem, modo_sabio, modo_supervisor)
414
-
415
  return resposta
416
 
417
- # ==================== INTERFACE GRADIO CUSTOMIZADA ====================
418
- with gr.Blocks(theme="soft") as iface:
419
- gr.Markdown("""
420
- # 🧠 Parceiro de Raciocínio v10 — Pipeline Fragmentada com Justificativas
421
-
422
- Sistema de conversação com análise multi-etapa, memória granular e justificativas completas.
423
 
424
- **Modo Amigo Sábio**: Reflexões filosóficas baseadas em Epicteto (requer epct0.md)
425
- **Modo Supervisor**: Verificação automática de fatos e autocorreção
426
 
427
- Cada passo mantém histórico completo com justificativas detalhadas da classificação.
428
  """)
429
 
430
- with gr.Row():
431
- with gr.Column(scale=4):
432
- chatbot = gr.Chatbot(
433
- height=600,
434
- label="Diálogo",
435
- type="messages",
436
- show_copy_button=True
437
- )
438
-
439
- with gr.Column(scale=1):
440
- modo_sabio = gr.Checkbox(
441
- label="🧘 Modo Amigo Sábio",
442
- value=False,
443
- info="Ativa reflexões filosóficas baseadas em Epicteto"
444
- )
445
- modo_supervisor = gr.Checkbox(
446
- label="🔍 Modo Supervisor",
447
- value=True,
448
- info="Ativa verificação de fatos e autocorreção"
449
- )
450
-
451
- gr.Markdown("### 💡 Exemplos")
452
- with gr.Column():
453
- ex1 = gr.Button("Carreira confusa", size="sm")
454
- ex2 = gr.Button("Everest altura", size="sm")
455
- ex3 = gr.Button("Ansiedade apresentação", size="sm")
456
- ex4 = gr.Button("IA explicação", size="sm")
457
 
458
- with gr.Row():
 
459
  msg = gr.Textbox(
460
- placeholder="Digite sua mensagem aqui... (Shift+Enter para nova linha)",
461
  label="Sua mensagem",
462
- lines=3,
463
- max_lines=10,
464
  scale=9,
465
- submit_btn=False # Desabilita envio com Enter
 
466
  )
467
- enviar_btn = gr.Button("📤 Enviar", scale=1, variant="primary")
468
-
469
- limpar_btn = gr.Button("🗑️ Limpar conversa")
470
 
471
- # Estado interno
472
- state = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
473
 
474
  # Função de envio
475
  def user_submit(mensagem, historico, modo_sabio_val, modo_supervisor_val):
476
  if not mensagem.strip():
477
  return historico, ""
478
 
479
- # Adiciona mensagem do usuário
480
  historico.append({"role": "user", "content": mensagem})
481
-
482
- # Processa resposta
483
  resposta = handle_chat(mensagem, historico, modo_sabio_val, modo_supervisor_val)
484
-
485
- # Adiciona resposta do assistente
486
  historico.append({"role": "assistant", "content": resposta})
487
 
488
  return historico, ""
@@ -494,7 +642,7 @@ with gr.Blocks(theme="soft") as iface:
494
  outputs=[chatbot, msg]
495
  )
496
 
497
- msg.submit( # Mantém submit para compatibilidade com Shift+Enter
498
  user_submit,
499
  inputs=[msg, chatbot, modo_sabio, modo_supervisor],
500
  outputs=[chatbot, msg]
@@ -502,18 +650,18 @@ with gr.Blocks(theme="soft") as iface:
502
 
503
  limpar_btn.click(
504
  lambda: ([], None),
505
- outputs=[chatbot, state]
506
  ).then(resetar_conversa, outputs=None)
507
 
508
  # Exemplos
509
- ex1.click(lambda: "Estou confuso sobre qual carreira seguir.", outputs=msg)
510
- ex2.click(lambda: "O Monte Everest tem mais de 9.000 metros de altura?", outputs=msg)
511
- ex3.click(lambda: "Por que sinto ansiedade antes de apresentações importantes?", outputs=msg)
512
- ex4.click(lambda: "Me fale sobre inteligência artificial e seus impactos.", outputs=msg)
513
 
514
  # ==================== EXECUÇÃO ====================
515
  if __name__ == "__main__":
516
  print("\n" + "="*70)
517
- print("🚀 Iniciando Parceiro de Raciocínio v10 - Pipeline Fragmentada")
518
  print("="*70 + "\n")
519
  iface.launch()
 
1
  import os
2
  import json
3
+ import re
4
  import gradio as gr
5
  import google.generativeai as genai
6
  from typing import Dict, List, Optional
7
+ import warnings
8
+
9
+ # Suprime warning do Python 3.10
10
+ warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")
11
 
12
  # ==================== CONFIGURAÇÃO DA API GEMINI ====================
13
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI"))
 
16
 
17
  # ==================== HISTÓRICOS GLOBAIS ====================
18
  historico_dialogo: List[Dict] = []
19
+ historico_passo1_triagem: List[Dict] = []
20
  historico_passo2_proposito: List[Dict] = []
21
  historico_passo3_motivacao: List[Dict] = []
22
  historico_passo4_ambiguidade: List[Dict] = []
 
36
 
37
  # ==================== PROMPTS PARA CADA PASSO ====================
38
 
39
+ def prompt_passo1_triagem(historico_dialogo: List[Dict], historico_passo1_completo: List[Dict], input_usuario: str) -> str:
40
+ """Prompt para TRIAGEM INICIAL: pergunta factual/direta OU dúvida aberta/complexa?"""
41
  return f"""
42
+ Você é um classificador de TIPO DE PERGUNTA em uma pipeline de raciocínio.
43
 
44
+ Sua única tarefa: determinar se a pergunta do usuário é FACTUAL/DIRETA ou ABERTA/COMPLEXA.
45
 
46
  HISTÓRICO DO CHAT (perguntas e respostas ao usuário):
47
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
48
 
49
+ HISTÓRICO COMPLETO DO PASSO 1 (todas as suas classificações anteriores):
50
  {json.dumps(historico_passo1_completo, indent=2, ensure_ascii=False)}
51
 
52
  NOVO INPUT DO USUÁRIO:
53
  "{input_usuario}"
54
 
55
+ DEFINIÇÕES:
56
+
57
+ **PERGUNTA FACTUAL/DIRETA:**
58
+ - Tem resposta objetiva e verificável
59
+ - Não requer análise de propósito, motivação ou ambiguidade
60
+ - Exemplos:
61
+ * "Qual a altura do Monte Everest?"
62
+ * "Python é uma linguagem compilada?"
63
+ * "Quando foi a Segunda Guerra Mundial?"
64
+ * "Quanto é 2+2?"
65
+
66
+ **DÚVIDA ABERTA/COMPLEXA:**
67
+ - Requer compreensão de contexto, propósito, motivação
68
+ - Pode ter múltiplas respostas válidas dependendo da perspectiva
69
+ - Exemplos:
70
+ * "Estou confuso sobre qual carreira seguir"
71
+ * "Como lidar com ansiedade?"
72
+ * "Por que me sinto assim?"
73
+ * "Me fale sobre inteligência artificial" (muito amplo)
74
+
75
  INSTRUÇÕES:
76
+ 1. Analise o histórico do chat para contexto (se o usuário está dando continuidade a uma conversa anterior).
77
+ 2. Se o usuário está respondendo a uma pergunta SUA anterior, classifique como "continuacao_dialogo".
78
+ 3. Se for primeira pergunta ou mudança de assunto, classifique como "factual_direta" ou "aberta_complexa".
79
+ 4. Se tiver QUALQUER dúvida sobre a classificação, marque como "aberta_complexa" (preferimos analisar mais do que menos).
 
80
 
81
+ RETORNE APENAS UM JSON VÁLIDO NO FORMATO (SEM MARKDOWN):
82
  {{
83
+ "tipo_pergunta": "factual_direta|aberta_complexa|continuacao_dialogo",
84
+ "justificativa": "Explique detalhadamente POR QUE você classificou assim",
85
+ "clareza": "baixa|media|alta",
86
+ "confianca_classificacao": "baixa|media|alta",
87
+ "decisao": "responder_direto|analisar_profundamente|aguardar_contexto"
88
  }}
89
+
90
+ IMPORTANTE: Responda APENAS com o objeto JSON. Sem blocos markdown, sem texto antes ou depois.
91
+ """
92
+
93
+ def prompt_resposta_direta(historico_dialogo: List[Dict], input_usuario: str, modo_sabio: bool) -> str:
94
+ """Prompt para resposta DIRETA a perguntas factuais."""
95
+ contexto_filosofico_str = ""
96
+ if modo_sabio and CONTEXTO_FILOSOFICO:
97
+ contexto_filosofico_str = f"""
98
+ CONTEXTO FILOSÓFICO (use sutilmente se relevante):
99
+ {CONTEXTO_FILOSOFICO[:500]}...
100
+
101
+ Aplique tom de Amigo Sábio: conversacional, empático, empoderador.
102
+ """
103
+
104
+ return f"""
105
+ Você é um assistente que responde perguntas FACTUAIS de forma DIRETA e OBJETIVA.
106
+
107
+ HISTÓRICO DO CHAT:
108
+ {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
109
+
110
+ PERGUNTA DO USUÁRIO:
111
+ "{input_usuario}"
112
+
113
+ {contexto_filosofico_str}
114
+
115
+ INSTRUÇÕES:
116
+ 1. Responda de forma clara, direta e factualmente correta.
117
+ 2. Seja conciso (2-4 frases no máximo).
118
+ 3. Se for uma pergunta verificável, cite fatos específicos.
119
+ 4. {"Use tom amigável e conversacional, não robótico." if modo_sabio else "Use tom informativo e profissional."}
120
+ 5. NÃO retorne JSON. Retorne APENAS o texto da resposta ao usuário.
121
+
122
+ RESPOSTA:
123
  """
124
 
125
  def prompt_passo2_proposito(historico_dialogo: List[Dict], historico_passo2_completo: List[Dict], ultimo_p1: Dict) -> str:
 
132
  HISTÓRICO DO CHAT:
133
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
134
 
135
+ ÚLTIMA CLASSIFICAÇÃO DO PASSO 1 (Triagem):
136
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
137
 
138
+ HISTÓRICO COMPLETO DO PASSO 2 (todas as suas análises anteriores de propósito):
139
  {json.dumps(historico_passo2_completo, indent=2, ensure_ascii=False)}
140
 
141
  INSTRUÇÕES:
142
+ 1. A pergunta foi classificada como ABERTA/COMPLEXA no Passo 1.
143
+ 2. Analise o histórico completo do PASSO 2 para entender como o propósito foi refinado.
144
  3. Tente inferir o propósito do usuário. Exemplos: "tomar uma decisão", "aprender algo novo", "validar uma crença", "resolver um problema prático", "desabafar".
145
  4. Se você tem alta confiança sobre o propósito, classifique como "alta".
146
+ 5. Se não consegue determinar com certeza, classifique como "baixa" ou "media".
147
 
148
+ RETORNE APENAS UM JSON VÁLIDO NO FORMATO (SEM MARKDOWN):
149
  {{
150
+ "justificativa": "Explique detalhadamente POR QUE você inferiu esse propósito",
151
  "analise": "Resumo da sua análise sobre o propósito",
152
  "proposito_inferido": "Descrição do propósito ou null se incerto",
153
  "confianca": "baixa|media|alta",
154
  "decisao": "prosseguir|pedir_esclarecimento",
155
  "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, coloque aqui uma pergunta natural. Caso contrário, null."
156
  }}
157
+
158
+ IMPORTANTE: Responda APENAS com o objeto JSON. Sem blocos markdown, sem texto antes ou depois.
159
  """
160
 
161
  def prompt_passo3_motivacao(historico_dialogo: List[Dict], historico_passo3_completo: List[Dict], ultimo_p1: Dict, ultimo_p2: Dict) -> str:
 
168
  HISTÓRICO DO CHAT:
169
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
170
 
171
+ ÚLTIMA CLASSIFICAÇÃO DO PASSO 1 (Triagem):
172
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
173
 
174
  ÚLTIMO REGISTRO DO PASSO 2 (Propósito):
175
  {json.dumps(ultimo_p2, indent=2, ensure_ascii=False)}
176
 
177
+ HISTÓRICO COMPLETO DO PASSO 3 (todas as suas análises anteriores de motivação):
178
  {json.dumps(historico_passo3_completo, indent=2, ensure_ascii=False)}
179
 
180
  INSTRUÇÕES:
181
+ 1. Use as análises anteriores para informar sua decisão.
182
+ 2. Tente identificar o gatilho emocional, contextual ou situacional.
183
+ 3. Exemplos de motivação: "frustração com situação atual", "curiosidade intelectual", "urgência de decisão", "busca por validação".
184
+ 4. Se você tem alta confiança sobre a motivação, classifique como "alta".
185
+
186
+ RETORNE APENAS UM JSON VÁLIDO NO FORMATO (SEM MARKDOWN):
 
 
187
  {{
188
+ "justificativa": "Explique detalhadamente POR QUE você identificou essa motivação",
189
  "analise": "Resumo da sua análise sobre a motivação",
190
  "motivacao_inferida": "Descrição da motivação ou null se incerto",
191
  "confianca": "baixa|media|alta",
192
  "decisao": "prosseguir|pedir_esclarecimento",
193
  "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, coloque aqui uma pergunta natural. Caso contrário, null."
194
  }}
195
+
196
+ IMPORTANTE: Responda APENAS com o objeto JSON. Sem blocos markdown, sem texto antes ou depois.
197
  """
198
 
199
  def prompt_passo4_ambiguidade(historico_dialogo: List[Dict], historico_passo4_completo: List[Dict], ultimo_p1: Dict, ultimo_p2: Dict, ultimo_p3: Dict) -> str:
200
+ """Prompt para análise de AMBIGUIDADE: Existem múltiplos cenários válidos?"""
201
  return f"""
202
  Você é um analisador de AMBIGUIDADE DE CENÁRIO em uma pipeline de raciocínio multi-etapa.
203
 
204
+ Sua única tarefa: detectar se a pergunta/situação tem múltiplas interpretações que precisam ser esclarecidas.
205
 
206
  HISTÓRICO DO CHAT:
207
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
208
 
209
+ ÚLTIMA CLASSIFICAÇÃO DO PASSO 1 (Triagem):
210
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
211
 
212
  ÚLTIMO REGISTRO DO PASSO 2 (Propósito):
 
215
  ÚLTIMO REGISTRO DO PASSO 3 (Motivação):
216
  {json.dumps(ultimo_p3, indent=2, ensure_ascii=False)}
217
 
218
+ HISTÓRICO COMPLETO DO PASSO 4 (todas as suas análises anteriores):
219
  {json.dumps(historico_passo4_completo, indent=2, ensure_ascii=False)}
220
 
221
  INSTRUÇÕES:
222
+ 1. Analise se a resposta pode variar significativamente dependendo de uma perspectiva não explicitada.
223
+ 2. Se UMA ÚNICA resposta clara, classifique confiança como "alta" e decisao "prosseguir_resposta_final".
224
+ 3. Se múltiplos cenários válidos, classifique confiança como "baixa" e decisao "pedir_esclarecimento".
225
+
226
+ RETORNE APENAS UM JSON VÁLIDO NO FORMATO (SEM MARKDOWN):
 
 
 
 
 
227
  {{
228
+ "justificativa": "Explique detalhadamente POR QUE você identificou (ou não) ambiguidade",
229
+ "analise": "Resumo da sua análise",
230
+ "cenarios_possiveis": ["cenário 1", "cenário 2"] ou null,
231
  "confianca": "baixa|media|alta",
232
  "decisao": "prosseguir_resposta_final|pedir_esclarecimento",
233
+ "pergunta_esclarecimento": "Se decisao == pedir_esclarecimento, pergunta aberta e natural. Caso contrário, null."
234
  }}
235
+
236
+ IMPORTANTE: Responda APENAS com o objeto JSON. Sem blocos markdown, sem texto antes ou depois.
237
  """
238
 
239
  def prompt_sintetizador(historico_dialogo: List[Dict], ultimo_p1: Dict, ultimo_p2: Dict, ultimo_p3: Dict, ultimo_p4: Dict, modo_sabio: bool) -> str:
240
+ """Prompt para SÍNTESE FINAL de dúvidas abertas/complexas."""
241
  contexto_filosofico_str = ""
242
  if modo_sabio and CONTEXTO_FILOSOFICO:
243
  contexto_filosofico_str = f"""
 
246
 
247
  IMPORTANTE: Use este contexto como sua bússola interna. NÃO cite Epicteto explicitamente.
248
  Sua resposta deve soar como um amigo sábio, não como um professor de filosofia.
249
+ Seja conversacional, empático, use humor leve quando apropriado, e empodere o usuário.
250
  """
251
 
252
  return f"""
253
  Você é um SINTETIZADOR FINAL de uma pipeline de análise conversacional.
254
 
255
+ Sua tarefa: gerar uma resposta textual natural, completa e útil ao usuário.
256
 
257
  HISTÓRICO DO CHAT COMPLETO:
258
  {json.dumps(historico_dialogo, indent=2, ensure_ascii=False)}
259
 
260
+ ÚLTIMA CLASSIFICAÇÃO DO PASSO 1 (Triagem):
261
  {json.dumps(ultimo_p1, indent=2, ensure_ascii=False)}
262
 
263
  ÚLTIMO REGISTRO DO PASSO 2 (Propósito):
 
274
  INSTRUÇÕES:
275
  1. Analise as justificativas e decisões dos 4 passos.
276
  2. Se algum passo indicou "pedir_esclarecimento", RETORNE APENAS A PERGUNTA DE ESCLARECIMENTO (priorize o passo com menor confiança).
277
+ 3. Se todos indicaram "prosseguir" ou "prosseguir_resposta_final", gere uma resposta completa e bem fundamentada.
278
+ 4. {"Aplique persona de Amigo Sábio: conversacional, empático, empoderador." if modo_sabio else "Use tom informativo e objetivo."}
279
+ 5. Use as justificativas para entender profundamente o contexto e intenção do usuário.
280
+ 6. NÃO retorne JSON. Retorne APENAS o texto da resposta ao usuário.
281
 
282
  RESPOSTA FINAL:
283
  """
 
294
 
295
  # ==================== FUNÇÃO AUXILIAR: PARSE SEGURO DE JSON ====================
296
  def parse_json_seguro(texto: str) -> Dict:
297
+ """Tenta extrair JSON de resposta do LLM com múltiplos fallbacks."""
298
+ texto_original = texto
299
+
300
  try:
301
+ # 1. Remove blocos markdown repetidamente
302
+ while "```
303
+ texto = texto.replace("```json", "").replace("```
304
+
305
+ # 2. Tenta parsear diretamente
306
  return json.loads(texto)
307
+
308
  except json.JSONDecodeError as e:
309
+ print(f"⚠ Parse direto falhou: {e}")
310
+
311
+ # 3. FALLBACK: extrai primeiro objeto JSON válido usando regex
312
+ try:
313
+ match = re.search(r'\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\}))*\}))*\}', texto, re.DOTALL)
314
+ if match:
315
+ json_extraido = match.group(0)
316
+ print(f"✓ JSON extraído via regex")
317
+ return json.loads(json_extraido)
318
+ except Exception as e2:
319
+ print(f"⚠ Extração via regex também falhou: {e2}")
320
+
321
+ # 4. ÚLTIMO RECURSO: retorna objeto de erro estruturado
322
+ print(f"❌ Primeiros 500 chars do texto que falhou:\n{texto_original[:500]}\n...")
323
+ return {
324
+ "erro": "Parse JSON falhou",
325
+ "justificativa": "O LLM não retornou JSON válido",
326
+ "confianca": "baixa",
327
+ "decisao": "prosseguir"
328
+ }
329
+
330
+ def validar_analise_passo(analise: Dict, passo_nome: str, campos_obrigatorios: List[str]) -> Dict:
331
+ """Valida se a análise tem os campos mínimos necessários."""
332
+ if analise.get("erro"):
333
+ print(f"⚠ {passo_nome} retornou erro, usando fallback")
334
+ return {
335
+ "justificativa": f"Erro no {passo_nome}, continuando com valores padrão",
336
+ "confianca": "media",
337
+ "decisao": "prosseguir"
338
+ }
339
+
340
+ for campo in campos_obrigatorios:
341
+ if campo not in analise:
342
+ print(f"⚠ {passo_nome} faltando campo '{campo}', usando fallback")
343
+ analise[campo] = "N/A" if campo != "confianca" else "media"
344
+
345
+ return analise
346
 
347
  # ==================== PIPELINE PRINCIPAL ====================
348
  def processar_turno(mensagem_usuario: str, modo_sabio: bool = False, modo_supervisor: bool = True) -> str:
349
+ """Processa um turno através da pipeline otimizada com triagem."""
350
+ global historico_dialogo, historico_passo1_triagem, historico_passo2_proposito
 
 
 
351
  global historico_passo3_motivacao, historico_passo4_ambiguidade
352
 
353
+ # 1. Registrar input do usuário
354
  historico_dialogo.append({"user": mensagem_usuario, "assistant": None})
355
 
356
  print("\n" + "="*70)
357
  print(f"📩 NOVO INPUT DO USUÁRIO: {mensagem_usuario}")
358
  print("="*70)
359
 
360
+ # 2. PASSO 1: TRIAGEM (factual/direta vs. aberta/complexa)
361
+ print("\n🔍 PASSO 1: TRIAGEM - Classificando tipo de pergunta...")
362
+ prompt1 = prompt_passo1_triagem(historico_dialogo[:-1], historico_passo1_triagem, mensagem_usuario)
363
  resposta_p1 = chamada_gemini(counselor_model, prompt1)
364
  analise_p1 = parse_json_seguro(resposta_p1)
365
+ analise_p1 = validar_analise_passo(analise_p1, "PASSO 1", ["tipo_pergunta", "decisao", "justificativa"])
366
+ historico_passo1_triagem.append(analise_p1)
367
+
368
+ tipo_pergunta = analise_p1.get("tipo_pergunta", "aberta_complexa")
369
+ decisao = analise_p1.get("decisao", "analisar_profundamente")
370
+
371
+ print(f" Tipo: {tipo_pergunta}")
372
+ print(f" Decisão: {decisao}")
373
  print(f" Justificativa: {analise_p1.get('justificativa', 'N/A')[:100]}...")
374
 
375
+ # 3. SE FOR FACTUAL/DIRETA → Atalho: resposta direta (mas registra histórico dos passos)
376
+ if tipo_pergunta == "factual_direta" and decisao == "responder_direto":
377
+ print("\n⚡ ATALHO ATIVADO: Respondendo diretamente...")
378
+ print(" 📝 Registrando justificativas de pulos nos passos 2-4...")
379
+
380
+ # REGISTRAR PULO NO PASSO 2
381
+ analise_p2_pulo = {
382
+ "justificativa": f"Passo pulado: pergunta classificada como factual/direta no Passo 1. Não requer análise de propósito para perguntas objetivas com resposta verificável.",
383
+ "analise": "N/A - Passo não executado (atalho ativado)",
384
+ "proposito_inferido": "obter_informacao_factual",
385
+ "confianca": "alta",
386
+ "decisao": "pular_passo",
387
+ "pergunta_esclarecimento": None,
388
+ "passo_pulado": True
389
+ }
390
+ historico_passo2_proposito.append(analise_p2_pulo)
391
+ print(f" ✓ Passo 2 registrado: {analise_p2_pulo['justificativa'][:80]}...")
392
+
393
+ # REGISTRAR PULO NO PASSO 3
394
+ analise_p3_pulo = {
395
+ "justificativa": f"Passo pulado: pergunta factual/direta não requer análise de motivação. Usuário busca informação objetiva, não há contexto emocional ou situacional a explorar.",
396
+ "analise": "N/A - Passo não executado (atalho ativado)",
397
+ "motivacao_inferida": "curiosidade_factual",
398
+ "confianca": "alta",
399
+ "decisao": "pular_passo",
400
+ "pergunta_esclarecimento": None,
401
+ "passo_pulado": True
402
+ }
403
+ historico_passo3_motivacao.append(analise_p3_pulo)
404
+ print(f" ✓ Passo 3 registrado: {analise_p3_pulo['justificativa'][:80]}...")
405
+
406
+ # REGISTRAR PULO NO PASSO 4
407
+ analise_p4_pulo = {
408
+ "justificativa": f"Passo pulado: pergunta factual/direta tem resposta única e objetiva. Não há ambiguidade de cenário, apenas necessidade de verificação factual.",
409
+ "analise": "N/A - Passo não executado (atalho ativado)",
410
+ "cenarios_possiveis": None,
411
+ "confianca": "alta",
412
+ "decisao": "prosseguir_resposta_final",
413
+ "pergunta_esclarecimento": None,
414
+ "passo_pulado": True
415
+ }
416
+ historico_passo4_ambiguidade.append(analise_p4_pulo)
417
+ print(f" ✓ Passo 4 registrado: {analise_p4_pulo['justificativa'][:80]}...")
418
+
419
+ # GERAR RESPOSTA DIRETA
420
+ print("\n💬 Gerando resposta direta...")
421
+ prompt_direto = prompt_resposta_direta(historico_dialogo[:-1], mensagem_usuario, modo_sabio)
422
+ resposta_final = chamada_gemini(counselor_model, prompt_direto)
423
+
424
+ # Supervisão (se ativada)
425
+ if modo_supervisor:
426
+ print("\n🔍 MODO SUPERVISOR: Verificando fatos...")
427
+ feedback_supervisor = verificar_fatos(resposta_final)
428
+ if feedback_supervisor:
429
+ print(" ⚠ Divergências encontradas. Iniciando autocorreção...")
430
+ resposta_final = autocorrigir(resposta_final, feedback_supervisor)
431
+ else:
432
+ print(" ✓ Nenhuma divergência encontrada.")
433
+
434
+ historico_dialogo[-1]["assistant"] = resposta_final
435
+ print("\n✅ RESPOSTA DIRETA ENVIADA (Passos 2-4 registrados com justificativas de pulo)")
436
+ print("="*70 + "\n")
437
+ return resposta_final
438
+
439
+ # 4. SE FOR ABERTA/COMPLEXA → Pipeline completa (passos 2-4)
440
+ print("\n📊 PIPELINE COMPLETA ATIVADA: Analisando em profundidade...")
441
+
442
+ # PASSO 2: Propósito (EXECUÇÃO REAL)
443
  print("\n🎯 PASSO 2: Analisando PROPÓSITO...")
444
+ prompt2 = prompt_passo2_proposito(historico_dialogo[:-1], historico_passo2_proposito, analise_p1)
 
445
  resposta_p2 = chamada_gemini(counselor_model, prompt2)
446
  analise_p2 = parse_json_seguro(resposta_p2)
447
+ analise_p2 = validar_analise_passo(analise_p2, "PASSO 2", ["confianca", "decisao", "justificativa"])
448
+ analise_p2["passo_pulado"] = False
449
  historico_passo2_proposito.append(analise_p2)
450
  print(f" Confiança: {analise_p2.get('confianca', 'N/A')}")
451
  print(f" Decisão: {analise_p2.get('decisao', 'N/A')}")
452
  print(f" Justificativa: {analise_p2.get('justificativa', 'N/A')[:100]}...")
453
 
454
+ # PASSO 3: Motivação (EXECUÇÃO REAL)
455
  print("\n💡 PASSO 3: Analisando MOTIVAÇÃO...")
456
+ prompt3 = prompt_passo3_motivacao(historico_dialogo[:-1], historico_passo3_motivacao, analise_p1, analise_p2)
 
457
  resposta_p3 = chamada_gemini(counselor_model, prompt3)
458
  analise_p3 = parse_json_seguro(resposta_p3)
459
+ analise_p3 = validar_analise_passo(analise_p3, "PASSO 3", ["confianca", "decisao", "justificativa"])
460
+ analise_p3["passo_pulado"] = False
461
  historico_passo3_motivacao.append(analise_p3)
462
  print(f" Confiança: {analise_p3.get('confianca', 'N/A')}")
463
  print(f" Decisão: {analise_p3.get('decisao', 'N/A')}")
464
  print(f" Justificativa: {analise_p3.get('justificativa', 'N/A')[:100]}...")
465
 
466
+ # PASSO 4: Ambiguidade (EXECUÇÃO REAL)
467
  print("\n🔀 PASSO 4: Analisando AMBIGUIDADE...")
468
+ prompt4 = prompt_passo4_ambiguidade(historico_dialogo[:-1], historico_passo4_ambiguidade, analise_p1, analise_p2, analise_p3)
 
469
  resposta_p4 = chamada_gemini(counselor_model, prompt4)
470
  analise_p4 = parse_json_seguro(resposta_p4)
471
+ analise_p4 = validar_analise_passo(analise_p4, "PASSO 4", ["confianca", "decisao", "justificativa"])
472
+ analise_p4["passo_pulado"] = False
473
  historico_passo4_ambiguidade.append(analise_p4)
474
  print(f" Confiança: {analise_p4.get('confianca', 'N/A')}")
475
  print(f" Decisão: {analise_p4.get('decisao', 'N/A')}")
476
  print(f" Justificativa: {analise_p4.get('justificativa', 'N/A')[:100]}...")
477
 
478
+ # 5. SÍNTESE FINAL
479
  print("\n✨ SINTETIZANDO RESPOSTA FINAL...")
480
+ prompt_final = prompt_sintetizador(historico_dialogo[:-1], analise_p1, analise_p2, analise_p3, analise_p4, modo_sabio)
 
 
481
  resposta_final = chamada_gemini(counselor_model, prompt_final)
482
 
483
+ # Supervisão (apenas para respostas finais, não perguntas de esclarecimento)
484
+ if modo_supervisor and analise_p4.get('decisao') == 'prosseguir_resposta_final':
485
  print("\n🔍 MODO SUPERVISOR: Verificando fatos...")
486
  feedback_supervisor = verificar_fatos(resposta_final)
 
487
  if feedback_supervisor:
488
  print(" ⚠ Divergências encontradas. Iniciando autocorreção...")
489
  resposta_final = autocorrigir(resposta_final, feedback_supervisor)
490
+ else:
491
+ print(" ✓ Nenhuma divergência encontrada.")
492
 
 
493
  historico_dialogo[-1]["assistant"] = resposta_final
494
+ print("\n✅ RESPOSTA COMPLETA ENVIADA (Pipeline completa executada)")
 
495
  print("="*70 + "\n")
496
 
497
  return resposta_final
 
502
  prompt_supervisor = f"""
503
  Você é um Supervisor de IA, um fact-checker rigoroso e objetivo.
504
 
505
+ Analise o texto abaixo em busca de INCORREÇÕES FACTUAIS.
506
 
507
  - Analise apenas FATOS verificáveis (datas, nomes, estatísticas, conceitos científicos).
508
  - NÃO analise opiniões, conselhos filosóficos ou estrutura da resposta.
509
+ - Se encontrar divergências factuais, liste-as claramente.
510
+ - Se NÃO encontrar nenhuma divergência, responda APENAS: "NO_DIVERGENCE"
511
 
512
+ TEXTO:
513
  {texto}
514
 
515
+ RESPOSTA:
516
  """
517
  try:
518
  resposta = supervisor_model.generate_content(prompt_supervisor)
519
+ if "NO_DIVERGENCE" in resposta.text:
 
520
  return None
521
  else:
 
522
  return resposta.text
523
  except Exception as e:
524
  print(f"❌ Erro no Supervisor: {e}")
 
527
  def autocorrigir(resposta_original: str, feedback: str) -> str:
528
  """Solicita autocorreção ao modelo Conselheiro."""
529
  prompt_correcao = f"""
530
+ Você é um Conselheiro em refinamento.
531
 
532
+ Sua resposta continha uma imprecisão factual identificada pelo Supervisor.
533
 
534
  SUA RESPOSTA ORIGINAL:
535
  {resposta_original}
 
537
  FEEDBACK DO SUPERVISOR (CORREÇÃO):
538
  {feedback}
539
 
540
+ TAREFA:
541
  1. Aceite a correção sem ser defensivo.
542
+ 2. Reescreva integrando a correção naturalmente.
543
+ 3. Ao final, adicione "Nota de Refinamento:" explicando a correção.
544
 
545
  RESPOSTA CORRIGIDA:
546
  """
 
553
 
554
  # ==================== FUNÇÃO PARA LIMPAR HISTÓRICOS ====================
555
  def resetar_conversa():
556
+ """Reseta todos os históricos."""
557
+ global historico_dialogo, historico_passo1_triagem, historico_passo2_proposito
558
  global historico_passo3_motivacao, historico_passo4_ambiguidade
559
 
560
  historico_dialogo = []
561
+ historico_passo1_triagem = []
562
  historico_passo2_proposito = []
563
  historico_passo3_motivacao = []
564
  historico_passo4_ambiguidade = []
 
567
 
568
  # ==================== WRAPPER PARA GRADIO ====================
569
  def handle_chat(mensagem: str, historico_chat_gradio: List, modo_sabio: bool, modo_supervisor: bool):
570
+ """Wrapper para integração com Gradio."""
 
 
571
  if not historico_chat_gradio:
572
  resetar_conversa()
573
 
 
574
  resposta = processar_turno(mensagem, modo_sabio, modo_supervisor)
 
575
  return resposta
576
 
577
+ # ==================== INTERFACE GRADIO ====================
578
+ with gr.Blocks(theme="soft", css="""
579
+ #chatbot-container { height: calc(100vh - 250px); }
580
+ #input-row { margin-top: 10px; margin-bottom: 10px; }
581
+ """) as iface:
 
582
 
583
+ gr.Markdown("""
584
+ # 🧠 Parceiro de Raciocínio v11 — Pipeline Otimizada com Triagem
585
 
586
+ **Triagem inteligente**: Perguntas factuais resposta rápida (1 chamada) | Dúvidas complexas → análise profunda (5 chamadas)
587
  """)
588
 
589
+ # CHAT (ocupa altura da janela)
590
+ chatbot = gr.Chatbot(
591
+ elem_id="chatbot-container",
592
+ label="Diálogo",
593
+ type="messages",
594
+ show_copy_button=True,
595
+ height="100%"
596
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
 
598
+ # INPUT (logo abaixo do chat)
599
+ with gr.Row(elem_id="input-row"):
600
  msg = gr.Textbox(
601
+ placeholder="Digite sua mensagem... (Shift+Enter para nova linha)",
602
  label="Sua mensagem",
603
+ lines=2,
604
+ max_lines=6,
605
  scale=9,
606
+ submit_btn=False,
607
+ show_label=False
608
  )
609
+ enviar_btn = gr.Button("📤", scale=1, variant="primary", size="lg")
 
 
610
 
611
+ # CONTROLES E EXEMPLOS (abaixo do input)
612
+ with gr.Row():
613
+ with gr.Column(scale=2):
614
+ with gr.Row():
615
+ modo_sabio = gr.Checkbox(label="🧘 Modo Amigo Sábio", value=False, scale=1)
616
+ modo_supervisor = gr.Checkbox(label="🔍 Modo Supervisor", value=True, scale=1)
617
+ limpar_btn = gr.Button("🗑️ Limpar", scale=1, size="sm")
618
+
619
+ with gr.Column(scale=3):
620
+ gr.Markdown("**💡 Exemplos:**")
621
+ with gr.Row():
622
+ ex1 = gr.Button("📊 Everest altura", size="sm", scale=1)
623
+ ex2 = gr.Button("🤔 Carreira confusa", size="sm", scale=1)
624
+ ex3 = gr.Button("📊 Python compilada?", size="sm", scale=1)
625
+ ex4 = gr.Button("🤔 Ansiedade", size="sm", scale=1)
626
 
627
  # Função de envio
628
  def user_submit(mensagem, historico, modo_sabio_val, modo_supervisor_val):
629
  if not mensagem.strip():
630
  return historico, ""
631
 
 
632
  historico.append({"role": "user", "content": mensagem})
 
 
633
  resposta = handle_chat(mensagem, historico, modo_sabio_val, modo_supervisor_val)
 
 
634
  historico.append({"role": "assistant", "content": resposta})
635
 
636
  return historico, ""
 
642
  outputs=[chatbot, msg]
643
  )
644
 
645
+ msg.submit(
646
  user_submit,
647
  inputs=[msg, chatbot, modo_sabio, modo_supervisor],
648
  outputs=[chatbot, msg]
 
650
 
651
  limpar_btn.click(
652
  lambda: ([], None),
653
+ outputs=[chatbot, None]
654
  ).then(resetar_conversa, outputs=None)
655
 
656
  # Exemplos
657
+ ex1.click(lambda: "O Monte Everest tem mais de 9.000 metros de altura?", outputs=msg)
658
+ ex2.click(lambda: "Estou confuso sobre qual carreira seguir.", outputs=msg)
659
+ ex3.click(lambda: "Python é uma linguagem compilada ou interpretada?", outputs=msg)
660
+ ex4.click(lambda: "Como posso lidar com ansiedade antes de apresentações?", outputs=msg)
661
 
662
  # ==================== EXECUÇÃO ====================
663
  if __name__ == "__main__":
664
  print("\n" + "="*70)
665
+ print("🚀 Iniciando Parceiro de Raciocínio v11 - Pipeline Fragmentada com Triagem")
666
  print("="*70 + "\n")
667
  iface.launch()