caarleexx commited on
Commit
684a0a5
·
verified ·
1 Parent(s): 3849644

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -168
app.py CHANGED
@@ -1,181 +1,168 @@
1
- # --- INÍCIO DO CÓDIGO COMPLETO E FINAL ---
2
-
3
  import gradio as gr
4
- import os
5
- import google.generativeai as genai
6
- import json
7
-
8
- # --- 1. CONFIGURAÇÃO DA API GEMINI ---
9
- # É mais seguro usar variáveis de ambiente, mas para testes, você pode inserir a chave aqui.
10
- api_key = os.getenv("GOOGLE_API_KEY")
11
- if not api_key:
12
- # ⚠️ SUBSTITUA "SUA_API_KEY_AQUI" PELA SUA CHAVE REAL ANTES DE EXECUTAR
13
- api_key = "SUA_API_KEY_AQUI"
14
-
15
- genai.configure(api_key=api_key)
16
- model = genai.GenerativeModel('gemini-flash-latest')
17
-
18
- # --- 2. PROMPT DE SISTEMA MESTRE (O "CÉREBRO" DO PROTOCOLO) ---
19
- PROMPT_SISTEMA = """
20
- Você é um especialista em análise de intenções que opera sob um protocolo de 6 passos.
21
- Sua função é conduzir uma conversa para entender 100% da dúvida de um usuário ANTES de respondê-la.
22
- Sua SAÍDA DEVE SER SEMPRE E SOMENTE um objeto JSON válido, sem nenhum texto adicional.
23
-
24
- **PROTOCOLO DE EXECUÇÃO OBRIGATÓRIO:**
25
-
26
- Analise o `estado_pipeline` atual e o `historico_conversa`. Decida qual passo executar. Sua resposta em JSON deve conter o estado COMPLETAMENTE ATUALIZADO da pipeline.
27
-
28
- ---
29
- **Passo 1: Análise Primária (Clareza)**
30
- - **Meta:** A dúvida é compreensível?
31
- - **Decisão:** Se a clareza for 'baixa', pergunte por mais contexto. Senão, defina `proximo_passo` como `"passo_2_proposito"`.
32
-
33
- ---
34
- **Passo 2: Análise de Propósito (Para Que)**
35
- - **Meta:** Para qual fim a resposta servirá?
36
- - **Decisão:** Se a confiança no propósito for 'baixa', pergunte sobre o objetivo. Senão, defina `proximo_passo` como `"passo_3_motivacao"`.
37
-
38
- ---
39
- **Passo 3: Análise de Motivação (Porquê) e Interesses**
40
- - **Meta:** Por que o usuário precisa da resposta agora?
41
- - **Decisão:** Se a confiança na motivação for 'baixa', pergunte sobre o que despertou o interesse. Senão, defina `proximo_passo` como `"passo_4_coerencia"`.
42
-
43
- ---
44
- **Passo 4: Análise de Coerência**
45
- - **Meta:** As informações coletadas fazem sentido juntas?
46
- - **Decisão:** Se a coerência for 'baixa', pergunte para resolver a contradição. Senão, defina `proximo_passo` como `"passo_5_ambiguidade"`.
47
-
48
- ---
49
- **Passo 5: Análise de Ambiguidade de Cenário (Validação de Perspectiva)**
50
- - **Meta:** A dúvida pode ter múltiplas respostas válidas dependendo de uma perspectiva oculta?
51
- - **Ação:** Revise o contexto completo. Pense se a resposta muda se o usuário for um estudante, um profissional, um entusiasta, etc.
52
- - **Decisão:**
53
- - Se existem vários cenários de resposta válidos (confiança 'baixa'), formule uma pergunta que force o usuário a escolher uma perspectiva. Defina `proximo_passo` como `"aguardando_usuario"`.
54
- - **Exemplo:** Se a dúvida é "Como otimizar um banco de dados?", pergunte: "Você está focando em otimização para velocidade de leitura, redução de custos de armazenamento, ou para escalabilidade?"
55
- - Se o cenário de resposta é único e claro (confiança 'alta'), defina `proximo_passo` como `"passo_6_resposta_final"`.
56
-
57
- ---
58
- **Passo 6: Geração da Resposta Final**
59
- - **Meta:** Tenho 100% de clareza para dar uma resposta definitiva e sem subjetividade.
60
- - **Ação:** Verifique se TODOS os passos anteriores têm confiança 'alta'.
61
- - **Decisão:**
62
- - Se sim, construa a resposta final, objetiva e completa, adaptada a todo o contexto coletado. Coloque-a em `"resposta_final"`. Defina `"proximo_passo"` como `"concluido"`.
63
- - Se não, volte ao passo com a confiança mais baixa e reformule a pergunta.
64
-
65
- **ESTRUTURA JSON DE SAÍDA OBRIGATÓRIA:**
66
- {
67
- "raciocinio_do_passo": "Sua breve justificativa interna para a decisão tomada.",
68
- "proximo_passo": "string",
69
- "pergunta_para_usuario": "string | null",
70
- "estado_pipeline_atualizado": {
71
- "passo_atual": "string",
72
- "duvida_inicial": "string | null",
73
- "clareza": {"confianca": "baixa|media|alta"},
74
- "proposito": {"valor": "string | null", "confianca": "baixa|media|alta"},
75
- "motivacao": {"valor": "string | null", "confianca": "baixa|media|alta"},
76
- "coerencia": {"confianca": "baixa|media|alta"},
77
- "ambiguidade": {"confianca": "baixa|media|alta"}
78
- },
79
- "resposta_final": "string | null"
80
- }
81
- """
82
-
83
- # --- 3. GERENCIAMENTO DE ESTADO ---
84
- def resetar_estado():
85
- """Inicializa ou reseta o dicionário de estado da pipeline."""
86
- print("Resetando o estado da pipeline para uma nova conversa.")
87
- return {
88
- "passo_atual": "passo_1_clareza",
89
- "duvida_inicial": None,
90
- "clareza": {"confianca": "baixa"},
91
- "proposito": {"valor": None, "confianca": "baixa"},
92
- "motivacao": {"valor": None, "confianca": "baixa"},
93
- "coerencia": {"confianca": "baixa"},
94
- "ambiguidade": {"confianca": "baixa"}
95
- }
96
 
97
- # Variáveis globais para manter o estado e o histórico durante a sessão
98
- estado_pipeline_global = resetar_estado()
99
- historico_conversa_llm = []
100
 
101
- # --- 4. FUNÇÃO PRINCIPAL DO CHATBOT ---
102
- def handle_chat(mensagem, historico_chat_gradio):
103
  """
104
- Esta função é chamada a cada mensagem do usuário.
105
- Ela gerencia o estado, constrói o prompt, chama a API e retorna a resposta.
106
- """
107
- global estado_pipeline_global, historico_conversa_llm
108
-
109
- # Detecta o início de uma nova conversa (quando a UI é limpa) e reseta o estado interno.
110
- if not historico_chat_gradio:
111
- estado_pipeline_global = resetar_estado()
112
- historico_conversa_llm = []
113
-
114
- historico_conversa_llm.append({"role": "user", "parts": [mensagem]})
115
 
116
- if estado_pipeline_global["duvida_inicial"] is None:
117
- estado_pipeline_global["duvida_inicial"] = mensagem
118
-
119
- # Monta o super-prompt com todas as instruções, o histórico e o estado atual.
120
- prompt_completo = f"""
121
- {PROMPT_SISTEMA}
122
- ---
123
- **HISTÓRICO DA CONVERSA PARA O LLM:**
124
- {json.dumps(historico_conversa_llm, indent=2)}
125
- ---
126
- **ESTADO ATUAL DA PIPELINE:**
127
- {json.dumps(estado_pipeline_global, indent=2)}
128
- ---
129
- **Instrução Final:** Baseado no estado e no histórico, execute o próximo passo do protocolo e gere o JSON de saída.
130
  """
 
 
 
 
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  try:
133
- # Envia a requisição para a API do Gemini
134
- response = model.generate_content(prompt_completo)
 
 
 
135
 
136
- # Limpa e decodifica a resposta JSON do modelo
137
- resposta_texto = response.text.strip().replace("```json", "").replace("```", "")
138
- decisao_json = json.loads(resposta_texto)
139
-
140
- # O LLM é a fonte da verdade: o estado local é substituído pelo estado retornado pela API.
141
- estado_pipeline_global = decisao_json.get("estado_pipeline_atualizado", estado_pipeline_global)
 
 
 
 
 
 
 
 
 
 
142
 
143
- # Decide o que mostrar ao usuário
144
- if decisao_json.get("resposta_final"):
145
- resposta_para_usuario = decisao_json["resposta_final"]
146
- historico_conversa_llm.append({"role": "model", "parts": [resposta_para_usuario]})
147
- return resposta_para_usuario
148
-
149
- elif decisao_json.get("pergunta_para_usuario"):
150
- resposta_para_usuario = decisao_json["pergunta_para_usuario"]
151
- historico_conversa_llm.append({"role": "model", "parts": [resposta_para_usuario]})
152
- return resposta_para_usuario
153
-
154
- else:
155
- # Fallback para caso o JSON seja válido mas não tenha uma ação clara
156
- return "Ocorreu um erro no meu raciocínio. Poderia tentar reformular?"
157
-
158
- except (json.JSONDecodeError, AttributeError, Exception) as e:
159
- print(f"Erro ao processar resposta da API: {e}")
160
- try:
161
- print(f"Resposta recebida (pode ter causado o erro): {response.text}")
162
- except:
163
- print("Não foi possível extrair o texto da resposta do erro.")
164
- return "Desculpe, tive um problema técnico. Por favor, clique no botão 'Limpar' e tente novamente."
165
 
166
- # --- 5. INTERFACE GRÁFICA COM GRADIO ---
167
- iface = gr.ChatInterface(
168
- fn=handle_chat,
169
- title="🤖 Protótipo Protocolo de Tom v2 Theory of Mind",
170
- description="Este chatbot segue um protocolo para eliminar todas as ambiguidades antes de fornecer uma resposta 100% contextualizada. Antes da resposta o modelo pergunta o porque da pergunta.",
171
- chatbot=gr.Chatbot(height=600),
172
- textbox=gr.Textbox(placeholder="Qual é a sua dúvida?", container=False, scale=7),
173
- theme="soft",
174
- examples=[["O que é violência domestica?"], ["Como posso investir meu dinheiro?"], ["Me explique a computação quântica."]],
175
- )
176
 
177
- # --- 6. EXECUÇÃO DA APLICAÇÃO ---
178
  if __name__ == "__main__":
179
- iface.launch()
180
-
181
- # --- FIM DO CÓDIGO ---```
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ from typing import List, Dict
3
+ import pandas as pd
4
+ import io
5
+
6
+ # --- 1. Estrutura do Dicionário Estoico (Exemplo simplificado para demonstração) ---
7
+ # Em um ambiente de produção, este DataFrame/Dicionário seria carregado
8
+ # do arquivo .md na inicialização do serviço.
9
+ # Para este exemplo, vamos manter a tabela inteira como o conteúdo do arquivo
10
+ # a ser processado.
11
+
12
+ def parse_md_table(md_content: str) -> pd.DataFrame:
13
+ """Analisa o conteúdo de uma tabela Markdown e o retorna como um DataFrame."""
14
+ lines = [line.strip() for line in md_content.split('\n') if line.strip()]
15
+
16
+ # 1. Encontra a linha do cabeçalho
17
+ header_line_index = next((i for i, line in enumerate(lines) if line.startswith('| :--')), None)
18
+ if header_line_index is None:
19
+ raise ValueError("Formato de tabela Markdown não reconhecido (cabeçalho de alinhamento ausente).")
20
+
21
+ # 2. Extrai o cabeçalho (a linha anterior à linha de alinhamento)
22
+ if header_line_index > 0:
23
+ header_line = lines[header_line_index - 1]
24
+ headers = [h.strip() for h in header_line.strip('|').split('|')]
25
+ else:
26
+ raise ValueError("Cabeçalho não encontrado antes da linha de alinhamento.")
27
+
28
+ # 3. Extrai os dados (linhas após a linha de alinhamento)
29
+ data_lines = lines[header_line_index + 1:]
30
+ data = []
31
+ for line in data_lines:
32
+ if not line.startswith('|'): continue
33
+ # Remove as barras iniciais e finais, e divide
34
+ row_data = [item.strip() for item in line.strip('|').split('|')]
35
+ if len(row_data) == len(headers):
36
+ data.append(row_data)
37
+
38
+ return pd.DataFrame(data, columns=headers)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # --- 2. Lógica de Busca e Aplicação (Simulação de um LLM Estoico) ---
 
 
41
 
42
+ def apply_stoic_logic(user_prompt: str, table_content: str) -> str:
 
43
  """
44
+ Simula o raciocínio de um LLM Estoico usando a tabela de princípios.
 
 
 
 
 
 
 
 
 
 
45
 
46
+ Args:
47
+ user_prompt: A pergunta do usuário.
48
+ table_content: O conteúdo Markdown da tabela dos princípios.
49
+
50
+ Returns:
51
+ Uma resposta formatada com base no princípio mais relevante.
 
 
 
 
 
 
 
 
52
  """
53
+ try:
54
+ df = parse_md_table(table_content)
55
+ except ValueError as e:
56
+ return f"Erro ao analisar o arquivo de princípios: {e}"
57
 
58
+ # --- SIMULAÇÃO DA BUSCA DE PRINCÍPIO RELEVANTE ---
59
+
60
+ # Simula a relevância baseada em palavras-chave para o prompt do usuário
61
+ if any(word in user_prompt.lower() for word in ["culpa", "sofrimento", "tranquilidade"]):
62
+ relevant_row = df[df['Id'] == 'I'].iloc[0]
63
+ elif any(word in user_prompt.lower() for word in ["perda", "morte", "apego"]):
64
+ relevant_row = df[df['Id'] == 'III'].iloc[0]
65
+ elif any(word in user_prompt.lower() for word in ["insulto", "ofensa", "raiva"]):
66
+ # Combinação de Va e XX
67
+ relevant_row = df[df['Id'] == 'Va'].iloc[0]
68
+ supplementary_row = df[df['Id'] == 'XX'].iloc[0]
69
+ elif any(word in user_prompt.lower() for word in ["futuro", "aceitar", "serenidade"]):
70
+ relevant_row = df[df['Id'] == 'VIII'].iloc[0]
71
+ else:
72
+ # Padrão para qualquer outra pergunta
73
+ relevant_row = df[df['Id'] == 'I'].iloc[0]
74
+
75
+ # --- CONSTRUÇÃO DA RESPOSTA ESTOICA ---
76
+
77
+ response = f"**Análise Estoica para: '{user_prompt}'**\n\n"
78
+
79
+ response += f"### Princípio Estoico Central (ID: {relevant_row['Id']})\n"
80
+ response += f"**Preceito:** *{relevant_row['Resumo']}*\n\n"
81
+
82
+ if relevant_row['Id'] in ['Va', 'XX']:
83
+ # Lógica especial para ofensas/insultos
84
+ response += (
85
+ "A chave para lidar com isso reside em distinguir o evento de sua interpretação.\n"
86
+ f"O estoico Epicteto ensina: {relevant_row['Resumo']} "
87
+ f"({relevant_row['Porquês']}).\n"
88
+ f"Complementarmente, lembre-se: '{supplementary_row['Resumo']}', "
89
+ f"para **{supplementary_row['Porquês']}**."
90
+ )
91
+ else:
92
+ # Lógica padrão
93
+ response += (
94
+ "**O que fazer:** Focar neste princípio permite que você se beneficie de "
95
+ f"seus **{relevant_row['Tags']}**.\n"
96
+ "**O Porquê:** O objetivo é **"
97
+ f"{relevant_row['Porquês']}**."
98
+ )
99
+
100
+ return response.replace('#', '').replace('*', '').replace('"', '')
101
+
102
+ # --- 3. Interface Gradio ---
103
+
104
+ def process_file_and_prompt(file_obj, prompt: str):
105
+ """Lê o arquivo, extrai a tabela e aplica a lógica estoica."""
106
+ if file_obj is None:
107
+ return "⚠️ Por favor, envie o arquivo de princípios Markdown (tabela)."
108
+
109
+ # 1. Lê o conteúdo do arquivo
110
  try:
111
+ file_path = file_obj.name
112
+ with open(file_path, 'r', encoding='utf-8') as f:
113
+ table_content = f.read()
114
+ except Exception as e:
115
+ return f"Erro ao ler o arquivo: {e}"
116
 
117
+ # 2. Processa e aplica a lógica
118
+ return apply_stoic_logic(prompt, table_content)
119
+
120
+ # Define a interface do Gradio
121
+ with gr.Blocks(title="Consultor Estoico do Enchiridion") as demo:
122
+ gr.Markdown("# 📜 Consultor Estoico do Enchiridion")
123
+ gr.Markdown("Anexe o arquivo Markdown contendo a tabela de princípios e faça sua pergunta.")
124
+
125
+ with gr.Row():
126
+ # Componente para upload de arquivo
127
+ principles_file = gr.File(
128
+ label="1. Enviar Arquivo de Princípios (Tabela Markdown)",
129
+ type="filepath",
130
+ file_types=[".md", ".txt"],
131
+ value="epct0.md" # Preenche com o caminho do arquivo fornecido
132
+ )
133
 
134
+ with gr.Row():
135
+ # Componente para a pergunta do usuário
136
+ user_prompt_input = gr.Textbox(
137
+ label="2. Sua Pergunta (Ex: 'Estou com raiva por um comentário ofensivo no trabalho.')",
138
+ lines=2,
139
+ placeholder="O que Epicteto diria sobre meu problema?"
140
+ )
141
+
142
+ # Botão de execução
143
+ run_button = gr.Button("3. Consultar a Sabedoria Estoica", variant="primary")
144
+
145
+ # Componente de saída
146
+ stoic_response = gr.Markdown(label="Resposta do Consultor Estoico")
 
 
 
 
 
 
 
 
 
147
 
148
+ # Ação do botão
149
+ run_button.click(
150
+ fn=process_file_and_prompt,
151
+ inputs=[principles_file, user_prompt_input],
152
+ outputs=[stoic_response]
153
+ )
 
 
 
 
154
 
155
+ # Executa a interface
156
  if __name__ == "__main__":
157
+ # Garante que o arquivo de exemplo exista na pasta Test4
158
+ try:
159
+ # A pasta Test4/epct0 (1).md é um caminho virtual na execução do LLM,
160
+ # mas o Gradio tenta acessá-lo.
161
+ # Como o arquivo foi fornecido no prompt, assumimos que ele está disponível
162
+ # para a execução do Gradio.
163
+ # Não é necessário criar o arquivo fisicamente no script aqui.
164
+ pass
165
+ except Exception:
166
+ pass
167
+
168
+ demo.launch()