1. Retrievers

Ein Retriever ist eine von LangChain gekapselte Schnittstelle, die relevante Dokumente basierend auf unstrukturierten Abfragen zurückgeben kann. Der Zweck des Retriever-Designs besteht darin, die Abfrage lokaler Daten zu erleichtern. Als zugrunde liegende Implementierung des Retrievers kann Vektorspeicher verwendet werden, und LangChain unterstützt mehrere zugrunde liegende Implementierungen der Retriever-Schnittstelle.

2. Erste Schritte mit Retriever

2.1. Installation

Um zu zeigen, wie man einen Retriever erhält, verwenden wir die Qdrant-Vektordatenbank als Beispiel.

%pip install --upgrade --quiet qdrant-client

2.2. Erhalt des OpenAI API Keys

Bevor wir OpenAIEmbeddings verwenden, müssen wir den OpenAI API Key erhalten.

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")

2.3. Import von Dokumentendaten und Erhalt des Qdrant-Clients

Der folgende Code zeigt, wie man Dokumentendaten importiert und den Qdrant-Client erhält, um einen Retriever zu erstellen:

from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import Qdrant
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

loader = TextLoader("../../modules/state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()

qdrant = Qdrant.from_documents(
    docs,
    embeddings,
    path="/tmp/local_qdrant",
    collection_name="my_documents",
)

2.4. Erhalt des Retrievers

Der folgende Code zeigt, wie man einen Retriever von Qdrant erhält:

retriever = qdrant.as_retriever()
retriever

Sie können Dokumente zu einer Frage abfragen, wie folgt:

docs = retriever.get_relevant_documents("Was hat er über Ketanji Brown Jackson gesagt")

Sie können auch den Ähnlichkeitsschwellenwert für den Retriever wie folgt festlegen:

retriever = db.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.5}
)

Darüber hinaus können Sie den Retriever so einstellen, dass er die 'K' am meisten ähnlichen Datensätze zurückgibt. Zum Beispiel, um die zwei am meisten ähnlichen Datensätze zurückzugeben:

retriever = db.as_retriever(search_kwargs={"k": 2})

2.5. Verwendung von Retriever in LCEL

Da Retriever Runnable-Objekte sind, können wir sie leicht mit anderen Runnable-Objekten kombinieren, um Workflows zu orchestrieren:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

template = """Beantworte Fragen basierend nur auf dem folgenden Kontext:

{context}

Frage: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()

def format_docs(docs):
    return "\n\n".join([d.page_content for d in docs])

chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

chain.invoke("Was hat der Präsident über Technologie gesagt?")

Erklärung des Ablaufs:

  • Schritt 1: Das Ziel ist es, ein Dictionary zu generieren, das zwei Eigenschaften, context und question, für die Vorbereitung der Parameter für die Prompt-Vorlage enthält. Der context-Parameter wird vom retriever-Retriever basierend auf dem der invoke-Methode übergebenen Parameter "Was hat der Präsident über Technologie gesagt?" generiert. Dies ruft Dokumente ab, die der Frage ähnlich sind, formatiert dann das Dokumenten-Array mithilfe der format_docs-Funktion und weist es der context-Eigenschaft zu. Die RunnablePassthrough-Funktion kopiert den Parameter (Benutzereingabe-Frage) der Kette in die question-Eigenschaft.
  • Schritt 2: Das im ersten Schritt generierte Dictionary wird an die Prompt-Vorlage zur Formatierung übergeben.
  • Schritt 3: Die formatierte Eingabeaufforderung von der Prompt-Vorlage wird an das Modell übergeben.
  • Schritt 4: Das Ergebnis des Modellaufrufs wird an den Ausgabe-Parser StrOutputParser übergeben.

3. Eigener Retriever

4.1. Einführung in die Retriever-Schnittstelle

Die Retriever-Schnittstelle ist sehr einfach, und wir können leicht benutzerdefinierte Retriever schreiben.

4.2. Beispiel für benutzerdefinierten Retriever

Hier ist ein Beispiel für einen benutzerdefinierten Retriever, das zeigt, wie man einen benutzerdefinierten Retriever schreibt und verwendet, um relevante Dokumente abzurufen:

from langchain_core.retrievers import BaseRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from typing import List

class CustomRetriever(BaseRetriever):
  
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        return [Document(page_content=query]

retriever = CustomRetriever()

retriever.get_relevant_documents("bar")

Durch das Studium der obigen Abschnitte erhalten Sie ein tieferes Verständnis des Konzepts, der Abrufmethoden und der Anpassung von Retrievern.