Erstellen Sie ein KI-gestütztes multimodales RAG-System mit Docling und Granite

Autoren

BJ Hargrave

Open Source Developer, STSM

Erika Russi

Data Scientist

IBM

In diesem Tutorial verwenden Sie IBMs Docling und die Open-Source-Software IBM Granite Vision, textbasierte Einbettungen und generative KI- Modelle, um ein RAG-System zu erstellen. Diese Modelle sind über verschiedene Open Source Frameworks verfügbar. In diesem Tutorial verwenden wir Replicate, um die IBM Granite Vision und generative KI-Modelle zu verbinden, sowie HuggingFace, um eine Verbindung zum Einbetten-Modell herzustellen.

Multimodale Retrieval-Augmented Generation

Retrieval-Augmented Generation (RAG) ist eine Technik, die mit großen Sprachmodellen (LLMs) verwendet wird, um das Modell mit einer Wissensdatenbank außerhalb der Daten zu verbinden, auf denen das LLM trainiert wurde, ohne Feinabstimmung durchführen zu müssen. Das traditionelle RAG-System ist auf textbasierte Anwendungsfälle wie Textzusammenfassung und Chatbot beschränkt.

Multimodal RAG kann multimodale LLMs (MLLM) verwenden, um Informationen aus verschiedenen Datentypen zu verarbeiten, die in die in RAG verwendete externe Wissensdatenbank aufgenommen werden sollen. Multimodale Daten können Text, Bilder, Audio, Video oder andere Formen enthalten. Beliebte multimodale LLMs sind Googles Gemini, Metas Llama 3.2 sowie OpenAIs GPT-4 und GPT-4o.

Für dieses Rezept verwenden Sie ein IBM Granite-Modell, das verschiedene Modalitäten verarbeiten kann. Sie werden ein KI-System erstellen, das Benutzeranfragen in Echtzeit aus unstrukturierten Daten in einem PDF-Dokument beantwortet.

Tutorial-Überblick

Willkommen bei diesem Granite-Tutorial. In diesem Tutorial erfahren Sie, wie Sie die Kraft der fortschrittlichen Tools nutzen können, um eine KI-gestützte multimodale RAG-Pipeline aufzubauen. Dieses Tutorial führt Sie durch die folgenden Prozesse:

  • Dokumentenvorverarbeitung: Lernen Sie mithilfe von Docling, wie Sie Dokumente aus verschiedenen Quellen verarbeiten, analysieren und in verwendbare Formate umwandeln und in der Vektordatenbank speichern. Sie werden ein Granite-MLLM verwenden, um Bildbeschreibungen für Bilder in den Dokumenten zu generieren.
  • RAG: Erfahren Sie, wie Sie LLMs wie Granite mit externen Wissensdatenbanken verbinden, um die Antworten auf Anfragen zu verbessern und wertvolle Erkenntnisse zu gewinnen.
  • LangChain für Workflow-Integration: Entdecken Sie, wie Sie LangChain nutzen können, um Dokumentenverarbeitungs- und -abruf-Workflows zu optimieren und zu orchestrieren, wodurch eine nahtlose Interaktion zwischen verschiedenen Komponenten des Systems ermöglicht wird.

Dieses Tutorial verwendet drei hochmoderne Technologien:

  1. Docling: Ein Open-Source-Toolkit zum Parsen und Konvertieren von Dokumenten.
  2. Granite: Ein hochmodernes LLM, das robuste Funktionen für natürliche Sprache und ein Bildverarbeitungs-Sprachmodell zur Umwandlung von Bildern in Text bietet.
  3. LangChain: Ein leistungsstarkes Framework, das Anwendungen mit Sprachmodellen erstellt und entwickelt wurde, um komplexe Workflows zu vereinfachen und externe Tools nahtlos zu integrieren.

Am Ende dieses Tutorials werden Sie Folgendes erreicht haben:

  • Erwerben Sie Kompetenz in den Bereichen Dokumentenvorverarbeitung, Chunking und Bildverständnis.
  • Integrieren Sie Vektordatenbanken, um die Funktionen zu verbessern.
  • Verwenden Sie RAG, um effiziente und präzise Datenabrufe für reale Anwendungen durchzuführen.

Dieses Tutorial richtet sich an KI-Entwickler, Forscher und Enthusiasten, die ihr Wissen über Dokumentenmanagement und fortgeschrittene Techniken der Verarbeitung natürlicher Sprache (NLP) erweitern möchten. Das Tutorial ist auch im GitHub-Repository „Granite Snack Cookbook“ der IBM Granite Community in Form eines Jupyter Notebooks zu finden.

Voraussetzungen

  • Vertrautheit mit Python-Programmierung.
  • Grundkenntnisse in LLMs, NLP-Konzepten und Computer Vision.

Schritte

Schritt 1: Installieren von Abhängigkeiten

! echo "::group::Install Dependencies"
%pip install uv
! uv pip install git+https://github.com/ibm-granite-community/utils.git \
    transformers \
    pillow \
    langchain_classic \
    langchain_core \
    langchain_huggingface sentence_transformers \
    langchain_milvus 'pymilvus[milvus_lite]' \
    docling \
    'langchain_replicate @ git+https://github.com/ibm-granite-community/langchain-replicate.git'
! echo "::endgroup::"

Schritt 2: Auswahl der KI-Modelle

Protokollierung

Um einige Protokollierungsinformationen zu sehen, können wir die Protokollstufe INFO konfigurieren.

HINWEIS: Es ist in Ordnung, die Ausführung dieser Zelle zu überspringen.

import logging

logging.basicConfig(level=logging.INFO)


Laden Sie die Granite-Modelle

Geben Sie das Einbetten-Modell an, das für die Generierung von Texteinbettungsvektoren verwendet werden soll. Hier verwenden wir eines der Granite Embeddings-Modelle

Um ein anderes Einbetten-Modell zu verwenden, ersetzen Sie diese Codezelle durch eines aus diesem Embeddings-Modell-Rezept.

from langchain_huggingface import HuggingFaceEmbeddings
from transformers import AutoTokenizer

embeddings_model_path = “ibm-granite/granite-embedding-30m-english”
embeddings_model = HuggingFaceEmbeddings(
    model_name=embeddings_model_path,
)
embeddings_tokenizer = AutoTokenizer.from_pretrained(embeddings_model_path)

 

Geben Sie das MLLM an, das für das Bildverständnis verwendet werden soll. Wir werden das Granite Vision-Modell verwenden. 

from ibm_granite_community.notebook_utils import get_env_var
from langchain_community.llms import Replicate
from transformers import AutoProcessor

vision_model_path = “ibm-granite/granite-vision-3.2-2b”
vision_model = Replicate(
    model=vision_model_path,
    replicate_api_token=get_env_var(“REPLICATE_API_TOKEN”),
    model_kwargs={
        “max_tokens”: embeddings_tokenizer.max_len_single_sentence, # Set the maximum number of tokens to generate as output.
        “min_tokens”: 100, # Set the minimum number of tokens to generate as output.
    },
)
vision_processor = AutoProcessor.from_pretrained(vision_model_path)

 

Geben Sie das Sprachmodell an, das für die RAG-Generierung verwendet werden soll.  Hier verwenden wir den Replicate LangChain-Client, um uns mit einem Granite-Modell von der ibm-granite-Org auf Replicate zu verbinden.

Informationen zur Einrichtung von Replicate finden Sie unter Erste Schritte mit Replicate. Um eine Verbindung zu einem Modell eines anderen Anbieters als Replicate herzustellen, ersetzen Sie diese Codezelle durch eine aus dem LLM-Komponentenrezept.

Um eine Verbindung zu einem Modell eines anderen Anbieters als Replicate herzustellen, ersetzen Sie diese Codezelle durch eine aus dem LLM-Komponentenrezept.

from langchain_replicate import ChatReplicate

model_path = "ibm-granite/granite-4.0-h-small"
model = ChatReplicate(
    model=model_path,
    replicate_api_token=get_env_var("REPLICATE_API_TOKEN"),
    model_kwargs={
        "max_tokens": 1000, # Set the maximum number of tokens to generate as output.
        "min_tokens": 100, # Set the minimum number of tokens to generate as output.
    },
)

Schritt 3: Vorbereitung der Dokumente für die Vektor-Datenbank

In diesem Beispiel verwenden wir Docling aus einer Reihe von Quelldokumenten, um die Dokumente in Text und Bilder umzuwandeln. Der Text wird dann in Abschnitte aufgeteilt. Die Bilder werden vom MLLM verarbeitet, um Bildzusammenfassungen zu erstellen.

Verwenden Sie Docling, um die Dokumente herunterzuladen und in Text und Bilder umzuwandeln

Docling lädt die PDF-Dokumente herunter und verarbeitet sie, damit wir den Text und die Bilder der Dokumente erhalten können. Die PDF-Datei enthält verschiedene Datentypen, darunter Text, Tabellen, Grafiken und Bilder.

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions

pdf_pipeline_options = PdfPipelineOptions(
    do_ocr=False,
    generate_picture_images=True,
)
format_options = {
    InputFormat.PDF: PdfFormatOption(pipeline_options=pdf_pipeline_options),
}
converter = DocumentConverter(format_options=format_options)

sources = [
    “https://midwestfoodbank.org/images/AR_2020_WEB2.pdf”,
]
conversions = { source: converter.convert(source=source).document for source in sources }

 

Nach der Verarbeitung der Dokumente werden die Textelemente in den Dokumenten weiterverarbeitet. Wir teilen sie in geeignete Größen für das von uns verwendete Einbettungsmodell auf. Eine Liste der LangChain-Dokumente wird aus den Textabschnitten erstellt.

from docling_core.transforms.chunker.hybrid_chunker import HybridChunker
from docling_core.types.doc.document import TableItem
from langchain_core.documents import Document

doc_id = 0
texts: list[Document] = []
for source, docling_document in conversions.items():
    for chunk in HybridChunker(tokenizer=embeddings_tokenizer).chunk(docling_document):
        items = chunk.meta.doc_items
        if len(items) == 1 and isinstance(items[0], TableItem):
            continue # we will process tables later
        refs = “ “.join(map(lambda item: item.get_ref().cref, items))
        print(refs)
        text = chunk.text
        document = Document(
            page_content=text,
            metadata={
                “doc_id”: (doc_id:=doc_id+1),
                “source”: source,
                “ref”: refs,
            },
        )
        texts.append(document)

print(f”{len(texts)} text document chunks created”)

 

Als Nächstes verarbeiten wir alle Tabellen in den Dokumenten. Wir konvertieren die Tabellendaten in das Markdown-Format, um sie an das Sprachmodell zu übergeben. Eine Liste der LangChain-Dokumente wird aus den Markdown-Darstellungen der Tabelle erstellt.

from docling_core.types.doc.labels import DocItemLabel

doc_id = len(texts)
tables: list[Document] = []
for source, docling_document in conversions.items():
    for table in docling_document.tables:
        if table.label in [DocItemLabel.TABLE]:
            ref = table.get_ref().cref
            print(ref)
            text = table.export_to_markdown()
            document = Document(
                page_content=text,
                metadata={
                    “doc_id”: (doc_id:=doc_id+1),
                    “source”: source,
                    “ref”: ref
                },
            )
            tables.append(document)


print(f”{len(tables)} table documents created”)

 

Abschließend verarbeiten wir alle Bilder in den Dokumenten. Hier verwenden wir das Vision Language Model, um den Inhalt eines Bildes zu verstehen. In diesem Beispiel sind wir an allen Textinformationen im Bild interessiert. Sie könnten mit verschiedenen Prompt-Texten experimentieren, um zu sehen, wie sich die Ergebnisse verbessern lassen.

HINWEIS: Die Verarbeitung der Bilder kann sehr lange dauern, abhängig von der Anzahl der Bilder und dem Dienst, der das Bildsprachmodell ausführt.

import base64
import io
import PIL.Image
import PIL.ImageOps
from IPython.display import display

def encode_image(image: PIL.Image.Image, format: str = “png”) -> str:
    image = PIL.ImageOps.exif_transpose(image) or image
    image = image.convert(“RGB”)

    buffer = io.BytesIO()
    image.save(buffer, format)
    encoding = base64.b64encode(buffer.getvalue()).decode(“utf-8”)
    uri = f”data:image/{format};base64,{encoding}”
    return uri

# Feel free to experiment with this prompt
image_prompt = “If the image contains text, explain the text in the image.”
conversation = [
    {
        “role”: “user”,
        “content”: [
            {“type”: “image”},
            {“type”: “text”, “text”: image_prompt},
        ],        
    },
]
vision_prompt = vision_processor.apply_chat_template(
    conversation=conversation,
    add_generation_prompt=True,
)
pictures: list[Document] = []
doc_id = len(texts) + len(tables)
for source, docling_document in conversions.items():
    for picture in docling_document.pictures:
        ref = picture.get_ref().cref
        print(ref)
        image = picture.get_image(docling_document)
        if image:
            text = vision_model.invoke(vision_prompt, image=encode_image(image))
            document = Document(
                page_content=text,
                metadata={
                    “doc_id”: (doc_id:=doc_id+1),
                    “source”: source,
                    “ref”: ref,
                },
            )
            pictures.append(document)

print(f”{len(pictures)} image descriptions created”)

 

Dann können wir die LangChain-Dokumente anzeigen, die aus den Eingabedokumenten erstellt wurden.

import itertools
from docling_core.types.doc.document import RefItem

# Print all created documents
for document in itertools.chain(texts, tables):
    print(f”Document ID: {document.metadata[‘doc_id’]}”)
    print(f”Source: {document.metadata[‘source’]}”)
    print(f”Content:\n{document.page_content}”)
    print(“=” * 80) # Separator for clarity

for document in pictures:
    print(f”Document ID: {document.metadata[‘doc_id’]}”)
    source = document.metadata[‘source’]
    print(f”Source: {source}”)
    print(f”Content:\n{document.page_content}”)
    docling_document = conversions[source]
    ref = document.metadata[‘ref’]
    picture = RefItem(cref=ref).resolve(docling_document)
    image = picture.get_image(docling_document)
    print(“Image:”)
    display(image)
    print(“=” * 80) # Separator for clarity

Füllen der Vektordatenbank

Mithilfe des Einbettungsmodells laden wir die Dokumente aus den Textblöcken und die generierten Bildunterschriften in eine Vektordatenbank. Durch die Erstellung dieser Vektordatenbank können wir ganz einfach eine semantische Ähnlichkeitssuche über unsere Dokumente durchführen.

HINWEIS: Die Befüllung der Vektordatenbank kann je nach Einbettungsmodell und Dienst einige Zeit in Anspruch nehmen.

Auswahl Ihrer Vektordatenbank

Geben Sie die Datenbank an, die zum Speichern und Abrufen von Einbetten-Vektoren verwendet werden soll.

Um eine andere Vektordatenbank als Milvus zu verbinden, ersetzen Sie diese Codezelle durch eine aus diesem Vektorspeicher-Rezept.

import tempfile
from langchain_core.vectorstores import VectorStore
from langchain_milvus import Milvus

db_file = tempfile.NamedTemporaryFile(prefix=”vectorstore_”, suffix=”.db”, delete=False).name
print(f”The vector database will be saved to {db_file}”)

vector_db: VectorStore = Milvus(
    embedding_function=embeddings_model,
    connection_args={“uri”: db_file},
    auto_id=True,
    enable_dynamic_field=True,
    index_params={“index_type”: “AUTOINDEX”},
)

 

Wir fügen nun alle LangChain-Dokumente für die Text-, Tabellen- und Bildbeschreibungen zur Vektordatenbank hinzu.

import itertools
documents = list(itertools.chain(texts, tables, pictures))
ids = vector_db.add_documents(documents)
print(f”{len(ids)} documents added to the vector database”)

Schritt 4: RAG mit Granite

Nachdem wir unsere Dokumente erfolgreich konvertiert und vektorisiert haben, können wir unsere RAG-Pipeline einrichten.

Relevante Chunks abrufen

Hier testen wir die Vektordatenbank, indem wir nach Chunks mit relevanten Informationen für unsere Abfrage im Vektorraum suchen. Wir zeigen die Dokumente an, die mit der abgerufenen Bildbeschreibung verknüpft sind.

Probieren Sie gerne verschiedene Abfragen aus.

query = "How much was spent on food distribution relative to the amount of food distributed?"
for doc in vector_db.as_retriever().invoke(query):
    print(doc)
    print("=" * 80) # Separator for clarity

 

Das zurückgegebene Dokument sollte die Anfrage beantworten. Lassen Sie uns weitermachen und unsere RAG-Pipeline bauen.


Erstellen Sie die RAG-Pipeline für Granite

Zuerst erstellen wir die Eingabeaufforderungen, damit Granite die RAG-Abfrage ausführt. Wir verwenden die Granite-Chat-Vorlage und liefern die Platzhalterwerte, die die LangChain RAG-Pipeline ersetzen wird.

Als Nächstes bauen wir die RAG-Pipeline mithilfe der zuvor erstellten Granite-Prompt-Vorlagen auf.

from ibm_granite_community.langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_classic.chains.retrieval import create_retrieval_chain
from langchain_core.prompts import ChatPromptTemplate

# Create a Granite prompt for question-answering with the retrieved context
prompt_template = ChatPromptTemplate.from_template("{input}")

# Assemble the retrieval-augmented generation chain
combine_docs_chain = create_stuff_documents_chain(
    llm=model,
    prompt=prompt_template,
)
rag_chain = create_retrieval_chain(
    retriever=vector_db.as_retriever(),
    combine_docs_chain=combine_docs_chain,
)

Generieren Sie eine abrufgestützte Antwort auf eine Frage

Die Pipeline verwendet die Abfrage, um Dokumente aus der Vektordatenbank zu finden und sie als Kontext für die Abfrage zu verwenden.

from ibm_granite_community.notebook_utils import wrap_text

output = rag_chain.invoke({"input": query})

print(wrap_text(output['answer']))

Prima! Wir haben eine KI-Anwendung entwickelt, die das Wissen aus den Texten und Bildern der Quelldokumente erfolgreich nutzen kann.

Nächste Schritte

  • Erkunden Sie fortschrittliche RAG-Workflows für andere Branchen.
  • Experimentieren Sie mit anderen Dokumenttypen und größeren Datensätzen.
  • Optimieren Sie das Prompt Engineering für bessere Granite-Antworten.
Weiterführende Lösungen
IBM watsonx.ai

Trainieren, validieren, optimieren und implementieren Sie generative KI, Foundation Models und maschinelle Lernfunktionen mit IBM watsonx.ai, einem Studio der nächsten Generation für AI Builder in Unternehmen. Erstellen Sie KI-Anwendungen mit einem Bruchteil der Zeit und Daten.

Entdecken sie watsonx.ai
Lösungen im Bereich künstlicher Intelligenz

Setzen Sie KI in Ihrem Unternehmen ein – mit branchenführendem Fachwissen im Bereich KI und dem Lösungsportfolio von IBM an Ihrer Seite.

Erkunden Sie KI-Lösungen
KI-Services

Erfinden Sie kritische Workflows und Abläufe neu, indem Sie KI einsetzen, um Erfahrungen, Entscheidungsfindung in Echtzeit und den geschäftlichen Nutzen zu maximieren.

KI-Services entdecken
Machen Sie den nächsten Schritt

Profitieren Sie von einem einheitlichen Zugriff auf Funktionen, die den gesamten Lebenszyklus der KI-Entwicklung abdecken. Erstellen Sie leistungsstarke KI-Lösungen mit benutzerfreundlichen Oberflächen, Workflows und Zugriff auf branchenübliche APIs und SDKs.

  1. watsonx.ai erkunden
  2. Buchen Sie eine Live-Demo