DoclingとGraniteでAI搭載マルチモーダルRAGシステムを構築

共同執筆者

BJ Hargrave

Open Source Developer, STSM

Erika Russi

Data Scientist

IBM

このチュートリアルでは、IBMのDoclingとオープンソースのIBM Graniteビジョン、テキストベースの埋め込み生成AIモデルを使用してRAGシステムを作成します。これらのモデルは、オープンソースのフレームワークを介して利用できます。このチュートリアルでは、 Replicate を使ってIBMのGraniteビジョンおよび生成AIモデルに接続し、HuggingFaceを使って埋め込みモデルに接続します。

マルチモーダル検索拡張生成

検索拡張生成(RAG)は、大規模言語モデル(LLM)で使用される手法で、LLMが学習したデータの外部にある情報の知識ベースとモデルを接続し、ファイン・チューニングを行う必要がありません。従来のRAGは、テキストの要約やチャットボットなどのテキスト・ベースのユースケースに限定されています。

マルチモーダルRAGは、マルチモーダルLLM(MLLM)を使用して、RAGで使用される外部知識ベースの一部として含まれる複数の種類のデータからの情報を処理できます。マルチモーダル・データには、テキスト、画像、音声、動画、その他の形式が含まれます。一般的なマルチモーダルLLMには、Google社のGemini、Meta社のLlama 3.2、OpenAI社のGPT-4やGPT-4oなどがあります。

このレシピでは、さまざまなモダリティーを処理できるIBM Graniteモデルを使用します。PDF内の非構造化データからリアルタイムでユーザーのクエリーに応答するAIシステムを作成します。

チュートリアル概要

Graniteチュートリアルへようこそ。このチュートリアルでは、高度なツールの力を活用してAI搭載のマルチモーダルRAGパイプラインを構築する方法について説明します。このチュートリアルでは、次のプロセスについて説明します。

  • ドキュメント前処理:さまざまなソースからドキュメントを処理し、解析して使用可能な形式に変換し、Doclingを使用してベクトル・データベースに保管する方法を学びます。Granite MLLMを使用して、ドキュメント内の画像の画像説明を生成します。
  • RAG:GraniteなどのLLMを外部の知識ベースと接続して、クエリー応答を強化し、貴重な洞察を生成する方法を理解します。
  • ワークフロー統合のためのLangChain:LangChainを使用してドキュメント処理と抽出ワークフローを合理化および調整し、システムのさまざまなコンポーネント間のシームレスな対話を実現する方法をご覧ください。

チュートリアルでは、3つの最先端テクノロジーを使用しています。

  1. Doclingドキュメントを解析および変換するために使用されるオープンソース・ツールキット。
  2. Granite:堅牢な自然言語の機能を提供する最先端のLLMと、画像からテキストへの生成を提供するビジョン言語モデル。
  3. LangChain言語モデルによってアプリケーションを構築するために使用される強力なフレームワークで、複雑なワークフローを簡素化し、外部ツールをシームレスに統合するように設計されています。

このチュートリアルを完了すると、以下を達成できます。

  • ドキュメントの前処理、チャンク化、画像理解に習熟できるようになります。
  • ベクトル・データベースを統合して、抽出機能を強化します。
  • RAGを使用して、実世界のアプリケーションの効率的で正確なデータ取得を実行します。

このチュートリアルは、文書管理および高度な自然言語処理(NLP)技術に関する知識を深めたいAI開発者、研究者、愛好家向けに設計されています。このチュートリアルは、IBM GraniteコミュニティーのGranite Snack Cookbook GitHubにもJupyter Notebook形式で掲載されています。

前提条件

  • Pythonプログラミングに精通していること。
  • LLM、NLPの概念、コンピューター・ビジョンに関する基本的な理解。

手順

ステップ1:依存関係をインストールする

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

ステップ2:AIモデルの選択

ロギング

ロギング情報を確認するには、INFO log レベルを設定します。

注: このセルの実行は省略しても問題ありません。

import logging

logging.basicConfig(level=logging.INFO)


Graniteのモデルをロードする

テキスト埋め込みベクトルの生成に使用する埋め込みモデルを指定します。ここではGranite 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)

 

イメージの理解に使用するMLLMを指定します。ここでは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)

 

RAG生成操作に使用する言語モデルを指定します。ここではReplicate LangChainクライアントを使って、Replicate上のibm-granite orgのGraniteモデルに接続しています。

Replicateの設定については、「Replicateの始め方」をご覧ください。Replicate以外のプロバイダ上のモデルに接続するには、このコード・セルをLLMコンポーネント・レシピのものに置き換えてください。

Replicate以外のプロバイダ上のモデルに接続するには、このコード・セルを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.
    },
)

ステップ3:ベクトル・データベース用のドキュメントを準備する

この例では、一連のソース・ドキュメントから、Doclingを使用してドキュメントをテキストと画像に変換します。その後、テキストをチャンクに分割します。画像はMLLMによって処理され、画像の概要が生成されます。

Dockerを使用してドキュメントをダウンロードし、テキストと画像に変換します。

DoclingはPDFドキュメントをダウンロードして処理するので、ドキュメントに含まれるテキストと画像を取得できます。PDFには、テキスト、表、グラフ、画像など、さまざまなデータ・タイプがあります。

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 }

 

文書が処理されたら、文書内のテキスト要素をさらに処理します。使用している埋め込みモデルに適したサイズに分割します。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”)

 

次に、文書内のテーブルを処理します。テーブル・データをマークダウン形式に変換して、言語モデルに渡します。LangChainドキュメントのリストは、テーブルのマークダウン・レンダリングから作成されます。

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

 

最後に、文書内の画像を処理します。ここでは、ビジョン言語モデルを使用して、画像の内容を理解します。この例では、画像内のテキスト情報に注目します。さまざまなプロンプトを試して、成果がどのように改善するかを確認することをお勧めします。

注:画像の数とビジョン言語モデルを実行するサービスによっては、画像の処理に非常に長い時間がかかる場合があります。

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

 

その後、入力したドキュメントから作成されたLangChainドキュメントを表示できます。

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

ベクトル・データベースを読み込む

埋め込みモデルを使用して、テキスト・チャンクからドキュメントと生成された画像キャプションをベクトル・データベースにロードします。このベクトル・データベースを作成すると、ドキュメント全体で意味的な類似性検索を簡単に実行できます。

注:ベクトル・データベースの入力には、埋め込みモデルとサービスによっては時間がかかる場合があります。

ベクトル・データベースの選択

埋め込みベクトルの保存と取得に使用するデータベースを指定します。

ベクトル・データベース以外のベクトル・データベースに接続するには、このコード・セルをこのベクトル・ストアのレシピのものに置き換えてください。

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

 

テキスト、テーブル、画像の説明用のすべてのLangChainドキュメントをベクトルデータベースに追加しました。

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

ステップ4:Graniteを使用したRAG

ドキュメントの変換とベクトル化が正常に完了したので、RAGパイプラインをセットアップできます。

関連するチャンクの取得

ここでは、ベクトル空間内のクエリに関連する情報を含むチャンクを検索することにより、ベクトル・データベースをテストします。取得したイメージの説明に関連する文書が表示されます。

さまざまなクエリを自由に試してください。

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

 

返されるドキュメントは、クエリーに応答する必要があります。ではRAGパイプラインを構築してみましょう。


Granite用のRAGパイプラインを作成する

まず、GraniteがRAGクエリを実行するためのプロンプトを作成します。Graniteチャット・テンプレートを使用し、LangChain RAGパイプラインが置き換えるプレースホルダー値を提供します。

次に、以前に作成したGraniteプロンプトを使用してRAGパイプラインを構築します。

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

質問に対する検索拡張応答を生成する

パイプラインはクエリを使用してベクトル・データベースからドキュメントを検索し、それらをクエリのコンテキストとして使用します。

from ibm_granite_community.notebook_utils import wrap_text

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

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

素晴らしい!私たちは、ソース文書のテキストや画像からの知識をうまく活用できるAIアプリケーションを作成しました。

次のステップ

  • 他の業種・業務向けの高度なRAGワークフローをはこちら。
  • 他のドキュメント・タイプやより大規模なデータ・セットを試す。
  • プロンプト・エンジニアリングを最適化して、Graniteの応答を改善します。
関連ソリューション
IBM watsonx.ai

AI開発者向けの次世代エンタープライズ・スタジオであるIBM watsonx.aiを使用して、生成AI、基盤モデル、機械学習機能をトレーニング、検証、チューニング、導入しましょう。わずかなデータとわずかな時間でAIアプリケーションを構築できます。

watsonx.aiをご覧ください。
人工知能ソリューション

業界をリードするIBMのAI専門知識とソリューション製品群を使用すれば、ビジネスにAIを活用できます。

AIソリューションはこちら
AIサービス

AIの導入で重要なワークフローと業務を再構築し、エクスペリエンス、リアルタイムの意思決定とビジネス価値を最大化します。

AIサービスはこちら
次のステップ

AI開発ライフサイクル全体にわたる機能にワンストップでアクセスできます。使いやすいインターフェース、ワークフロー、業界標準のAPIやSDKを利用して、強力なAIソリューションを構築できます。

  1. watsonx.aiの詳細はこちら
  2. デモを予約