Das Langchain-Framework verstehen | von Mehul Gupta | Data Science in Ihrer Tasche | September 2023

Data Science in Ihrer Tasche

Ketten, Agenten, Retriever, Vorlagen und mehr

Foto von Alejandro Ortiz auf Unsplash

LLMs sind das heißeste Thema der Stadt. Doch in letzter Zeit hat sich der Schwerpunkt von LLMs auf die Verwendung dieser LLMs für reale Probleme verlagert, für die Langchain ein Framework zur Implementierung verschiedener Anwendungsfälle bereitstellt. Aber Langchain ist ein komplexes Framework mit viel Fachjargon. Heute werden wir versuchen, die verschiedenen Elemente des Langchain-Frameworks und deren Verwendung zu verstehen. Lesen Sie den vorherigen Teil für eine Anleitung zu verschiedenen Anwendungsfällen, für die Langchain verwendet werden kann

Dieser Blog enthält kurze Erklärungen zu

Modelle (LLM und ChatModel)

Eingabeaufforderungsvorlagen (verschiedene Vorlagen für Eingabeaufforderungen)

Ausgabeparser (So strukturieren Sie Ihre Ausgabe)

Ketten (Verkettung von LLMs oder anderen Tools für komplexe Probleme)

Erinnerung (um sich an frühere Gespräche zu erinnern)

Retriever (Zugriff auf externe Dokumente/Kontext mithilfe von LLMs)

Agenten (um eine Abfolge von Aktionen auszuführen, flexibler und vielfältiger als Ketten)

Rückrufe (Protokollierung von Ereignissen)

Codes und Beispiele sowie Video-Tutorials

Lasst uns loslegen

Langchain bietet zwei Arten von Modellen

  • LLMs: Solche Modelle ähneln den allgemeinen LLM-Modellen, die für jeden Zweck verwendet werden können. Die Eingabeaufforderung ist ein String/eine String-Vorlage, während Sie mit ChatGPT interagieren
from langchain.llms import OpenAI
llm = OpenAI()

llm("Explain how AI wil change the world")

  • ChatModels: Unter der Haube nutzen diese Modelle LLMs, sind spezifisch für Chatbots und bestehen aus drei Hauptkomponenten

HumanMessage: Die vom Benutzer gegebene Eingabeaufforderung

AIMessage: Die vom LLM gegebene Antwort

SystemMessage: Es handelt sich um einen Kontext, den wir über seine Rolle an das Chatmodel übergeben können.

from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage,SystemMessage

messages = [SystemMessage(
content="You are a Grammar Teacher who responds Yes for correct Grammar input"),
HumanMessage(content="I love, programming.")]

chat = ChatOpenAI()
chat(messages)

Wie Sie sehen können, fungiert dieses Chat-Modell jetzt als Grammatiklehrer für jede Eingabeaufforderung. Sie müssen nicht die gesamte Eingabeaufforderung erneut schreiben, wenn der Anwendungsfall derselbe ist.

Wie kann diese Eingabeaufforderung anpassbarer gestaltet werden?

Wie Sie sicher schon erraten haben, handelt es sich hierbei um vordefinierte Vorlagen für Eingabeaufforderungen für ein LLM, sodass Sie keine Eingabeaufforderungen von Grund auf neu schreiben müssen. Es gibt jedoch unterschiedliche Arten dieser Eingabeaufforderungen

PromptTemplate: Dies wird verwendet, um eine Eingabeaufforderung aus einer Zeichenfolgeneingabe zu erstellen. Dies kann als Basisversion von Vorlagen betrachtet werden, bei der Sie je nach Bedarf eine Variable übergeben oder einfach eine ganze Eingabeaufforderung schreiben können. Diese werden normalerweise mit den oben erläuterten LLM-Modellen verwendet.

from langchain.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template(
"Write {lines} lines about {topic}"
)
prompt_template.format(lines="5", topic="India")

OR

prompt_template = PromptTemplate.from_template(
"Write 5 lines about India"
)
prompt_template.format()

In diesem Beispiel können Sie sehen, wie wir zwei Variablen, Zeilen und Thema verwenden, wobei die Kernaufforderung gleich bleibt. Jetzt kann dies als „Essay-Generator“-App für jedes Thema fungieren, bei dem Sie die Gesamtzeilen und Themen anpassen können. Oder Sie geben einfach eine Zeichenfolgenaufforderung ohne Variablen ein. Erfahren Sie, wie PromptTemplate für mehrere Anwendungsfälle verwendet werden kann

Lesen Sie auch  Die nächste Front im Phishing-Krieg

ChatPromptTemplate

Da PromptTemplate für LLM-Modelle gedacht ist, dient ChatPromptTemplate für ChatModels zum Anpassen von Eingabeaufforderungen für ChatModels. Lassen Sie uns ein Beispiel verstehen

from langchain.prompts import ChatPromptTemplate
from langchain.prompts.chat import SystemMessage, HumanMessagePromptTemplate
from langchain.chat_models import ChatOpenAI

template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=("You are a python coder AI that helps user with bugs
and writing programs")),
HumanMessagePromptTemplate.from_template("{text}"),
]
)

llm = ChatOpenAI()
llm(template.format_messages(text='Check whether a number is prime or not'))

Das Verständnis der obigen Vorlage ist einfach

Da wir in ChatModels drei Rollen hatten, haben wir auch in dieser ChatPromptTemplate drei Rollen: System, KI und Mensch. Wir haben separate Vorlagen für drei Rollen, wie im Beispiel zu sehen ist.

Ähnlich wie bei PromptTemplate können wir Variablen in der Vorlage übergeben, um die Eingabeaufforderung an verschiedene Anwendungsfälle anzupassen. Dies gilt auch für SystemMessage.

Großartig, aber wie erhält man ein konstantes Format in der Ausgabe? LLMs erhalten häufig auch unterschiedliche Formate für ähnliche Abfragen

Sie können nicht nur Ihre Eingabeaufforderung anpassen, sondern auch Formate für die Ausgabe festlegen, unabhängig davon, ob Sie eine Liste von Elementen wünschen, vor jeder Ausgabe ein Präfix hinzufügen oder eine Art Validierungsregel für die Ausgabe hinzufügen möchten. Sehen wir uns ein Beispiel an

from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI

output_parser = CommaSeparatedListOutputParser()

format_instructions = output_parser.get_format_instructions()

prompt = PromptTemplate(
template="List down 5 items that start with letter {alphabet}",
input_variables=["alphabet"],
partial_variables={"format_instructions": format_instructions}
)

model = OpenAI(temperature=0)
output = model(prompt.format(alphabet="j"))

output_parser.parse(output)

Hier können Sie sehen, dass wir mit CommaSeparatedListOutputParser() die Ausgabe für das LLM so eingestellt haben, dass sie immer eine Liste von Elementen ist und nichts anderes.

Das obige Beispiel ähnelte eher den Nebenfunktionen von Langchain. Das wichtigste Merkmal sind Ketten. Was sind eigentlich Ketten?

Sie können als auf LLMs geschriebene Programme betrachtet werden, die bestimmte Aufgaben ausführen können. Intern im Backend sind Ketten nichts anderes als die Verkettung mehrerer LLMs miteinander oder mit anderen Elementen wie einigen Tool-Integrationen von Drittanbietern. Beispiel: Das folgende Beispiel zeigt, wie mit einer SQL-Datenbank mithilfe von SQLDatabaseChain interagiert wird, um SQL-Abfragen zu generieren.

from langchain.utilities import SQLDatabase
from langchain.llms import OpenAI
from langchain_experimental.sql import SQLDatabaseChain

db = SQLDatabase.from_uri("sqlite:///Chinook.db")
llm = OpenAI(temperature=0, verbose=True)
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)

db_chain.run("How many employees have joined the organization last year?")

Hier verwenden wir eine spezialisierte Kette, die neben LLMs weitere Komponenten unter der Haube hat, die beim Abrufen der Daten vom Server helfen. So haben wir viele Ketten, von denen jede einer bestimmten Aufgabe gewidmet ist. Sehen Sie sich die Ergebnisse für den obigen Code unten an

Wenn Sie jemals mit ChatGPT interagiert haben, ist Ihnen aufgefallen, dass es über einen Speicher verfügt und Sie Fragen auf der Grundlage früherer Fragen oder Antworten im Gespräch stellen können. Wie füge ich diesen Speicher zu einem LLM hinzu? Wir können dem folgenden Codeausschnitt für LLM-Modelle folgen

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory

llm = OpenAI(temperature=0)

template = """You are a nice chatbot having a conversation with a human.

Previous conversation:
{chat_history}

New human question: {question}
Response:"""
prompt = PromptTemplate.from_template(template)

memory = ConversationBufferMemory(memory_key="chat_history")

conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)

Der obige Code bedarf einiger Erklärungen

  • Speicher wird hinzugefügt, ähnlich wie wir eine Variable an Vorlagen übergeben (wie Sie in den obigen Beispielen gesehen haben). Dabei ist das Format/die Vorlage wichtig.
  • Die einzige Änderung besteht nun darin, dass wir ein ConversationalBufferMemory()-Objekt erstellen und diese Variable unter „Vorherige Konversationen“ an diese Funktion übergeben
  • Dieses Pufferspeicherobjekt wird dann dem LLMChain()-Objekt zugewiesen, das die Speicherung historischer Informationen ermöglicht.
Lesen Sie auch  Die „Guerreras“ hören nach der Niederlage gegen die Niederlande (29-21) auf, um die Weltmeisterschaft zu kämpfen.

Oft möchten Sie dem LLM einen Kontext bereitstellen, der Open Source oder privat ist. Wenn Sie beispielsweise für eine Organisation XYZ arbeiten, möchten Sie möglicherweise eine Frage-und-Antwort-Runde zu einem Bericht durchführen. Wie kann man LLM den Kontext zu diesem Bericht bereitstellen? Natürlich durch Hochladen an einen Ort, an dem LLM darauf zugreifen kann. Retrieval-Elemente helfen bei der Nutzung dieses externen Kontexts bei der Generierung einer Antwort durch ein LLM, das als Retrieval Augmented Generation (RAG) bezeichnet wird. Jetzt kann diese externe Ressource eine Datenbank, eine CSV-Datei, eine Textdatei, ein PDF oder ein YouTube-Video sein. Dieses Retrieval-System besteht aus den folgenden Hauptkomponenten

Dokumentenlader: Diese Komponente hilft beim Laden der externen Ressource in den Speicher

Dokumententransformator: Hierbei handelt es sich hauptsächlich um Vorverarbeitungsschritte, sobald das externe Dokument in den Speicher geladen wird (z. B. Tokenisierung des Textes, Zeichenaufteilung usw.).

Einbettungsmodell: Um diese Dokumente zu speichern, müssen wir zunächst Einbettungen für den vorhandenen Text erstellen.

Vektordatenbanken/Store: Datenbanken, die auf die Speicherung von Vektordaten spezialisiert sind.

Retriever: Rufen Sie relevante Einträge aus der Vektor-Datenbank ab, sobald eine Eingabeaufforderung mit LLM ausgeführt wird

Erfahren Sie mehr über Vector DB im folgenden Blog

Unten finden Sie eine kleine Demo zur Verwendung einer Textdatei und zur Durchführung einer Frage-und-Antwort-Runde

from langchain.chains import RetrievalQA
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.llms import OpenAI
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma

#Document loader
loader = TextLoader("abc.txt")
documents = loader.load()

#Document Transoformer
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

#Embedding model
embeddings = OpenAIEmbeddings()

#Vector DB to store embeddings
docsearch = Chroma.from_documents(texts, embeddings)

#Retriever being used using parameter retriever
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=docsearch.as_retriever())

query = "What this document about? Summarize it in a paragraph"
qa.run(query)

Ebenso können Sie YouTube-Videos, PDFs, CSVs, JSON-Dateien und sogar Github-Repos laden, wie in den folgenden Tutorials erläutert

Sehr eng mit dem Konzept der Ketten verwandt, werden Agenten auch verwendet, um mehrere Schritte mithilfe eines LLM auszuführen. Ein wesentlicher Unterschied besteht in der Art und Weise, wie mehrere Aktionen von Ketten und Agenten ausgeführt werden. Im Fall von Agenten verwendet das Backend ein LLM, um zu entscheiden, welche Aktion (von allen verfügbaren Optionen/Tools) ausgeführt werden soll, und ist daher flexibel, während im Fall von Ketten eine fest codierte Abfolge von Schritten ausgeführt wird. Außerdem sind Agenten vielfältiger und können in mehrere unterschiedliche Tools integriert werden. Zum Beispiel:

Lesen Sie auch  Wer ist für den PEPEFORK $PORK Airdrop berechtigt? Vollständige Anleitung | von BrianDefiMedium | März 2024

Sie haben einen Agenten, der Zugriff darauf hat

Google-Suche

Mathe-Rechner

Ein Retriever für ein externes Dokument

Wenn Sie nun eine Abfrage ausführen, entscheidet das LLM abhängig von der Abfrage, welches Tool verwendet werden soll, oder es verwendet möglicherweise kein Tool, wenn die Abfrage es nicht erfordert, z. B. „Hallo LLM“. Aber im Falle einer Kette,

Möglicherweise können Sie mit vordefinierten Ketten in Langchain nicht mehrere, unterschiedliche Funktionen bereitstellen.

Selbst wenn Sie dies tun, müssen Sie den gesamten Code und die Schrittfolge fest codieren. Selbst wenn für die Abfrage kein Tool erforderlich ist, wird es verwendet (und kann aufgrund mangelnder Kompatibilität möglicherweise auch einen Fehler auslösen). Daher keine Flexibilität

Wenn Sie also eine flexiblere, dynamischere App wünschen, sind Agenten die bessere Option. Wenn Ihr Anwendungsfall jedoch konstant ist, können Ketten verwendet werden.

Das Erstellen eines Agenten wird im folgenden Codeausschnitt erläutert. Wir stellen ihm ein Google-Suchtool zur Verfügung

#!pip install langchain openai google-search-results

from langchain.agents import Tool
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory
from langchain.llms import OpenAI
from langchain.utilities import SerpAPIWrapper
from langchain.agents import initialize_agent

#You need to get serp_api and openai_api for this.
search = SerpAPIWrapper(serpapi_api_key=serp_api)
llm=OpenAI(openai_api_key=api_key)

tools = [
Tool(
name = "Current Search",
func=search.run,
description="useful for when you need to answer questions about current events or the current state of the world"

),]

agent_chain = initialize_agent(tools, llm, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION)
agent_chain.run("Explain what happend in G20 meeting, 2023 that happened in Delhi?")

Wenn Sie den obigen Code ausführen, werden Sie feststellen, dass der Agent nicht auf eine Reihe von Schritten festgelegt ist, sondern die Google-Suche und deren Funktionen verwendet, um zu einer Antwort zu gelangen. Hier haben wir nur ein Tool bereitgestellt, Sie können jedoch auch mehrere Tools verwenden. Es gibt viele vordefinierte Tools von Langchains, die direkt verwendet werden können. Alternativ können Sie bei Bedarf auch ein benutzerdefiniertes Tool erstellen.

Wie verwende ich mehrere Tools mit demselben Agenten?

Wie erstelle ich ein benutzerdefiniertes Tool mit einem Python-Paket?

LangChain bietet ein Rückrufsystem, das Ihnen die Integration in verschiedene Phasen Ihrer Sprachmodellanwendung ermöglicht und Zwecke wie Protokollierung, Überwachung, Streaming und andere wichtige Funktionen erfüllt.

Der folgende Codeausschnitt erklärt, wie jedes Ereignis mithilfe des Standardausgabe-Rückrufs protokolliert wird

from langchain.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate

handler = StdOutCallbackHandler()
llm = OpenAI()
prompt = PromptTemplate.from_template("Write {number} lines about {animal} ")

# Constructor callback: First, let's explicitly set the StdOutCallbackHandler when initializing our chain
chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler])
chain.run(number=2,animal='tiger')

Dadurch wird jede von LLMChain() ausgeführte Aktion gedruckt, um eine Antwort zurückzugeben.

Damit sind wir mit allen wichtigen Komponenten des Langchain-Frameworks fertig.

Bis bald !!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.