import os import streamlit as st from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex, StorageContext from llama_index.core.storage.docstore import SimpleDocumentStore from llama_index.llms.ollama import Ollama from llama_index.embeddings.ollama import OllamaEmbedding from llama_index.core.node_parser import LangchainNodeParser from langchain.text_splitter import RecursiveCharacterTextSplitter from llama_index.core.storage.chat_store import SimpleChatStore from llama_index.core.memory import ChatMemoryBuffer from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.chat_engine import CondensePlusContextChatEngine from llama_index.retrievers.bm25 import BM25Retriever from llama_index.core.retrievers import QueryFusionRetriever from llama_index.vector_stores.chroma import ChromaVectorStore import chromadb import nest_asyncio import os from llama_index.core import VectorStoreIndex, SimpleDirectoryReader from llama_index.llms.huggingface import HuggingFaceLLM from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI from llama_index.core import Settings from typing import List, Optional from llama_index.core import PromptTemplate import torch from llama_index.embeddings.huggingface import HuggingFaceEmbedding import logging import sys from PIL import Image #Configuração da imagem da aba im = Image.open("pngegg.png") st.set_page_config(page_title = "Chatbot Carômetro", page_icon=im, layout = "wide") #Removido loop e adicionado os.makedirs os.makedirs("bm25_retriever", exist_ok=True) os.makedirs("chat_store", exist_ok=True) os.makedirs("chroma_db", exist_ok=True) os.makedirs("documentos", exist_ok=True) os.makedirs("curadoria", exist_ok=True) os.makedirs("chroma_db_curadoria", exist_ok=True) # Configuração do Streamlit st.sidebar.title("Configuração de LLM") sidebar_option = st.sidebar.radio("Selecione o LLM", ["gpt-3.5-turbo", "NuExtract-1.5"]) # logo_url = 'app\logos\logo-sicoob.jpg' # st.sidebar.image(logo_url) import base64 #Configuração da imagem da sidebar with open("sicoob-logo.png", "rb") as f: data = base64.b64encode(f.read()).decode("utf-8") st.sidebar.markdown( f"""
""", unsafe_allow_html=True, ) #if sidebar_option == "Ollama": # Settings.llm = Ollama(model="llama3.2:latest", request_timeout=500.0, num_gpu=1) # Settings.embed_model = OllamaEmbedding(model_name="nomic-embed-text:latest") if sidebar_option == "gpt-3.5-turbo": from llama_index.llms.openai import OpenAI from llama_index.embeddings.openai import OpenAIEmbedding Settings.llm = OpenAI(model="gpt-3.5-turbo") Settings.embed_model = OpenAIEmbedding(model_name="text-embedding-ada-002") elif sidebar_option == 'NuExtract-1.5': logging.basicConfig(stream=sys.stdout, level=logging.INFO) logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout)) #Embedding do huggingface Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-en-v1.5" ) #Carregamento do modelo local, descomentar o modelo desejado llm = HuggingFaceLLM( context_window=2048, max_new_tokens=256, generate_kwargs={"do_sample": False}, #query_wrapper_prompt=query_wrapper_prompt, #model_name="Qwen/Qwen2.5-Coder-32B-Instruct", #model_name="Qwen/Qwen2.5-14B-Instruct", # model_name="meta-llama/Llama-3.2-3B", #model_name="HuggingFaceH4/zephyr-7b-beta", # model_name="meta-llama/Meta-Llama-3-8B", model_name="numind/NuExtract-1.5", #model_name="meta-llama/Llama-3.2-3B", tokenizer_name="numind/NuExtract-1.5", device_map="auto", tokenizer_kwargs={"max_length": 2048}, # uncomment this if using CUDA to reduce memory usage model_kwargs={"torch_dtype": torch.bfloat16}, ) chat = [ {"role": "user", "content": "Hello, how are you?"}, {"role": "assistant", "content": "I'm doing great. How can I help you today?"}, {"role": "user", "content": "I'd like to show off how chat templating works!"}, ] from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("numind/NuExtract-1.5") tokenizer.apply_chat_template(chat, tokenize=False) Settings.chunk_size = 512 Settings.llm = llm else: raise Exception("Opção de LLM inválida!") # Diretórios configurados pelo usuário chat_store_path = os.path.join("chat_store", "chat_store.json") documents_path = os.path.join("documentos") chroma_storage_path = os.path.join("chroma_db") # Diretório para persistência do Chroma chroma_storage_path_curadoria = os.path.join("chroma_db_curadoria") # Diretório para 'curadoria' bm25_persist_path = os.path.join("bm25_retriever") curadoria_path = os.path.join("curadoria") # Configuração de leitura de documentos documents = SimpleDirectoryReader(input_dir=documents_path).load_data() # Configuração do Chroma e BM25 com persistência docstore = SimpleDocumentStore() docstore.add_documents(documents) db = chromadb.PersistentClient(path=chroma_storage_path) chroma_collection = db.get_or_create_collection("dense_vectors") vector_store = ChromaVectorStore(chroma_collection=chroma_collection) # Configuração do StorageContext storage_context = StorageContext.from_defaults( docstore=docstore, vector_store=vector_store ) # Criação/Recarregamento do índice com embeddings if os.path.exists(chroma_storage_path): index = VectorStoreIndex.from_vector_store(vector_store) else: splitter = LangchainNodeParser( RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=64) ) index = VectorStoreIndex.from_documents( documents, storage_context=storage_context, transformations=[splitter] ) vector_store.persist() # Criação/Recarregamento do BM25 Retriever if os.path.exists(os.path.join(bm25_persist_path, "params.index.json")): bm25_retriever = BM25Retriever.from_persist_dir(bm25_persist_path) else: bm25_retriever = BM25Retriever.from_defaults( docstore=docstore, similarity_top_k=2, language="portuguese", # Idioma ajustado para seu caso ) os.makedirs(bm25_persist_path, exist_ok=True) bm25_retriever.persist(bm25_persist_path) #Adicionado documentos na pasta curadoria, foi setado para 1200 o chunk pra receber pergunta, contexto e resposta curadoria_documents = SimpleDirectoryReader(input_dir=curadoria_path).load_data() curadoria_docstore = SimpleDocumentStore() curadoria_docstore.add_documents(curadoria_documents) db_curadoria = chromadb.PersistentClient(path=chroma_storage_path_curadoria) chroma_collection_curadoria = db_curadoria.get_or_create_collection("dense_vectors_curadoria") vector_store_curadoria = ChromaVectorStore(chroma_collection=chroma_collection_curadoria) # Configuração do StorageContext para 'curadoria' storage_context_curadoria = StorageContext.from_defaults( docstore=curadoria_docstore, vector_store=vector_store_curadoria ) # Criação/Recarregamento do índice com embeddings para 'curadoria' if os.path.exists(chroma_storage_path_curadoria): curadoria_index = VectorStoreIndex.from_vector_store(vector_store_curadoria) else: curadoria_splitter = LangchainNodeParser( RecursiveCharacterTextSplitter(chunk_size=1200, chunk_overlap=100) ) curadoria_index = VectorStoreIndex.from_documents( curadoria_documents, storage_context=storage_context_curadoria, transformations=[curadoria_splitter] ) vector_store_curadoria.persist() curadoria_retriever = curadoria_index.as_retriever(similarity_top_k=2) # Combinação de Retrievers (Embeddings + BM25) vector_retriever = index.as_retriever(similarity_top_k=2) retriever = QueryFusionRetriever( [vector_retriever, bm25_retriever, curadoria_retriever], similarity_top_k=2, num_queries=4, mode="reciprocal_rerank", use_async=True, verbose=True, query_gen_prompt=( "Gere {num_queries} perguntas de busca relacionadas à seguinte pergunta. " "Priorize o significado da pergunta sobre qualquer histórico de conversa. " "Se o histórico não for relevante para a pergunta, ignore-o. " "Não adicione explicações, notas ou introduções. Apenas escreva as perguntas. " "Pergunta: {query}\n\n" "Perguntas:\n" ), ) # Configuração do chat engine nest_asyncio.apply() memory = ChatMemoryBuffer.from_defaults(token_limit=3900) query_engine = RetrieverQueryEngine.from_args(retriever) chat_engine = CondensePlusContextChatEngine.from_defaults( query_engine, memory=memory, context_prompt=( "Você é um assistente virtual capaz de interagir normalmente, além de" " fornecer informações sobre organogramas e listar funcionários." " Aqui estão os documentos relevantes para o contexto:\n" "{context_str}" "\nInstrução: Use o histórico da conversa anterior, ou o contexto acima, para responder." ), verbose=True, ) # Armazenamento do chat chat_store = SimpleChatStore() if os.path.exists(chat_store_path): chat_store = SimpleChatStore.from_persist_path(persist_path=chat_store_path) else: chat_store.persist(persist_path=chat_store_path) # Interface do Chatbot st.title("Chatbot Carômetro") st.write("Este chatbot pode te ajudar a conseguir informações relevantes sobre os carômetros da Sicoob.") if "chat_history" not in st.session_state: st.session_state.chat_history = [] user_input = st.chat_input("Digite sua pergunta") if user_input: response = chat_engine.chat(user_input) st.session_state.chat_history.append(f"user: {user_input}") st.session_state.chat_history.append(f"assistant: {response}") for message in st.session_state.chat_history: role, text = message.split(":", 1) with st.chat_message(role.strip().lower()): st.write(text.strip())