Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import json | |
| from dotenv import load_dotenv | |
| from langchain_openai import ChatOpenAI | |
| from langchain_core.prompts import ChatPromptTemplate | |
| from langchain_community.document_loaders import DirectoryLoader, TextLoader | |
| from langchain_text_splitters import RecursiveCharacterTextSplitter | |
| from langchain_chroma import Chroma | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| from prompts import PROMPT_TEMPLATE, CLASSIFIER_PROMPT | |
| # Configuração da página Streamlit | |
| st.set_page_config(page_title="Central da Visão - Assistente", page_icon="👁️") | |
| st.title("👁️ Assistente Virtual - Central da Visão") | |
| load_dotenv() | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") | |
| if not OPENAI_API_KEY: | |
| st.error("A chave da API da OpenAI não foi encontrada. Verifique as Secrets do Space.") | |
| st.stop() | |
| # Configurações do banco de dados Chroma | |
| CHROMA_PATH = "chroma_db" | |
| DOCS_PATH = "docs" | |
| with open('know_graph.json', 'r', encoding='utf-8') as f: | |
| knowledge_graph_data = json.dumps(json.load(f), ensure_ascii=False) | |
| def load_or_create_db(): | |
| """ | |
| Carrega o banco Chroma se existir, ou cria do zero se não encontrar. | |
| """ | |
| model_name = "Qwen/Qwen3-Embedding-0.6B" | |
| model_kwargs = {"device": "cpu", | |
| "trust_remote_code": True} | |
| encode_kwargs = {"normalize_embeddings": True} | |
| embeddings = HuggingFaceEmbeddings( | |
| model_name=model_name, | |
| model_kwargs=model_kwargs, | |
| encode_kwargs=encode_kwargs | |
| ) | |
| # Verifica se o banco já existe | |
| if os.path.exists(CHROMA_PATH) and os.listdir(CHROMA_PATH): | |
| print("Banco de dados encontrado. Carregando...") | |
| return Chroma(persist_directory=CHROMA_PATH, embedding_function=embeddings) | |
| else: | |
| print("Banco não encontrado. Criando nova base de conhecimento...") | |
| # Garante que a pasta docs existe | |
| if not os.path.exists(DOCS_PATH): | |
| os.makedirs(DOCS_PATH) | |
| st.warning(f"A pasta '{DOCS_PATH}' estava vazia. O assistente não tem documentos para ler.") | |
| return Chroma(embedding_function=embeddings) | |
| # Carrega os arquivos .txt | |
| loader = DirectoryLoader( | |
| DOCS_PATH, | |
| glob="**/*.txt", | |
| loader_cls=TextLoader, | |
| loader_kwargs={"encoding": "utf-8"} | |
| ) | |
| documents = loader.load() | |
| if not documents: | |
| st.warning("Nenhum arquivo .txt encontrado em 'docs/'.") | |
| return Chroma(embedding_function=embeddings) | |
| # Divide em chunks | |
| text_splitter = RecursiveCharacterTextSplitter( | |
| chunk_size=1000, | |
| chunk_overlap=200 | |
| ) | |
| chunks = text_splitter.split_documents(documents) | |
| # Cria e salva o banco | |
| db = Chroma.from_documents( | |
| documents=chunks, | |
| embedding=embeddings, | |
| persist_directory=CHROMA_PATH | |
| ) | |
| return db | |
| # Inicializa o banco de dados | |
| db_chroma = load_or_create_db() | |
| # Sessão de chat | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [ | |
| {"role": "assistant", "content": "Olá! Sou o consultor virtual da Central da Visão. Como posso ajudar você hoje?"} | |
| ] | |
| def get_response(query): | |
| historico = st.session_state.messages[-6:] | |
| chat_history_str = "" | |
| for msg in historico: | |
| prefix = "Usuário: " if msg["role"] == "user" else "Consultor: " | |
| chat_history_str += f"{prefix}{msg['content']}\n" | |
| model = ChatOpenAI(model_name='gpt-4o-mini', openai_api_key=OPENAI_API_KEY) | |
| prompt_classifier = ChatPromptTemplate.from_template(CLASSIFIER_PROMPT) | |
| classifier_input = prompt_classifier.format( | |
| knowledge_graph=knowledge_graph_data, | |
| chat_history=chat_history_str, | |
| question=query | |
| ) | |
| classification_response = model.invoke(classifier_input).content | |
| cleaned_res = classification_response.replace("```json", "").replace("```", "").strip() | |
| try: | |
| classification = json.loads(cleaned_res) | |
| except: | |
| classification = {"estagio": "Nao identificado", "objecao_id": "nenhuma", "argumento_base": ""} | |
| # Busca de documentos relevantes | |
| docs_chroma = db_chroma.similarity_search(query, k=5) | |
| context_text = "\n\n".join([doc.page_content for doc in docs_chroma]) | |
| diretriz_venda = f"\n[DIRETRIZ ESTRATÉGICA]: Estágio: {classification.get('estagio')}. " \ | |
| f"Objeção: {classification.get('objecao_id')}. " \ | |
| f"Argumento sugerido: {classification.get('argumento_base')}" | |
| full_context = context_text + diretriz_venda | |
| # Prompt | |
| prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE) | |
| prompt = prompt_template.format(context=full_context, | |
| chat_history=chat_history_str, | |
| question=query) | |
| # Modelo de linguagem | |
| response = model.invoke(prompt) | |
| return response.content, classification | |
| # Interface | |
| for message in st.session_state.messages: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| if query := st.chat_input("Digite sua dúvida aqui..."): | |
| st.session_state.messages.append({"role": "user", "content": query}) | |
| with st.chat_message("user"): | |
| st.markdown(query) | |
| with st.chat_message("assistant"): | |
| with st.spinner("Consultando base de dados..."): | |
| try: | |
| response_text, info_vendas = get_response(query) | |
| with st.expander("📊 Log de Vendas (Inteligência)"): | |
| st.write(f"**Estágio:** {info_vendas.get('estagio')}") | |
| st.write(f"**Objeção Identificada:** {info_vendas.get('objecao_id')}") | |
| st.caption(f"Argumento base: {info_vendas.get('argumento_base')}") | |
| st.markdown(response_text) | |
| st.session_state.messages.append({"role": "assistant", "content": response_text}) | |
| except Exception as e: | |
| st.error(f"Ocorreu um erro ao processar: {e}") |