Mit dem Aufkommen großer Sprachmodelle und ihren beeindruckenden Fähigkeiten werden viele ausgefallene Anwendungen auf der Grundlage riesiger LLM-Anbieter wie OpenAI und Anthropic erstellt. Der Mythos hinter solchen Anwendungen ist das RAG-Framework, das in den folgenden Artikeln ausführlich erläutert wurde:
Um sich mit RAG vertraut zu machen, empfehle ich die Lektüre dieser Artikel. Dieser Beitrag überspringt jedoch die Grundlagen und führt Sie direkt zum Erstellen Ihrer eigenen RAG-Anwendung, die lokal auf Ihrem Laptop ausgeführt werden kann, ohne sich Gedanken über Datenschutz und Token-Kosten machen zu müssen.
Wir werden eine Anwendung erstellen, die ChatPD F ähnelt, aber einfacher ist. Hier können Benutzer über eine unkomplizierte Benutzeroberfläche ein PDF-Dokument hochladen und Fragen stellen. Unser Tech-Stack ist mit Langchain, Ollama und Streamlit super einfach.
Okay, beginnen wir mit der Einrichtung.
Wie oben erwähnt, ist die Einrichtung und Ausführung von Ollama unkompliziert. Erster Besuch
Öffnen Sie als Nächstes Ihr Terminal und führen Sie den folgenden Befehl aus, um die neueste Version abzurufen
ollama pull mistral
Führen Sie anschließend ollama list
aus, um zu überprüfen, ob das Modell korrekt gezogen wurde. Die Terminalausgabe sollte wie folgt aussehen:
Wenn der LLM-Server noch nicht läuft, initiieren Sie ihn nun mit ollama serve
. Wenn eine Fehlermeldung wie "Error: listen tcp 127.0.0.1:11434: bind: address already in use"
, bedeutet dies, dass der Server standardmäßig bereits ausgeführt wird, und Sie können mit dem nächsten Schritt fortfahren.
Der zweite Schritt in unserem Prozess ist der Aufbau der RAG-Pipeline. Aufgrund der Einfachheit unserer Anwendung benötigen wir hauptsächlich zwei Methoden: ingest
und ask
.
Die ingest
akzeptiert einen Dateipfad und lädt ihn in zwei Schritten in den Vektorspeicher: Erstens teilt sie das Dokument in kleinere Teile auf, um das Token-Limit des LLM zu berücksichtigen. Zweitens vektorisiert es diese Chunks mithilfe von Qdrant FastEmbeddings und speichert sie in Chroma.
Die ask
Methode verarbeitet Benutzeranfragen. Benutzer können eine Frage stellen, und dann ruft RetrievalQAChain mithilfe von Vektorähnlichkeitssuchtechniken die relevanten Kontexte (Dokumentblöcke) ab.
Mit der Frage des Benutzers und den abgerufenen Kontexten können wir eine Eingabeaufforderung verfassen und eine Vorhersage vom LLM-Server anfordern.
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
Die Eingabeaufforderung stammt vom Langchain-Hub:
Erfahren Sie mehr über LLM-Prompting-Techniken
Weitere Details zur Umsetzung:
ingest
: Wir verwenden PyPDFLoader, um die vom Benutzer hochgeladene PDF-Datei zu laden. Der von Langchain bereitgestellte RecursiveCharacterSplitter teilt dieses PDF dann in kleinere Teile auf. Es ist wichtig, komplexe Metadaten, die von ChromaDB nicht unterstützt werden, mithilfe der Funktion filter_complex_metadata
von Langchain herauszufiltern.
Für die Vektorspeicherung wird Chroma verwendet, gekoppelt mit
ask
: Diese Methode übergibt einfach die Frage des Benutzers an unsere vordefinierte Kette und gibt dann das Ergebnis zurück.
clear
: Diese Methode wird verwendet, um die vorherige Chat-Sitzung und den Speicher zu löschen, wenn eine neue PDF-Datei hochgeladen wird. Für eine einfache Benutzeroberfläche verwenden wir
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()
Führen Sie diesen Code mit dem Befehl streamlit run app.py
, um zu sehen, wie er aussieht.
Okay, das ist es! Wir haben jetzt eine ChatPDF-Anwendung, die vollständig auf Ihrem Laptop läuft. Da sich dieser Beitrag hauptsächlich darauf konzentriert, einen allgemeinen Überblick darüber zu geben, wie Sie Ihre eigene RAG-Anwendung erstellen, gibt es mehrere Aspekte, die einer Feinabstimmung bedürfen. Sie können die folgenden Vorschläge in Betracht ziehen, um Ihre App zu verbessern und Ihre Fähigkeiten weiterzuentwickeln:
Zum Schluss vielen Dank fürs Lesen. Wenn Sie diese Informationen nützlich finden, erwägen Sie bitte ein Abonnement von my
Vollständiger Quellcode: