import chardet import torch from langchain_openai import ChatOpenAI, OpenAI from langchain_core.prompts import PromptTemplate from langchain.prompts import PromptTemplate from sentence_transformers import SentenceTransformer import os import pandas as pd import json current_dir = os.getcwd() api_key_file = "api_key.json" def load_api_key(): if not 'OPENAI_API_KEY' in os.environ: with open(api_key_file, 'r') as file: api_key = json.load(file)['api_key'] else: api_key = os.environ['OPENAI_API_KEY'] return api_key def load_dictionary(json_path): with open(json_path, 'r', encoding='utf-8') as file: return json.load(file) def detect_encoding(file_path): with open(file_path, 'rb') as file: raw_data = file.read() result = chardet.detect(raw_data) return result['encoding'] def load_text(file_path): encoding = detect_encoding(file_path) with open(file_path, 'r', encoding=encoding) as file: return file.read() def search_query(query, embeddings_tensor, model, segment_contents, file_names, k=5): query_embedding = torch.tensor(model.encode(query)).unsqueeze(0) similarities = torch.mm(query_embedding, embeddings_tensor.t()).squeeze(0) topk_similarities, topk_indices = torch.topk(similarities, k) top_segments = [segment_contents[idx] for idx in topk_indices] top_file_names = [file_names[idx] for idx in topk_indices] top_similarities = topk_similarities.tolist() return top_segments, top_file_names, top_similarities def load_embeddings(file_path="embeddings/embeddings.xlsx"): embeddings_df = pd.read_excel(os.path.join(current_dir, file_path)) embeddings = embeddings_df.iloc[:, :-3].values segment_contents = embeddings_df['segment_content'].values num_segment_contents = len(segment_contents) num_documents = embeddings_df['file_name'].nunique() file_names = embeddings_df['file_name'].values model_name = embeddings_df['model_name'].values[0] return { "embeddings": embeddings, "segment_contents": segment_contents, "num_documents": num_documents, "num_segment_contents": num_segment_contents, "file_names": file_names, "model_name": model_name, } def generate_answer_with_references(query, data, api_key): embeddings = data["embeddings"] segment_contents = data["segment_contents"] model_name = data["model_name"] file_names = data["file_names"] embeddings_tensor = torch.tensor(embeddings, dtype=torch.float32) model = SentenceTransformer(model_name) dictionary_path = os.path.join(current_dir, 'documents_names.json') file_name_dict = load_dictionary(dictionary_path) file_names = [file_name_dict.get(name, name) for name in file_names] top_segments, top_file_names, top_similarities = search_query(query, embeddings_tensor, model, segment_contents, file_names, k=5) context = "\n----\n".join(top_segments) prompt_template = """ Você é um assistente de inteligência artificial que responde a perguntas baseadas nos documentos de forma detalhada na forma culta da língua portuguesa. Não é possível gerar informações ou fornecer informações que não estejam contidas nos documentos recuperados. Se a informação não se encontra nos documentos, responda com: Não foi possível encontrar a informação requerida nos documentos. Contexto: {context} Pergunta: {query} Resposta:""".format(context=context, query=query) qa_prompt = PromptTemplate.from_template(prompt_template) llm = ChatOpenAI(api_key=api_key, model="gpt-3.5-turbo") response = llm.invoke(qa_prompt.template) resposta = response.content total_tokens = response.response_metadata['token_usage']['total_tokens'] prompt_tokens = response.response_metadata['token_usage']['prompt_tokens'] return resposta, total_tokens, prompt_tokens, top_segments, top_file_names, top_similarities, prompt_template def rag_response(query, data, detailed_response): api_key = load_api_key() resposta, total_tokens, prompt_tokens, top_segments, top_file_names, top_similarities, prompt_template = generate_answer_with_references(query, data, api_key) file_names = [x[0] for x in top_file_names] file_links = {x[0]: x[1] for x in top_file_names} if detailed_response==True: references_detail = "\n\n".join([ f"* Segmento: {segment}\nArquivo: {file_name}\nSimilaridade: {similarity:.4f}" for segment, file_name, similarity in zip(top_segments, file_names, top_similarities)]) formatted_detailed_response = f"Resposta:\n\n{resposta}\n\nPrompt:\n{prompt_template}\n\nPrompt Tokens: {prompt_tokens}\nTotal Tokens: {total_tokens}\n\n{references_detail}" return formatted_detailed_response else: file_set = set(file_name for file_name in file_names) references = "\n".join("{}".format(file_links[file_name], file_name) for file_name in file_set) formatted_response = f"{resposta}\n\n----\n{references}" return formatted_response