cdv-teste / app.py
joaopedrocgf's picture
Adicionando novas funcionalidades
46e8b1c
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)
@st.cache_resource
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}")