ProfRod100 commited on
Commit
ac49ae9
·
verified ·
1 Parent(s): 81ba821

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -80
app.py CHANGED
@@ -18,7 +18,10 @@ BASELINE_PATH = "baseline_pipe.pkl"
18
 
19
 
20
  def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
21
- """Treina um baseline pequeno (caso o aluno não envie o .pkl)."""
 
 
 
22
  from datasets import load_dataset
23
  import pandas as pd
24
 
@@ -27,10 +30,12 @@ def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
27
 
28
  df = pd.DataFrame({"text": ds_small["content"], "label": ds_small["label"]})
29
 
30
- pipe = Pipeline([
31
- ("tfidf", TfidfVectorizer(max_features=25000, ngram_range=(1, 2))),
32
- ("clf", LogisticRegression(max_iter=1200)),
33
- ])
 
 
34
 
35
  pipe.fit(df["text"], df["label"])
36
  joblib.dump(pipe, save_path)
@@ -46,125 +51,173 @@ def load_baseline():
46
  baseline_model = load_baseline()
47
 
48
 
49
- def classify_sentiment(text):
50
- if not text.strip():
 
 
 
 
51
  return {"erro": "Digite algo para analisar."}
52
 
53
  proba = baseline_model.predict_proba([text])[0]
54
- label = "positivo" if np.argmax(proba) == 1 else "negativo"
55
  conf = float(np.max(proba))
56
  return {"sentimento": label, "confianca": round(conf, 3)}
57
 
58
 
59
  # ============================================================
60
- # 2. IA Generativa — LLaMA-3-8B-Instruct
61
  # ============================================================
62
 
63
- GEN_MODEL = "meta-llama/Meta-Llama-3-8B-Instruct"
64
 
 
65
  generator = hf_pipeline(
66
- "text-generation",
67
  model=GEN_MODEL,
68
- max_new_tokens=180,
69
- temperature=0.5,
70
- top_p=0.9
71
  )
72
 
73
-
74
- SYSTEM_PROMPT = """
75
- Você é um atendente virtual profissional, educado e empático.
76
- Responda sempre em português do Brasil, de forma natural
77
- e sem mencionar que é uma IA. Nunca justifique o processo interno.
78
- """
79
 
80
 
81
- def build_final_prompt(history, user_msg, sentimento_json):
82
- sentimento = sentimento_json.get("sentimento", "neutro")
 
 
 
 
 
 
83
 
84
  if sentimento == "negativo":
85
- intent = """
86
- O cliente está insatisfeito. Mostre empatia, peça desculpas,
87
- demonstre interesse em resolver e peça informações adicionais.
88
- """
 
89
  elif sentimento == "positivo":
90
- intent = """
91
- O cliente está satisfeito. Agradeça com entusiasmo,
92
- reforce os pontos positivos e demonstre proximidade.
93
- """
 
94
  else:
95
- intent = """
96
- Sentimento indefinido. Responda de forma neutra, cordial e prestativa.
97
- """
98
-
99
- history_text = "\n".join(
100
- [f"Cliente: {msg[0]}\nAtendente: {msg[1]}" for msg in history]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  )
102
 
103
- final_prompt = f"""
104
- {SYSTEM_PROMPT}
105
-
106
- Contexto da conversa:
107
- {history_text}
108
-
109
- Instruções:
110
- {intent}
111
 
112
- Mensagem do cliente:
113
- "{user_msg}"
114
 
115
- Gere uma resposta natural, com 2 a 4 frases.
116
- """
 
 
 
 
 
 
 
117
 
118
- return final_prompt
 
 
119
 
 
120
 
121
- def chat_generate(history, user_input):
122
- if not user_input.strip():
123
- return history, "Digite uma mensagem."
 
 
 
 
124
 
125
- sentimento = classify_sentiment(user_input)
126
- final_prompt = build_final_prompt(history, user_input, sentimento)
127
-
128
- result = generator(final_prompt)[0]["generated_text"]
129
-
130
- # Extrair somente a resposta final (remove prompt repetido)
131
- if final_prompt in result:
132
- result = result.split(final_prompt)[-1].strip()
133
-
134
- history.append((user_input, result))
135
- return history, result
136
 
137
 
138
  # ============================================================
139
- # 3. Interface Gradio (compatível com HF antigo)
140
  # ============================================================
141
 
142
- with gr.Blocks(title="Chatbot de Sentimentos - Prof. Rodrigo", theme=gr.themes.Default()) as demo:
 
 
 
143
  gr.Markdown(
144
  """
145
  # Chatbot de Sentimentos (ML + IA Generativa)
 
146
  **Professor Rodrigo — Projeto Final ML & DL**
147
 
148
- - Classificação de sentimento com TF-IDF + Regressão Logística
149
- - Respostas naturais geradas por LLaMA-3-8B-Instruct
150
- - Suporte a histórico de conversa
151
- - Envie `baseline_pipe.pkl` na aba **Files** caso tenha treinado seu próprio modelo
 
152
  """
153
  )
154
 
 
155
  with gr.Tab("Análise de Sentimento"):
156
- text_in = gr.Textbox(label="Digite um comentário", lines=4)
157
- text_out = gr.JSON(label="Resultado")
158
- btn_analisar = gr.Button("Analisar sentimento")
159
- btn_analisar.click(classify_sentiment, inputs=text_in, outputs=text_out)
 
 
 
160
 
161
- with gr.Tab("Chatbot (Conversação + Resposta)"):
162
- chatbot = gr.Chatbot(label="Histórico de conversa")
163
- user_box = gr.Textbox(label="Mensagem do cliente", lines=3)
164
- send_btn = gr.Button("Enviar e gerar resposta")
165
 
166
- send_btn.click(chat_generate,
167
- inputs=[chatbot, user_box],
168
- outputs=[chatbot, user_box])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
  demo.launch()
 
18
 
19
 
20
  def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
21
+ """
22
+ Treina um baseline pequeno usando o dataset amazon_polarity.
23
+ Só é executado se baseline_pipe.pkl não existir no Space.
24
+ """
25
  from datasets import load_dataset
26
  import pandas as pd
27
 
 
30
 
31
  df = pd.DataFrame({"text": ds_small["content"], "label": ds_small["label"]})
32
 
33
+ pipe = Pipeline(
34
+ [
35
+ ("tfidf", TfidfVectorizer(max_features=25000, ngram_range=(1, 2))),
36
+ ("clf", LogisticRegression(max_iter=1200)),
37
+ ]
38
+ )
39
 
40
  pipe.fit(df["text"], df["label"])
41
  joblib.dump(pipe, save_path)
 
51
  baseline_model = load_baseline()
52
 
53
 
54
+ def classify_sentiment(text: str):
55
+ """
56
+ Classifica o texto como positivo/negativo e retorna um dicionário.
57
+ """
58
+ text = text.strip()
59
+ if not text:
60
  return {"erro": "Digite algo para analisar."}
61
 
62
  proba = baseline_model.predict_proba([text])[0]
63
+ label = "positivo" if int(np.argmax(proba)) == 1 else "negativo"
64
  conf = float(np.max(proba))
65
  return {"sentimento": label, "confianca": round(conf, 3)}
66
 
67
 
68
  # ============================================================
69
+ # 2. IA Generativa — FLAN-T5 (modelo aberto, sem token)
70
  # ============================================================
71
 
72
+ GEN_MODEL = os.getenv("GEN_MODEL_ID", "google/flan-t5-base")
73
 
74
+ # text2text-generation é o modo ideal para FLAN-T5
75
  generator = hf_pipeline(
76
+ "text2text-generation",
77
  model=GEN_MODEL,
 
 
 
78
  )
79
 
80
+ SYSTEM_PROMPT = (
81
+ "Você é um atendente virtual educado, empático e profissional "
82
+ "de uma loja online. Responda SEMPRE em português do Brasil, "
83
+ "com 2 a 4 frases claras, naturais e diretas. "
84
+ "Não mencione que é um modelo de IA e não explique o processo interno."
85
+ )
86
 
87
 
88
+ def build_prompt(history, user_msg: str, sentimento_json: dict) -> str:
89
+ """
90
+ Monta o prompt para o FLAN-T5 usando histórico + sentimento.
91
+ Nada disso deve aparecer explicitamente na resposta.
92
+ """
93
+ sentimento = sentimento_json.get("sentimento")
94
+ if not sentimento or "erro" in sentimento_json:
95
+ sentimento = "nao identificado"
96
 
97
  if sentimento == "negativo":
98
+ orientacao = (
99
+ "O cliente está insatisfeito. Mostre empatia, peça desculpas, "
100
+ "demonstre interesse em resolver e, se fizer sentido, peça "
101
+ "dados adicionais (pedido, produto, canal de contato)."
102
+ )
103
  elif sentimento == "positivo":
104
+ orientacao = (
105
+ "O cliente está satisfeito. Agradeça com entusiasmo, "
106
+ "reforce os pontos positivos e convide o cliente a continuar "
107
+ "comprando ou usando o serviço."
108
+ )
109
  else:
110
+ orientacao = (
111
+ "O sentimento não está claro. Responda de forma neutra, cordial "
112
+ "e prestativa, buscando ajudar o cliente."
113
+ )
114
+
115
+ # Monta o histórico em texto simples
116
+ history_text = ""
117
+ if history:
118
+ for u, b in history:
119
+ history_text += f"Cliente: {u}\nAtendente: {b}\n"
120
+
121
+ prompt = (
122
+ f"{SYSTEM_PROMPT}\n\n"
123
+ "Use as instruções abaixo apenas como contexto, sem citar explicitamente:\n"
124
+ f"- Sentimento detectado: {sentimento}\n"
125
+ f"- Orientação de tom: {orientacao}\n\n"
126
+ "Histórico da conversa (se houver):\n"
127
+ f"{history_text}\n"
128
+ "Nova mensagem do cliente:\n"
129
+ f"Cliente: {user_msg}\n\n"
130
+ "Escreva apenas a resposta do Atendente em português do Brasil."
131
  )
132
 
133
+ return prompt
 
 
 
 
 
 
 
134
 
 
 
135
 
136
+ def generate_reply(history, user_msg: str):
137
+ """
138
+ - Classifica sentimento da nova mensagem
139
+ - Gera resposta usando FLAN-T5
140
+ - Atualiza o histórico
141
+ """
142
+ user_msg = user_msg.strip()
143
+ if not user_msg:
144
+ return history, "", "Digite uma mensagem antes de enviar."
145
 
146
+ sentimento = classify_sentiment(user_msg)
147
+ if "erro" in sentimento:
148
+ return history, "", sentimento["erro"]
149
 
150
+ prompt = build_prompt(history, user_msg, sentimento)
151
 
152
+ out = generator(
153
+ prompt,
154
+ max_length=200,
155
+ do_sample=True,
156
+ temperature=0.7,
157
+ top_p=0.9,
158
+ )[0]["generated_text"].strip()
159
 
160
+ # Atualiza histórico para o Chatbot
161
+ new_history = history + [(user_msg, out)]
162
+ # Limpa o campo de texto do usuário e também retornamos o JSON do sentimento
163
+ return new_history, "", sentimento
 
 
 
 
 
 
 
164
 
165
 
166
  # ============================================================
167
+ # 3. Interface Gradio (compatível com HF Spaces)
168
  # ============================================================
169
 
170
+ with gr.Blocks(
171
+ title="Chatbot de Sentimentos - Prof. Rodrigo",
172
+ theme=gr.themes.Default(),
173
+ ) as demo:
174
  gr.Markdown(
175
  """
176
  # Chatbot de Sentimentos (ML + IA Generativa)
177
+
178
  **Professor Rodrigo — Projeto Final ML & DL**
179
 
180
+ - Classificação: TF-IDF + Regressão Logística (baseline, `amazon_polarity`)
181
+ - Geração de resposta: `google/flan-t5-base` (modelo aberto, sem token)
182
+ - Histórico de conversa mantido na aba de chatbot
183
+ - Você pode enviar um `baseline_pipe.pkl` na aba **Files** do Space para usar
184
+ um modelo de sentimentos treinado pelo seu grupo.
185
  """
186
  )
187
 
188
+ # ----------------- Aba 1: Análise isolada -----------------
189
  with gr.Tab("Análise de Sentimento"):
190
+ text_in = gr.Textbox(
191
+ label="Digite um comentário",
192
+ lines=4,
193
+ placeholder="Ex.: O produto chegou quebrado, fiquei muito chateado.",
194
+ )
195
+ text_out = gr.JSON(label="Resultado da classificação")
196
+ btn_analisar = gr.Button("Analisar sentimento", variant="primary")
197
 
198
+ btn_analisar.click(classify_sentiment, inputs=text_in, outputs=text_out)
 
 
 
199
 
200
+ # ----------------- Aba 2: Chatbot com histórico -----------------
201
+ with gr.Tab("Chatbot (Análise + Resposta)"):
202
+ chatbot = gr.Chatbot(
203
+ label="Histórico de conversa com o atendente virtual",
204
+ height=400,
205
+ )
206
+ user_box = gr.Textbox(
207
+ label="Mensagem do cliente",
208
+ lines=3,
209
+ placeholder="Ex.: Estou chateado, o produto é ruim.",
210
+ )
211
+ sentimento_box = gr.JSON(
212
+ label="Sentimento da última mensagem analisada",
213
+ )
214
+ send_btn = gr.Button("Enviar e gerar resposta", variant="primary")
215
+
216
+ # Quando clicar, gera resposta + atualiza histórico + mostra sentimento
217
+ send_btn.click(
218
+ generate_reply,
219
+ inputs=[chatbot, user_box],
220
+ outputs=[chatbot, user_box, sentimento_box],
221
+ )
222
 
223
  demo.launch()