如何制作一个 PDF 聊天机器人来保持每次访问的上下文分开?

问题描述 投票:0回答:1

我是 Streamlit 的初级用户,我使用 Gemini 创建了一个聊天机器人,用于在 .PDF 文件的上下文中提出问题。

Github 在这里(app.py)。它运作良好,但我注意到当两个或更多用户同时使用该网站时,答案的上下文会混淆 也就是说,用户 1 上传 PDF 并开始向模型询问有关某个主题的问题。然后,用户 2 在另一台计算机上上传另一个主题的另一个 PDF。用户 1 的答案开始包含用户 2 的上下文

所以我想知道 Streamlit 是否可以隔离每个使用聊天机器人的用户的上下文。

编辑: 整个脚本:

# Import the necessary libraries
import streamlit as st
from PyPDF2 import PdfReader
import os
from dotenv import load_dotenv
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
import google.generativeai as genai
from langchain_community.vectorstores import FAISS 
from langchain.chains.question_answering import load_qa_chain
from langchain.prompts import PromptTemplate
from google.generativeai.types.safety_types import HarmBlockThreshold, HarmCategory
from langchain_community.output_parsers.rail_parser import GuardrailsOutputParser
import asyncio

# Function to extract text from several PDF documents
def get_pdf_text(pdf_docs):
    text = ""
    for pdf in pdf_docs:
        pdf_reader = PdfReader(pdf) # Initialize a PDF reader for each document
        for page in pdf_reader.pages: # Iterate on each PDF page
            text += page.extract_text() # Extract the text from the page and add it to the text variable
    return text # Returns the concatenated text of all PDFs

# Function to split the text into parts that are easier to manage and process
def get_text_chunks(text):
    # Configure the text splitter to split the text into chunks, each up to 10,000 characters long, with an overlap of 1,000 characters
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=10000, chunk_overlap=1000)
    chunks = text_splitter.split_text(text) # Split the text into parts (chunks)
    return chunks

# Function to create a vector store from text chunks
def get_vector_store(text_chunks, api_key):
    embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001", api_key=api_key) # Load the embedding model
    vector_store = FAISS.from_texts(text_chunks, embedding=embeddings) # Create a FAISS vector store from the text blocks
    vector_store.save_local("faiss_index") # Save the vector store locally for later use

# Function to create a chain of conversational responses using a template
def get_conversational_chain(api_key):
    # Detailed instructions on chatbot operation and response format - in Brazilian Portuguese and for types of documents of public interest in the country
    instructions = """
    Sempre termine as respostas com "Todas as informações precisam ser checadas com as fontes das informações".
    Você é um assistente para analisar documentos .PDF com um contexto jornalístico. Por exemplo: 
    documentos da Lei nº 12.527/2011 (Lei de Acesso à Informação), contratos públicos, processos judiciais etc.
    Explique os passos de forma simples. Mantenha as respostas concisas e inclua links para ferramentas, pesquisas e páginas da Web das quais você cita informações.
    Quando o usuário pedir recursos, certifique-se de que cita as ligações para a investigação ou exemplos.
    Se eu lhe pedir para resumir uma passagem, escreva-a ao nível universitário
    Quando for relevante, divida os tópicos em partes mais pequenas e fáceis de entender. Quando estiver a editar um texto para mim, 
    faça uma lista de pontos com todas as alterações no final.
    Antes de começar uma tarefa, respire fundo e execute-a passo a passo.
    Seja claro, breve e ordenado nas respostas. Seja direto e claro.
    Evite opiniões e tente ser neutro.
    Se baseie nas classes processuais do Direito no Brasil que estão neste site -  https://www.cnj.jus.br/sgt/consulta_publica_classes.php
    Se não souber a resposta diga que não sabe
    
    Quando analisar documentos de processos judiciais procure priorizar nos resumos:
    - Verifique se é uma petição inicial, decisão ou sentença
    - Faça uma apresentação da ação e de suas partes: breve síntese do processo e de seus pólos ativo e passivo, indicando o tipo de processo, advogados e magistrados
    - Motivos que levaram o autor a ajuizar a ação: explicação sucinta do porquê de o autor ter proposto a ação em face do réu
    - O que o autor requereu com a ação: citação de todos os requerimentos e pedidos que o autor realizou através do processo, tanto liminarmente quanto no mérito
    - Resultado das decisões: exposição do que foi decidido nas decisões interlocutórias (liminares) e na sentença
    - Status: Ao final do resumo é importante que se indique o status do processo

    Quando analisar documentos de licitações ou contratos públicos, saiba isto:
    As licitações públicas no Brasil são um processo administrativo formal e transparente, utilizado pela administração pública para contratar bens, serviços ou obras. O objetivo principal é garantir a aplicação dos princípios da administração pública, como impessoalidade, publicidade, economicidade, eficiência, moralidade e igualdade.
Etapas do Processo Licitatório:
    Fase Preparatória: A administração pública define a necessidade de contratar um bem, serviço ou obra e elabora o edital da licitação, que contém todas as regras e procedimentos do processo.
    Divulgação do Edital: O edital é publicado em diário oficial e em outros meios de comunicação, para que empresas e pessoas interessadas possam tomar conhecimento e participar da licitação.
    Apresentação de Propostas: As empresas e pessoas interessadas apresentam suas propostas, que devem atender às exigências do edital.
    Julgamento das Propostas: Uma comissão de licitação analisa as propostas e seleciona a mais vantajosa para a administração pública, levando em consideração critérios como preço, qualidade, prazo e experiência do licitante.
    Adjudicação e Contratação: A administração pública adjudica o contrato à empresa vencedora da licitação e formaliza o contrato.
    Execução do Contrato: A empresa vencedora executa o contrato, fornecendo o bem, serviço ou obra contratado.
    Fiscalização e Recebimento: A administração pública fiscaliza a execução do contrato e recebe o bem, serviço ou obra, após verificar se está de acordo com o contratado.
Modalidades de Licitação:
A Lei de Licitações e Contratos (Lei nº 14.133/2021) prevê diversas modalidades de licitação, cada uma com suas características e procedimentos específicos. As modalidades mais comuns são:
    Pregão: Modalidade mais rápida e simples, utilizada para compras de bens e serviços de valor pequeno ou médio.
    Concorrência: Modalidade utilizada para compras de bens e serviços de valor elevado, obras públicas e serviços de engenharia.
    Tomada de Preços: Modalidade utilizada para contratação de obras públicas e serviços de engenharia de valor pequeno ou médio.
    Concurso: Modalidade utilizada para a seleção de projetos técnicos, científicos ou artísticos.
    Leilão: Modalidade utilizada para a venda de bens públicos, como imóveis e veículos.
Importância das Licitações Públicas:
As licitações públicas são importantes para garantir:
    Transparência: O processo licitatório é público e transparente, o que permite que qualquer cidadão possa acompanhar as etapas do processo e fiscalizar a utilização dos recursos públicos.
    Competitividade: As empresas e pessoas interessadas competem entre si para apresentar a proposta mais vantajosa para a administração pública, o que garante a obtenção de melhores preços e serviços.
    Eficiência: A administração pública contrata o bem, serviço ou obra que melhor atende às suas necessidades, com o melhor custo-benefício.
    Moralidade: O processo licitatório contribui para prevenir a corrupção e o favorecimento de empresas ou pessoas específicas.
Onde Obter Mais Informações:
Para mais informações sobre licitações públicas no Brasil, você pode consultar os seguintes sites:
    Portal da Transparência: https://portaldatransparencia.gov.br/
    Ministério da Economia: https://www.gov.br/economia/pt-br
    Tribunal de Contas da União: https://www.TCU.gov.br/

    No Brasil, o termo mais comum para se referir a licitações sem concorrentes é inexigibilidade ou dispensa de licitação.
Este termo está previsto na Lei de Licitações e Contratos (Lei nº 14.133/2021), que define as hipóteses em que a administração pública pode contratar bens, serviços ou obras sem a necessidade de realizar licitação.
Outras expressões que podem ser utilizadas para se referir a licitações sem concorrentes:
    Licitação deserta: Essa expressão é utilizada para indicar que a licitação não teve nenhum participante, ou seja, nenhuma empresa apresentou proposta.
    Licitação única: Essa expressão é utilizada para indicar que apenas uma empresa apresentou proposta, o que significa que a licitação não foi competitiva.
    Contratação direta: Essa expressão é utilizada para se referir à modalidade de contratação que a administração pública pode utilizar em casos de inexigibilidade de licitação.
É importante ressaltar que a inexigibilidade de licitação não é sinônimo de falta de transparência ou de controle. A Lei de Licitações e Contratos estabelece diversas regras e procedimentos que a administração pública deve seguir para garantir a lisura e a economicidade na contratação de bens, serviços ou obras, mesmo em casos de inexigibilidade de licitação.
Alguns exemplos de situações em que a inexigibilidade de licitação pode ser aplicada:
    Aquisição de bens ou serviços com fornecedor único: Quando existe apenas um único fornecedor para o bem ou serviço que a administração pública precisa adquirir, a licitação torna-se inviável.
    Contratação em caso de emergência: Em situações de urgência ou calamidade pública, a administração pública pode contratar bens, serviços ou obras sem licitação, para garantir o atendimento imediato das necessidades da população.
    Contratação de serviços artísticos ou culturais: A Lei de Licitações e Contratos permite a contratação direta de artistas ou profissionais de cultura, sem a necessidade de licitação, para a realização de obras de arte, espetáculos ou outros eventos culturais.

    Os documentos que trazem respostas de um pedido de acesso à informação pela Lei nº 12.527/2011 (LAI - Lei de Acesso à Informação) normalmente possuem:
- Nome do órgão público
- Nomes dos setores do órgão público responsáveis pelas informações
- Assunto
- Resumo da demanda
- Informações complementares
- Nomes das pessoas responsáveis pela resposta do pedido da LAI
- Data da resposta
É importante que a análise dos documentos que citam a LAI feita por este chatbot tragam informações:
- Data
- Protocolo NUP
- Nome do órgão público
- Nomes das pessoas responsáveis pela resposta do pedido da LAI
- Data da resposta
- E demais informações de resumo que demonstrem se o pedido da LAI foi totalmente atendido, parcialmente ou foi negado

    """

    prompt_template = f"""
    {instructions}
    Contexto:\n{{context}}\n
    Questão: \n{{question}}\n

    Resposta:
    """
    
    # Load the conversational AI model with the specified security settings
    model = ChatGoogleGenerativeAI(model="gemini-1.0-pro", 
                                   temperature=0,
                                   candidate_count=1,
                                   safety_settings = {
                                       HarmCategory.HARM_CATEGORY_UNSPECIFIED: HarmBlockThreshold.BLOCK_ONLY_HIGH,
                                       HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
                                       HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_ONLY_HIGH,
                                       HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
                                       HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_ONLY_HIGH
                                      }, 
                                  api_key=api_key)
    
    prompt = PromptTemplate(template=prompt_template, input_variables=["context", "question"]) # Configure the prompt template
    chain = load_qa_chain(model, chain_type="stuff", prompt=prompt) # Load the Q&A string with the template and prompt
    return chain

# Function to process user input and generate responses
def user_input(user_question, api_key):
    if 'history' not in st.session_state: # Initialize session history, if not already present
        st.session_state.history = []

    embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001", api_key=api_key) # Load embeddings
    new_db = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True) # Load the local FAISS index
    docs = new_db.similarity_search(user_question) # Perform similarity search with user question
    chain = get_conversational_chain(api_key) # Get the conversation chain

    # Get a response from the chatbot
    response = chain({"input_documents": docs, "question": user_question}, return_only_outputs=True) 
    st.session_state.history.append({"question": user_question, "answer": response["output_text"]}) # Attach the interaction to the history
    
    for interaction in st.session_state.history:
        st.write(f":bust_in_silhouette: {interaction['question']}") # Show the question
        st.write(f"🤖{interaction['answer']}") # Show the answer

# Main function for configuring the Streamlit application
def main():   
    st.set_page_config(page_title="Chatbot com vários PDFs", page_icon=":books:")
    st.header("Chatbot com vários PDFs :books:")

    with st.sidebar:
        st.title("Menu:")
        st.markdown("""
        - Se encontrar erros de processamento, reinicie com F5.
        """)
        
        st.warning(
            """
        Atenção: Os documentos que você compartilhar com o modelo de IA generativa podem ser usados pelo Gemini para treinar o sistema. Portanto, evite compartilhar documentos PDF que contenham:
        1. Dados bancários e financeiros
        2. Dados de sua própria empresa
        3. Informações pessoais
        4. Informações de propriedade intelectual
        5. Conteúdos autorais
        
        E não use IA para escrever um texto inteiro! O auxílio é melhor para gerar resumos, filtrar informações ou auxiliar a entender contextos - que depois devem ser checados. Inteligência Artificial comete erros!
        
        Este projeto não se responsabiliza pelos conteúdos criados a partir deste site.
        """
            )
        st.sidebar.title("Sobre este app")
        st.sidebar.info(
            "Este aplicativo foi desenvolvido por Reinaldo Chaves. "
            "Para mais informações, contribuições e feedback, visite o repositório do projeto: "
            "[GitHub](https://github.com/reichaves/chatgeminipdfs)."
        )

    
    # Create a new loop if there isn't an existing one
    try:
        loop = asyncio.get_event_loop()
        if loop.is_closed():
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

    # Field to enter the Gemini API Key
    if "api_key" not in st.session_state:
        st.session_state.api_key = ""
    if "uploaded_pdfs" not in st.session_state:
        st.session_state.uploaded_pdfs = []

    if not st.session_state.api_key:
        st.write("Digite sua API Key do Gemini")
        api_key = st.text_input("API Key do Gemini", type="password")
        st.markdown(
            f'<p style="font-size:18px;">Veja como obter uma API Key neste <a href="https://ai.google.dev/gemini-api/docs/api-key?hl=pt-br">site</a>!</p>',
            unsafe_allow_html=True)
        if api_key:
            st.session_state.api_key = api_key

    # Upload PDF documents
    if not st.session_state.uploaded_pdfs:
        st.write("Por favor, faça o upload e processe os documentos PDF para ativar o chat")
        pdf_docs = st.file_uploader("Carregar PDFs", type=["pdf"], accept_multiple_files=True)
        if pdf_docs:
            st.session_state.uploaded_pdfs = pdf_docs

    if st.session_state.api_key and st.session_state.uploaded_pdfs:
        genai.configure(api_key=st.session_state.api_key)
        #st.write(f"Chave API fornecida: {api_key}")  # Adding a debug log
        
        with st.sidebar:           
            if st.session_state.uploaded_pdfs:
                with st.spinner("Processando..."):
                    raw_text = get_pdf_text(st.session_state.uploaded_pdfs)
                    text_chunks = get_text_chunks(raw_text)
                    get_vector_store(text_chunks, st.session_state.api_key)
                    st.success("Done")
                    st.session_state['docs_processed'] = True
            else:
                st.error("Por favor, faça o upload de pelo menos um arquivo PDF antes de processar.")
    
        if st.session_state['docs_processed']:
            user_question = st.text_input("Faça perguntas para 'entrevistar' o PDF (por exemplo, processos judicias, contratos públicos, respostas da LAI etc). Se citar siglas nas perguntas coloque - a sigla e o seu significado. Atenção: Todas as respostas precisam ser checadas!", key="user_question_input")
            if user_question:
                user_input(user_question, st.session_state.api_key)
    
if __name__ == "__main__":
    main() 
pdf chatbot streamlit multiple-instances google-gemini
1个回答
0
投票

我注意到,当两个或更多用户同时使用该网站时 此时,答案的上下文是混合的,即用户 1 上传了一个 PDF 并开始向模型询问有关某个主题的问题。然后用户2, 在另一台计算机上上传另一个主题的另一个 PDF。用户1的 答案开始包含用户 2 的上下文

为了解决这个问题,每个pdf都应该存储在PDF表中并与用户相关。因此,在您的应用程序中,您应该跟踪用户创建的 pdf。

User
Pdf
之间存在一对多关系,即一个用户可以拥有多个PDF,但每个PDF仅与一个用户关联。在
PDF
表中,应该有
user_id
Foregin Key 字段引用
user.id
。这样,PDF 表中的每个条目都通过 user_id 字段与
Use
r 表中的特定条目相关联。

您应该在应用程序中集成 sql 数据库,并在

PDF
User
表之间设置正确的关系。 Streamlit 有用于连接到不同 SQL 数据库的文档。

© www.soinside.com 2019 - 2024. All rights reserved.