AI Personal Learning
und praktische Anleitung

ChatOllama Notes | Implementierung von Advanced RAG for Productivity und Redis-basierten Dokumentendatenbanken

 

ChatOllama Es ist ein quelloffener Chatbot, der auf LLMs basiert. Für eine detaillierte Beschreibung von ChatOllama klicken Sie auf den untenstehenden Link.


 

ChatOllama | Ollama-basierte 100% Lokale RAG-Anwendung

 

ChatOllama Das ursprüngliche Ziel war es, den Nutzern eine native RAG-Anwendung 100% zur Verfügung zu stellen.

 

Im Laufe der Zeit haben sich immer mehr Nutzer mit wertvollen Anforderungen gemeldet. Jetzt unterstützt "ChatOllama" mehrere Sprachmodelle, darunter:

 

Mit ChatOllama können Benutzer:

  • Verwalten von Ollama-Modellen (ziehen/löschen)
  • Verwalten der Wissensbasis (Erstellen/Löschen)
  • Freier Dialog mit LLMs
  • Verwaltung der persönlichen Wissensbasis
  • Kommunikation mit LLMs durch eine persönliche Wissensdatenbank

 

In diesem Artikel werde ich mich damit beschäftigen, wie man fortgeschrittene RAG für die Produktion erreichen kann. Ich habe die grundlegenden und fortgeschrittenen Techniken von RAG erlernt, aber es gibt immer noch eine Menge Dinge, um die man sich kümmern muss, um RAG in die Produktion zu bringen. Ich werde die Arbeit, die in ChatOllama geleistet wurde, und die Vorbereitungen, die getroffen wurden, um RAG in die Produktion zu bringen, teilen.

 

ChatOllama Notes | Implementierung von erweiterter RAG-Produktivität und Redis-basierter Dokumentendatenbank-1

ChatOllama | RAG Constructed Runs

 

In der ChatOllama-Plattform verwenden wir dieLangChain.jsum den RAG-Prozess zu manipulieren. In der Wissensdatenbank von ChatOllama werden über die ursprüngliche RAG hinausgehende übergeordnete Dokumentenabfragen verwendet. Lassen Sie uns einen tiefen Einblick in die Architektur und die Details der Implementierung nehmen. Wir hoffen, Sie finden es erhellend.

 

 

Parent Document Retriever Dienstprogramm

 

Damit der Parent Document Retriever in einem realen Produkt funktionieren kann, müssen wir seine Grundprinzipien verstehen und die richtigen Speicherelemente für die Produktion auswählen.

 

Grundprinzipien von Parent Document Retriever

Bei der Aufteilung von Dokumenten für Abrufzwecke treten häufig widersprüchliche Anforderungen auf:

  • Sie möchten vielleicht, dass die Dokumente so klein wie möglich sind, damit der eingebettete Inhalt die Bedeutung möglichst genau wiedergeben kann. Wenn der Inhalt zu lang ist, können die eingebetteten Informationen ihre Bedeutung verlieren.
  • Außerdem sollten die Dokumente lang genug sein, um sicherzustellen, dass jeder Absatz eine vollständige Volltextumgebung hat.

 

Angetrieben von LangChain.jsParentDocumentRetrieverDieses Gleichgewicht wird durch die Aufteilung des Dokuments in Teile (Chunks) erreicht. Bei jedem Abruf werden die Chunks extrahiert und dann das jedem Chunk entsprechende übergeordnete Dokument gesucht, wodurch eine größere Auswahl an Dokumenten zurückgegeben wird. Im Allgemeinen ist das übergeordnete Dokument mit den Chunks über die Dokument-ID verknüpft. Wie das funktioniert, werden wir später noch genauer erklären.

Beachten Sie, dass hier dieübergeordnetes DokumentBezieht sich auf kleine Teile von Quelldokumenten.

 

bauen

Werfen wir einen Blick auf das allgemeine Architekturdiagramm des übergeordneten Dokumentenabrufs.

Jedes Teilstück wird verarbeitet und in Vektordaten umgewandelt, die dann in der Vektordatenbank gespeichert werden. Das Abrufen dieser Chunks erfolgt wie bei der ursprünglichen RAG-Einrichtung.

Das übergeordnete Dokument (Block-1, Block-2, ...... block-m) wird in kleinere Teile aufgeteilt. Es ist erwähnenswert, dass hier zwei verschiedene Textsegmentierungsmethoden verwendet werden: eine größere für übergeordnete Dokumente und eine kleinere für kleinere Blöcke. Jedem übergeordneten Dokument wird eine Dokument-ID zugewiesen, und diese ID wird dann als Metadaten in dem entsprechenden Chunk gespeichert. Dadurch wird sichergestellt, dass jeder Chunk sein entsprechendes übergeordnetes Dokument anhand der in den Metadaten gespeicherten Dokument-ID finden kann.

Der Prozess der Suche nach übergeordneten Dokumenten ist nicht derselbe wie hier. Anstelle einer Ähnlichkeitssuche wird eine Dokument-ID angegeben, um das entsprechende übergeordnete Dokument zu finden. In diesem Fall ist ein Key-Value-Speichersystem für die Aufgabe ausreichend.

 

ChatOllama Notes | Implementierung von erweiterter RAG-Produktivität und Redis-basierter Dokumentendatenbank-2

Parent Document Retriever

 

Bereich Lagerung

Es gibt zwei Arten von Daten, die gespeichert werden müssen:

  • Kleinräumige Vektordaten
  • String-Daten des übergeordneten Dokuments, die dessen Dokument-ID enthalten

 

Alle wichtigen Vektordatenbanken sind verfügbar. Von "ChatOllama" habe ich Chroma gewählt.

Für die Daten der übergeordneten Dokumente habe ich mich für Redis entschieden, das beliebteste und am besten skalierbare System zur Speicherung von Schlüsselwerten.

 

Was in LangChain.js noch fehlt

LangChain.js bietet eine Reihe von Möglichkeiten zur Integration von Byte- und Dokumentenspeicherung:

 

Lagerung | 🦜️🔗 Langchain

Die Speicherung von Daten in Form von Schlüssel-Wert-Paaren ist schnell und effizient und stellt ein leistungsstarkes Werkzeug für LLM-Anwendungen dar. Das Basis-Repository...

js.langchain.com

 

Unterstützung für "IORedis":

 

IORedis | 🦜️🔗 Langchain

Dieses Beispiel zeigt, wie die RedisByteStore-Basis-Repository-Integration verwendet wird, um Speicher für den Chatverlauf einzurichten.

js.langchain.com

 

Der fehlende Teil des RedisByteStore ist dieSammlungMechanismen.

 

Bei der Verarbeitung von Dokumenten aus verschiedenen Wissensbasen wird jede Wissensbasis in einerSammlungDie Sammlungen werden in Form von Sammlungen organisiert, und die Dokumente in derselben Bibliothek werden in Vektordaten umgewandelt und in einer Vektordatenbank wie Chroma in einer derSammlungIn der Versammlung.

 

Angenommen, wir wollen eine Wissensbasis löschen. Wir können natürlich eine Sammlung in der Chroma-Datenbank löschen. Aber wie bereinigen wir den Dokumentenspeicher entsprechend den Dimensionen der Wissensdatenbank? Da Komponenten wie RedisByteStore keine `collection`-Funktionalität unterstützen, musste ich sie selbst implementieren.

 

ChatOllama RedisDocStore

In Redis gibt es keine eingebaute Funktion namens "collection". Entwickler implementieren normalerweise die Funktion "Sammlung" durch die Verwendung von Präfixschlüsseln. Die folgende Abbildung zeigt, wie Präfixe verwendet werden können, um verschiedene Sammlungen zu identifizieren:

 

ChatOllama Notes | Implementierung von erweiterter RAG-Produktivität und Redis-basierter Dokumentendatenbank-3

Redis-Schlüsseldarstellungen mit Präfixen

 

Schauen wir uns nun an, wie es umgesetzt wurde, umRedisder Dokumentenablagefunktion für den Hintergrund.

 

Schlüsselbotschaft:

  • Jeder `RedisDocstore` wird mit einem `Namespace` Parameter initialisiert. (Die Benennung von Namespaces und Sammlungen ist im Moment nicht standardisiert).
  • Die Schlüssel werden zunächst für Get- und Set-Operationen im "Namensraum" verarbeitet.

 

import { Document } from "@langchain/core/documents" ;
import { BaseStoreInterface } from "@langchain/core/stores" ;
importiere { Redis } von "ioredis".
import { createRedisClient } from "@/server/store/redis" ;

export class RedisDocstore implements BaseStoreInterface
{
_Namensraum: Zeichenkette;
Klient: Redis.

constructor(namespace: string) {
this._namespace = namespace;
this._client = createRedisClient();
}

serializeDocument(doc: Document): string {
return JSON.stringify(doc);
}

deserializeDocument(jsonString: string): Document {
const obj = JSON.parse(jsonString);
return new Document(obj);
}

getNamespacedKey(key: string): string {
return `${this._namespace}:${key}`;
}

getKeys(): Promise {
return new Promise((resolve, reject) => {
const stream = this._client.scanStream({ match: this._namespace + '*' });

const keys: string[] = [];
stream.on('data', (resultKeys) => {
keys.push(... .resultKeys);
});

stream.on('end', () => {
resolve(Schlüssel).
});

stream.on('error', (err) => {
zurückweisen(err);
});
});
}

addText(key: string, value: string) {
this._client.set(this.getNamespacedKey(key), value);
}

async search(search: string): Promise {
const result = await this._client.get(this.getNamespacedKey(search));
if (!result) {
throw new Error(`ID ${Suche} nicht gefunden.`);
} sonst {
const document = this.deserializeDocument(result);
Dokument zurückgeben;
}
}

/**
* :: Fügt dem Speicher neue Dokumente hinzu.
* @param texts Ein Objekt, bei dem die Schlüssel Dokument-IDs und die Werte die Dokumente selbst sind.
* @returns Void
*/
async add(texts: Record): Promise {
for (const [Schlüssel, Wert] of Object.entries(texts)) {
console.log(`Hinzufügen von ${Schlüssel} zum Speicher: ${this.serializeDocument(value)}`);
}

const keys = [... .await this.getKeys()];
const overlapping = Object.keys(texts).filter((x) => keys.includes(x));

if (overlapping.length > 0) {
throw new Error(`Versucht, bereits vorhandene IDs hinzuzufügen: ${überlappend}`);
}

for (const [Schlüssel, Wert] of Object.entries(texts)) {
this.addText(key, this.serialiseDocument(value)); this.
}
}

async mget(keys: string[]): Promise {
return Promise.all(keys.map((key) => {
const document = this.search(key);
Dokument zurückgeben;
}));
}

async mset(keyValuePairs: [string, Document][]): Promise {
await Promise.all(
keyValuePairs.map(([Schlüssel, Wert]) => this.add({ [Schlüssel]: Wert }))
);
}

async mdelete(_keys: string[]): Promise {
throw new Error("Nicht implementiert.");
}

// eslint-disable-next-line require-yield
async *yieldKeys(_prefix?: string): AsyncGenerator {
throw new Error("Nicht implementiert");
}

async deleteAll(): Promise {
return new Promise((resolve, reject) => {
lassen Sie Cursor = '0';

const scanCallback = (err, result) => {
if (err) {
zurückweisen(err);
Rückkehr;
}

const [nextCursor, keys] = result;

// Schlüssel löschen, die dem Präfix entsprechen
keys.forEach((key) => {
this._client.del(key);
});

// Wenn der Cursor '0' ist, haben wir alle Tasten durchlaufen
if (nextCursor === '0') {
resolve().
} sonst {
// Fortsetzen des Scannens mit dem nächsten Cursor
this._client.scan(nextCursor, 'MATCH', `${this._namespace}:*`, scanCallback);
}
};

// Start des ersten SCAN-Vorgangs
this._client.scan(cursor, 'MATCH', `${this._namespace}:*`, scanCallback);
});
}
}

 

Sie können diese Komponente nahtlos mit ParentDocumentRetriever verwenden:

retriever = new ParentDocumentRetriever({
vectorstore: chromaClient.
docstore: new RedisDocstore(collectionName),
parentSplitter: new RecursiveCharacterTextSplitter({
chunkOverlap: 200,
chunkSize: 1000,
}),
childSplitter: new RecursiveCharacterTextSplitter({
chunkOverlap: 50,
chunkSize: 200,
}),
kindK: 20.
parentK: 5.
});

Wir haben jetzt eine skalierbare Speicherlösung für fortgeschrittene RAG, Parent Document Retriever, zusammen mit `Chroma` und `Redis`.

AI Leichtes Lernen

Der Leitfaden für Laien zum Einstieg in die KI

Hilft Ihnen, die Nutzung von KI-Tools kostengünstig und von Null an zu erlernen.KI ist, wie Bürosoftware, eine wesentliche Fähigkeit für jeden. Die Beherrschung von KI verschafft Ihnen einen Vorteil bei der Stellensuche und die Hälfte des Aufwands bei Ihrer zukünftigen Arbeit und Ihrem Studium.

Details ansehen>
Darf nicht ohne Genehmigung vervielfältigt werden:Chef-KI-Austauschkreis " ChatOllama Notes | Implementierung von Advanced RAG for Productivity und Redis-basierten Dokumentendatenbanken

Chef-KI-Austauschkreis

Der Chief AI Sharing Circle konzentriert sich auf das KI-Lernen und bietet umfassende KI-Lerninhalte, KI-Tools und praktische Anleitungen. Unser Ziel ist es, den Nutzern dabei zu helfen, die KI-Technologie zu beherrschen und gemeinsam das unbegrenzte Potenzial der KI durch hochwertige Inhalte und den Austausch praktischer Erfahrungen zu erkunden. Egal, ob Sie ein KI-Anfänger oder ein erfahrener Experte sind, dies ist der ideale Ort für Sie, um Wissen zu erwerben, Ihre Fähigkeiten zu verbessern und Innovationen zu verwirklichen.

Kontaktieren Sie uns
de_DE_formalDeutsch (Sie)