Aufbau einer mehrsprachigen RAG mit Milvus, LangChain und OpenAI LLM
In den letzten zwei Jahren hat sich Retrieval Augmented Generation (RAG) schnell zu einer der beliebtesten Techniken für den Aufbau von GenAI-Anwendungen entwickelt, die auf large language models (LLMs) basieren. Im Kern verbessert RAG die Ausgabe eines LLMs durch die Bereitstellung von Kontextinformationen, auf die das Modell nicht vortrainiert wurde. Multilingual RAG ist ein erweitertes RAG, das Textdaten in mehreren Sprachen verarbeitet.
Yujian Tang, CEO von OSS4AI, sprach kürzlich bei einem Unstructured Data Meetup, das von Zilliz veranstaltet wurde. Er diskutierte über RAG und seine grundlegenden Komponenten und demonstrierte, wie man eine mehrsprachige RAG konstruiert, um verschiedene reale sprachliche Herausforderungen anzugehen.
In diesem Beitrag fassen wir die wichtigsten Erkenntnisse aus Yujians Präsentation zusammen und führen Sie durch die Implementierung einer mehrsprachigen RAG. Wenn Sie mehr über den Vortrag von Yujian erfahren möchten, empfehlen wir Ihnen, sich seine Präsentation auf YouTube anzusehen.
Was ist RAG und wie funktioniert es?
Eine wesentliche Einschränkung von LLM-gestützten Anwendungen ist ihre Abhängigkeit von den Daten, auf denen sie trainiert wurden. Wenn das LLM während des Pre-Trainings nicht mit bestimmten Informationen oder einer ganzen Wissensdomäne konfrontiert wurde, würde es die linguistischen Beziehungen nicht verstehen, die zur Generierung genauer Antworten erforderlich sind. Dieser Mangel an Daten kann dazu führen, dass das LLM entweder zugibt, dass es die Antwort nicht kennt, oder, schlimmer noch, "halluziniert" und falsche Informationen liefert.
RAG ist eine beliebte Technik, die das Problem der Halluzinationen von LLMs angeht, indem sie ihnen zusätzliche Kontextinformationen liefert. Sie ermöglicht es Entwicklern und Unternehmen auch, auf ihre privaten oder geschützten Daten zuzugreifen, ohne sich Gedanken über Sicherheitsprobleme zu machen.
Abbildung 1: Wie funktioniert RAG? (https://assets.zilliz.com/Figure_1_How_RAG_works_246044aacf.png)
RAG beginnt mit einem Einbettungsmodell, das Textdaten in Vektoreinbettungen umwandelt, numerische Repräsentationen, die die semantische Bedeutung des Textes erfassen. Das RAG-System speichert diese Vektoren dann in einer Vektordatenbank wie Milvus oder Zilliz Cloud, die sie für die Ähnlichkeitssucheh indiziert.
Wenn ein Benutzer eine Anfrage stellt, wandelt das Einbettungsmodell die Eingabe ebenfalls in einen Vektor um. Dann vergleicht das RAG-System die Ähnlichkeit dieses Abfragevektors mit den Vektoren in der Vektordatenbank, indem es ihre Distanz im hochdimensionalen Vektorraum berechnet. Wenn relevante Daten gefunden werden, ruft das RAG-System diese Informationen ab und fügt sie der ursprünglichen Anfrage hinzu, um eine neue Aufforderung für den LLM zu bilden. Das LLM nutzt diese zusätzlichen Informationen, um eine genauere und kontextuell relevante Antwort zu generieren, die über das hinausgeht, was es allein auf der Grundlage seiner Trainingsdaten produzieren könnte.
Was ist Multilingual RAG?
Multilingual RAG erweitert die Fähigkeiten des traditionellen RAG um die Unterstützung mehrerer Sprachen. Es integriert ein Einbettungsmodell, das in verschiedenen Sprachen trainiert wurde, so dass das System in der Lage ist, Antworten in verschiedenen Sprachen zu verarbeiten und zu generieren. Mit diesem mehrsprachigen Ansatz kann das RAG-System Anfragen in jeder beliebigen Sprache bearbeiten, relevante Informationen unabhängig von der Originalsprache abrufen und genaue, kontextbezogene Antworten in der vom Benutzer bevorzugten Sprache liefern.
How to Build a Multilingual RAG Application: a Step-by-Step Guide
Nachdem wir nun die Kernkonzepte und Komponenten von RAG kennengelernt haben, wollen wir nun Schritt für Schritt eine mehrsprachige RAG-Anwendung implementieren.
Diese Beispielanwendung besteht aus zwei Teilen: einem Web Scraper und der Hauptanwendung.
Der Web Scraper holt sich den benötigten Datensatz aus dem Internet.
Die Hauptanwendung erstellt Vektoreinbettungen, führt eine Vektorähnlichkeitssuche durch und generiert die Antworten.
Der Web Scraper
Zunächst werden wir Daten aus [Wikipedia] (https://www.wikipedia.org/) scrapen und sie als Kontextinformationen für dieses RAG-Beispiel verwenden.
Definieren Sie die Titel: Wir beginnen mit der Definition einer Liste namens
wiki_titles, die eine Liste von Städten enthält. Jede Stadt steht für eine Textdatei, die der Web Scraper mit dem Inhalt des entsprechenden Wikipedia-Eintrags füllen wird. Zum Beispiel wird "Atlanta.txt" Text enthalten, der von der Atlanta-Seite in Wikipedia stammt.Daten auslesen: Wir durchlaufen jede Stadt in "wiki_titles", stellen eine GET-Anfrage an die Wikipedia-API und extrahieren den Seiteninhalt aus der JSON-Antwort. Der Text wird dann für jede Stadt in einer entsprechenden Textdatei gespeichert.
from pathlib import Path
importiere Anfragen
wiki_titles = [
"Atlanta",
"Berlin",
"Boston",
"Kairo",
"Chicago",
"Kopenhagen",
"Houston",
"Karachi",
"Lissabon",
"London",
"Moskau",
"München",
"Paris",
"Pékin", # Französisch für Peking
"San Francisco",
"Seattle",
"Schanghai",
"Tokio",
"Toronto",
]
data_path = Path("./french_city_data")
data_path.mkdir(exist_ok=True) # Sicherstellen, dass das Verzeichnis existiert
for title in wiki_titles:
response = requests.get(
"https://fr.wikipedia.org/w/api.php",
params={
"action": "query",
"format": "json",
"titles": Titel,
"prop": "extracts",
"explaintext": True",
},
).json()
page = next(iter(response["query"]["pages"].values()))
wiki_text = page.get("extract", "") # Verwenden Sie .get(), um KeyError zu vermeiden
if wiki_text: # Prüfen, ob der Auszug nicht leer ist
with open(data_path / f"{title}.txt", "w") as fp:
fp.write(wiki_text)
sonst:
print(f "Kein Auszug gefunden für {Titel}")
Vorbereiten der Umgebung
Richten Sie zunächst Ihre Entwicklungsumgebung ein, indem Sie die erforderlichen Bibliotheken installieren: die Milvus-Vektordatenbank, LangChain, OpenAI und Satztransformatoren.
Zusätzlich müssen Sie Ihren API-Schlüssel angeben, wenn Sie sich über eine API, wie z.B. OpenAI, mit einem LLM verbinden. Dieser Schlüssel kann in einer separaten .env-Datei gespeichert werden und mit load_dotenv() und os abgerufen werden.
Hier ist der Code, um die Bibliotheken zu installieren und den API-Schlüssel zu laden:
!pip install -qU pymilvus langchain sentence-transformers tiktoken openai
von dotenv importieren load_dotenv
importieren os
load_dotenv() # Umgebungsvariablen aus der .env-Datei laden
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") # Den API-Schlüssel setzen
Bibliotheken installieren: Verwenden Sie "pip", um "pymilvus", "langchain", "sentence-transformers", "tiktoken" und "openai" zu installieren.
Umgebungsvariablen laden: Verwenden Sie
dotenv, um Umgebungsvariablen aus einer.env-Datei zu laden.API-Schlüssel setzen:** Rufen Sie den OpenAI-API-Schlüssel aus den Umgebungsvariablen ab und setzen Sie ihn.
Stellen Sie sicher, dass Ihre .env-Datei Ihren OpenAI-API-Schlüssel im folgenden Format enthält:
OPENAI_API_KEY=Ihr_api_schlüssel_hier
Initialisierung des LLM
Nachdem Sie Ihre Umgebung eingerichtet haben, besteht der nächste Schritt darin, den LLM zu definieren, den Sie in Ihrer Anwendung verwenden werden. Im folgenden Codeschnipsel erreichen wir dies, indem wir die OpenAI-Bibliothek importieren und den LLM mit dem Konstruktor von OpenAI definieren.
from langchain.llms import OpenAI
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
Sie können sich für einen Open-Source-LLM entscheiden, um die mit OpenAI-API-Aufrufen verbundenen Kosten zu vermeiden. Hugging Face bietet Hunderttausende von Deep-Learning-Modellen, mit denen Sie experimentieren können. Um eine Open-Source-LLM von HuggingFace zu verwenden, müssen Sie die [Transformers-Bibliothek] (https://huggingface.co/docs/transformers/index) importieren.
from transformers import pipeline
llm = pipeline('text-generation', model='gpt2') # Ersetzen Sie 'gpt2' durch Ihr gewünschtes Modell
Sie können zwischen OpenAI und einem Open-Source-Modell wechseln, indem Sie den entsprechenden Import- und Initialisierungscode ändern.
Auswahl eines geeigneten Einbettungsmodells
Beim Aufbau eines mehrsprachigen RAG-Systems ist [die Wahl des Einbettungsmodells] (https://zilliz.com/blog/choosing-the-right-embedding-model-for-your-data) genauso wichtig wie die Wahl des LLM, da das Einbettungsmodell mit der Sprache, mit der Sie arbeiten, kompatibel sein muss. Für dieses Beispiel, bei dem die Sprache Französisch ist, sind die standardmäßigen HuggingFace-Einbettungen ausreichend. Für andere Sprachen müssen Sie jedoch das am besten geeignete Einbettungsmodell ermitteln und verwenden.
Das MTEB Leaderboard auf HuggingFace ist eine wertvolle Ressource für die Suche nach Einbettungsmodellen. Dieses Leaderboard listet die leistungsstärksten Einbettungsmodelle für verschiedene Sprachen auf, z. B. die von Yujian identifizierten chinesischen und polnischen Einbettungen. Wenn Sie ein Einbettungsmodell auswählen, müssen Sie den Namen des Modells als Parameter angeben.
Hier erfahren Sie, wie Sie das Einbettungsmodell einrichten:
from langchain_community.embeddings import HuggingFaceEmbeddings
Einbettungen = HuggingFaceEmbeddings()
# Alternativ wird für die chinesischen Einbettungen das Modell als Parameter # übergeben, z.B.,
# HuggingFaceEmbeddings(model_name="TownsWu/PEG")
Laden und Aufteilen der Daten in Chunks
Als Nächstes laden wir die Städtedateien, die wir aus Wikipedia entnommen haben, und unterteilen sie in Segmente oder Chunks. Durch die Aufteilung des Textes in Chunks wird vermieden, dass eine Abfrage mit dem gesamten Dokument verglichen wird, was die Effizienz der Informationsbeschaffung erhöht. Je kleiner die Chunks sind, was durch den Parameter chunk_size bestimmt wird, desto größer ist die Genauigkeit, aber desto mehr Abrufvorgänge sind erforderlich. Je mehr sich die Chunks überschneiden, wie durch den Parameter "chunk_overlap" definiert, desto geringer ist die Wahrscheinlichkeit, dass der Kontext verloren geht - allerdings auf Kosten einer größeren Redundanz.
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema importiere Dokument
files = os.listdir("./french_city_data")
datei_texte = []
for file in files:
with open(f"./french_city_data/{file}") as f:
datei_text = f.read()
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=512, chunk_overlap=64,
)
Texte = text_splitter.split_text(datei_text)
for i, chunked_text in enumerate(texts):
file_texts.append(Document(page_content=chunked_text,
metadata={"doc_title": file.split(".")[0], "chunk_num":i}))
Laden von Dokumenten in Milvus
Nach dem Zerlegen der Städtedateien und dem Speichern als Liste von Dokumenten müssen wir sie in einen Vektorspeicher laden - in diesem Fall die Milvus-Vektordatenbank. Der folgende Code übernimmt das anfängliche Laden und die Aktualisierung, wenn die Städtedaten in Milvus gespeichert sind.
from langchain_community.vectorstores import Milvus
# Für den ersten Lauf
vector_store = Milvus.from_documents(
file_texts,
embedding=Einbettungen,
connection_args={"host": "localhost", "port": 19530},
collection_name="french_cities"
)
# wenn Ihre Daten bereits in Milvus gespeichert sind
vector_store = Milvus(
embedding_function=embeddings,
connection_args={"host": "localhost", "port": 19530},
collection_name="Französische Städte"
)
Einen Retriever erstellen
Als nächstes initialisieren wir unseren Retriever, eine Schnittstelle, die Dokumente aus einer bestimmten Quelle auf der Grundlage einer bestimmten Abfrage zurückgibt. Der folgende Code verwendet den Vektorspeicher, den wir im letzten Schritt erstellt haben, als Retriever und weist ihn einer Variablen zu.
Abrufer = vector_store.as_abrufer()
Prompt-Vorlage mit LangChain erstellen
Prompt-Vorlagen ermöglichen es Ihnen, Ihre Eingaben in einen LLM innerhalb Ihrer Anwendung präzise zu formatieren. Sie sind besonders nützlich für Fälle, in denen Sie dieselbe Prompt-Gliederung wiederverwenden möchten, aber mit kleinen Anpassungen - wie in unserer mehrsprachigen RAG-Anwendung, in der wir dieselbe Prompt-Vorlage für verschiedene Sprachen verwenden können.
Prompt-Vorlagen ermöglichen es Ihnen auch, einen Prompt aus dynamischen Eingaben zu erstellen, z. B. aus Benutzereingaben oder Daten, die aus einem Vektorspeicher abgerufen werden. In unserer Anwendung werden wir die Frage, die direkt in die Kette übergeben wird, und den Kontext, den der Retriever aus dem Vektorspeicher erhält, dynamisch einfügen.
from langchain.prompts import ChatPromptTemplate
template="""
Sie sind ein Assistent für die Beantwortung von Fragen. Benutze die folgenden Teile des abgerufenen Kontexts, um die Frage zu beantworten. Wenn Sie die Antwort nicht wissen, sagen Sie einfach, dass Sie sie nicht wissen.
Verwenden Sie maximal drei Sätze und fassen Sie die Antwort kurz.
Antworten Sie auf Französisch.
Frage: {Frage}
Kontext: {Kontext}
Antwort:"""
prompt = ChatPromptTemplate.from_template(template)
Verkettung der Komponenten zur Erstellung der RAG-Anwendung
Die Verkettung ist ein Prozess, der Komponenten miteinander verbindet, um durchgängige KI-Anwendungen zu erstellen, was eine der Schlüsselfunktionen von LangChain ist.
Der folgende Code zeigt, wie eine Kette aufgebaut wird, die die folgenden Elemente enthält: den Kontext vom Retriever, den Eingabeprompt, der von der Funktion runnablepassthrough() verarbeitet wird, die Prompt-Vorlage, den LLM und den stroutparser(), der die Antwort des Kettenaufrufs ausgibt.
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
chain = (
{"Kontext": Retriever, "Frage": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
Erstellen von Abfragen mit der Kette
Sobald die Kette aufgebaut ist, können Sie sie mit verschiedenen Abfragen aufrufen. Zum Beispiel:
response = chain.invoke("Erzählen Sie mir eine historische Tatsache über Karachi.")
Diese Abfrage ergibt die folgende Antwort in französischer Sprache!
"Karachi a été mentionnée pour la première fois dans l'ouvrage Histoire des plantes de Théophraste au IIIe siècle av. J.-C. Sie wurde zu Beginn des XIX. Jahrhunderts von den Briten besetzt und 1839 zur Hauptstadt von Sind ernannt. 1876 wurde der spätere Gründer Pakistans, Muhammad Ali Jinnah, in Karachi geboren und eingetragen."
Um die Mehrsprachigkeit zu demonstrieren, hier eine weitere Anfrage, diesmal auf Französisch:
response_2 = chain.invoke("Racontez-moi un fait historique sur Karachi.")
Trotz der gleichen zugrundeliegenden Frage führen die unterschiedlichen Sprachen (Englisch und Französisch) zu unterschiedlichen Einbettungen, was zu einer unterschiedlichen Ausgabe führt:
"Karachi ist eine Stadt, die von den Briten zu Beginn des XIX. Jahrhunderts gegründet wurde und die Hauptstadt des Sind ist. Sie war ein wichtiges Wirtschaftszentrum und erlebte einen rasanten Aufschwung, vor allem dank ihres Hafens. Seit 1980 ist die Stadt Schauplatz ethnischer und religiöser Konflikte, und 2012 wurde sie zum Schauplatz des verheerendsten Industriebrandes der Geschichte."
Herzlichen Glückwunsch! Sie haben erfolgreich eine mehrsprachige RAG-Anwendung erstellt. Denken Sie daran, dass Einbettungen von zentraler Bedeutung dafür sind, wie der LLM Sprachen interpretiert. Wählen Sie die am besten geeignete Einbettung zur Unterstützung mehrerer Sprachen und integrieren Sie sie in Ihre Anwendung.
Zusammenfassung
Wow! Dies ist ein ziemlich langer Beitrag. Lassen Sie uns einige der wichtigsten Punkte rekapitulieren.
Retrieval augmented generation (RAG) ist ein Rahmenwerk, das die LLM-Ausgabe durch Einfügen zusätzlicher Daten in Eingabeaufforderungen erweitert. RAG kann die lästigen LLM-Halluzinationsprobleme lösen.
Multilingual RAG ist ein erweitertes RAG, das mehrsprachige Dokumente verarbeitet.
Einbettungsmodelle, Vektordatenbanken und LLMs sind drei Kernkomponenten von RAG-Anwendungen.
Die wichtigste Überlegung bei der Entwicklung mehrsprachiger RAG-Anwendungen ist die Wahl des Einbettungsmodells. Das MTEB Leaderboard von HuggingFace ist eine hervorragende Ressource, um das richtige Modell für Ihre Anwendung zu finden.
Weitere Ressourcen
Leistungsstarke Einbettungsmodelle für Ihre GenAI-Apps | Zilliz
RAG-Verbesserungstechniken:
Verbesserung von RAG mit Wissensgraphen unter Verwendung von WhyHow
Optimierung von RAG mit Rerankern: Die Rolle und Kompromisse](https://zilliz.com/learn/optimize-rag-with-rerankers-the-role-and-tradeoffs)
Praktische Tipps und Tricks für Entwickler, die RAG-Anwendungen erstellen](https://zilliz.com/blog/praticial-tips-and-tricks-for-developers-building-rag-applications)
Infrastruktur-Herausforderungen bei der Skalierung von RAG mit benutzerdefinierten KI-Modellen](https://zilliz.com/blog/infrastructure-challenges-in-scaling-rag-with-custom-ai-models)
Weiterlesen

How to Install and Run OpenClaw (Previously Clawdbot/Moltbot) on Mac
Turn your Mac into an AI gateway for WhatsApp, Telegram, Discord, iMessage, and more — in under 5 minutes.

Will Amazon S3 Vectors Kill Vector Databases—or Save Them?
AWS S3 Vectors aims for 90% cost savings for vector storage. But will it kill vectordbs like Milvus? A deep dive into costs, limits, and the future of tiered storage.

Similarity Metrics for Vector Search
Exploring five similarity metrics for vector search: L2 or Euclidean distance, cosine distance, inner product, and hamming distance.
