Eine Frage Antwort Forschungs Chatbot Mit Amazon Bedrock Und Langchain Erstellen Eine Frage Antwort Forschungs Chatbot Mit Amazon Bedrock Und Langchain Erstellen

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Einführung

Nicht allzu lange ist es her, dass ich versucht habe, einen einfachen benutzerdefinierten Chatbot zu erstellen, der vollständig auf meinem CPU laufen würde.

Die Ergebnisse waren erschreckend, die Anwendung stürzte häufig ab. Dies ist jedoch keine schockierende Entwicklung. Wie sich herausstellte, ist es das Programmieräquivalent dazu, ein Kleinkind einen Berg erklimmen zu lassen, ein 13-Milliarden-Parameter-Modell auf einem 600-Dollar-Computer unterzubringen.

Dieses Mal habe ich einen ernsthafteren Versuch unternommen, einen Forschungs-Chatbot mit einem End-to-End-Projekt zu erstellen, das AWS nutzt, um die für den Aufbau der Anwendung benötigten Modelle zu hosten und den Zugriff darauf zu ermöglichen.

Der folgende Artikel beschreibt meine Bemühungen, RAG (Retrieval-Augmented Generation) zu nutzen, um einen leistungsstarken Forschungs-Chatbot zu erstellen, der Fragen mit Informationen aus Forschungsarbeiten beantwortet.

Ziel

Ziel dieses Projekts ist es, einen Frage-Antwort-Chatbot mit dem RAG-Framework zu erstellen. Er wird Fragen mit dem Inhalt von PDF-Dokumenten aus dem arXIV-Repository beantworten.

Bevor wir in das Projekt einsteigen, betrachten wir die Architektur, den Tech-Stack und das Verfahren zum Aufbau des Chatbots.

Chatbot-Architektur

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Das obige Diagramm veranschaulicht den Workflow für die LLM-Anwendung (Large Language Model).

Wenn ein Nutzer eine Abfrage in einer Benutzeroberfläche eingibt, wird die Abfrage mit einem Embedding-Modell transformiert. Dann ruft die Vektor-Datenbank die ähnlichsten Embeddings ab und sendet sie zusammen mit dem eingebetteten Abfrage an das LLM. Das LLM nutzt den bereitgestellten Kontext, um eine präzise Antwort zu generieren, die dem Nutzer in der Benutzeroberfläche angezeigt wird.

Tech-Stack

Für den Aufbau der RAG-Anwendung mit den in der Architektur gezeigten Komponenten werden mehrere Tools benötigt. Die wichtigsten Tools sind folgende:

  1. Amazon Bedrock

Amazon Bedrock ist ein serverloser Dienst, der Nutzern über eine API den Zugriff auf Modelle ermöglicht. Da er ein Pay-as-you-go-System verwendet und nach der Anzahl der verwendeten Token berechnet wird, ist er für Entwickler sehr bequem und kostengünstig.

Bedrock wird für den Zugriff sowohl auf das Embedding-Modell als auch auf das LLM verwendet. Für die Konfiguration muss zunächst ein IAM-Benutzer mit Zugriff auf den Dienst erstellt werden. Außerdem muss im Voraus der Zugriff auf die gewünschten Modelle gewährt werden.

  1. FAISS

FAISS ist eine beliebte Bibliothek im Data-Science-Bereich und wird in diesem Projekt zur Erstellung der Vektor-Datenbank verwendet. Sie ermöglicht eine schnelle und effiziente Abrufung relevanter Dokumente basierend auf einem Ähnlichkeitsmaß. Sie ist außerdem kostenlos, was immer hilfreich ist.

  1. LangChain

Das LangChain-Framework erleichtert die Erstellung und Nutzung der RAG-Komponenten (z.B. Vektor-Store, LLM).

  1. Chainlit

Die Chainlit-Bibliothek wird für die Entwicklung der Benutzeroberfläche des Chatbots verwendet. Sie ermöglicht es Nutzern, mit minimalem Code eine ästhetische Frontend zu erstellen und bietet Funktionen, die für Chatbot-Anwendungen geeignet sind.

Hinweis: Der technische Teil des Artikels enthält Code-Snippets von Chainlit-Operationen, geht aber nicht auf die Syntax oder Funktionsweise dieser Operationen ein.

  1. Docker

Für Portabilität und einfache Bereitstellung wird die Anwendung mit Docker containerisiert.

Verfahren

Die Entwicklung der LLM-Anwendung erfordert folgende Schritte. Jeder Schritt wird einzeln untersucht.

  1. Laden der PDF-Dokumente
  2. Aufbau des Vektor-Stores
  3. Erstellen der Retrieval-Kette
  4. Entwerfen der Benutzeroberfläche
  5. Ausführen der Chatbot-Anwendung
  6. Ausführen der Anwendung in einem Docker-Container

Schritt 1 – Laden der PDF-Dokumente

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

ArXIV ist ein Repository mit einer Vielzahl kostenloser, quelloffener Artikel und Arbeiten zu Themen von Wirtschaft bis Ingenieurwesen. Die Backend-Daten der Anwendung werden aus einigen Dokumenten über LLMs aus dem Repository bestehen.

Nachdem die ausgewählten Dokumente in einem Verzeichnis gespeichert wurden, werden sie mit LangChains PyPDFDirectoryLoader geladen und mit dem RecursiveCharacterTextSplitter in Textabschnitte unterteilt.

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFDirectoryLoader

def load_pdfs(chunk_size=3000, chunk_overlap=100):

    # load the pdf documents
    loader=PyPDFDirectoryLoader("PDF Documents")
    documents=loader.load()

    # split the documents into chunks
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, 
                                                   chunk_overlap=chunk_overlap)
    docs = text_splitter.split_documents(documents=documents)
    return docs

Schritt 2 – Aufbau des Vektor-Stores

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Die in Schritt 1 erstellten Textabschnitte werden mit Amazons Titan Text Embeddings Modell in Embeddings umgewandelt. Darauf kann per Code mit boto3, dem Amazon SDK für Python, zugegriffen werden. Das Titan-Modell kann über die model_id identifiziert werden, die in der Bedrock-Dokumentation angegeben ist.

from langchain_community.embeddings import BedrockEmbeddings
from langchain_community.vectorstores.faiss import FAISS

def create_vector_store(docs):

    # Set up bedrock client
    bedrock = create_client()
    bedrock_embeddings=BedrockEmbeddings(model_id='amazon.titan-embed-text-v1', client=bedrock)

    # create and save the vector store
    vector_store = FAISS.from_documents(docs, bedrock_embeddings)
    vector_store.save_local("faiss_index")
    
    return None

Die eingebetteten Abschnitte werden in einem FAISS-Vektor-Store gespeichert, der lokal als „faiss_index“ gespeichert wird.

Schritt 3 – Laden des LLM

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Das LLM für die Anwendung wird Metas 13 Milliarden Parameter großes Llama 2 Modell sein. Genau wie das Embedding-Modell wird das LLM über Amazon Bedrock zugänglich gemacht.

from langchain.llms.bedrock import Bedrock

def create_llm(bedrock_client):

    # load llama2 
    llm = Bedrock(model_id='meta.llama2-13b-chat-v1', 
                  client=bedrock_client,
                  streaming=True,
                  model_kwargs={'temperature':0})
    return llm

Ein bemerkenswerter Parameter ist die temperature, die die Zufälligkeit der Ausgabe des Modells beeinflusst. Da die Anwendung für die Forschung konzipiert ist, wird die Zufälligkeit minimiert, indem temperature auf 0 gesetzt wird.

Schritt 4 – Erstellen der Retrieval-Kette

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

In LangChain ist eine „Kette“ ein Wrapper, der eine Reihe von Ereignissen in einer bestimmten Reihenfolge ermöglicht. In dieser RAG-Anwendung erhält die Kette die Nutzerabfrage und ruft die ähnlichsten Abschnitte aus dem Vektor-Store ab. Die Kette sendet dann die eingebettete Abfrage und die abgerufenen Abschnitte an das geladene LLM, das unter Verwendung des bereitgestellten Kontexts eine Antwort generiert.

from langchain.llms.bedrock import Bedrock
from langchain.memory import ChatMessageHistory, ConversationBufferMemory
from langchain.chains import RetrievalQA, ConversationalRetrievalChain

# load llm
llm = create_llm(bedrock_client=bedrock_client)

# load embeddings and vector store
bedrock_embeddings=BedrockEmbeddings(model_id='amazon.titan-embed-text-v1', client=bedrock_client)
vector_store = FAISS.load_local('faiss_index', bedrock_embeddings, allow_dangerous_deserialization=True)

# create memory history
message_history = ChatMessageHistory()
memory = ConversationBufferMemory(
    memory_key="chat_history",
    output_key="answer",
    chat_memory=message_history,
    return_messages=True,
)

# create qa chain
qa_chain = ConversationalRetrievalChain.from_llm(llm, 
                                       chain_type='stuff', 
                                       retriever=vector_store.as_retriever(search_type='similarity', search_kwargs={"k":3}),
                                       return_source_documents=True,
                                       memory=memory)

Die Kette integriert auch den ConversationBufferMemory, der es dem Chatbot ermöglicht, sich an vorherige Abfragen zu erinnern. So kann der Nutzer Folgefragen stellen.

Ein weiterer erwähnenswerter Hyperparameter ist das k für den Retriever, das angibt, wie viele Embeddings aus dem Vektor-Store abgerufen werden sollen. Für diesen Anwendungsfall setzen wir k auf 3, d.h. die LLM-Anwendung verwendet 3 Embeddings als Kontext, um jede Abfrage zu beantworten.

Schritt 5 – Erstellen der Benutzeroberfläche

Bisher wurden die Backend-Komponenten der Anwendung entwickelt, daher ist es an der Zeit, an der Frontend zu arbeiten. Chainlit erleichtert den Aufbau von Benutzeroberflächen für LangChain-Anwendungen, da der vorhandene Code nur mit zusätzlichen Chainlit-Befehlen modifiziert werden muss.

Chainlit wird zur Erstellung der Funktion verwendet, die die Kette einrichtet.

import chainlit as cl

@cl.on_chat_start
async def create_qa_chain():

    # create client 
    bedrock_client = create_client()

    # load llm
    llm = create_llm(bedrock_client=bedrock_client)

    # load embeddings and vector store
    bedrock_embeddings=BedrockEmbeddings(model_id='amazon.titan-embed-text-v1', client=bedrock_client)
    vector_store = FAISS.load_local('faiss_index', bedrock_embeddings, allow_dangerous_deserialization=True)
    
    # create memory history
    message_history = ChatMessageHistory()
    memory = ConversationBufferMemory(
        memory_key="chat_history",
        output_key="answer",
        chat_memory=message_history,
        return_messages=True,
    )

    # create qa chain
    qa_chain = ConversationalRetrievalChain.from_llm(llm, 
                                           chain_type='stuff', 
                                           retriever=vector_store.as_retriever(search_type='similarity', search_kwargs={"k":3}),
                                           return_source_documents=True,
                                           memory=memory
                                           )
    
    # add custom messages to the user interface
    msg = cl.Message(content="Loading the bot...")
    await msg.send()
    msg.content = "Hi, Welcome to the QA Chatbot! Please ask your question."
    await msg.update()
    
    cl.user_session.set('qa_chain' ,qa_chain)

Es wird auch zur Erstellung der Funktion verwendet, die die Kette nutzt, um Antworten zu generieren und an den Nutzer zu senden.

import chainlit as cl

@cl.on_message
async def generate_response(query):
    qa_chain = cl.user_session.get('qa_chain')

    res = await qa_chain.acall(query.content, callbacks=[cl.AsyncLangchainCallbackHandler(
        stream_final_answer=True, 
        )])

    # extract results and source documents
    result, source_documents = res['answer'], res['source_documents']

    # Extract all values associated with the 'metadata' key
    source_documents = str(source_documents)
    metadata_values = re.findall(r"metadata={'source': '([^']*)', 'page': (\d+)}", source_documents)

    # Convert metadata_values into a single string
    pattern = r'PDF Documents|\\'
    metadata_string = "\n".join([f"Source: {re.sub(pattern, '', source)}, page: {page}" for source, page in metadata_values])

    # add metadata (i.e., sources) to the results
    result += f'\n\n{metadata_string}'

    # send the generated response to the user
    await cl.Message(content=result).send()

Die Chainlit-Dekoratoren sind eine notwendige Ergänzung. Der on_chat_start-Dekorator definiert die Operationen, die ausgeführt werden sollen, wenn die Chat-Sitzung gestartet wird (d.h. Einrichten der Kette), während der on_message-Dekorator die Operationen definiert, die ausgeführt werden sollen, wenn der Nutzer eine Abfrage eingibt (d.h. Senden der Antwort).

Darüber hinaus integriert der Code die Verwendung von async und await-Befehlen, so dass die Aufgaben asynchron gehandhabt werden.

Da die LLM-Anwendung für die Forschung konzipiert ist, enthält die generierte Antwort nach der Ähnlichkeitssuche die Quellen der aus dem Vektor-Store abgerufenen Embeddings. Dies macht die generierten Antworten zitierfähig und daher glaubwürdiger in den Augen des Nutzers.

Schritt 6 – Ausführen der Chatbot-Anwendung

Nachdem alle Komponenten im Chatbot-Workflow erstellt wurden, kann die Anwendung ausgeführt und getestet werden. Mit Chainlit kann eine Sitzung mit einer einfachen Zeile gestartet werden:

chainlit run <app.py>

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Der Chatbot läuft nun! Er zeigt die im Code bereitgestellte Nachricht beim Start der Sitzung.

Testen wir ihn mit einer einfachen Abfrage:

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Wenn eine Abfrage eingegeben wird, ist die Antwort präzise und verständlich. Außerdem enthält sie die Quellen der 3 Vektor-Embeddings, die zur Generierung der Antwort verwendet wurden, einschließlich des Namens des Dokuments und der Seitenzahl.

Um sicherzustellen, dass der Chatbot sich an vorherige Abfragen erinnert, können wir eine Folgefrage stellen.

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Hier fragen wir nach „einem anderen Beispiel“, ohne zusätzliche Informationen darüber zu geben, welches Beispiel benötigt wird. Da der Bot sich erinnert, weiß er, dass sich die Abfrage auf vorgefertigte LLMs bezieht.

Insgesamt arbeitet die Anwendung auf einem zufriedenstellenden Niveau. Ein Aspekt, der in einem Artikel nicht demonstriert werden kann, ist die deutlich geringere Rechenleistung, die zum Ausführen des Chatbots benötigt wird. Da AWS das Embedding-Modell und das LLM hostet, besteht kein Risiko von Abstürzen durch übermäßige CPU-Auslastung.

Schritt 7 – Containerisieren der Anwendung

Obwohl der Chatbot läuft, bleibt noch ein Schritt übrig. Die LLM-Anwendung muss noch mit Docker für eine einfachere Portabilität und Versionskontrolle containerisiert werden.

Der erste Schritt für die Containerisierung ist die Entwicklung der Dockerfile.

In dieser Dockerfile erstellen wir ein Python-Image als Basis, definieren Argumente für die Access Key ID und den geheimen Zugriffsschlüssel von AWS, installieren die requirements.txt-Datei im Container, kopieren das aktuelle Verzeichnis in den Container und führen die Chainlit-Anwendung aus.

Von hier an ist es ziemlich einfach. Der Aufbau des Docker-Images erfolgt mit einem Einzeiler:

docker build --build-arg AWS_ACCESS_KEY_ID=<your_access_key_id> --build-arg AWS_SECRET_ACCESS_KEY=<your_secret_access_key> -t chainlit_app .

Der obige Befehl erstellt ein Image mit dem Namen chainlit_app. Es enthält die AWS-Zugriffs-Key-ID und den AWS-Geheimschlüssel als Argumente, da sie benötigt werden, um über die API auf die Modelle in Amazon Bedrock zuzugreifen.

Schließlich kann die Anwendung in einem Docker-Container ausgeführt werden:

docker run -d --name chainlit_app -p 8000:8000 chainlit_app

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Die Anwendung läuft nun auf Port 8000! Da die Anwendung lokal ausgeführt wird, wird der Chatbot unter http://localhost:8000/ gehostet.

Sehen wir nach, ob die RAG-Komponenten (einschließlich der AWS Bedrock-Modelle) noch funktionieren, indem wir eine Abfrage stellen.

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Es funktioniert genauso wie erwartet!

Nächste Schritte

Der aktuelle Chatbot kann Abfragen mit angemessener Leistung und zu geringen Kosten beantworten. Die Anwendung wird jedoch immer noch lokal ausgeführt und verwendet Standardparameter. Es gibt also noch Möglichkeiten, die Leistung und Benutzerfreundlichkeit des Chatbots weiter zu verbessern.

  1. Rigorose Tests durchführen

Die LLM-Anwendung scheint effektiv zu arbeiten, die Antworten sind präzise und akkurat. Dennoch muss das Tool noch rigorosen Tests unterzogen werden, bevor es als benutzbar gelten kann.

Die Tests dienen in erster Linie dazu sicherzustellen, dass die Antwortgenauigkeit maximiert und Halluzinationen minimiert werden.

  1. Fortschrittliche RAG-Techniken implementieren

Wenn der Chatbot bestimmte Arten von Fragen nicht beantworten kann oder generell schlecht abschneidet, wäre es ratsam, den Einsatz fortschrittlicher RAG-Techniken in Betracht zu ziehen, um bestimmte Aspekte des Workflows wie den Abruf von Inhalten aus der Vektor-Datenbank zu verbessern.

  1. Frontend aufpolieren

Derzeit verwendet das Tool das Standardfrontend von Chainlit. Um das Tool ästhetischer und intuitiver zu gestalten, kann das UI-Design weiter angepasst werden.

Außerdem kann die Zitierfunktion des Chatbots (d.h. die Identifizierung der Quelle der Antwort) verbessert werden, indem ein Hyperlink bereitgestellt wird, damit der Nutzer sofort zu der Seite gehen kann, die die benötigten Informationen enthält.

  1. In der Cloud bereitstellen

Wenn dieses Tool einer größeren Nutzerbasis angeboten werden soll, wäre der nächste Schritt, es auf einem Remote-Server mit Cloud-Plattformen wie Amazon EC2 und Amazon ECS zu deployen. Mit vielen Cloud-Plattformen sind hohe Skalierbarkeit, Verfügbarkeit und Leistung erreichbar, aber da dieses Tool AWS Bedrock nutzt, wäre der natürliche nächste Schritt, andere Ressourcen im AWS-Ökosystem zu nutzen.

Fazit

Eine Frage-Antwort-Forschungs-Chatbot mit Amazon Bedrock und LangChain erstellen

Bei der Arbeit an diesem Projekt war ich überwältigt davon, wie weit sich der Bereich Data Science entwickelt hat. NLP-Anwendungen, die generative KI nutzen, wären vor 5 Jahren nur schwer zu erstellen gewesen, da es erhebliche Zeit, Geld und Manpower erfordert hätte.

Im Jahr 2024 können solche Tools mit nur einem Menschen, etwas Code und minimalen Kosten (das ganze Projekt hat bisher weniger als 1 Dollar gekostet) erstellt werden. Man fragt sich, was in den kommenden Jahren möglich sein wird.

Für diejenigen, die mehr über den Quellcode des Projekts erfahren möchten, besuchen Sie bitte das GitHub-Repository:

anair123/Building-a-Research-Chatbot-with-AWS-and-Llama-2 (github.com)

Vielen Dank fürs Lesen!

Referenzen

  1. Stehle, J., Eusebius, N., Khanuja, M., Roy, M., & Pathak, R. (n.d.). Getting started with Amazon Titan text embeddings in Amazon bedrock … https://aws.amazon.com/blogs/machine-learning/getting-started-with-amazon-titan-text-embeddings/

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert