ProfRod100 commited on
Commit
630354a
·
verified ·
1 Parent(s): 7b2d825

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -51
app.py CHANGED
@@ -1,5 +1,4 @@
1
  import os
2
- import json
3
  import gradio as gr
4
  import joblib
5
  import numpy as np
@@ -26,6 +25,7 @@ def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
26
  from datasets import load_dataset
27
  import pandas as pd
28
 
 
29
  ds = load_dataset("amazon_polarity", split="train")
30
  ds_small = ds.shuffle(seed=42).select(range(max_samples))
31
 
@@ -44,6 +44,9 @@ def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
44
 
45
 
46
  def load_baseline():
 
 
 
47
  if os.path.exists(BASELINE_PATH):
48
  return joblib.load(BASELINE_PATH)
49
  return train_small_baseline()
@@ -52,26 +55,29 @@ def load_baseline():
52
  baseline_model = load_baseline()
53
 
54
 
55
- def classify_sentiment(text: str):
56
  """
57
- Classifica o texto como positivo/negativo e retorna um dicionário Python.
 
 
 
 
 
58
  """
59
- text = text.strip()
60
  if not text:
61
- return {"erro": "Digite algo para analisar."}
62
 
63
  proba = baseline_model.predict_proba([text])[0]
64
- label = "positivo" if int(np.argmax(proba)) == 1 else "negativo"
 
65
  conf = float(np.max(proba))
66
- return {"sentimento": label, "confianca": round(conf, 3)}
67
 
68
-
69
- def classify_sentiment_str(text: str) -> str:
70
- """
71
- Versão para a interface: devolve o resultado como string JSON formatada.
72
- """
73
- result = classify_sentiment(text)
74
- return json.dumps(result, ensure_ascii=False, indent=2)
75
 
76
 
77
  # ============================================================
@@ -93,22 +99,35 @@ SYSTEM_PROMPT = (
93
  )
94
 
95
 
96
- def build_prompt(history, user_msg: str, sentimento_json: dict) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  """
98
  Monta o prompt para o FLAN-T5 usando histórico + sentimento.
99
- Nada disso deve aparecer explicitamente na resposta.
100
  """
101
- sentimento = sentimento_json.get("sentimento")
102
- if not sentimento or "erro" in sentimento_json:
103
- sentimento = "nao identificado"
104
 
105
- if sentimento == "negativo":
106
  orientacao = (
107
  "O cliente está insatisfeito. Mostre empatia, peça desculpas, "
108
  "demonstre interesse em resolver e, se fizer sentido, peça "
109
  "dados adicionais (pedido, produto, canal de contato)."
110
  )
111
- elif sentimento == "positivo":
112
  orientacao = (
113
  "O cliente está satisfeito. Agradeça com entusiasmo, "
114
  "reforce os pontos positivos e convide o cliente a continuar "
@@ -120,19 +139,19 @@ def build_prompt(history, user_msg: str, sentimento_json: dict) -> str:
120
  "e prestativa, buscando ajudar o cliente."
121
  )
122
 
123
- # Monta o histórico em texto simples
124
- history_text = ""
125
  if history:
126
  for u, b in history:
127
- history_text += f"Cliente: {u}\nAtendente: {b}\n"
128
 
129
  prompt = (
130
  f"{SYSTEM_PROMPT}\n\n"
131
- "Use as instruções abaixo apenas como contexto, sem citar explicitamente:\n"
132
- f"- Sentimento detectado: {sentimento}\n"
133
- f"- Orientação de tom: {orientacao}\n\n"
 
134
  "Histórico da conversa (se houver):\n"
135
- f"{history_text}\n"
136
  "Nova mensagem do cliente:\n"
137
  f"Cliente: {user_msg}\n\n"
138
  "Escreva apenas a resposta do Atendente em português do Brasil."
@@ -141,24 +160,25 @@ def build_prompt(history, user_msg: str, sentimento_json: dict) -> str:
141
  return prompt
142
 
143
 
144
- def generate_reply(history, user_msg: str):
145
  """
146
- - Classifica sentimento da nova mensagem
147
- - Gera resposta usando FLAN-T5
148
- - Atualiza o histórico
149
- - Devolve também o sentimento como string para exibir na UI
 
 
 
150
  """
151
- user_msg = user_msg.strip()
152
  if not user_msg:
153
- return history, "", "Digite uma mensagem antes de enviar."
154
 
155
- sentimento = classify_sentiment(user_msg)
156
- if "erro" in sentimento:
157
- return history, "", json.dumps(sentimento, ensure_ascii=False, indent=2)
158
 
159
- prompt = build_prompt(history, user_msg, sentimento)
160
 
161
- out = generator(
162
  prompt,
163
  max_length=200,
164
  do_sample=True,
@@ -166,13 +186,24 @@ def generate_reply(history, user_msg: str):
166
  top_p=0.9,
167
  )[0]["generated_text"].strip()
168
 
169
- new_history = history + [(user_msg, out)]
170
- sentimento_str = json.dumps(sentimento, ensure_ascii=False, indent=2)
171
- return new_history, "", sentimento_str
 
 
 
 
 
 
 
 
 
 
 
172
 
173
 
174
  # ============================================================
175
- # 3. Interface Gradio (sem gr.JSON)
176
  # ============================================================
177
 
178
  with gr.Blocks(
@@ -193,7 +224,7 @@ with gr.Blocks(
193
  """
194
  )
195
 
196
- # ---------- Aba 1: Análise isolada ----------
197
  with gr.Tab("Análise de Sentimento"):
198
  text_in = gr.Textbox(
199
  label="Digite um comentário",
@@ -201,14 +232,18 @@ with gr.Blocks(
201
  placeholder="Ex.: O produto chegou quebrado, fiquei muito chateado.",
202
  )
203
  text_out = gr.Textbox(
204
- label="Resultado da classificação (JSON)",
205
  lines=4,
206
  )
207
  btn_analisar = gr.Button("Analisar sentimento", variant="primary")
208
 
209
- btn_analisar.click(classify_sentiment_str, inputs=text_in, outputs=text_out)
 
 
 
 
210
 
211
- # ---------- Aba 2: Chatbot com histórico ----------
212
  with gr.Tab("Chatbot (Análise + Resposta)"):
213
  chatbot = gr.Chatbot(
214
  label="Histórico de conversa com o atendente virtual",
@@ -220,15 +255,18 @@ with gr.Blocks(
220
  placeholder="Ex.: Estou chateado, o produto é ruim.",
221
  )
222
  sentimento_box = gr.Textbox(
223
- label="Sentimento da última mensagem analisada (JSON)",
224
  lines=4,
225
  )
226
  send_btn = gr.Button("Enviar e gerar resposta", variant="primary")
227
 
228
  send_btn.click(
229
- generate_reply,
230
  inputs=[chatbot, user_box],
231
  outputs=[chatbot, user_box, sentimento_box],
232
  )
233
 
234
- demo.launch()
 
 
 
 
1
  import os
 
2
  import gradio as gr
3
  import joblib
4
  import numpy as np
 
25
  from datasets import load_dataset
26
  import pandas as pd
27
 
28
+ # Carrega split de treino completo e amostra max_samples exemplos
29
  ds = load_dataset("amazon_polarity", split="train")
30
  ds_small = ds.shuffle(seed=42).select(range(max_samples))
31
 
 
44
 
45
 
46
  def load_baseline():
47
+ """
48
+ Carrega o baseline de disco ou treina um novo se não existir.
49
+ """
50
  if os.path.exists(BASELINE_PATH):
51
  return joblib.load(BASELINE_PATH)
52
  return train_small_baseline()
 
55
  baseline_model = load_baseline()
56
 
57
 
58
+ def classify_sentiment(text):
59
  """
60
+ Classifica o texto como positivo/negativo e devolve UMA STRING.
61
+ Exemplo:
62
+
63
+ Resultado da classificação:
64
+ Sentimento: positivo
65
+ Confiança: 0.942
66
  """
67
+ text = (text or "").strip()
68
  if not text:
69
+ return "Erro: digite algo para analisar."
70
 
71
  proba = baseline_model.predict_proba([text])[0]
72
+ pred = int(np.argmax(proba))
73
+ label = "positivo" if pred == 1 else "negativo"
74
  conf = float(np.max(proba))
 
75
 
76
+ out = []
77
+ out.append("Resultado da classificação:")
78
+ out.append(f"Sentimento: {label}")
79
+ out.append(f"Confiança: {conf:.3f}")
80
+ return "\n".join(out)
 
 
81
 
82
 
83
  # ============================================================
 
99
  )
100
 
101
 
102
+ def detect_sentiment_raw(text):
103
+ """
104
+ Versão simplificada só para o chatbot.
105
+ Retorna (label, conf) ou (None, None) em caso de erro.
106
+ """
107
+ text = (text or "").strip()
108
+ if not text:
109
+ return None, None
110
+
111
+ proba = baseline_model.predict_proba([text])[0]
112
+ pred = int(np.argmax(proba))
113
+ label = "positivo" if pred == 1 else "negativo"
114
+ conf = float(np.max(proba))
115
+ return label, conf
116
+
117
+
118
+ def build_prompt(history, user_msg, sent_label, sent_conf):
119
  """
120
  Monta o prompt para o FLAN-T5 usando histórico + sentimento.
121
+ Tudo aqui é texto simples.
122
  """
 
 
 
123
 
124
+ if sent_label == "negativo":
125
  orientacao = (
126
  "O cliente está insatisfeito. Mostre empatia, peça desculpas, "
127
  "demonstre interesse em resolver e, se fizer sentido, peça "
128
  "dados adicionais (pedido, produto, canal de contato)."
129
  )
130
+ elif sent_label == "positivo":
131
  orientacao = (
132
  "O cliente está satisfeito. Agradeça com entusiasmo, "
133
  "reforce os pontos positivos e convide o cliente a continuar "
 
139
  "e prestativa, buscando ajudar o cliente."
140
  )
141
 
142
+ hist_txt = ""
 
143
  if history:
144
  for u, b in history:
145
+ hist_txt += f"Cliente: {u}\nAtendente: {b}\n"
146
 
147
  prompt = (
148
  f"{SYSTEM_PROMPT}\n\n"
149
+ "Use as informações abaixo apenas como contexto, sem citá-las:"
150
+ f"\n- Sentimento detectado: {sent_label}"
151
+ f"\n- Confiança aproximada: {sent_conf}"
152
+ f"\n- Orientação de tom: {orientacao}\n\n"
153
  "Histórico da conversa (se houver):\n"
154
+ f"{hist_txt}\n"
155
  "Nova mensagem do cliente:\n"
156
  f"Cliente: {user_msg}\n\n"
157
  "Escreva apenas a resposta do Atendente em português do Brasil."
 
160
  return prompt
161
 
162
 
163
+ def generate_reply(history, user_msg):
164
  """
165
+ Função usada pelo botão do chatbot.
166
+
167
+ - Recebe o histórico atual e a nova mensagem
168
+ - Detecta sentimento
169
+ - Gera resposta com FLAN-T5
170
+ - Retorna:
171
+ history_atualizado, "", texto_sentimento
172
  """
173
+ user_msg = (user_msg or "").strip()
174
  if not user_msg:
175
+ return history, "", "Erro: digite uma mensagem antes de enviar."
176
 
177
+ sent_label, sent_conf = detect_sentiment_raw(user_msg)
 
 
178
 
179
+ prompt = build_prompt(history, user_msg, sent_label, sent_conf)
180
 
181
+ gen_out = generator(
182
  prompt,
183
  max_length=200,
184
  do_sample=True,
 
186
  top_p=0.9,
187
  )[0]["generated_text"].strip()
188
 
189
+ new_history = history + [(user_msg, gen_out)]
190
+
191
+ sentiment_text = (
192
+ "Sentimento detectado para a última mensagem:\n"
193
+ f"- Rótulo: {sent_label}\n"
194
+ f"- Confiança aproximada: {sent_conf:.3f}"
195
+ if sent_label is not None
196
+ else "Não foi possível detectar o sentimento."
197
+ )
198
+
199
+ # history → Chatbot
200
+ # "" → limpa a caixa do usuário
201
+ # sentiment_text → textbox com sentimento
202
+ return new_history, "", sentiment_text
203
 
204
 
205
  # ============================================================
206
+ # 3. Interface Gradio (tipos simples + API desativada)
207
  # ============================================================
208
 
209
  with gr.Blocks(
 
224
  """
225
  )
226
 
227
+ # ---------- Aba 1: Análise de Sentimento ----------
228
  with gr.Tab("Análise de Sentimento"):
229
  text_in = gr.Textbox(
230
  label="Digite um comentário",
 
232
  placeholder="Ex.: O produto chegou quebrado, fiquei muito chateado.",
233
  )
234
  text_out = gr.Textbox(
235
+ label="Resultado da classificação",
236
  lines=4,
237
  )
238
  btn_analisar = gr.Button("Analisar sentimento", variant="primary")
239
 
240
+ btn_analisar.click(
241
+ fn=classify_sentiment,
242
+ inputs=text_in,
243
+ outputs=text_out,
244
+ )
245
 
246
+ # ---------- Aba 2: Chatbot (Análise + Resposta) ----------
247
  with gr.Tab("Chatbot (Análise + Resposta)"):
248
  chatbot = gr.Chatbot(
249
  label="Histórico de conversa com o atendente virtual",
 
255
  placeholder="Ex.: Estou chateado, o produto é ruim.",
256
  )
257
  sentimento_box = gr.Textbox(
258
+ label="Sentimento da última mensagem",
259
  lines=4,
260
  )
261
  send_btn = gr.Button("Enviar e gerar resposta", variant="primary")
262
 
263
  send_btn.click(
264
+ fn=generate_reply,
265
  inputs=[chatbot, user_box],
266
  outputs=[chatbot, user_box, sentimento_box],
267
  )
268
 
269
+
270
+ if __name__ == "__main__":
271
+ # Usa fila do Gradio e desativa a API aberta (evita o bug "No API found")
272
+ demo.queue(api_open=False).launch()