File size: 14,096 Bytes
497ffa2 47e651b 497ffa2 3ad2a10 497ffa2 899e3cc dc74977 a8fdcb1 497ffa2 a8fdcb1 497ffa2 2797f1c 16e8eb3 2797f1c 5c9e296 2797f1c 497ffa2 a8fdcb1 497ffa2 a8fdcb1 12866ba a8fdcb1 497ffa2 a8fdcb1 edb7a77 c579dc3 41553f2 c579dc3 41553f2 c579dc3 6667dfd c579dc3 41553f2 6667dfd c579dc3 27a673d c579dc3 899e3cc a8fdcb1 b13514f a8fdcb1 a10e0b8 a8fdcb1 a10e0b8 a8fdcb1 b13514f 97bc0af 2ef31e9 97bc0af 2ef31e9 a8fdcb1 97bc0af f24e538 97bc0af 16e8eb3 97bc0af 16e8eb3 97bc0af 16e8eb3 97bc0af 2ef31e9 97bc0af 2ef31e9 97bc0af 99d8de2 97bc0af 497ffa2 0441e1c 497ffa2 0441e1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 5232e60 497ffa2 dc74977 2797f1c 497ffa2 448e07c 1e6b989 2a504be 448e07c 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 448e07c 2797f1c 497ffa2 2797f1c 497ffa2 2797f1c 497ffa2 448e07c 497ffa2 2797f1c 16e8eb3 e10ec3d 97bc0af 448e07c 16e8eb3 2797f1c 20b5908 cb83b6d e10ec3d 2797f1c 97bc0af 6aa407c 2797f1c 97bc0af 2797f1c 6aa407c 2797f1c 97bc0af 6aa407c 448e07c 899e3cc 497ffa2 |
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 |
import os
import time
import shutil
import pandas as pd
from sqlalchemy import create_engine
from langchain_openai import ChatOpenAI
from langchain_community.agent_toolkits import create_sql_agent
from langchain_community.utilities import SQLDatabase
from huggingface_hub import InferenceClient
import gradio as gr
from dotenv import load_dotenv
import logging
from sqlalchemy.types import DateTime, Integer, Float
load_dotenv()
UPLOAD_DIR = "uploaded_data"
os.makedirs(UPLOAD_DIR, exist_ok=True)
DEFAULT_CSV_PATH = "tabela.csv"
UPLOADED_CSV_PATH = os.path.join(UPLOAD_DIR, "tabela.csv")
SQL_DB_PATH = "data.db"
HUGGINGFACE_API_KEY = os.getenv("HUGGINGFACE_API_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
LLAMA_MODELS = {
"LLaMA 70B": "meta-llama/Llama-3.3-70B-Instruct",
"LlaMA 8B": "meta-llama/Llama-3.1-8B-Instruct",
"Qwen 32B": "Qwen/QwQ-32B"
}
MAX_TOKENS_MAP = {
"meta-llama/Llama-3.3-70B-Instruct": 900,
"meta-llama/Llama-3.1-8B-Instruct": 700,
"Qwen/QwQ-32B": 8192
}
hf_client = InferenceClient(
provider="together", api_key=HUGGINGFACE_API_KEY
)
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
query_cache = {}
history_log = []
recent_history = []
show_history_flag = False
engine = None
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def get_active_csv_path():
"""Retorna o CSV ativo: o carregado ou o padrão.."""
if os.path.exists(UPLOADED_CSV_PATH):
logging.info(f"[CSV] Usando arquivo CSV carregado: {UPLOADED_CSV_PATH}")
return UPLOADED_CSV_PATH
else:
logging.info(f"[CSV] Usando arquivo CSV padrão: {DEFAULT_CSV_PATH}")
return DEFAULT_CSV_PATH
def create_engine_and_load_db(csv_path, sql_db_path):
if os.path.exists(sql_db_path):
print("Banco de dados SQL já existe. Carregando...")
return create_engine(f"sqlite:///{sql_db_path}")
else:
print("Banco de dados SQL não encontrado. Criando...")
engine = create_engine(f"sqlite:///{sql_db_path}")
df = pd.read_csv(
csv_path,
sep=";",
encoding='utf-8',
parse_dates=["DATA_INICIAL", "DATA_FINAL"],
dayfirst=True,
on_bad_lines="skip"
)
colunas_para_float = [
"PRECO_VISTA", "PRECO_CHEIO"
]
colunas_para_int = [
"QUANTIDADE", "TOTAL_PAGINAS_CAPA", "VALOR_MEDIDA", "DIAS_VALIDADE"
]
for col in colunas_para_float:
if col in df.columns:
df[col] = pd.to_numeric(df[col].replace("-", None), errors="coerce")
for col in colunas_para_int:
if col in df.columns:
df[col] = pd.to_numeric(df[col].replace("-", None), errors="coerce")
df[col] = df[col].where(df[col].dropna() == df[col].dropna().astype(int))
df[col] = df[col].astype("Int64")
sql_dtype = {
"DATA_INICIAL": DateTime(),
"DATA_FINAL": DateTime(),
"QUANTIDADE": Integer(),
"PRECO_VISTA": Float(),
"PRECO_CHEIO": Float(),
"TOTAL_PAGINAS_CAPA": Integer(),
"VALOR_MEDIDA": Integer(),
"DIAS_VALIDADE": Integer()
}
print("[DEBUG] Tipos das colunas:")
print(df.dtypes)
df.to_sql("tabela", engine, index=False, if_exists="replace", dtype=sql_dtype)
print("Banco de dados SQL criado com sucesso!")
return engine
def handle_csv_upload(file):
global engine, db, sql_agent
try:
file_path = file.name
shutil.copy(file_path, UPLOADED_CSV_PATH)
logging.info(f"[UPLOAD] CSV salvo como: {UPLOADED_CSV_PATH}")
engine = create_engine_and_load_db(UPLOADED_CSV_PATH, SQL_DB_PATH)
db = SQLDatabase(engine=engine)
logging.info("[UPLOAD] Novo banco carregado e DB atualizado.")
sql_agent = create_sql_agent(
ChatOpenAI(model="gpt-4o-mini", temperature=0),
db=db,
agent_type="openai-tools",
verbose=True,
max_iterations=40,
return_intermediate_steps=True
)
logging.info("[UPLOAD] Novo banco carregado e agente recriado. Cache limpo.")
query_cache.clear()
history_log.clear()
recent_history.clear()
return "✅ CSV carregado com sucesso!"
except Exception as e:
logging.error(f"[ERRO] Falha ao processar novo CSV: {e}")
return f"❌ Erro ao processar CSV: {e}"
def reset_app():
global engine, db, sql_agent, query_cache, history_log, recent_history
try:
if os.path.exists(UPLOADED_CSV_PATH):
os.remove(UPLOADED_CSV_PATH)
logging.info("[RESET] CSV personalizado removido.")
engine = create_engine_and_load_db(DEFAULT_CSV_PATH, SQL_DB_PATH)
db = SQLDatabase(engine=engine)
sql_agent = create_sql_agent(ChatOpenAI(model="gpt-4o-mini", temperature=0), db=db, agent_type="openai-tools", verbose=True, max_iterations=40, return_intermediate_steps=True)
query_cache.clear()
history_log.clear()
recent_history.clear()
return "🔄 Sistema resetado para o estado inicial."
except Exception as e:
return f"❌ Erro ao resetar: {e}"
engine = create_engine_and_load_db(get_active_csv_path(), SQL_DB_PATH)
db = SQLDatabase(engine=engine)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
sql_agent = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True, max_iterations=40, return_intermediate_steps=True)
def generate_initial_context(db_sample):
return (
f"Você é um assistente que gera queries SQL objetivas e eficientes. Sempre inclua LIMIT 20 nas queries. Aqui está o banco de dados:\n\n"
f"Exemplos do banco de dados:\n{db_sample.head().to_string(index=False)}\n\n"
"\n***IMPORTANTE***: Detecte automaticamente o idioma da pergunta do usuário e responda sempre no mesmo idioma."
"\nEsta base contém os SKUs (produtos) que foram promocionados por meio de TABLOIDE OU PROMOCAO OU ANUNCIO.\n"
"Cada linha representa um SKU OU PRODUTO único PRESENTE NO TABLOIDE OU PROMOCAO OU ANUNCIO, incluindo sua descrição completa, os veículos OU MIDIAS de promoção utilizados e o respectivo período em que a promoção ocorreu.\n"
"\nInformações imporatantes:\n"
"- Use `LIKE '%<palavras-chave>%'` para buscas em colunas de texto.\n"
"- Quando o usuário mencionar uma categoria, procure nas colunas: `CATEGORIA_PRODUTO_SKU`.\n"
"- Se o usuário se referir a Nestle, o jeito correto de se escrever é Nestle sem acento e não Nestlé.\n"
"- Você está usando um banco de dados SQLite.\n"
"\nRetorne apenas a pergunta e a query SQL mais eficiente para entregar ao agent SQL do LangChain para gerar uma resposta para a pergunta. O formato deve ser:\n"
"\nPergunta: <pergunta do usuário>\n"
"\nOpção de Query SQL:\n<query SQL>"
"\nIdioma: <idioma>"
)
def is_greeting(user_query):
greetings = ["olá", "oi", "bom dia", "boa tarde", "boa noite", "oi, tudo bem?"]
return user_query.lower().strip() in greetings
def query_with_llama(user_query, db_sample, selected_model_name):
model_id = LLAMA_MODELS[selected_model_name]
max_tokens = MAX_TOKENS_MAP.get(model_id, 512)
initial_context = generate_initial_context(db_sample)
formatted_history = "\n".join(
[f"{msg['role'].capitalize()}: {msg['content']}" for msg in recent_history[-2:]]
)
full_prompt = f"{initial_context}\n\nHistórico recente:\n{formatted_history}\n\nPergunta do usuário:\n{user_query}"
logging.info(f"[DEBUG] Contexto enviado ao ({selected_model_name}):\n{full_prompt}\n")
start_time = time.time()
try:
response = hf_client.chat.completions.create(
model=model_id,
messages=[{"role": "system", "content": full_prompt}],
max_tokens=max_tokens,
stream=False
)
llama_response = response["choices"][0]["message"]["content"]
end_time = time.time()
logging.info(f"[DEBUG] Resposta do {selected_model_name} para o Agent SQL:\n{llama_response.strip()}\n[Tempo de execução: {end_time - start_time:.2f}s]\n")
return llama_response.strip(), model_id
except Exception as e:
logging.error(f"[ERRO] Falha ao interagir com o modelo {selected_model_name}: {e}")
return None, model_id
def query_sql_agent(user_query, selected_model_name):
try:
if user_query in query_cache:
print(f"[CACHE] Retornando resposta do cache para a consulta: {user_query}")
return query_cache[user_query]
if is_greeting(user_query):
greeting_response = "Olá! Estou aqui para ajudar com suas consultas. Pergunte algo relacionado aos dados carregados no agente!"
query_cache[user_query] = greeting_response
return greeting_response
column_data = pd.read_sql_query("SELECT * FROM tabela LIMIT 10", engine)
llama_instruction = query_with_llama(user_query, column_data, selected_model_name)
if not llama_instruction:
return "Erro: O modelo Llama não conseguiu gerar uma instrução válida."
print("------- Agent SQL: Executando query -------")
response = sql_agent.invoke({"input": llama_instruction})
sql_response = response.get("output", "Erro ao obter a resposta do agente.")
query_cache[user_query] = sql_response
return sql_response
except Exception as e:
return f"Erro ao consultar o agente SQL: {e}"
advanced_mode_enabled = False # Novo estado global
def toggle_advanced_mode(state):
global advanced_mode_enabled
advanced_mode_enabled = state
logging.info(f"[MODO AVANÇADO] {'Ativado' if state else 'Desativado'}")
return "Modo avançado ativado." if state else "Modo avançado desativado."
def refine_response_with_llm(user_question, sql_response, chart_md=""):
prompt = (
f"Pergunta do usuário:\n{user_question}\n\n"
f"Resposta gerada pelo agente SQL:\n{sql_response}\n\n"
"Sua tarefa é refinar, complementar e melhorar a resposta.\n"
"Adicione interpretações estatísticas ou insights relevantes."
)
logging.info(f"[DEBUG] Prompt enviado ao modelo de refinamento:\n{prompt}\n")
try:
response = hf_client.chat.completions.create(
model=LLAMA_MODELS["LLaMA 70B"],
messages=[{"role": "system", "content": prompt}],
max_tokens=1200,
stream=False
)
improved_response = response["choices"][0]["message"]["content"]
logging.info(f"[DEBUG] Resposta do modelo de refinamento:\n{improved_response}\n")
return improved_response + ("\n\n" + chart_md if chart_md else "")
except Exception as e:
logging.error(f"[ERRO] Falha ao refinar resposta com LLM: {e}")
return sql_response + ("\n\n" + chart_md if chart_md else "")
def chatbot_response(user_input, selected_model_name):
start_time = time.time()
response = query_sql_agent(user_input, selected_model_name)
end_time = time.time()
model_id = LLAMA_MODELS[selected_model_name]
if advanced_mode_enabled:
response = refine_response_with_llm(user_input, response)
history_log.append({
"Modelo LLM": model_id,
"Pergunta": user_input,
"Resposta": response,
"Tempo de Resposta (s)": round(end_time - start_time, 2)
})
recent_history.append({"role": "user", "content": user_input})
recent_history.append({"role": "assistant", "content": response})
if len(recent_history) > 4:
recent_history.pop(0)
recent_history.pop(0)
return response
def toggle_history():
global show_history_flag
show_history_flag = not show_history_flag
return history_log if show_history_flag else {}
with gr.Blocks(theme=gr.themes.Soft()) as demo:
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("## Configurações")
model_selector = gr.Dropdown(list(LLAMA_MODELS.keys()), value="LLaMA 70B", label="")
csv_file = gr.File(file_types=[".csv"], label="")
upload_feedback = gr.Markdown()
advanced_checkbox = gr.Checkbox(label="Refinar Resposta")
reset_btn = gr.Button("Resetar")
with gr.Column(scale=4):
gr.Markdown("## Reasoning Agent")
chatbot = gr.Chatbot(height=500)
msg = gr.Textbox(placeholder="Digite sua pergunta aqui...", lines=1, label="")
btn = gr.Button("Enviar", variant="primary")
history_btn = gr.Button("Histórico", variant="secondary")
history_output = gr.JSON()
download_file = gr.File(visible=False)
def respond(message, chat_history, selected_model):
response = chatbot_response(message, selected_model)
chat_history.append((message, response))
return "", chat_history
def handle_csv_and_clear_chat(file):
feedback = handle_csv_upload(file)
return feedback, []
def reset_all():
feedback = reset_app()
return feedback, [], None
msg.submit(respond, [msg, chatbot, model_selector], [msg, chatbot])
btn.click(respond, [msg, chatbot, model_selector], [msg, chatbot])
history_btn.click(toggle_history, outputs=history_output)
csv_file.change(handle_csv_and_clear_chat, inputs=csv_file, outputs=[upload_feedback, chatbot])
reset_btn.click(reset_all, outputs=[upload_feedback, chatbot, csv_file])
advanced_checkbox.change(toggle_advanced_mode, inputs=advanced_checkbox, outputs=[])
if __name__ == "__main__":
demo.launch(share=False) |