File size: 16,353 Bytes
edf86ce
 
d3bc90c
 
8a1f7e0
183b339
398cb23
 
 
f3cfeb4
398cb23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6f2214d
398cb23
 
 
 
 
183b339
398cb23
 
f3cfeb4
8a1f7e0
edf86ce
398cb23
f3cfeb4
060d39d
8a1f7e0
4a29746
183b339
46fbf78
398cb23
46fbf78
398cb23
46fbf78
398cb23
46fbf78
 
398cb23
46fbf78
398cb23
46fbf78
 
 
 
 
 
398cb23
 
 
 
 
46fbf78
398cb23
 
 
 
 
 
 
 
 
 
 
 
46fbf78
 
398cb23
46fbf78
 
 
 
 
398cb23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46fbf78
 
398cb23
 
 
 
 
 
46fbf78
 
 
 
 
 
183b339
398cb23
183b339
 
 
 
 
 
 
 
 
 
 
8a1f7e0
398cb23
 
 
 
 
 
 
 
 
8a1f7e0
 
 
 
 
 
 
 
 
 
 
 
398cb23
8a1f7e0
 
 
 
 
 
 
398cb23
8a1f7e0
 
d3bc90c
8a1f7e0
d3bc90c
 
8a1f7e0
 
d3bc90c
8a1f7e0
 
 
 
 
 
 
d3bc90c
8a1f7e0
4a29746
8a1f7e0
398cb23
46fbf78
 
398cb23
46fbf78
 
 
 
 
 
 
 
4a29746
183b339
4a29746
 
 
 
 
183b339
 
 
d3bc90c
8a1f7e0
060d39d
d3bc90c
 
 
 
f72e739
183b339
4a29746
8a1f7e0
183b339
4a29746
d3bc90c
f72e739
060d39d
4a29746
060d39d
4a29746
060d39d
8a1f7e0
 
 
4a29746
183b339
398cb23
183b339
 
 
 
 
 
 
 
 
 
 
 
4a29746
398cb23
4a29746
 
 
 
 
 
 
 
 
 
 
 
 
398cb23
 
 
 
4a29746
 
 
 
 
398cb23
4a29746
 
 
398cb23
 
 
 
 
 
 
 
 
 
8a1f7e0
398cb23
8a1f7e0
 
 
183b339
 
 
46fbf78
 
 
 
 
 
 
 
 
 
 
 
 
8a1f7e0
 
 
 
 
 
 
 
 
276dd63
 
 
 
8a1f7e0
46fbf78
8a1f7e0
 
 
 
 
 
 
276dd63
183b339
398cb23
183b339
 
 
 
 
 
 
 
 
f3cfeb4
398cb23
8a1f7e0
edf86ce
398cb23
46fbf78
 
f3cfeb4
 
060d39d
276dd63
 
060d39d
f72e739
276dd63
 
 
 
 
 
398cb23
edf86ce
f3cfeb4
060d39d
f3cfeb4
edf86ce
060d39d
f3cfeb4
 
 
 
060d39d
e6ddbc4
edf86ce
 
398cb23
 
 
 
 
 
 
 
 
e6ddbc4
398cb23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
import gradio as gr
from datetime import datetime
from sentence_transformers import SentenceTransformer
import numpy as np
from typing import Dict, List, Tuple
from textblob import TextBlob
import json
import os
from pathlib import Path

# Configuração inicial
def setup_environment():
    """Configura o ambiente inicial e carrega os dados necessários."""
    # Verifica se o arquivo JSON existe
    if not Path("coach_data.json").exists():
        raise FileNotFoundError("O arquivo coach_data.json não foi encontrado")
    
    try:
        # Carrega os dados do JSON
        with open('coach_data.json', 'r', encoding='utf-8') as f:
            data = json.load(f)
            return (
                data['perguntas'],
                data['tone_patterns'],
                data['respostas_coach']
            )
    except json.JSONDecodeError:
        raise ValueError("Erro ao decodificar o arquivo JSON")
    except KeyError as e:
        raise KeyError(f"Chave obrigatória não encontrada no JSON: {e}")

# Carrega o modelo de embeddings
try:
    model = SentenceTransformer('all-MiniLM-L6-v2')
except Exception as e:
    raise RuntimeError(f"Erro ao carregar o modelo de embeddings: {e}")

# Carrega os dados do coach
PERGUNTAS, TONE_PATTERNS, RESPOSTAS_COACH = setup_environment()

class EnhancedCoach:
    def __init__(self):
        """Inicializa o coach com os estados necessários."""
        self.pergunta_atual = 0
        self.inicio = datetime.now()
        self.historico_respostas = []
        self.sessao_completa = False
        self.tone_history = []
        self.response_quality_metrics = []
    
    def analyze_response_quality(self, text: str) -> Dict[str, float]:
        """Analisa a qualidade da resposta do usuário."""
        sentences = [s.strip() for s in text.split('.') if s.strip()]
        words = text.lower().split()
        
        metrics = {
            "depth": self._calculate_depth(text, words),
            "clarity": self._calculate_clarity(sentences),
            "specificity": self._calculate_specificity(text, words),
            "actionability": self._calculate_actionability(sentences)
        }
        
        self.response_quality_metrics.append(metrics)
        return metrics
    
    def _calculate_depth(self, text: str, words: List[str]) -> float:
        """Calcula a profundidade da resposta."""
        if not words:
            return 0.0
            
        unique_words = len(set(words))
        word_length_avg = sum(len(word) for word in words) / len(words)
        sentences = [s.strip() for s in text.split('.') if s.strip()]
        
        word_variety = unique_words / len(words)
        sentence_length = len(sentences)
        complexity = word_length_avg / 5
        
        depth_score = (word_variety * 0.4 + 
                      min(sentence_length / 3, 1.0) * 0.4 + 
                      complexity * 0.2)
        
        return min(1.0, depth_score)
    
    def _calculate_clarity(self, sentences: List[str]) -> float:
        """Calcula a clareza da resposta."""
        if not sentences:
            return 0.0
        avg_length = sum(len(s.split()) for s in sentences) / len(sentences)
        return 1.0 if 10 <= avg_length <= 20 else 0.7
    
    def _calculate_specificity(self, text: str, words: List[str]) -> float:
        """Calcula a especificidade da resposta."""
        specific_indicators = [
            "exemplo", "especificamente", "concretamente", 
            "situação", "caso", "quando", "onde", "como",
            "projeto", "equipe", "reunião", "feedback",
            "resultado", "impacto", "mudança", "melhoria",
            "implementei", "desenvolvi", "criei", "estabeleci",
            "eu", "minha", "nosso", "realizei", "fiz"
        ]
        
        indicator_count = sum(text.lower().count(ind) for ind in specific_indicators)
        response_length_factor = min(len(words) / 20, 1.0)
        
        return min(1.0, (indicator_count * 0.7 + response_length_factor * 0.3))
    
    def _calculate_actionability(self, sentences: List[str]) -> float:
        """Calcula a acionabilidade da resposta."""
        action_verbs = [
            "implementar", "fazer", "criar", "desenvolver", "estabelecer", 
            "planejar", "executar", "medir", "avaliar", "iniciar",
            "construir", "liderar", "coordenar", "definir", "ajustar"
        ]
        if not sentences:
            return 0.0
        actionable = sum(1 for s in sentences 
                        if any(verb in s.lower() for verb in action_verbs))
        return min(1.0, actionable / len(sentences))

    def analisar_tom(self, texto: str) -> Tuple[str, float]:
        """Analisa o tom predominante da resposta."""
        texto_lower = texto.lower()
        blob = TextBlob(texto)
        
        tone_scores = {}
        for tone, patterns in TONE_PATTERNS.items():
            score = sum(texto_lower.count(pattern) for pattern in patterns)
            tone_scores[tone] = score * (1 + abs(blob.sentiment.polarity))
            
        predominant_tone = max(tone_scores.items(), key=lambda x: x[1])
        return predominant_tone[0], predominant_tone[1]

    def analisar_sentimento(self, texto: str) -> str:
        """Analisa o sentimento geral da resposta."""
        positive_words = [
            "consegui", "superei", "aprendi", "melhorei", "efetivo",
            "cresci", "evoluí", "realizei", "alcancei", "progresso"
        ]
        negative_words = [
            "difícil", "desafiador", "complicado", "problema", "falha",
            "obstáculo", "limitação", "erro", "confuso", "inseguro"
        ]
        
        texto_lower = texto.lower()
        positive_count = sum(1 for word in positive_words if word in texto_lower)
        negative_count = sum(1 for word in negative_words if word in texto_lower)
        
        if positive_count > negative_count:
            return "positive"
        elif negative_count > positive_count:
            return "improvement"
        return "neutral"

    def extrair_acao_especifica(self, texto: str) -> str:
        """Extrai uma ação específica da resposta do usuário."""
        sentences = texto.split('.')
        for sentence in sentences:
            if any(action in sentence.lower() for action in ["eu", "minha", "realizei", "fiz"]):
                return sentence.strip()
        return texto.split('.')[0].strip()

    def encontrar_melhor_resposta(self, texto_usuario: str, categoria: str) -> str:
        """Encontra a melhor resposta do coach baseada no texto do usuário."""
        sentimento = self.analisar_sentimento(texto_usuario)
        acao_especifica = self.extrair_acao_especifica(texto_usuario)
        
        respostas_categoria = RESPOSTAS_COACH[categoria][sentimento]
        user_embedding = model.encode(texto_usuario)
        
        melhor_resposta = None
        maior_similaridade = -1
        
        for template in respostas_categoria:
            context_embedding = model.encode(template["context"])
            similaridade = np.dot(user_embedding, context_embedding)
            
            if similaridade > maior_similaridade:
                maior_similaridade = similaridade
                melhor_resposta = template["response"]
        
        return melhor_resposta.format(specific_action=acao_especifica.lower())
    
    def gerar_resposta(self, texto_usuario: str) -> str:
        """Gera uma resposta completa do coach."""
        quality_metrics = self.analyze_response_quality(texto_usuario)
        
        if quality_metrics["depth"] < 0.15 and quality_metrics["specificity"] < 0.1:
            return """### Feedback Inicial 💭

Para oferecer um feedback mais valioso, poderia compartilhar mais detalhes específicos sobre sua experiência? 
Alguns aspectos que enriqueceriam sua reflexão:
- Exemplos concretos da situação
- Ações específicas tomadas
- Resultados observados"""

        if self.sessao_completa:
            self.__init__()
            
        if self.pergunta_atual >= len(PERGUNTAS):
            tempo = (datetime.now() - self.inicio).seconds // 60
            return self.gerar_sumario_final(tempo)
            
        tom_predominante, intensidade = self.analisar_tom(texto_usuario)
        self.tone_history.append(tom_predominante)
        
        pergunta_atual = PERGUNTAS[self.pergunta_atual]
        self.historico_respostas.append(texto_usuario)
        
        feedback = self.encontrar_melhor_resposta(
            texto_usuario, 
            pergunta_atual["categoria"]
        )
        
        tom_insight = self._gerar_insight_tom(tom_predominante, intensidade)
        padrao_identificado = self._analisar_padroes()
        
        resposta = f"""### Feedback Personalizado 💭\n\n{feedback}{padrao_identificado}\n\n{tom_insight}"""
        resposta += self._gerar_pontos_aprofundamento()
        
        self.pergunta_atual += 1
        if self.pergunta_atual < len(PERGUNTAS):
            resposta += self._gerar_proxima_pergunta()
        else:
            self.sessao_completa = True
            tempo = (datetime.now() - self.inicio).seconds // 60
            resposta += self.gerar_sumario_final(tempo)
        
        return resposta

    def _gerar_insight_tom(self, tom: str, intensidade: float) -> str:
        """Gera insights baseados no tom da resposta."""
        insights = {
            "confiante": "Sua confiança ao abordar este tema é notável. Como você construiu esta segurança?",
            "reflexivo": "Sua abordagem reflexiva traz profundidade à análise. Continue explorando diferentes perspectivas.",
            "hesitante": "Percebo algumas incertezas naturais do processo. Que apoio ajudaria a fortalecer sua confiança?",
            "pragmatico": "Seu foco em resultados práticos é valioso. Como você equilibra isso com visão de longo prazo?",
            "emocional": "Sua conexão emocional com a liderança demonstra comprometimento genuíno."
        }
        
        if intensidade > 2:
            return f"\n\n💡 {insights[tom]} Sua expressão é particularmente intensa neste aspecto."
        return f"\n\n💡 {insights[tom]}"

    def _analisar_padroes(self) -> str:
        """Analisa padrões nas respostas do usuário."""
        if len(self.historico_respostas) <= 1:
            return ""
            
        sentimento_atual = self.analisar_sentimento(self.historico_respostas[-1])
        sentimento_anterior = self.analisar_sentimento(self.historico_respostas[-2])
        
        if sentimento_atual == sentimento_anterior == "positive":
            return "\n\n💡 Observo um padrão consistente de confiança em suas respostas. Continue desenvolvendo esses pontos fortes!"
        elif sentimento_atual == sentimento_anterior == "improvement":
            return "\n\n💡 Percebo que você está identificando áreas de desenvolvimento. Vamos focar em estratégias práticas para esses desafios."
        return ""

    def _gerar_pontos_aprofundamento(self) -> str:
        """Gera pontos para aprofundamento da reflexão."""
        return """

#### Pontos para Aprofundamento:
1. Como essa experiência se conecta com seus valores de liderança?
2. Que recursos específicos você identificou como necessários?
3. Qual seria o próximo marco de desenvolvimento nessa área?"""

    def _gerar_proxima_pergunta(self) -> str:
        """Gera a próxima pergunta da sequência."""
        proxima = PERGUNTAS[self.pergunta_atual]
        return f"""\n\n### Próxima Reflexão: {proxima['categoria'].title()} 🎯\n\n{proxima['pergunta']}\n\nTome um momento para refletir e conectar com suas experiências..."""

    def primeira_pergunta(self) -> str:
        """Gera a mensagem inicial e primeira pergunta do coach."""
        return f"""### 👋 Bem-vindo à sua Jornada de Desenvolvimento!

Vamos explorar aspectos importantes da sua liderança através de reflexões guiadas.

{PERGUNTAS[0]['pergunta']}

Tome um momento para conectar com suas experiências e compartilhe sua perspectiva..."""

    def gerar_sumario_final(self, tempo: int) -> str:
        """Gera o sumário final da sessão."""
        sentimentos = [self.analisar_sentimento(resp) for resp in self.historico_respostas]
        predominante = max(set(sentimentos), key=sentimentos.count)
        
        tone_pattern = max(set(self.tone_history), key=self.tone_history.count)
        tone_insight = f"\n\n#### Padrão de Comunicação:\nSeu estilo predominante é {tone_pattern}, o que sugere {self._interpretar_padrao_tom(tone_pattern)}"
        
        avg_metrics = {
            key: sum(m[key] for m in self.response_quality_metrics) / len(self.response_quality_metrics)
            for key in ["depth", "clarity", "specificity", "actionability"]
        }
        
        quality_insights = "\n\n#### Insights de Qualidade das Respostas:"
        if avg_metrics["depth"] > 0.7:
            quality_insights += "\n- Suas reflexões demonstram profundidade significativa"
        if avg_metrics["specificity"] > 0.7:
            quality_insights += "\n- Você fornece exemplos concretos e detalhados"
        if avg_metrics["actionability"] > 0.7:
            quality_insights += "\n- Suas respostas são orientadas para ação"
        
        if predominante == "positive":
            perfil = "Você demonstra forte autoconhecimento e confiança em sua liderança."
        elif predominante == "improvement":
            perfil = "Você demonstra excelente capacidade de identificar oportunidades de desenvolvimento."
        else:
            perfil = "Você demonstra uma abordagem equilibrada entre conquistas e desafios."
        
        return f"""
### 🎉 Jornada de Desenvolvimento Concluída!

⏱️ Tempo de reflexão: {tempo} minutos
📝 Temas explorados: {len(PERGUNTAS)}

#### Perfil de Liderança Observado:
{perfil}{tone_insight}{quality_insights}

#### Recomendações Personalizadas:
1. Implemente uma ação específica mencionada em suas reflexões esta semana
2. Mantenha um diário de liderança focado nos temas discutidos
3. Estabeleça checkpoints mensais para revisar seu progresso

Deseja iniciar uma nova jornada de desenvolvimento com outros temas?"""

    def _interpretar_padrao_tom(self, tom: str) -> str:
        """Interpreta o padrão de tom identificado nas respostas."""
        interpretacoes = {
            "confiante": "uma base sólida para influenciar e liderar equipes.",
            "reflexivo": "uma capacidade valiosa de considerar múltiplas perspectivas.",
            "hesitante": "uma oportunidade para fortalecer sua confiança através da prática.",
            "pragmatico": "um foco valioso em resultados e implementação.",
            "emocional": "uma forte conexão com o impacto humano da liderança."
        }
        return interpretacoes.get(tom, "um estilo único de liderança.")

def criar_interface():
    """Cria a interface do usuário usando Gradio."""
    coach = EnhancedCoach()
    
    with gr.Blocks(title="Coach de Liderança", theme=gr.themes.Soft()) as app:
        gr.Markdown("""# 🚀 Coach de Liderança
        Desenvolva sua liderança através de reflexão guiada e feedback personalizado.""")
        
        chat = gr.Chatbot(
            value=[[None, coach.primeira_pergunta()]],
            height=600,
            show_label=False
        )
        
        with gr.Row():
            txt = gr.Textbox(
                placeholder="Compartilhe sua reflexão aqui...",
                lines=4,
                label="Sua Resposta"
            )
            btn = gr.Button("Enviar", variant="primary")
        
        def responder(mensagem, historico):
            if not mensagem.strip():
                return "", historico
            
            resposta = coach.gerar_resposta(mensagem)
            historico.append([mensagem, resposta])
            return "", historico
        
        txt.submit(responder, [txt, chat], [txt, chat])
        btn.click(responder, [txt, chat], [txt, chat])
    
    return app

def main():
    """Função principal para iniciar a aplicação."""
    try:
        app = criar_interface()
        app.launch()
    except Exception as e:
        print(f"Erro ao iniciar a aplicação: {e}")
        raise

if __name__ == "__main__":
    main()