Cree un sistema RAG multimodal con IA con Docling y Granite

Autores

BJ Hargrave

Open Source Developer, STSM

Erika Russi

Data Scientist

IBM

En este tutorial, utilizará Docling de IBM y la visión de código abierto IBM Granite, embeddings basados en texto y modelos de IA generativa para crear un sistema RAG. Estos modelos están disponibles a través de varios marcos de código abierto. En este tutorial, utilizaremos Replicate para conectarnos a los modelos de visión y IA generativa de IBM Granite y HuggingFace para conectarnos al modelo de embeddings.

Generación aumentada por recuperación multimodal

La generación aumentada por recuperación (RAG) es una técnica utilizada con modelos de lenguaje de gran tamaño (LLM) para conectar el modelo con una base de conocimiento de información fuera de los datos con los que se ha entrenado el LLM sin tener que realizar un ajustes precisos. La RAG tradicional se limita a casos de uso basados en texto, como el resumen de texto y los chatbots.

La RAG multimodal puede utilizar LLM multimodales (MLLM) para procesar información de múltiples tipos de datos que se incluirán como parte de la base de conocimiento externa utilizada en RAG. Los datos multimodales pueden incluir texto, imágenes, audio, vídeo u otras formas. 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

Le damos la bienvenida a este tutorial de Granite. En este tutorial, aprenderá a aprovechar el poder de las herramientas avanzadas para construir un oleoducto RAG multimodal con 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. Para ello, utilizará un MLLM de Granite con el que generará descripciones de las imágenes que aparecen en los documentos.
  • RAG: comprenda cómo conectar un LLM como Granite con bases de conocimiento externas para mejorar las respuestas a las consultas y generar conocimientos valiosos.
  • LangChain para la Integración de flujos de trabajo: descubra cómo utilizar LangChain para agilizar y orquestar 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 punta:

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

Al final de este tutorial, habrá logrado lo siguiente:

  • Adquiera competencias en preprocesamiento de documentos, fragmentación y 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, investigadores y entusiastas de la IA que buscan ampliar sus conocimientos sobre gestión documental y técnicas avanzadas de procesamiento del lenguaje natural (PLN). El tutorial también se puede encontrar en el repositorio de GitHub "Granite Snack Cookbook" de la comunidad de IBM Granite, en formato Jupyter Notebook.

Requisitos previos

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

Pasos

Paso 1: Instale 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: Selección de los modelos de IA

Información de registro

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

NOTA: Puede omitir la ejecución de esta celda.

import logging

logging.basicConfig(level=logging.INFO)


Cargue los modelos Granite

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

Para utilizar un modelo de embeddings diferente, sustituya esta celda de código por la correspondiente a la receta del modelo de embeddings.

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. Utilizaremos el modelo de visión de 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.  En este caso, utilizamos el cliente Replicate LangChain para conectarnos a un modelo Granite de la organización ibm-granite en Replicate.

Para configurarse con Replicate, consulte Getting Started with 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 fuente, utilizamos Docling para convertir los documentos en texto e imágenes. A continuación, el texto se divide en fragmentos. El MLLM procesa las imágenes para generar resúmenes.

Utilice Docling para descargar los documentos y convertirlos en 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, seguimos procesando los elementos de texto que contienen. Los dividimos en tamaños apropiados para el modelo de embeddings que estamos usando. A partir de los fragmentos de texto se crea una lista de documentos LangChain.

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 las tablas de los documentos. Convertimos los datos de la tabla al formato Markdown para pasarlos al modelo de lenguaje. Se crea una lista de documentos de LangChain a partir de las renderizaciones 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”)

 

Por último, procesamos las imágenes de 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 tardar mucho tiempo, en función del número de imágenes y del servicio que ejecute el modelo de lenguaje visual.

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

 

A continuación, 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

Mediante el modelo de embedding, cargamos los documentos, los fragmentos de texto y los subtítulos de imágenes generados en una base de datos vectorial. La creación de esta base de datos vectorial nos permite realizar fácilmente búsquedas de similitud semántica en todos nuestros documentos.

NOTA: la población de la base de datos vectorial puede tardar algún tiempo, en función del modelo de embedding y del servicio.

Elija su base de datos vectorial

Especifique la base de datos que se utilizará para almacenar y recuperar vectores de embedding.

Para conectarse a una base de datos vectorial distinta de Milvus, sustituya esta celda de código por la que encontrará en la receta de este 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 añadimos todos los documentos LangChain para las descripciones de texto, tablas e 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 y vectorizado correctamente nuestros documentos, podemos configurar nuestro pipeline RAG.

Recupere 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 a la descripción de la imagen recuperada.

No dude en 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. Vamos a construir nuestro pipeline RAG.


Cree el pipeline 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 el pipeline RAG de LangChain reemplazará.

A continuación, construimos el pipeline RAG utilizando las plantillas de instrucción 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']))

¡Genial! Hemos creado una aplicación de IA capaz de aprovechar con éxito los conocimientos del texto y las imágenes de los documentos fuente.

Próximos pasos

  • Explore flujos de trabajo RAG avanzados para otros sectores.
  • Experimente con otros tipos de documentos y conjuntos de datos más grandes.
  • Optimice el prompt engineering para mejorar las respuestas de Granite.
Soluciones relacionadas
IBM watsonx.ai

Entrene, valide, ajuste e implemente IA generativa, modelos fundacionales y capacidades de machine learning con IBM watsonx.ai, un estudio empresarial de nueva generación para desarrolladores de IA. Cree 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 líder en IA del sector de IBM y junto a su cartera de soluciones.

Explore las soluciones de IA
Servicios de IA

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

Explore los servicios de IA
Dé el siguiente paso

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

  1. Explore watsonx.ai
  2. Solicite una demostración en directo