Erstellung einer End-to-End-GenAI-App mit Ruby und Milvus
Die Einführung spezialisierter GenAI-Frameworks wie LangChain hat es uns ermöglicht, schnell und einfach anspruchsvolle KI-Anwendungen zu erstellen, indem wir leistungsstarke große Sprachmodelle (LLMs) wie ChatGPT und LLaMA nutzen. LangChain ermöglicht es uns beispielsweise, mit nur wenigen Codezeilen eine leistungsstarke Anwendung für Retrieval Augmented Generation (RAG) zu erstellen, ohne tiefgehendes theoretisches KI-Wissen zu benötigen.
Dieser Trend bedeutet, dass heutzutage Data Scientists und Machine-Learning-Engineers nicht mehr die Einzigen sind, die GenAI-Anwendungen erstellen können. Full-Stack-Engineers oder Softwareentwickler können nun GenAI-Apps mit LangChain erstellen.
Diese GenAI-Frameworks sind jedoch in der Regel in Python geschrieben, und wir wissen, dass einige Full-Stack-Engineers und Softwareentwickler Python in ihren Projekten nur selten verwenden. Daher besteht Bedarf an Erweiterungen dieser GenAI-Frameworks in anderen Programmiersprachen, damit diese Full-Stack-Engineers leistungsstarke LLMs nutzen können, um GenAI-Anwendungen in ihren Softwareprojekten zu erstellen.
In einem kürzlich gehaltenen Vortrag stellte Andrei Bondarev, Solution Architect bei Source Labs LLC, eine Ruby-Erweiterung von LangChain namens LangChain.rb vor, um es Full-Stack-Engineers zu erleichtern, GenAI-Anwendungen in ihren Softwareprojekten zu erstellen.
Sehen Sie sich diesen Vortrag auf Youtube an
Bevor wir jedoch besprechen, wie man eine GenAI-Anwendung mit Ruby erstellt, wollen wir kurz die Funktionsweise von Retrieval Augmented Generation (RAG), einem beliebten Anwendungsfall von GenAI, untersuchen.
Wie RAG funktioniert
Es ist kein Geheimnis, dass Daten die Goldgrube jeder GenAI-App sind. Sie dienen als Informationsquelle, die von GenAI verwendet wird, um faktische und genaue Antworten zu generieren. Von allen derzeit verfügbaren Daten können 80 % als unstrukturierte Daten klassifiziert werden.
Unstrukturierte Daten beziehen sich auf Daten, die keinem vordefinierten Datenformat entsprechen. Diese Art von Daten umfasst Bilder, Text, Ton und Videos. Damit Maschinen diese unstrukturierten Datentypen verstehen können, müssen wir sie in ein numerisches Format namens Vektoreinbettungen umwandeln.
Grundlegende Konzepte von Vektoreinbettungen
Eine Einbettung besteht aus einem n-dimensionalen Vektor, wobei n die Dimensionalität der Einbettung bezeichnet. Die Dimensionalität hängt vom Deep-Learning-Modell ab, das die Daten in eine Einbettung umwandelt. Eine Einbettung trägt die semantische Bedeutung der Daten, die sie repräsentiert.
Wir können Deep-Learning-Modelle verwenden, um verschiedene Datenmodalitäten in Einbettungen umzuwandeln. Wenn wir beispielsweise Textdaten haben, können wir OpenAI- oder Sentence Transformer-Modelle verwenden, um diese Textdaten in eine Einbettung umzuwandeln. Wenn wir Bilddaten haben, können wir spezialisierte vortrainierte Modelle verwenden, die in der Lage sind, Bildmerkmale zu extrahieren, wie etwa Vision Transformer, als Einbettungsmodell.
Da eine Einbettung die semantische Bedeutung der Daten trägt, die sie repräsentiert, können wir die Ähnlichkeit dieser Einbettung mit anderen Einbettungen im sogenannten Vektorraum berechnen. Einbettungen mit ähnlichen semantischen Bedeutungen werden im Vektorraum nahe beieinander platziert, wie Sie in der folgenden Visualisierung sehen können:
Vektoreinbettungen in einem Vektorraum.png
Einbettung verwandter Wörter im Vektorraum
Wie im obigen Bild gezeigt, liegen die Embeddings von "queen" und "king" nahe beieinander, ebenso wie "woman" und "man". Die euklidische Distanz zwischen "queen-king" und "woman-man" wäre ebenfalls ungefähr gleich, da sie ähnliche Bedeutungen tragen.
Dieses Konzept ist die Grundlage einer Vektorsuchoperation, bei der wir die Ähnlichkeit zwischen einem Embedding und mehreren Embeddings berechnen.
Die Rolle der Vektordatenbank in der Vektorsuche und in RAG-Anwendungen
Die Implementierung einer Vektorsuche ist unkompliziert, wenn wir nur mit einer kleinen Menge an Embeddings arbeiten. In realen Anwendungsfällen haben wir es jedoch häufig mit Tausenden, Millionen oder sogar Milliarden von Embeddings zu tun. Daher benötigen wir eine Lösung, um die Embeddings effizient zu speichern und schnelle Vektorsuchen darauf durchzuführen.
Hier kommt eine Vektordatenbank wie Milvus ins Spiel. Milvus ist eine Open-Source-Vektordatenbank, in der Sie enorme Mengen an Embeddings speichern und Vektorsuchen auf diesen Embeddings in Sekundenbruchteilen durchführen können.
Der Workflow der Umwandlung unstrukturierter Daten in Embeddings und ihrer Speicherung in Milvus
Der Workflow der Umwandlung unstrukturierter Daten in Embeddings und ihrer Speicherung in Milvus
Vektordatenbanken spielen auch eine entscheidende Rolle in beliebten GenAI-Apps wie RAG. Wie Sie vielleicht bereits wissen, besteht das Hauptziel von RAG darin, die Genauigkeit von Antworten zu verbessern, die von LLMs wie ChatGPT und LLaMA generiert werden, indem ihnen Kontext bereitgestellt wird, der bei der Beantwortung der Anfrage des Benutzers nützlich sein kann.
In einer RAG-Anwendung wird die Anfrage des Benutzers nach dem Empfang mithilfe eines Embedding-Modells in ein Embedding umgewandelt. Anschließend wird eine Vektorsuche durchgeführt, bei der das Anfrage-Embedding mit Kontext-Embeddings verglichen wird, die in der Vektordatenbank wie Milvus gespeichert sind. Die ähnlichsten Kontextdaten werden dann abgerufen und zusammen mit der Anfrage an das LLM übergeben. Das LLM kann dann die Informationen aus dem Kontext verwenden, um eine kontextualisierte Antwort zur Beantwortung der Anfrage des Benutzers zu generieren.
RAG
RAG-Workflow
LangChain als beliebtes GenAI-Framework
LangChain ist ein Framework, das es einfach macht, GenAI-Apps mit modernsten LLM-Modellen zu erstellen und zu entwickeln. Es lässt sich problemlos in beliebte LLM-Anbieter wie OpenAI, Anthropic und Google sowie in Vektordatenbankanbieter wie Zilliz integrieren.
LangChain bietet außerdem flexible Abstraktionen für die Entwicklung von KI-Anwendungen auf LLM-Basis, sodass Datenwissenschaftler und Softwareentwickler anspruchsvolle Systeme wie RAG mit nur wenigen Codezeilen unkompliziert erstellen können.
Nehmen wir zum Beispiel an, wir möchten den Inhalt von diesem Blogbeitrag mit GPT-4 zusammenfassen. Wir können diese Aufgabe mit dem folgenden Code erledigen:
import os
from langchain.chains.summarize import load_summarize_chain
from langchain_community.document_loaders import WebBaseLoader
from langchain_openai import ChatOpenAI
os.environ["LANGCHAIN_TRACING_V2"] = "True"
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
docs = loader.load()
llm = ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo-1106")
chain = load_summarize_chain(llm, chain_type="stuff")
result = chain.invoke(docs)
"""
Ausgabe: Der Artikel behandelt das Konzept von LLM-gestützten autonomen Agenten, mit Schwerpunkt auf den Komponenten Planung, Gedächtnis und Tool-Nutzung. Er enthält Fallstudien und Proof-of-Concept-Beispiele sowie Herausforderungen und Verweise auf verwandte Forschung. Der Autor betont das Potenzial von LLMs bei der Erstellung leistungsstarker Problemlösungsagenten, hebt aber auch Einschränkungen wie begrenzte Kontextlänge und die Zuverlässigkeit von Schnittstellen in natürlicher Sprache hervor.
"""
Wie Sie sehen können, können wir mit ungefähr nur 10 Zeilen Code das GPT-4-Modell nutzen, um einen langen Blogbeitrag präzise zusammenzufassen.
Mit LangChain können Sie auch komplexere Aufgaben ausführen. Zum Beispiel können Sie einen langen Text aus einem PDF-Dokument in Abschnitte aufteilen, jeden Abschnitt mithilfe eines Embedding-Modells Ihrer Wahl in ein Embedding umwandeln, das Embedding dieser Abschnitte in einer Vektordatenbank speichern und anschließend RAG durchführen.
import getpass
import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores.milvus import Milvus
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain.embeddings import SentenceTransformerEmbeddings
from langchain_openai import ChatOpenAI
# Set the API key
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
llm = ChatOpenAI(model="gpt-4")
# Text to be processed
texts = "This is a very long text.....:"
# Split text into chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) # Example chunk size and overlap
chunk_texts = text_splitter.split_text(texts)
# Instantiate embedding model
embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
# Store embeddings of chunks inside Milvus DB
vector_db = Milvus.from_texts(texts=chunk_texts, embedding=embeddings, collection_name="rag_milvus")
retriever = vector_db.as_retriever()
# Define function to format documents
def format_docs(docs):
return "\\n\\n".join(doc.page_content for doc in docs)
# Perform RAG (Retrieval Augmented Generation)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| llm
| StrOutputParser()
)
# Example question
question = "What is the main idea of the text?"
# Execute RAG chain and print the result
for chunk in rag_chain.stream(question):
print(chunk, end="", flush=True)
Abgesehen von den oben bereitgestellten Demos bietet LangChain eine breite Palette an Funktionalitäten. Es integriert beispielsweise LLMs und APIs aus externen Quellen wie Wetter-Apps, Rechnern oder Google Search. Dieser Ansatz ermöglicht es LLMs, Informationen aus diesen Quellen zu nutzen, um genauere und kontextbezogenere Antworten zu generieren. Wir werden die detaillierte Implementierung dieses Ansatzes in den kommenden Abschnitten sehen.
Sie können auch alle Funktionalitäten von LangChain auf ihrer Dokumentationsseite erkunden.
GenAI-App-Entwicklung mit Ruby und Milvus
Python ist zur De-facto-Programmiersprache für KI-Forschungs- und Entwicklungsframeworks geworden, einschließlich LangChain. Gleichzeitig bleibt Ruby für die schnelle Software- und Webanwendungsentwicklung beliebt.
Wie Sie jedoch im vorherigen Abschnitt gesehen haben, eröffnet die Einführung von LangChain Softwareentwicklern Möglichkeiten, die Leistungsfähigkeit von LLMs in ihre Web-Apps zu integrieren, ohne die detaillierten Theorien von LLMs und KI im Allgemeinen zu kennen.
Diese Fähigkeit hat eine wachsende Nachfrage geschaffen, diese GenAI-Entwicklungsframeworks auf andere Sprachen auszuweiten, die Full-Stack-Entwicklern vertrauter sind, wie Ruby. Um diese Nachfrage zu erfüllen, stellte Andrei Bondarev LangChain.rb vor, die Ruby-Erweiterung des ursprünglichen LangChain-Frameworks.
LangChain.rb ermöglicht Ruby-Full-Stack-Entwicklern, LLM-gestützte Web-Apps zu erstellen, ohne den Aufwand, mehrere Programmiersprachen in ihre Projekte einzubinden. Damit kannst du beliebte Vektordatenbanken, LLMs und externe Ressourcen problemlos in deine LLM-Web-Apps integrieren.
LangChain.rb hat dieselben allgemeinen Funktionalitäten wie das ursprüngliche LangChain, wie zum Beispiel:
Prompt-Management: Prompt-Vorlagen für LLMs deiner Wahl erstellen, laden und speichern
Validierung der Kontextlänge: die Kontextlänge der Eingaben entsprechend der Kontextlänge von LLMs und Embedding-Modellen deiner Wahl validieren
Daten-Chunking: Daten nach vordefinierten Regeln in Chunks aufteilen, bevor sie in Vektordatenbanken deiner Wahl aufgenommen werden
Conversation Memory: einen Chat mit einem LLM in einem Speicher persistieren
In den folgenden Abschnitten demonstrieren wir die Entwicklung einfacher LLM-gestützter Apps mithilfe von LangChain.rb.
Allgemeine RAG-Apps mit LangChain.rb
In diesem ersten Beispiel erstellen wir eine einfache und schnelle RAG-Anwendung mit LangChain.rb. Bevor du LangChain.rb für dein Ruby-Projekt verwenden kannst, stelle sicher, dass du das Gem installierst, indem du den folgenden Befehl ausführst:
gem install langchainrb
In diesem Projekt verwenden wir Milvus als Vektordatenbank und Modelle von OpenAI sowohl als LLM- als auch als Embedding-Modelle. Um Milvus zu starten, müssen wir Milvus in Docker installieren und den Container mit dem folgenden Befehl starten:
# Download the installation script
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
# Start the Docker container
bash standalone_embed.sh start
Nachdem wir nun den Docker-Container gestartet haben, instanziieren wir Milvus und die Modelle, die wir für unsere RAG-Anwendung verwenden werden.
require 'langchain'
milvus = Langchain::Vectorsearch::Milvus.new(
url: ENV["MILVUS_URL"],
index_name: "Documents",
llm: Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
)
Als Erstes müssen wir ein Schema innerhalb der Milvus-Vektordatenbank und die entsprechende Indexierungsmethode erstellen. Als Nächstes müssen wir dieses Schema laden, bevor wir damit eine Vektorsuche durchführen können.
# Create default schema
milvus.create_default_schema
# Create default index
milvus.create_default_index
# Load default schema
milvus.load_default_schema
Jetzt können wir einige Daten in unser Schema aufnehmen. Angenommen, wir haben eine PDF-Datei in unserem lokalen Verzeichnis, die Informationen zu Mitarbeiterleistungen enthält. Wenn wir alle Texte in dieser PDF-Datei innerhalb der Milvus-Datenbank speichern möchten, können wir dies tun, indem wir die folgenden Befehle ausführen:
pdf = Langchain.root.join("path/to/my.pdf")
# Add PDF inside of Milvus
milvus.add_data(path: pdf)
Sobald du die obigen Befehle ausführst, erledigt LangChain die gesamte Vorverarbeitung im Hintergrund. Es parst den Text in der PDF-Datei, teilt ihn in mehrere Chunks auf, wandelt jeden Chunk in Embeddings um und speichert die Embeddings anschließend in der Milvus-Vektordatenbank.
Nachdem wir unsere Daten in der Milvus-Vektordatenbank gespeichert haben, können wir damit beginnen, Fragen zu unserem PDF-Dokument zu stellen. Angenommen, wir möchten fragen: „Wie lautet die Urlaubsregelung des Unternehmens? Wie viel kann ich frei nehmen?“ Dann können wir unsere LLMs in einem RAG-System fragen, indem wir einfach diese eine Codezeile ausführen:
response = milvus.ask(question: "What’s the company’s vacation policy? How much can I take off?")
puts response
"""
Response:
=> The company's vacation policy allows employees to take any reasonable amount of time off with pay,
as long as they consult with their manager in advance and get their work done.
"""
Und das war’s! Zusätzlich zum Erstellen einer allgemeinen RAG-Anwendung können wir mit LangChain.rb auch eine agentische RAG-Anwendung erstellen, die wir im nächsten Abschnitt besprechen werden.
Agents nutzen, um mit Drittanbieter-Tools zu interagieren
Die Haupteinschränkung vieler LLMs ist ihr Wissens-Stichtag. GPT-4 hat beispielsweise einen Stichtag im April 2023. Das bedeutet, dass wir keine genaue Antwort vom LLM erhalten, wenn wir nach allgemeinen oder faktischen Ereignissen nach April 2023 fragen möchten.
Um dieses Problem zu lösen, ermöglicht uns LangChain.rb, eine agentische RAG-Anwendung zu erstellen. Diese Art von RAG-Anwendung fügt eine weitere Intelligenzebene hinzu, die einen „Agenten“ enthält, der als Entscheidungsträger fungiert. Der Agent analysiert die Benutzeranfrage und entscheidet dann über die effektivsten Drittanbieter-Tools, die den am besten geeigneten Kontext zur Beantwortung der Anfrage liefern können.
Angenommen, wir möchten unser LLM nach dem aktuellen Wetter in New York fragen. Mit einem allgemeinen RAG-System kann das LLM das Echtzeit-Wetter in New York nicht kennen. Es wird höchstwahrscheinlich anfangen zu halluzinieren und uns irgendwelche zufälligen Wettervorhersagen geben.
Der agentische RAG-Workflow (1).png
Workflow eines agentischen RAG
Agentic RAG löst dieses Problem, indem es uns ermöglicht, Tools oder APIs wie die OpenWeather API im System zu verwenden, um das Echtzeit-Wetter in New York zu erhalten. Der Agent verarbeitet zuerst die Benutzeranfrage und entscheidet dann über die Tools, die relevanten Kontext zur Beantwortung der Anfrage liefern können, bevor er den Kontext zu einer präzisen Antwort synthetisiert.
Die folgende Demo verwendet Drittanbieter-Tools innerhalb unseres RAG-Systems, wie einen Taschenrechner, die OpenWeather-App und Google Search.
weather = Langchain::Tool::Weather.new(api_key: ENV["OPEN_WEATHER_API_KEY"])
google_search = Langchain::Tool::GoogleSearch.new(api_key: ENV["SERPAPI_API_KEY"])
calculator = Langchain::Tool::Calculator.new
Als Nächstes müssen wir diese drei Tools mit dem folgenden Befehl in unser RAG-System hinzufügen:
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
agent = Langchain::Agent::ReActAgent.new(
llm: openai,
tools: [weather, google_search, calculator]
)
Jetzt können wir anfangen, unserem LLM Fragen zu stellen. Angenommen, wir möchten die folgende Frage stellen: „Find current weather in Boston, MA, and Washington, D.C., and take an average.“
response = agent.run(question: "Find current weather in Boston, MA and Washington, D.C. and take an average")
Ausgabe von RAG mit Tool-Integration
Ausgabe von RAG mit Tool-Integration
Wie Sie im obigen Screenshot sehen können, konnte unser agentisches RAG-System die Anfrage präzise beantworten. Lassen Sie uns den Workflow dieses RAG-Systems aufschlüsseln:
Die Anfrage wurde zuerst an das OpenAI LLM gesendet.
Der Agent erkannte die Notwendigkeit, die OpenWeather API zu verwenden, um das aktuelle Wetter in Boston und Washington D.C. zu erhalten.
Nach dem Abrufen der Wetterdaten stellte der Agent fest, dass die Anfrage erforderte, den Durchschnitt des Wetters aus den beiden Städten zu bilden.
Der Agent rief dann ein Taschenrechner-Tool auf, um den Wetterdurchschnitt zu berechnen.
Schließlich synthetisierte das LLM die Ergebnisse zu einer kohärenten Antwort und gab sie an den Benutzer zurück.
Dieses Beispiel demonstriert die Leistungsfähigkeit des agentischen RAG-Ansatzes. Durch die Einbindung externer Tools und APIs überwindet das System die Einschränkungen des Wissens-Stichtags des LLM und liefert eine genaue, aktuelle Antwort auf die Anfrage des Benutzers.
Verwendung von Agenten zur Interaktion mit internen Datenbanken
Wir können agentisches RAG auch verwenden, um mit unseren internen Datenbanken zu interagieren. Das ist sehr nützlich, da wir Erkenntnisse über unsere Daten in menschenähnlicher Sprache abfragen können, anstatt uns auf traditionelle SQL-Abfragen zu verlassen.
Angenommen, wir haben einen Online-Shop und Benutzerdaten, die in einer Datenbank gespeichert sind. Normalerweise müssten wir SQL-Abfragen schreiben, um Erkenntnisse aus diesen Daten zu extrahieren. Mit agentischem RAG müssen wir lediglich das LLM nach der gewünschten Erkenntnis fragen, und die Antwort wird sofort zurückgegeben.
Nehmen wir zum Beispiel an, wir möchten wissen, wie viele Benutzerdatensätze in der Datenbank gespeichert sind. Wir können einfach fragen: „How many users are there?“ und die folgenden Befehle ausführen:
require 'langchain'
# Datenbankverbindung instanziieren
database = Langchain::Tool::Database.new(connection_string: "postgres://localhost:5432/my_database")
# OpenAI-LLM-Instanz erstellen
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
# SQLAgent mit dem LLM und der Datenbankverbindung erstellen
agent = Langchain::Agent::SQLAgent.new(
llm: openai,
db: database
)
# Dem Agenten eine Frage stellen
response = agent.run("Wie viele Benutzer gibt es?")
Unten sehen Sie die Beispielausgabe des Befehls:
Ausgabe von RAG mit SQL-Integration
Ausgabe von RAG mit SQL-Integration
Wie Sie sehen können, war unser agentenbasiertes RAG-System in der Lage, eine spezifische Frage zu unseren Datenbankdaten korrekt zu beantworten. Der Workflow des Agenten ähnelt dem vorherigen Beispiel:
Die Abfrage wurde an das OpenAI LLM gesendet.
Der Agent analysierte die Abfrage und stellte fest, dass eine Datenbankabfrage erforderlich ist, um die Anzahl der Benutzer zu zählen.
Das LLM generierte auf Grundlage des Datenbanktabellenschemas eine geeignete SQL-Abfrage.
Die SQL-Abfrage wurde gegen die Datenbank ausgeführt und lieferte das Ergebnis zurück.
Die Datenbankausgabe wurde an das LLM zurückgesendet.
Das LLM synthetisierte das Datenbankergebnis zu einer kohärenten, für Menschen lesbaren Antwort und stellte sie als endgültige Antwort bereit.
Fazit
Die Einführung von LangChain macht LLMs für Fachleute zugänglich, die möglicherweise keine tiefgreifenden Kenntnisse in KI- und Data-Science-Theorien haben. Wir können mit LangChain mit nur wenigen Codezeilen eine leistungsstarke RAG-Anwendung erstellen.
Diese Zugänglichkeit ist der Grund, warum Andrei Bondarev LangChain.rb eingeführt hat, eine LangChain-Erweiterung für Ruby. Dieses Framework ermöglicht es Full-Stack-Entwicklern, die leistungsstarke Performance von LLMs in ihre Webanwendungen zu integrieren, ohne umfassende KI-Expertise zu benötigen. Darüber hinaus beseitigt LangChain.rb den Aufwand für Full-Stack-Entwickler, zu einer anderen Programmiersprache wechseln zu müssen, wenn sie LLMs in ihren Webanwendungen nutzen möchten.
Weiterlesen

What Is a Vector Lakebase?
A Vector Lakebase is a unified, lake-native data architecture for AI that combines vector-database-grade serving with open lake storage, reusable lake-level indexes, and a shared semantic layer.

Our Journey to 35K+ GitHub Stars: The Real Story of Building Milvus from Scratch
Join us in celebrating Milvus, the vector database that hit 35.5K stars on GitHub. Discover our story and how we’re making AI solutions easier for developers.

Democratizing AI: Making Vector Search Powerful and Affordable
Zilliz democratizes AI vector search with Milvus 2.6 and Zilliz Cloud for powerful, affordable scalability, cutting costs in infrastructure, operations, and development.


