Erweiterte RAG-Techniken Teil 1: Datenverarbeitung
Diskussion und Umsetzung von Techniken zur Steigerung der RAG-Leistung. Teil 1 von 2, der sich auf die Datenverarbeitungs- und Aufnahmekomponente einer fortschrittlichen RAG-Pipeline konzentriert.
Dies ist Teil 1 unserer Erkundung fortgeschrittener RAG-Techniken. Klicken Sie hier für Teil 2!
Die kürzlich erschienene Arbeit „Searching for Best Practices in Retrieval-Augmented Generation“ bewertet empirisch die Wirksamkeit verschiedener RAG-Verbesserungstechniken mit dem Ziel, eine Reihe von Best Practices für RAG zu ermitteln.

Die von Wang und Kollegen empfohlene RAG-Pipeline.
Wir werden einige dieser vorgeschlagenen Best Practices umsetzen, nämlich diejenigen, die darauf abzielen, die Qualität der Suche zu verbessern (Sentence Chunking, HyDE, Reverse Packing).
Aus Gründen der Kürze lassen wir die Techniken zur Effizienzsteigerung (Abfrageklassifizierung und Zusammenfassung) aus.
Wir werden auch einige Techniken anwenden, die nicht behandelt wurden, die ich persönlich aber für nützlich und interessant halte (Metadateneinbindung, zusammengesetzte Multi-Field-Einbettungen, Abfrageanreicherung).
Zum Schluss führen wir einen kurzen Test durch, um zu sehen, ob sich die Qualität unserer Suchergebnisse und generierten Antworten im Vergleich zur Ausgangslage verbessert hat. Los geht's!
RAG-Übersicht
RAG zielt darauf ab, LLMs zu verbessern, indem Informationen aus externen Wissensdatenbanken abgerufen werden, um die generierten Antworten anzureichern. Durch die Bereitstellung domänenspezifischer Informationen können LLMs schnell an Anwendungsfälle außerhalb des Umfangs ihrer Trainingsdaten angepasst werden; dies ist wesentlich kostengünstiger als eine Feinabstimmung und einfacher, sie auf dem neuesten Stand zu halten.
Maßnahmen zur Verbesserung der Qualität von Ampelbewertungen konzentrieren sich typischerweise auf zwei Bereiche:
Verbesserung der Qualität und Klarheit der Wissensbasis.
Verbesserung der Abdeckung und Spezifität von Suchanfragen.
Diese beiden Maßnahmen sollen das Ziel erreichen, die Wahrscheinlichkeit zu erhöhen, dass der LLM Zugang zu relevanten Fakten und Informationen hat und somit weniger wahrscheinlich Halluzinationen hat oder auf sein eigenes Wissen zurückgreift, das möglicherweise veraltet oder irrelevant ist.
Die Vielfalt der Methoden lässt sich in wenigen Sätzen nur schwer verdeutlichen. Um die Dinge verständlicher zu machen, gehen wir direkt zur Umsetzung über.

Abbildung 1: Die vom Autor verwendete RAG-Pipeline.
Inhaltsverzeichnis
Aufstellen
Der gesamte Code ist im Searchlabs-Repository zu finden.
Das Wichtigste zuerst. Sie benötigen Folgendes:
Eine elastische Cloud-Bereitstellung
Eine LLM-API – In diesem Notebook verwenden wir eine GPT-4o-Bereitstellung auf Azure OpenAI.
Python Version 3.12.4 oder höher
Wir werden den gesamten Code aus dem main.ipynb-Notebook ausführen.
Klonen Sie nun das Repository mit git, navigieren Sie zu supporting-blog-content/advanced-rag-techniques und führen Sie anschließend die folgenden Befehle aus:
# Create a new virtual environment named 'rag_env'
python -m venv rag_env
# Activate the virtual environment (for Unix-based systems)
source rag_env/bin/activate
# (For Windows)
.\rag_env\Scripts\activate
# Install packages listed in requirements.txt
pip install -r requirements.txtSobald das erledigt ist, erstellen Sie eine .env-Datei. Datei öffnen und die folgenden Felder ausfüllen (Referenz in .env.example). Ein Dank geht an meinen Co-Autor Claude-3.5 für die hilfreichen Kommentare.
# Elastic Cloud: Found in the 'Deployment' page of your Elastic Cloud
# console
ELASTIC_CLOUD_ENDPOINT=""
ELASTIC_CLOUD_ID=""
# Elastic Cloud: Created during deployment setup or in 'Security'
# settings
ELASTIC_USERNAME=""
ELASTIC_PASSWORD=""
# Elastic Cloud: The name of the index you created in Kibana or via API
ELASTIC_INDEX_NAME=""
# Azure AI Studio: Found in 'Keys and Endpoint' section of your Azure
# OpenAI resource
AZURE_OPENAI_KEY_1=""
AZURE_OPENAI_KEY_2=""
AZURE_OPENAI_REGION=""
AZURE_OPENAI_ENDPOINT=""
# Azure AI Studio: Found in 'Deployments' section of your Azure OpenAI
# resource
AZURE_OPENAI_DEPLOYMENT_NAME=""
# Using BAAI/bge-small-en-v1.5 because I think it is a good balance of
# resource efficiency and performance.
HUGGINGFACE_EMBEDDING_MODEL="BAAI/bge-small-en-v1.5"Als Nächstes wählen wir das zu importierende Dokument aus und legen es im Dokumentenordner ab. Für diesen Artikel verwenden wir den Elastic NV Jahresbericht 2023. Es handelt sich um ein ziemlich anspruchsvolles und komplexes Dokument, perfekt geeignet, um unsere RAG-Techniken einem Stresstest zu unterziehen.

Elastic-Jahresbericht 2023
Jetzt sind wir bereit, lasst uns mit der Einnahme beginnen. Öffnen Sie main.ipynb und führen Sie die ersten beiden Zellen aus, um alle Pakete zu importieren und alle Dienste zu initialisieren.
Einlesen, Verarbeiten und Einbetten von Dokumenten
Dateningestion
Persönliche Anmerkung: Ich bin von der Benutzerfreundlichkeit von LlamaIndex begeistert. In den alten Zeiten vor LLMs und LlamaIndex war das Einlesen von Dokumenten in verschiedenen Formaten ein mühsamer Prozess, bei dem man esoterische Pakete aus allen möglichen Quellen zusammentragen musste. Jetzt ist es auf einen einzigen Funktionsaufruf reduziert. Wild.
Die SimpleDirectoryReader lädt alle Dokumente in den directory_path. -Dateien. Für .pdf -Dateien gibt sie eine Liste von Dokumentobjekten zurück, die ich in Python-Dictionaries umwandle, da ich diese einfacher zu handhaben finde.
# llamaindex_processor.py
from llama_index.core import SimpleDirectoryReader
class LlamaIndexProcessor:
def __init__(self):
pass
def load_documents(self, directory_path):
'''
Load all documents in directory
'''
reader = SimpleDirectoryReader(input_dir=directory_path)
return reader.load_data()
# main.ipynb
llamaindex_processor=LlamaIndexProcessor()
documents=llamaindex_processor.load_documents('./documents/')
documents=[dict(doc_obj) for doc_obj in documents]Jedes Wörterbuch enthält den Schlüsselinhalt im Feld text . Es enthält außerdem nützliche Metadaten wie Seitenzahl, Dateiname, Dateigröße und Dateityp.
{
'id_': '5f76f0b3-22d8-49a8-9942-c2bbab14f63f',
'metadata': {'page_label': '5',
'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
'file_path': '/Users/han/Desktop/Projects/truckasaurus/documents/Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
'file_type': 'application/pdf',
'file_size': 3724426,
'creation_date': '2024-07-27',
'last_modified_date': '2024-07-27'},
'text': 'Table of Contents\nPage\nPART I\nItem 1. Business 3\n15 Item 1A. Risk Factors\nItem 1B. Unresolved Staff Comments 48\nItem 2. Properties 48\nItem 3. Legal Proceedings 48\nItem 4. Mine Safety Disclosures 48\nPART II\nItem 5. Market for Registrant's Common Equity, Related Stockholder Matters and Issuer Purchases of \nEquity Securities49\nItem 6. [Reserved] 49\nItem 7. Management's Discussion and Analysis of Financial Condition and Results of Operations 50\nItem 7A. Quantitative and Qualitative Disclosures About Market Risk 64\nItem 8. Financial Statements and Supplementary Data 66\nItem 9. Changes in and Disagreements With Accountants on Accounting and Financial Disclosure 100\n100\n101Item 9A. Controls and Procedures\nItem 9B. Other Information\nItem 9C. Disclosure Regarding Foreign Jurisdictions That Prevent Inspections 101\nPART III\n102\n102\n102\n102Item 10. Directors, Executive Officers and Corporate Governance\nItem 11. Executive Compensation\nItem 12. Security Ownership of Certain Beneficial Owners and Management, and Related Stockholder Matters \nItem 13. Certain Relationships and Related Transactions, and Director Independence\nItem 14. Principal Accountant Fees and Services 102\nPART IV\n103\n105Item 15. Exhibits and Financial Statement Schedules \nItem 16. Form 10-K Summary\nSignatures 106\ni',
...
}Chunking auf Satzebene, tokenweise
Als Erstes sollten wir unsere Dokumente in Abschnitte von einheitlicher Länge unterteilen (um Konsistenz und Verwaltbarkeit zu gewährleisten). Einbettungsmodelle haben individuelle Token-Limits (maximale Eingabegröße, die sie verarbeiten können). Tokens sind die grundlegenden Einheiten des Textes, die Prozesse modellieren. Um Informationsverluste (Abschneidung oder Auslassung von Inhalten) zu vermeiden, sollten wir Texte bereitstellen, die diese Grenzen nicht überschreiten (indem wir längere Texte in kleinere Abschnitte aufteilen).
Das Chunking hat einen erheblichen Einfluss auf die Leistung. Im Idealfall stellt jeder Abschnitt eine in sich abgeschlossene Informationseinheit dar, die Kontextinformationen zu einem einzelnen Thema erfasst. Zu den Chunking-Methoden gehören das Chunking auf Wortebene, bei dem Dokumente anhand der Wortanzahl aufgeteilt werden, und das semantische Chunking, das ein LLM verwendet, um logische Trennpunkte zu identifizieren.
Die Segmentierung auf Wortebene ist zwar günstig, schnell und einfach, birgt aber das Risiko, Sätze zu trennen und dadurch den Kontext zu zerstören. Die semantische Segmentierung wird langsam und teuer, insbesondere bei Dokumenten wie dem 116-seitigen Elastic Annual Report.
Lasst uns einen Mittelweg wählen. Die Segmentierung auf Satzebene ist zwar immer noch einfach, kann aber den Kontext besser erhalten als die Segmentierung auf Wortebene und ist dabei deutlich günstiger und schneller. Zusätzlich werden wir ein gleitendes Fenster implementieren, um einen Teil des umgebenden Kontextes zu erfassen und die Auswirkungen der Absatzteilung zu verringern.
# chunker.py
import uuid
import re
class Chunker:
def __init__(self, tokenizer):
self.tokenizer = tokenizer
def split_into_sentences(self, text):
"""Split text into sentences."""
return re.split(r'(?<=[.!?])\s+', text)
def sentence_wise_tokenized_chunk_documents(self, documents, chunk_size=512, overlap=20, min_chunk_size=50):
'''
1. Split text into sentences.
2. Tokenize using the provided tokenizer method.
3. Build chunks up to the chunk_size limit.
4. Create an overlap based on tokens - to preserve context.
5. Only keep chunks that meet the minimum token size requirement.
'''
chunked_documents = []
for doc in documents:
sentences = self.split_into_sentences(doc['text'])
tokens = []
sentence_boundaries = [0]
# Tokenize all sentences and keep track of sentence boundaries
for sentence in sentences:
sentence_tokens = self.tokenizer.encode(sentence, add_special_tokens=True)
tokens.extend(sentence_tokens)
sentence_boundaries.append(len(tokens))
# Create chunks
chunk_start = 0
while chunk_start < len(tokens):
chunk_end = chunk_start + chunk_size
# Find the last complete sentence that fits in the chunk
sentence_end = next((i for i in sentence_boundaries if i > chunk_end), len(tokens))
chunk_end = min(chunk_end, sentence_end)
# Create the chunk
chunk_tokens = tokens[chunk_start:chunk_end]
# Check if the chunk meets the minimum size requirement
if len(chunk_tokens) >= min_chunk_size:
# Create a new document object for this chunk
chunk_doc = {
'id_': str(uuid.uuid4()),
'chunk': chunk_tokens,
'original_text': self.tokenizer.decode(chunk_tokens),
'chunk_index': len(chunked_documents),
'parent_id': doc['id_'],
'chunk_token_count': len(chunk_tokens)
}
# Copy all other fields from the original document
for key, value in doc.items():
if key != 'text' and key not in chunk_doc:
chunk_doc[key] = value
chunked_documents.append(chunk_doc)
# Move to the next chunk start, considering overlap
chunk_start = max(chunk_start + chunk_size - overlap, chunk_end - overlap)
return chunked_documents
# main.ipynb
# Initialize Embedding Model
HUGGINGFACE_EMBEDDING_MODEL = os.environ.get('HUGGINGFACE_EMBEDDING_MODEL')
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)
# Initialize Chunker
chunker=Chunker(embedder.tokenizer)Die Klasse Chunker verwendet den Tokenizer des Einbettungsmodells zum Kodieren und Dekodieren von Text. Wir werden nun Blöcke von jeweils 512 Token erstellen, mit einer Überlappung von 20 Token. Dazu teilen wir den Text in Sätze auf, tokenisieren diese Sätze und fügen die tokenisierten Sätze dann unserem aktuellen Chunk hinzu, bis wir keine weiteren mehr hinzufügen können, ohne unser Token-Limit zu überschreiten.
Zum Schluss werden die Sätze wieder in den Originaltext dekodiert, um sie einzubetten. Dieser wird in einem Feld namens original_text gespeichert. Die Chunks werden in einem Feld namens chunk gespeichert. Um unnötige Dokumente (auch: überflüssige Dokumente) zu reduzieren, werden wir alle Dokumente verwerfen, die kürzer als 50 Token sind.
Lassen Sie uns das anhand unserer Dokumente prüfen:
chunked_documents=chunker.sentence_wise_tokenized_chunk_documents(documents, chunk_size=512)Und Sie erhalten Textabschnitte zurück, die etwa so aussehen:
print(chunked_documents[4]['original_text'])
[CLS] the aggregate market value of the ordinary shares held by non - affiliates of the registrant,
based on the closing price of the shares of ordinary shares on the new york stock exchange on
october 31, 2022 ( the last business day of the registrant 's second fiscal quarter ), was
approximately $ 6. 1 billion. [SEP] [CLS] as of may 31, 2023, the registrant had 97, 390, 886
ordinary shares, par value €0. 01 per share, outstanding. [SEP] [CLS] documents incorporated by
reference portions of the registrant 's definitive proxy statement relating to the registrant 's 2
023 annual general meeting of shareholders are incorporated by reference into part iii of this annual
...
...Metadateneinbindung und -generierung
Wir haben unsere Dokumente in Abschnitte unterteilt. Nun ist es an der Zeit, die Daten anzureichern. Ich möchte zusätzliche Metadaten generieren oder extrahieren. Diese zusätzlichen Metadaten können genutzt werden, um die Suchleistung zu beeinflussen und zu verbessern.
Wir definieren eine DocumentEnricher -Klasse, deren Aufgabe es ist, eine Liste von Dokumenten (Python-Dictionaries) und eine Liste von Prozessorfunktionen entgegenzunehmen. Diese Funktionen werden auf die Spalte original_text der Dokumente angewendet und ihre Ergebnisse in neuen Feldern gespeichert.
Zuerst extrahieren wir mithilfe von TextRank Schlüsselphrasen. TextRank ist ein graphenbasierter Algorithmus, der Schlüsselphrasen und -sätze aus Texten extrahiert, indem er ihre Wichtigkeit anhand der Beziehungen zwischen den Wörtern ordnet.
Als nächstes generieren wir potenzielle_Fragen mithilfe von GPT-4o.
Zum Schluss extrahieren wir Entitäten mithilfe von Spacy.
Da der Code für jeden dieser Punkte recht umfangreich und komplex ist, verzichte ich darauf, ihn hier wiederzugeben. Bei Interesse sind die Dateien in den unten stehenden Codebeispielen gekennzeichnet.
Starten wir die Datenanreicherung:
# documentenricher.py
from tqdm import tqdm
class DocumentEnricher:
def __init__(self):
pass
def enrich_document(self, documents, processors, text_col='text'):
for doc in tqdm(documents, desc="Enriching documents using processors: "+str(processors)):
for (processor, field) in processors:
metadata=processor(doc[text_col])
if isinstance(metadata, list):
metadata='\n'.join(metadata)
doc.update({field: metadata})
# main.ipynb
# Initialize processor classes
nltkprocessor=NLTKProcessor() // nltk_processor.py
entity_extractor=EntityExtractor() // entity_extractor.py
gpt4o = LLMProcessor(model='gpt-4o') // llm.py
# Initialize LLM
documentenricher=DocumentEnricher()
# Create new fields in the documents - These are the outputs of the processor functions.
processors=[
(nltkprocessor.textrank_phrases, "keyphrases"),
(gpt4o.generate_questions, "potential_questions"),
(entity_extractor.extract_entities, "entities")
]
# .enrich_document() will modify chunked_docs in place.
# To view the results, we'll print chunked_docs in the next few cells!
documentenricher.enrich_document(chunked_docs, text_col='original_text', processors=processors)Und sehen Sie sich die Ergebnisse an:
Von TextRank extrahierte Schlüsselphrasen
Diese Schlüsselbegriffe stehen stellvertretend für die Kernthemen des jeweiligen Abschnitts. Wenn eine Anfrage mit Cybersicherheit zu tun hat, wird die Punktzahl dieses Abschnitts erhöht.
print(chunked_documents[25]['keyphrases'])
'elastic agent stop', 'agent stop malware',
'stop malware ransomware', 'malware ransomware environment',
'ransomware environment wide', 'environment wide visibility',
'wide visibility threat', 'visibility threat detection',
'sep cl key', 'cl key feature'Mögliche Fragen, die von GPT-4o generiert wurden
Diese potenziellen Fragen könnten direkt mit den Suchanfragen der Nutzer übereinstimmen und so zu einer Verbesserung der Punktzahl führen. Wir fordern GPT-4o auf, Fragen zu generieren, die mithilfe der im aktuellen Chunk enthaltenen Informationen beantwortet werden können.
print(chunked_documents[25]['potential_questions'])
1. What are the primary functions that Elastic Agent provides in terms of cybersecurity?
2. Describe how Logstash contributes to data management within an IT environment.
3. List and explain any key features of Logstash mentioned in the document.
4. How does Elastic Agent enhance environment-wide visibility in threat detection?
5. What capabilities does Logstash offer for handling data beyond simple collection?
6. In what ways does the document suggest that Elastic Agent stops malware and ransomware?
7. Can you identify any relationships between the functionalities of Elastic Agent and Logstash in an integrated environment?
8. What implications might the advanced threat detection capabilities of Elastic Agent have for organizational security policies?
9. Compare and contrast the roles of Elastic Agent and Logstash based on their described functions.
10. How might the centralized collection ability of Logstash support the threat detection capabilities of Elastic Agent?Von Spacy extrahierte Entitäten
Diese Entitäten dienen einem ähnlichen Zweck wie die Schlüsselphrasen, erfassen aber die Namen von Organisationen und Einzelpersonen, die bei der Extraktion von Schlüsselphrasen möglicherweise nicht erfasst werden.
print(chunked_documents[29]['entities'])
'appdynamics', 'apm data', 'azure sentinel',
'microsoft', 'mcafee', 'broadcom', 'cisco',
'dynatrace', 'coveo', 'lucidworks'Zusammengesetzte Mehrfeldeinbettungen
Nachdem wir unsere Dokumente nun mit zusätzlichen Metadaten angereichert haben, können wir diese Informationen nutzen, um robustere und kontextsensitive Einbettungen zu erstellen.
Lassen Sie uns den aktuellen Stand des Prozesses noch einmal betrachten. Wir haben in jedem Dokument vier Interessensgebiete.
{
"chunk": "...",
"keyphrases": "...",
"potential_questions": "...",
"entities": "..."
}Jedes Feld repräsentiert eine andere Perspektive auf den Kontext des Dokuments und hebt möglicherweise einen wichtigen Bereich hervor, auf den sich das LLM konzentrieren sollte.

Pipeline zur Metadatenanreicherung
Der Plan besteht darin, jedes dieser Felder einzubetten und dann eine gewichtete Summe der Einbettungen zu erstellen, die als zusammengesetzte Einbettung bezeichnet wird.
Mit etwas Glück wird dieses Composite Embedding dem System ermöglichen, kontextsensitiver zu werden, und zusätzlich einen weiteren einstellbaren Hyperparameter zur Steuerung des Suchverhaltens einführen.
Zunächst betten wir jedes Feld ein und aktualisieren jedes Dokument direkt, indem wir unser lokal definiertes Einbettungsmodell verwenden, das wir zu Beginn des Notebooks main.ipynb importiert haben.
# EmbeddingModel defined in embedding_model.py
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)
cols_to_embed=['keyphrases', 'potential_questions', 'entities']
embedding_cols=[]
for col in cols_to_embed:
# Works on text input
embedding_col=embedder.embed_documents_text_wise(chunked_documents, text_field=col)
embedding_cols.append(embedding_col)
# Works on token input
embedding_col=embedder.embed_documents_token_wise(chunked_documents, token_field="chunk")
embedding_cols.append(embedding_col)Jede Einbettungsfunktion gibt das Feld der Einbettung zurück, welches einfach das ursprüngliche Eingabefeld mit einem Suffix _embedding ist.
Definieren wir nun die Gewichtungen unserer zusammengesetzten Einbettung:
embedding_cols=[
'keyphrases_embedding',
'potential_questions_embedding',
'entities_embedding',
'chunk_embedding']
combination_weights=[
0.1,
0.15,
0.05,
0.7
]Mithilfe der Gewichtungen können Sie den einzelnen Komponenten Prioritäten zuweisen, basierend auf Ihrem Anwendungsfall und der Qualität Ihrer Daten. Intuitiv betrachtet hängt die Größe dieser Gewichtungen vom semantischen Wert jeder Komponente ab. Da der Chunk-Text selbst mit Abstand den größten Informationsgehalt aufweist, weise ich ihm eine Gewichtung von 70 % zu. Da es sich bei den Entitäten um die kleinsten handelt, nämlich lediglich um eine Liste von Organisations- oder Personennamen, weise ich ihnen eine Gewichtung von 5 % zu. Die genaue Festlegung dieser Werte muss empirisch, also für jeden Anwendungsfall einzeln, erfolgen.
Zum Schluss schreiben wir eine Funktion, um die Gewichtungen anzuwenden und unser zusammengesetztes Embedding zu erstellen. Um Speicherplatz zu sparen, löschen wir auch alle Komponenteneinbettungen.
from tqdm import tqdm
def combine_embeddings(objects, embedding_cols, combination_weights, primary_embedding='primary_embedding'):
# Ensure the number of weights matches the number of embedding columns
assert len(embedding_cols) == len(combination_weights), "Number of embedding columns must match number of weights"
# Normalize weights to sum to 1
weights = np.array(combination_weights) / np.sum(combination_weights)
for obj in tqdm(objects, desc="Combining embeddings"):
# Initialize the combined embedding
combined = np.zeros_like(obj[embedding_cols[0]])
# Compute the weighted sum
for col, weight in zip(embedding_cols, weights):
combined += weight * np.array(obj[col])
# Add the new combined embedding to the object
obj.update({primary_embedding:combined.tolist()})
# Remove the original embedding columns
for col in embedding_cols:
obj.pop(col, None)
combine_embeddings(chunked_documents, embedding_cols, combination_weights)Hiermit ist unsere Dokumentenbearbeitung abgeschlossen. Wir haben nun eine Liste von Dokumentobjekten, die folgendermaßen aussehen:
{ 'id_': '7fe71686-5cd0-4831-9e79-998c6dbeae0c', 'chunk': [2312, 14613, ...], 'original_text': 'if an emerging growth company, indicate by check mark if the registrant has elected not to use the extended ...', 'chunk_index': 3, 'chunk_token_count': 399, 'metadata': {'page_label': '3', 'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf', ... 'keyphrases': 'sep cl unk\ncheck mark registrant\ncl unk indicate\nunk indicate check\nindicate check mark\nprincipal executive office\naccelerate filer unk\ncompany unk emerge\nunk emerge growth\nemerge growth company', 'potential_questions': '1. What are the different types of registrant statuses mentioned in the document?\n2. Under what section of the Sarbanes-Oxley Act must registrants file a report on the effectiveness of their internal ...', 'entities': 'the effe ctiveness of\nsection 13\nSEP\nUNK\nsection 21e\n1934\n1933\nu. s. c.\nsection 404\nsection 12\nal', 'primary_embedding': [-0.3946287803351879, -0.17586839850991964, ...] }Indexierung zu Elastic
Lasst uns unsere Dokumente per Massen-Upload in Elastic Search hochladen. Zu diesem Zweck habe ich vor langer Zeit eine Reihe von Elastic-Helper-Funktionen in elastic_helpers.py definiert. Es handelt sich um einen sehr langen Codeabschnitt, daher konzentrieren wir uns auf die Funktionsaufrufe.
es_bulk_indexer.bulk_upload_documents Funktioniert mit beliebigen Listen von Wörterbuchobjekten und nutzt dabei die praktischen dynamischen Zuordnungen von Elasticsearch.
# Initialize Elasticsearch
ELASTIC_CLOUD_ID = os.environ.get('ELASTIC_CLOUD_ID')
ELASTIC_USERNAME = os.environ.get('ELASTIC_USERNAME')
ELASTIC_PASSWORD = os.environ.get('ELASTIC_PASSWORD')
ELASTIC_CLOUD_AUTH = (ELASTIC_USERNAME, ELASTIC_PASSWORD)
es_bulk_indexer = ESBulkIndexer(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)
es_query_maker = ESQueryMaker(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)
# Define Index Name
index_name=os.environ.get('ELASTIC_INDEX_NAME')
# Create index and bulk upload
index_exists = es_bulk_indexer.check_index_existence(index_name=index_name)
if not index_exists:
logger.info(f"Creating new index: {index_name}")
es_bulk_indexer.create_es_index(es_configuration=BASIC_CONFIG, index_name=index_name)
success_count = es_bulk_indexer.bulk_upload_documents(
index_name=index_name,
documents=chunked_documents,
id_col='id_',
batch_size=32
)Gehe zu Kibana und überprüfe, ob alle Dokumente indexiert wurden. Es sollten 224 sein. Nicht schlecht für ein so umfangreiches Dokument!

Indexierte Jahresberichtsdokumente in Kibana
Katzenbruch
Lasst uns eine Pause machen, der Artikel ist etwas anspruchsvoll, ich weiß. Schaut euch meine Katze an:

Seht nur, wie wütend sie ist!
Liebenswert. Der Hut ist verschwunden und ich vermute fast, dass sie ihn gestohlen und irgendwo versteckt hat :(
Herzlichen Glückwunsch, dass du es so weit geschafft hast :)
Seien Sie in Teil 2 wieder dabei, wenn wir unsere RAG-Pipeline testen und bewerten!
Anhang
Definitionen
1. Satzgliederung
Eine Vorverarbeitungstechnik, die in RAG-Systemen verwendet wird, um Text in kleinere, sinnvolle Einheiten zu unterteilen.
Prozess:
Eingabe: Großer Textblock (z. B. Dokument, Absatz)
Ausgabe: Kleinere Textsegmente (typischerweise Sätze oder kleine Satzgruppen)
Zweck:
Erzeugt detaillierte, kontextspezifische Textsegmente
Ermöglicht eine präzisere Indizierung und einen schnelleren Abruf.
Verbessert die Relevanz der abgerufenen Informationen in RAG-Systemen
Eigenschaften:
Segmente sind semantisch aussagekräftig.
Kann unabhängig indexiert und abgerufen werden.
Oft wird etwas Kontext beibehalten, um die Verständlichkeit für sich allein zu gewährleisten.
Vorteile:
Verbessert die Abrufgenauigkeit
Ermöglicht eine gezieltere Erweiterung in RAG-Pipelines.
2. HyDE (Hypothetisches Dokumenteneinbetten)
Eine Technik, die ein LLM verwendet, um ein hypothetisches Dokument zur Abfrageerweiterung in RAG-Systemen zu generieren.
Prozess:
Eingabeanfrage an einen LLM
LLM generiert ein hypothetisches Dokument, das die Anfrage beantwortet.
Das generierte Dokument einbetten
Verwenden Sie die Einbettung für die Vektorsuche
Hauptunterschied:
Traditionelles RAG: Gleicht Suchanfragen mit Dokumenten ab
HyDE: Ordnet Dokumente einander zu
Zweck:
Verbesserung der Abfrageleistung, insbesondere bei komplexen oder mehrdeutigen Anfragen
Erfasst einen reichhaltigeren semantischen Kontext als eine kurze Anfrage
Vorteile:
Nutzt das Wissen des LLM, um Anfragen zu erweitern
Kann potenziell die Relevanz der abgerufenen Dokumente verbessern
Herausforderungen:
Erfordert zusätzliche LLM-Inferenz, was die Latenz und die Kosten erhöht.
Die Leistung hängt von der Qualität des generierten hypothetischen Dokuments ab.
3. Rückwärtsverpackung
Eine in RAG-Systemen verwendete Technik, um Suchergebnisse neu zu ordnen, bevor sie an den LLM weitergeleitet werden.
Prozess:
Die Suchmaschine (z. B. Elasticsearch) gibt Dokumente in absteigender Reihenfolge ihrer Relevanz zurück.
Die Reihenfolge ist umgekehrt, das wichtigste Dokument steht nun an letzter Stelle.
Zweck:
Nutzt den Aktualitätsbias von LLMs aus, die sich tendenziell stärker auf die neuesten Informationen in ihrem Kontext konzentrieren.
Gewährleistet, dass im Kontextfenster des LLM die relevantesten Informationen stets aktuell sind.
Beispiel: Ursprüngliche Reihenfolge: [Relevantester, Zweitwichtigster, Drittwichtigster, ...] Umgekehrte Reihenfolge: [..., Drittwichtigster, Zweitwichtigster, Relevantester]
4. Abfrageklassifizierung
Eine Technik zur Optimierung der Effizienz von RAG-Systemen durch die Bestimmung, ob eine Anfrage RAG erfordert oder direkt vom LLM beantwortet werden kann.
Prozess:
Entwickeln Sie einen benutzerdefinierten Datensatz, der speziell auf den verwendeten LLM zugeschnitten ist.
Trainieren Sie ein spezialisiertes Klassifizierungsmodell
Nutzen Sie das Modell, um eingehende Anfragen zu kategorisieren.
Zweck:
Verbessern Sie die Systemeffizienz, indem Sie unnötige RAG-Verarbeitung vermeiden.
Direkte Anfragen an den am besten geeigneten Antwortmechanismus
Anforderungen:
LLM-spezifischer Datensatz und Modell
Kontinuierliche Optimierung zur Aufrechterhaltung der Genauigkeit
Vorteile:
Reduziert den Rechenaufwand für einfache Abfragen
Verbessert möglicherweise die Antwortzeit für Nicht-RAG-Anfragen
5. Zusammenfassung
Eine Technik zur Komprimierung abgerufener Dokumente in RAG-Systemen.
Prozess:
Relevante Dokumente abrufen
Erstellen Sie prägnante Zusammenfassungen jedes Dokuments.
Verwenden Sie in der RAG-Pipeline Zusammenfassungen anstelle vollständiger Dokumente.
Zweck:
Verbessern Sie die RAG-Performance, indem Sie sich auf wesentliche Informationen konzentrieren.
Rauschen und Störungen durch weniger relevante Inhalte reduzieren
Vorteile:
Verbessert möglicherweise die Relevanz der LLM-Antworten
Ermöglicht die Einbeziehung weiterer Dokumente innerhalb der Kontextgrenzen.
Herausforderungen:
Gefahr, wichtige Details in der Zusammenfassung zu verlieren
Zusätzlicher Rechenaufwand für die Zusammenfassungserstellung
6. Einbeziehung von Metadaten
Eine Technik zur Anreicherung von Dokumenten mit zusätzlichen Kontextinformationen.
Metadatentypen:
Schlüsselwörter
Titel
Termine
Angaben zur Autorschaft
Klappentexte
Zweck:
Erweitern Sie die dem Ampelsystem zur Verfügung stehenden Kontextinformationen.
LLM-Studierenden ein klareres Verständnis des Dokumentinhalts und dessen Relevanz vermitteln
Vorteile:
Verbessert möglicherweise die Abrufgenauigkeit
Verbessert die Fähigkeit des LLM-Programms, den Nutzen von Dokumenten zu beurteilen
Durchführung:
Kann während der Dokumentenvorverarbeitung erfolgen.
Möglicherweise sind zusätzliche Datenextraktions- oder -generierungsschritte erforderlich.
7. Zusammengesetzte Mehrfeld-Einbettungen
Eine fortschrittliche Einbettungstechnik für RAG-Systeme, die separate Einbettungen für verschiedene Dokumentkomponenten erstellt.
Prozess:
Relevante Felder identifizieren (z. B. Titel, Schlüsselwörter, Klappentext, Hauptinhalt)
Erzeugen Sie separate Einbettungen für jedes Feld.
Diese Einbettungen können kombiniert oder gespeichert werden, um sie beim Abruf zu verwenden.
Unterschied zum Standardverfahren:
Traditionell: Einmaliges Einbetten für das gesamte Dokument
Komposit: Mehrere Einbettungen für verschiedene Dokumentaspekte
Zweck:
Erstellen Sie differenziertere und kontextbezogene Dokumentendarstellungen
Informationen aus einer größeren Vielfalt von Quellen innerhalb eines Dokuments erfassen
Vorteile:
Verbessert möglicherweise die Leistung bei mehrdeutigen oder vielschichtigen Anfragen.
Ermöglicht eine flexiblere Gewichtung verschiedener Dokumentaspekte bei der Recherche.
Herausforderungen:
Erhöhte Komplexität bei der Einbettung von Speicher- und Abrufprozessen
Möglicherweise sind komplexere Matching-Algorithmen erforderlich.
8. Abfrageanreicherung
Eine Technik zur Erweiterung der ursprünglichen Suchanfrage um verwandte Begriffe, um die Suchabdeckung zu verbessern.
Prozess:
Analysieren Sie die ursprüngliche Anfrage
Generieren Sie Synonyme und semantisch verwandte Phrasen
Erweitern Sie die Abfrage um diese zusätzlichen Begriffe
Zweck:
Erhöhen Sie den Bereich potenzieller Übereinstimmungen im Dokumentenkorpus.
Verbesserung der Abfrageleistung für Anfragen mit spezifischer oder technischer Sprache
Vorteile:
Ruft möglicherweise relevante Dokumente ab, die nicht exakt den ursprünglichen Suchbegriffen entsprechen.
Kann dazu beitragen, Vokabeldiskrepanz zwischen Anfragen und Dokumenten zu überwinden.
Herausforderungen:
Gefahr der Abfrageabweichung bei unsachgemäßer Implementierung
Kann den Rechenaufwand im Abrufprozess erhöhen




