Cree un sistema RAG multimodal impulsado por IA con Docling y Granite

Autores

BJ Hargrave

Open Source Developer, STSM

Erika Russi

Data Scientist

IBM

En este tutorial, utilizará Docling de IBM e IBM Granite Vision de código abierto, modelos de incorporación basados en texto y modelos de IA generativa para crear un sistema RAG. Estos modelos están disponibles a través de diversos entornos de código abierto. En este tutorial, emplearemos Replicate para conectarnos a los modelos de visión e IA generativa de IBM Granite y HuggingFace para conectarnos al modelo de incorporación.

Generación aumentada por recuperación multimodal

La generación aumentada por recuperación (RAG, por sus siglas en inglés) es una técnica utilizada con modelos de lenguaje grandes (LLM) para conectar el modelo con una base de conocimientos de información fuera de los datos con los que se ha entrenado el LLM sin tener que realizar ajustes finos. El RAG tradicional se limita a casos de uso basados en texto, como el resumen de texto y los chatbots.

RAG multimodal puede usar LLM multimodales (MLLM) para procesar información de múltiples tipos de datos que se incluirán como parte de la base de conocimientos externa utilizada en RAG. Los datos multimodales pueden incluir texto, imágenes, audio, video u otros formatos. Los LLM multimodales populares incluyen Gemini de Google, Llama 3.2 de Meta y GPT-4 y GPT-4o de OpenAI.

Para esta receta, utilizará un modelo IBM Granite capaz de procesar diferentes modalidades. Creará un sistema de IA para responder en tiempo real a las consultas de los usuarios a partir de datos no estructurados en un PDF.

Descripción general del tutorial

Bienvenido a este tutorial de Granite. En este tutorial, aprenderá a aprovechar el poder de las herramientas avanzadas para crear un pipeline de RAG multimodal impulsado por IA. Este tutorial le guiará a través de los siguientes procesos:

  • Preprocesamiento de documentos: aprenda a manejar documentos de diversas fuentes, analizarlos y transformarlos en formatos utilizables y almacenarlos en bases de datos vectoriales mediante Docling. Utilizará un MLLM de Granite para generar descripciones de imágenes de imágenes en los documentos.
  • RAG: comprenda cómo conectar LLM como Granite con bases de conocimiento externas para mejorar las respuestas a las consultas y generar insights valiosos.
  • LangChain para la integración de flujos de trabajo: descubra cómo utilizar LangChain para optimizar y coordinar los flujos de trabajo de procesamiento y recuperación de documentos, lo que permite una interacción fluida entre los diferentes componentes del sistema.

Este tutorial utiliza tres tecnologías de punta:

  1. Docling: un kit de herramientas de código abierto empleado para analizar y convertir documentos.
  2. Granite: un LLM de última generación que ofrece sólidas capacidades de lenguaje natural y un modelo de lenguaje visual que permite generar texto a partir de imágenes.
  3. LangChain: un poderoso marco empleado para construir aplicaciones impulsadas por modelos de lenguaje, diseñado para simplificar flujos de trabajo complejos e integrar herramientas externas de forma perfecta.

Al final de este tutorial, logrará lo siguiente:

  • Obtenga aptitud en el preprocesamiento de documentos, la fragmentación y la comprensión de imágenes.
  • Integre bases de datos vectoriales para mejorar las capacidades de recuperación.
  • Utilice RAG para realizar una recuperación de datos eficiente y precisa para aplicaciones del mundo real.

Este tutorial está diseñado para desarrolladores de IA, investigadores y entusiastas que buscan mejorar su conocimiento de la gestión de documentos y técnicas avanzadas de procesamiento de lenguaje natural. El tutorial también se puede encontrar en el Granite Snack Cookbook GitHub de la comunidad IBM Granite en forma de Jupyter Notebook.

Requisitos previos

  • Familiaridad con la programación en Python.
  • Comprensión básica de LLM, conceptos de PLN y visión artificial.

Pasos

Paso 1: Instalar dependencias

! 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::"

Paso 2: Seleccionar los modelos de IA

Registro

Para ver información de registro, podemos configurar el nivel de registro INFO.

NOTA: Está bien omitir la ejecución de esta celda.

import logging

logging.basicConfig(level=logging.INFO)


Cargar los modelos de Granite

Especifique el modelo de incorporación que se utilizará para generar vectores de incorporación de texto. Aquí utilizaremos uno de los modelos de incorporación de Granite.

Para utilizar un modelo de incorporación diferente, sustituya esta celda de código por una de esta receta de modelo de incorporación.

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)

 

Especifique el MLLM que se utilizará para la comprensión de imágenes. Usaremos el modelo de visión Granite. 

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)

 

Especifique el modelo de lenguaje que se utilizará para la operación de generación de RAG.  Aquí utilizamos el cliente Replicate LangChain para conectarnos a un modelo Granite desde la organización ibm-granite en Replicate.

Para configurar Replicate, consulte Introducción a Replicate. Para conectarse a un modelo en un proveedor distinto de Replicate, sustituya esta celda de código por una de la receta del componente LLM.

Para conectarse a un modelo en un proveedor distinto de Replicate, sustituya esta celda de código por una de la receta del componente LLM.

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.
    },
)

Paso 3: Preparar los documentos para la base de datos vectorial

En este ejemplo, a partir de un conjunto de documentos originales, utilizamos Docling para convertir los documentos en texto e imágenes. Luego, el texto se divide en fragmentos. Las imágenes son procesadas por el MLLM para generar resúmenes de imágenes.

Utilice Docling para descargar los documentos y convertirlos a texto e imágenes

Docling descargará los documentos PDF y los procesará para que podamos obtener el texto y las imágenes que contienen. En el PDF, hay varios tipos de datos, incluidos texto, tablas, gráficos e imágenes.

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 }

 

Una vez procesados los documentos, procedemos a procesar los elementos de texto que contienen. Los dividimos en tamaños apropiados para el modelo de incorporación que estamos usando. Se crea una lista de documentos LangChain a partir de los fragmentos de texto.

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”)

 

A continuación, procesamos todas las tablas de los documentos. Convertimos los datos de la tabla a formato markdown para pasar al modelo de lenguaje. Se crea una lista de documentos LangChain a partir de las representaciones de markdown de la tabla.

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”)

 

Finalmente, procesamos cualquier imagen en los documentos. Aquí utilizamos el modelo de lenguaje de visión para comprender el contenido de una imagen. En este ejemplo, nos interesa cualquier información textual de la imagen. Es posible que desee experimentar con diferentes textos de instrucciones para ver cómo podrían mejorar los resultados.

NOTA: El procesamiento de las imágenes puede llevar mucho tiempo dependiendo de la cantidad de imágenes y del servicio que ejecuta el modelo de lenguaje de visión.

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”)

 

Luego podemos mostrar los documentos LangChain creados a partir de los documentos de entrada.

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

Rellene la base de datos vectorial

Usando el modelo de incorporación, cargamos los documentos de los fragmentos de texto y generamos los subtítulos de imagen en una base de datos vectorial. La creación de esta base de datos vectorial nos permite realizar fácilmente una búsqueda de similitud semántica en nuestros documentos.

NOTA: La población de la base de datos vectorial puede llevar algún tiempo dependiendo de su modelo de incorporación y servicio.

Elija su base de datos vectorial

Especifique la base de datos que se utilizará para almacenar y recuperar vectores de incorporación.

Para conectarse a una base de datos vectorial distinta a Milvus, sustituya esta celda de código por una de esta receta de almacén de vectores.

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”},
)

 

Ahora agregamos todos los documentos LangChain para el texto, las tablas y las descripciones de imágenes a la base de datos vectorial.

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”)

Paso 4: RAG con Granite

Ahora que hemos convertido correctamente nuestros documentos y los hemos vectorizado, podemos configurar el pipeline de RAG.

Recuperar fragmentos relevantes

Aquí probamos la base de datos vectorial buscando fragmentos con información relevante para nuestra consulta en el espacio vectorial. Mostramos los documentos asociados con la descripción de la imagen recuperada.

Siéntase libre de probar diferentes consultas.

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

 

El documento devuelto debe responder a la consulta. Avancemos y construyamos nuestro pipeline de RAG.


Crear el pipeline de RAG para Granite

Primero creamos las instrucciones para que Granite realice la consulta RAG. Usamos la plantilla de chat de Granite y proporcionamos los valores provisionales que la tubería RAG de LangChain reemplazará.

A continuación, construimos el pipeline de RAG mediante las plantillas de instrucciones de Granite creadas anteriormente.

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,
)

Genere una respuesta aumentada por recuperación a una pregunta

El pipeline utiliza la consulta para localizar documentos de la base de datos vectorial y utilizarlos como contexto para la consulta.

from ibm_granite_community.notebook_utils import wrap_text

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

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

¡Impresionante! Hemos creado una aplicación de IA que puede aprovechar con éxito el conocimiento del texto y las imágenes de los documentos de origen.

Próximos pasos

  • Explore flujos de trabajo avanzados de RAG para otras industrias.
  • Experimente con otros tipos de documentos y conjuntos de datos más grandes.
  • Optimice la ingeniería rápida para obtener mejores respuestas de Granite.
Soluciones relacionadas
IBM watsonx.ai

Entrene, valide, ajuste y despliegue IA generativa, modelos fundacionales y capacidades de machine learning con IBM watsonx.ai, un estudio empresarial de próxima generación para creadores de IA. Diseñe aplicaciones de IA en menos tiempo y con menos datos.

Descubra watsonx.ai
Soluciones de inteligencia artificial

Ponga la IA a trabajar en su negocio con la experiencia en IA líder en la industria y la cartera de soluciones de IBM a su lado.

Explore las soluciones de IA
Servicios de IA

Reinvente los flujos de trabajo y las operaciones críticas añadiendo IA para maximizar las experiencias, la toma de decisiones en tiempo real y el valor empresarial.

Conozca los servicios de IA
Dé el siguiente paso

Obtenga acceso único a capacidades que abarcan el ciclo de vida del desarrollo de IA. Produzca potentes soluciones de IA con interfaces fáciles de usar, flujos de trabajo y acceso a API y SDK estándar de la industria.

Explore watsonx.ai Reserve una demostración en vivo