paint-brush
Un tutorial sobre cómo construir su propio RAG y cómo ejecutarlo localmente: Langchain + Ollama + Streamlitpor@vndee
6,983 lecturas
6,983 lecturas

Un tutorial sobre cómo construir su propio RAG y cómo ejecutarlo localmente: Langchain + Ollama + Streamlit

por Duy Huynh8m2023/12/15
Read on Terminal Reader

Demasiado Largo; Para Leer

Para familiarizarse con RAG, recomiendo leer estos artículos. Sin embargo, esta publicación omitirá los conceptos básicos y lo guiará directamente en la creación de su propia aplicación RAG que pueda ejecutarse localmente en su computadora portátil sin preocuparse por la privacidad de los datos y el costo de los tokens. Crearemos una aplicación similar a ChatPDF pero más simple. Donde los usuarios pueden cargar un documento PDF y hacer preguntas a través de una interfaz de usuario sencilla. Nuestra pila tecnológica es muy sencilla con Langchain, Ollama y Streamlit.
featured image - Un tutorial sobre cómo construir su propio RAG y cómo ejecutarlo localmente: Langchain + Ollama + Streamlit
Duy Huynh HackerNoon profile picture

Con el auge de los modelos de lenguajes grandes y sus impresionantes capacidades, se están creando muchas aplicaciones sofisticadas sobre proveedores gigantes de LLM como OpenAI y Anthropic. El mito detrás de tales aplicaciones es el marco RAG, que se explica detalladamente en los siguientes artículos:


Para familiarizarse con RAG, recomiendo leer estos artículos. Sin embargo, esta publicación omitirá los conceptos básicos y lo guiará directamente en la creación de su propia aplicación RAG que pueda ejecutarse localmente en su computadora portátil sin preocuparse por la privacidad de los datos y el costo de los tokens.


Construiremos una aplicación similar a ChatPD F pero más simple. Donde los usuarios pueden cargar un documento PDF y hacer preguntas a través de una interfaz de usuario sencilla. Nuestra pila tecnológica es muy sencilla con Langchain, Ollama y Streamlit.

  • Servidor LLM : el componente más crítico de esta aplicación es el servidor LLM. Gracias a Ollama , tenemos un servidor LLM robusto que se puede configurar localmente, incluso en una computadora portátil. Mientras llama.cpp es una opción, encuentro que Ollama, escrito en Go, es más fácil de configurar y ejecutar.


  • RAG : Sin lugar a dudas, las dos bibliotecas líderes en el dominio LLM son Cadena Lang y LLamIndex . Para este proyecto, usaré Langchain debido a que estoy familiarizado con él por mi experiencia profesional. Un componente esencial de cualquier marco RAG es el almacenamiento vectorial. estaremos usando croma aquí, ya que se integra bien con Langchain.


  • UI de chat : la interfaz de usuario también es un componente importante. Aunque hay muchas tecnologías disponibles, prefiero usar iluminado , una biblioteca de Python, para su tranquilidad.


Bien, comencemos a configurarlo.

Configurar Ollama

Como se mencionó anteriormente, configurar y ejecutar Ollama es sencillo. Primera visita ollama.ai y descargue la aplicación adecuada para su sistema operativo.


A continuación, abra su terminal y ejecute el siguiente comando para obtener la última versión Mistral-7B . Si bien hay muchos otros Modelos LLM disponibles Elijo Mistral-7B por su tamaño compacto y calidad competitiva.


 ollama pull mistral


Luego, ejecute ollama list para verificar si el modelo se extrajo correctamente. La salida del terminal debería parecerse a la siguiente:

Ahora, si el servidor LLM aún no se está ejecutando, inícielo con ollama serve . Si encuentra un mensaje de error como "Error: listen tcp 127.0.0.1:11434: bind: address already in use" , indica que el servidor ya se está ejecutando de forma predeterminada y puede continuar con el siguiente paso.

Construya el oleoducto RAG

El segundo paso de nuestro proceso es construir el oleoducto RAG. Dada la simplicidad de nuestra aplicación, necesitamos principalmente dos métodos: ingest y ask .


El método ingest acepta una ruta de archivo y la carga en un almacenamiento vectorial en dos pasos: primero, divide el documento en fragmentos más pequeños para acomodar el límite de token del LLM; en segundo lugar, vectoriza estos fragmentos utilizando Qdrant FastEmbeddings y los almacena en Chroma.


El método ask maneja las consultas de los usuarios. Los usuarios pueden plantear una pregunta y luego RetrievalQAChain recupera los contextos relevantes (fragmentos de documentos) utilizando técnicas de búsqueda por similitud de vectores.


Con la pregunta del usuario y los contextos recuperados, podemos redactar un mensaje y solicitar una predicción del servidor LLM.

 from langchain.vectorstores import Chroma from langchain.chat_models import ChatOllama from langchain.embeddings import FastEmbedEmbeddings from langchain.schema.output_parser import StrOutputParser from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.schema.runnable import RunnablePassthrough from langchain.prompts import PromptTemplate from langchain.vectorstores.utils import filter_complex_metadata class ChatPDF: vector_store = None retriever = None chain = None def __init__(self): self.model = ChatOllama(model="mistral") self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100) self.prompt = PromptTemplate.from_template( """ <s> [INST] You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise. [/INST] </s> [INST] Question: {question} Context: {context} Answer: [/INST] """ ) def ingest(self, pdf_file_path: str): docs = PyPDFLoader(file_path=pdf_file_path).load() chunks = self.text_splitter.split_documents(docs) chunks = filter_complex_metadata(chunks) vector_store = Chroma.from_documents(documents=chunks, embedding=FastEmbedEmbeddings()) self.retriever = vector_store.as_retriever( search_type="similarity_score_threshold", search_kwargs={ "k": 3, "score_threshold": 0.5, }, ) self.chain = ({"context": self.retriever, "question": RunnablePassthrough()} | self.prompt | self.model | StrOutputParser()) def ask(self, query: str): if not self.chain: return "Please, add a PDF document first." return self.chain.invoke(query) def clear(self): self.vector_store = None self.retriever = None self.chain = None

El mensaje proviene del centro Langchain: Langchain RAG solicita Mistral . Este mensaje se ha probado y descargado miles de veces y sirve como un recurso confiable para aprender sobre las técnicas de mensajes de LLM.


Puede obtener más información sobre las técnicas de estimulación de LLM. aquí .


Más detalles sobre la implementación:


  • ingest : Usamos PyPDFLoader para cargar el archivo PDF cargado por el usuario. El RecursiveCharacterSplitter, proporcionado por Langchain, luego divide este PDF en partes más pequeñas. Es importante filtrar metadatos complejos que no son compatibles con ChromaDB utilizando la función filter_complex_metadata de Langchain.


    Para el almacenamiento de vectores, se utiliza Chroma, junto con Qdrant FastEmbed como nuestro modelo de incrustación. Este modelo liviano luego se transforma en un perro perdiguero con un umbral de puntuación de 0,5 y k=3, lo que significa que devuelve los 3 fragmentos principales con las puntuaciones más altas por encima de 0,5. Finalmente, construimos una cadena de conversación simple usando LECL .


  • ask : este método simplemente pasa la pregunta del usuario a nuestra cadena predefinida y luego devuelve el resultado.


  • clear : este método se utiliza para borrar la sesión de chat anterior y el almacenamiento cuando se carga un nuevo archivo PDF.

Borre una interfaz de usuario simple

Para una interfaz de usuario simple, usaremos iluminado , un marco de interfaz de usuario diseñado para la creación rápida de prototipos de aplicaciones de IA/ML.

 import os import tempfile import streamlit as st from streamlit_chat import message from rag import ChatPDF st.set_page_config(page_title="ChatPDF") def display_messages(): st.subheader("Chat") for i, (msg, is_user) in enumerate(st.session_state["messages"]): message(msg, is_user=is_user, key=str(i)) st.session_state["thinking_spinner"] = st.empty() def process_input(): if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0: user_text = st.session_state["user_input"].strip() with st.session_state["thinking_spinner"], st.spinner(f"Thinking"): agent_text = st.session_state["assistant"].ask(user_text) st.session_state["messages"].append((user_text, True)) st.session_state["messages"].append((agent_text, False)) def read_and_save_file(): st.session_state["assistant"].clear() st.session_state["messages"] = [] st.session_state["user_input"] = "" for file in st.session_state["file_uploader"]: with tempfile.NamedTemporaryFile(delete=False) as tf: tf.write(file.getbuffer()) file_path = tf.name with st.session_state["ingestion_spinner"], st.spinner(f"Ingesting {file.name}"): st.session_state["assistant"].ingest(file_path) os.remove(file_path) def page(): if len(st.session_state) == 0: st.session_state["messages"] = [] st.session_state["assistant"] = ChatPDF() st.header("ChatPDF") st.subheader("Upload a document") st.file_uploader( "Upload document", type=["pdf"], key="file_uploader", on_change=read_and_save_file, label_visibility="collapsed", accept_multiple_files=True, ) st.session_state["ingestion_spinner"] = st.empty() display_messages() st.text_input("Message", key="user_input", on_change=process_input) if __name__ == "__main__": page()


Ejecute este código con el comando streamlit run app.py para ver cómo se ve.

¡Está bien, eso es todo! Ahora tenemos una aplicación ChatPDF que se ejecuta completamente en su computadora portátil. Dado que esta publicación se centra principalmente en brindar una descripción general de alto nivel sobre cómo crear su propia aplicación RAG, hay varios aspectos que necesitan ajustes. Puede considerar las siguientes sugerencias para mejorar su aplicación y desarrollar aún más sus habilidades:


  • Agregar memoria a la cadena de conversación : actualmente, no recuerda el flujo de la conversación. Agregar memoria temporal ayudará a su asistente a ser consciente del contexto.


  • Permitir la carga de múltiples archivos : está bien conversar sobre un documento a la vez. Pero imagina si pudiéramos conversar sobre varios documentos: podrías poner toda tu estantería allí. ¡Eso sería genial!


  • Utilice otros modelos LLM : si bien Mistral es eficaz, existen muchas otras alternativas disponibles. Quizás encuentres un modelo que se adapte mejor a tus necesidades, como LlamaCode para desarrolladores. Sin embargo, recuerda que la elección del modelo depende de tu hardware, especialmente de la cantidad de RAM que tengas 💵


  • Mejorar el proceso de RAG : hay espacio para la experimentación dentro de RAG. Es posible que desee cambiar la métrica de recuperación, el modelo de incrustación... o agregar capas como un reclasificador para mejorar los resultados.


Finalmente, gracias por leer. Si encuentra útil esta información, considere suscribirse a mi Subpila o mi personal Blog . Planeo escribir más sobre las aplicaciones RAG y LLM, y puedes sugerir temas dejando un comentario a continuación. ¡Salud!


Código fuente completo: https://github.com/vndee/local-rag-example