LangChain Document Loaders: 詳細解説 🦜️🔗

AI / 機械学習

多様なデータソースからLLMへ:LangChainのドキュメントローダーを使いこなす

近年、大規模言語モデル(LLM)は目覚ましい進化を遂げ、自然言語処理の様々なタスクで驚異的な能力を発揮しています。しかし、LLMが持つ知識は、その訓練データに基づいているため、「最新の情報」や「特定のドメインに関する専門知識」、「インターネットに公開されていない社内データ」など、外部の情報を取り込んで利用するには工夫が必要です。

ここで重要な役割を果たすのが、LangChainというフレームワークです。LangChainは、LLMを用いたアプリケーション開発を簡素化するためのライブラリであり、PythonやJavaScript/TypeScriptで利用可能です(Python版が全ての機能を利用でき推奨されています)。LangChainは、外部データソースとの連携、プロンプトの管理、複数の処理ステップの連結(チェイン)、記憶(メモリ)の保持、エージェントによる自律的なタスク実行など、LLMアプリケーション開発に必要な様々な機能を提供します。

その中でも、本記事で焦点を当てるのが Document Loaders(ドキュメントローダー) です。Document Loadersは、LangChainの「Retrieval(検索)」モジュールの一部であり、様々な形式のデータソースから情報を読み込み、LLMが処理しやすい統一された形式(Documentオブジェクト)に変換する役割を担います。この機能により、開発者はファイルシステム上のテキストファイル、PDF、CSV、JSON、HTML、Markdown、さらにはWebページ、YouTube動画の文字起こし、データベース、各種SaaS(Notion, Google Drive, Slackなど)といった多種多様なソースから、簡単にデータをLLMアプリケーションに取り込むことができます。まさに、LLMに外部世界の知識を与えるための入り口となる重要なコンポーネントと言えるでしょう。🚪

この記事では、LangChainのDocument Loadersの基本的な概念から、主要なローダーの種類、使い方、そして応用的なテクニックまでを詳しく解説していきます。

LangChainのDocument Loaderは、様々なデータソースからテキスト情報を抽出し、それをDocumentオブジェクトのリストとして返します。Documentオブジェクトは、主に以下の2つの要素で構成されます。

  • page_content (文字列): ドキュメント本体のテキストコンテンツです。これがLLMによって処理される主要な情報となります。
  • metadata (辞書): ドキュメントに関する追加情報(メタデータ)です。例えば、読み込み元のファイルパス、WebページのURL、CSVの行番号、ドキュメントの作成日などが含まれます。メタデータは、情報のフィルタリングや、出典の明示、後続処理でのコンテキスト付与などに役立ちます。

各Document Loaderは、そのソースに特有の設定パラメータを持ちますが、基本的な使い方は共通しています。通常、ローダーのインスタンスを作成し、.load()メソッドを呼び出すことで、データソースからDocumentオブジェクトのリストを取得します。一部のローダーは、メモリ効率を高めるために、データを一度に全て読み込むのではなく、必要になったタイミングで逐次読み込む.lazy_load()(または.aload() for async)メソッドも提供しています。


# 例: CSVLoaderの基本的な使い方
from langchain_community.document_loaders.csv_loader import CSVLoader

# ローダーのインスタンス化 (特定のパラメータを指定)
loader = CSVLoader(file_path='./example.csv', source_column='source_name')

# ドキュメントの読み込み
documents = loader.load()

# 最初のドキュメントの内容とメタデータを表示
print(f"Content: {documents[0].page_content[:100]}...") # 最初の100文字を表示
print(f"Metadata: {documents[0].metadata}")
      

このようにして読み込まれたDocumentオブジェクトは、後続のステップ、例えばText Splittersによる分割、Embeddingsによるベクトル化、Vector Storesへの格納、そしてRetrieversによる検索といった、RAG (Retrieval-Augmented Generation) システムの構築に利用されます。

LangChainは、非常に多くのDocument Loaderを提供しており、その数は100種類以上にも及びます。ここでは、特に利用頻度の高い主要なローダーをいくつかピックアップし、その使い方を解説します。

注意: LangChainのバージョンアップに伴い、多くのローダーはlangchain_communityパッケージや、特定のインテグレーションパッケージ(例: langchain_openai, langchain_google_genai など)に移動しています。利用する際は、公式ドキュメントで最新のインポートパスを確認してください。

1. TextLoader

最も基本的なローダーの一つで、プレーンなテキストファイル(.txt, .md など)を読み込みます。


from langchain_community.document_loaders import TextLoader

loader = TextLoader("path/to/your/document.txt", encoding="utf-8") # エンコーディング指定可能
documents = loader.load()

print(f"Loaded {len(documents)} document(s).")
print(documents[0].page_content[:200])
print(documents[0].metadata)
        
ファイルタイプ: .txt, .md, etc.

2. CSVLoader

CSVファイルを読み込みます。デフォルトでは各行が1つのDocumentオブジェクトになります。特定の列をpage_contentに、他の列をmetadataに割り当てるなど、柔軟な設定が可能です。


from langchain_community.document_loaders.csv_loader import CSVLoader

# 特定の列をコンテンツ、ファイルパスを行のメタデータのsourceに指定
loader = CSVLoader(
    file_path='path/to/your/data.csv',
    csv_args={ # csvモジュールのreaderに渡す引数
        'delimiter': ',',
        'quotechar': '"',
        'fieldnames': ['column1', 'content_column', 'metadata_column'] # ヘッダーがない場合など
    },
    source_column='metadata_column' # sourceメタデータとして使う列を指定
)
documents = loader.load()

print(f"Loaded {len(documents)} rows as documents.")
print(documents[0].page_content)
print(documents[0].metadata) # {'source': 'value_from_metadata_column', 'row': 0} のようになる
        
ファイルタイプ: .csv

3. DirectoryLoader

指定したディレクトリ内のファイルを再帰的に読み込みます。ファイルの種類に応じて、内部で使用するローダーを指定できます(loader_cls)。デフォルトではUnstructuredLoaderが使われることが多いですが、特定の拡張子に対して特定のローダーをマッピングすることも可能です。


from langchain_community.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader

# 特定の拡張子に特定のローダーをマッピング
loader = DirectoryLoader(
    'path/to/your/directory',
    glob="**/*.txt", # .txt ファイルのみを対象とする場合
    loader_cls=TextLoader, # .txt ファイルにはTextLoaderを使用
    show_progress=True, # 進捗を表示
    use_multithreading=True # マルチスレッドで高速化 (ファイル数が多い場合に有効)
)
# または、拡張子ごとにローダーを指定
# loader = DirectoryLoader(
#     'path/to/your/directory',
#     glob="**/*", # 全てのファイルを対象
#     loader_mapping={
#         ".pdf": PyPDFLoader,
#         ".txt": TextLoader,
#         # 他の拡張子とローダーのマッピングを追加
#     },
#     show_progress=True
# )

documents = loader.load()
print(f"Loaded {len(documents)} documents from the directory.")
        
用途: 複数ファイルの一括読み込み

4. WebBaseLoader / RecursiveURLLoader / BeautifulSoup

Webページの内容を取得します。WebBaseLoaderは基本的なHTML取得機能を提供し、しばしばBeautifulSoupTransformerと組み合わせてHTMLタグを除去したり、特定の要素を抽出したりします。RecursiveURLLoaderは、指定したURLからリンクを辿り、複数のページを再帰的に取得できます。


# WebBaseLoader の例
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import BeautifulSoupTransformer # HTMLタグ除去などに使う

loader = WebBaseLoader("https://example.com/some_page.html")
docs = loader.load()

# BeautifulSoupTransformerでHTMLタグを除去・整形
# bs_transformer = BeautifulSoupTransformer()
# docs_transformed = bs_transformer.transform_documents(docs, tags_to_extract=["p", "li", "div"])
# print(docs_transformed[0].page_content[:500])

print(docs[0].page_content[:500]) # タグ付きのまま取得
print(docs[0].metadata)

# RecursiveURLLoader の例
from langchain_community.document_loaders import RecursiveUrlLoader
from langchain.utils.html import extract_sub_links # link extractor function example

# url = "https://docs.python.org/3/"
# loader = RecursiveUrlLoader(url=url, max_depth=2, extractor=lambda x: extract_sub_links(x, url))
# docs = loader.load()
# print(f"Loaded {len(docs)} documents recursively.")
        
用途: Webサイトスクレイピング

注: Webスクレイピングを行う際は、対象サイトの利用規約 (robots.txtなど) を遵守してください。

5. PyPDFLoader / UnstructuredPDFLoader

PDFファイルを読み込みます。PyPDFLoaderpypdfライブラリを使用し、各ページを1つのDocumentとして読み込むのが基本です。UnstructuredPDFLoaderunstructuredライブラリを使用し、より高度なレイアウト解析(表や画像の検出など)を行うことができますが、依存ライブラリのインストールが必要です。


# PyPDFLoader の例 (pypdfをインストール: pip install pypdf)
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("path/to/your/document.pdf")
pages = loader.load_and_split() # ページごとに分割して読み込み

print(f"Loaded {len(pages)} pages from the PDF.")
print(pages[0].page_content[:300])
print(pages[0].metadata) # {'source': 'path/to/your/document.pdf', 'page': 0}

# UnstructuredPDFLoader の例 (unstructuredと関連ライブラリをインストール)
# pip install "unstructured[pdf]"
# from langchain_community.document_loaders import UnstructuredPDFLoader
# loader = UnstructuredPDFLoader("path/to/your/document.pdf", mode="elements") # 要素ごとに分割
# documents = loader.load()
# print(f"Loaded {len(documents)} elements from the PDF.")
        
ファイルタイプ: .pdf

6. JSONLoader

JSONファイルまたはJSON Lines (JSONL) ファイルを読み込みます。jqスキーマを使って、JSON内のどの部分をpage_contentmetadataとして抽出するかを柔軟に指定できます。


# JSONLoader の例 (jqをインストール: pip install jq)
from langchain_community.document_loaders import JSONLoader
import json

# サンプルJSONファイルを作成 (例)
json_data = [{
    "text": "これは最初のドキュメントです。",
    "source": "file1.json",
    "author": "Alice"
}, {
    "text": "これは二番目のドキュメントです。",
    "source": "file1.json",
    "author": "Bob"
}]
file_path = './data.json'
with open(file_path, 'w', encoding='utf-8') as f:
    json.dump(json_data, f, ensure_ascii=False, indent=2)

# jqスキーマで抽出するフィールドを指定
# この例では、各オブジェクトの 'text' を page_content に、
# 'source' と 'author' を metadata に抽出
loader = JSONLoader(
    file_path=file_path,
    jq_schema='.[].text', # page_content になる部分
    metadata_func=lambda record, metadata: { # metadata を抽出する関数
        "source": record.get("source"),
        "author": record.get("author"),
        "original_metadata_source": metadata.get("source") # 元のファイルパスなども追加可能
    },
    json_lines=False # JSON Lines形式の場合はTrue
)

documents = loader.load()
print(f"Loaded {len(documents)} documents from JSON.")
print(documents[0].page_content)
print(documents[0].metadata)
        
ファイルタイプ: .json, .jsonl

その他の便利なローダー

上記以外にも、LangChainは特定のユースケースに対応した多数のローダーを提供しています。

  • NotionDirectoryLoader: NotionからエクスポートしたMarkdown/CSVファイルを読み込む。
  • GoogleDriveLoader: Google Drive上のファイルを読み込む(認証設定が必要)。
  • ArxivLoader: arXivから論文情報を取得する。
  • WikipediaLoader: Wikipediaの記事を取得する。
  • YoutubeLoader: YouTube動画の文字起こしを取得する(別途文字起こしライブラリが必要な場合あり)。
  • UnstructuredURLLoader: unstructuredを用いてWebページを解析・読み込みする。
  • Docx2txtLoader: Microsoft Word (.docx) ファイルを読み込む。
  • DataFrameLoader: Pandas DataFrameからデータを読み込む。
  • 各種データベースローダー (BigQuery, DuckDB, MongoDBなど)
  • 各種SaaS連携ローダー (Slack, Figma, GitHub Issuesなど)

これらのローダーの詳細や使い方は、LangChainの公式ドキュメント (Integrations – Document Loaders) で確認できます。🔍

1. Lazy Loading (遅延読み込み)

巨大なファイルや大量のファイルを扱う際、.load()ですべてを一度にメモリに読み込むと、リソースを大量に消費してしまう可能性があります。このような場合に.lazy_load()(または非同期版の.aload())を使用すると、イテレータとしてDocumentオブジェクトを一つずつ取得できます。これにより、メモリ使用量を抑えながらデータを処理できます。


from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("path/to/large/document.pdf")
lazy_documents = loader.lazy_load()

# イテレータとしてドキュメントを処理
for i, doc in enumerate(lazy_documents):
    print(f"Processing document {i+1}...")
    # ここで各ドキュメントに対する処理を行う
    # process_document(doc)
    if i == 4: # 例: 最初の5ページだけ処理
        break
      

2. メタデータのカスタマイズ

多くのローダーでは、読み込むドキュメントに付与されるメタデータをカスタマイズできます。例えば、CSVLoadersource_column引数やJSONLoadermetadata_func引数を使うことで、元のデータに含まれる情報をメタデータとして活用できます。これにより、後段の処理で情報の出所を追跡したり、特定の条件でドキュメントをフィルタリングしたりすることが容易になります。


# CSVLoader でメタデータを追加する例
loader = CSVLoader(
    file_path='data.csv',
    metadata_columns=['author', 'category'] # これらの列をメタデータに追加
)
documents = loader.load()
print(documents[0].metadata) # {'source': 'data.csv', 'row': 0, 'author': 'value', 'category': 'value'}
      

3. エラーハンドリング

特にDirectoryLoaderなどで複数のファイルを処理する場合、一部のファイルが破損していたり、予期しない形式だったりして読み込みに失敗することがあります。DirectoryLoadersilent_errors=Trueオプションを使用すると、エラーが発生したファイルをスキップして処理を続行できます。


from langchain_community.document_loaders import DirectoryLoader, TextLoader

loader = DirectoryLoader(
    'path/with/potentially/bad/files',
    glob="**/*.txt",
    loader_cls=TextLoader,
    silent_errors=True # エラーを無視して続行
)
documents = loader.load()
print(f"Successfully loaded {len(documents)} documents.")
      

4. 複数のローダーの結合 (MergedDataLoader)

異なる種類のソース(例: ローカルのPDFファイルとWebページ)からデータをまとめて読み込みたい場合、MergedDataLoaderを使用できます。これは、複数のローダーインスタンスを受け取り、それらすべてからドキュメントをロードします。


# MergedDataLoader の例は公式ドキュメント参照推奨
# from langchain_community.document_loaders import MergedDataLoader, PyPDFLoader, WebBaseLoader

# loader_pdf = PyPDFLoader("local_doc.pdf")
# loader_web = WebBaseLoader("https://example.com")

# merged_loader = MergedDataLoader(loaders=[loader_pdf, loader_web])
# all_documents = merged_loader.load()
# print(f"Loaded {len(all_documents)} documents from multiple sources.")
      

5. カスタムDocument Loaderの作成

LangChainが提供するローダーで対応できない特殊なデータソースやAPIからデータを読み込みたい場合は、BaseLoaderクラスを継承して独自のDocument Loaderを作成することも可能です。loadまたはlazy_loadメソッドを実装し、Documentオブジェクトを返すようにします。


from langchain_core.document_loaders import BaseLoader
from langchain_core.documents import Document
from typing import List, Iterator

class CustomLineLoader(BaseLoader):
    """ファイルを1行ずつ読み込むカスタムローダーの例"""
    def __init__(self, file_path: str):
        self.file_path = file_path

    def lazy_load(self) -> Iterator[Document]:
        """ファイルを1行ずつ読み込み、Documentとしてyieldする"""
        with open(self.file_path, 'r', encoding='utf-8') as f:
            for i, line in enumerate(f):
                yield Document(
                    page_content=line.strip(),
                    metadata={'source': self.file_path, 'line': i + 1}
                )

    # def load(self) -> List[Document]:
    #     """ファイルを全て読み込み、Documentのリストを返す (lazy_loadがあれば通常不要)"""
    #     return list(self.lazy_load())

# カスタムローダーの使用
custom_loader = CustomLineLoader("my_custom_data.log")
for doc in custom_loader.lazy_load():
    print(doc.page_content, doc.metadata)
    # 最初の数行だけ処理するなど
    if doc.metadata['line'] >= 3:
        break
      

Document Loadersは、LangChainを用いたLLMアプリケーション、特にRAG (Retrieval-Augmented Generation) システムにおいて、データを取り込む最初のステップです。その後の処理フローは一般的に以下のようになります。

  1. Document Loading (データの読み込み): 様々なソースからデータをDocumentオブジェクトとして読み込む (本記事のテーマ)。
  2. Document Splitting (データの分割): 読み込んだDocumentを、LLMが扱いやすいように、またベクトル検索に適したサイズに分割する (TextSplittersを使用)。分割単位は文字数、トークン数、特定の区切り文字(例: Markdownの見出し)など、様々な方法がある。適切な分割は後続の検索精度に大きく影響する。
  3. Text Embedding (テキストのベクトル化): 分割されたテキストチャンクを、意味的な類似性を捉えた数値ベクトル(埋め込みベクトル)に変換する (Embeddingsモデルを使用)。OpenAI, Hugging Face, Googleなど様々なモデルが利用可能。
  4. Vector Storage (ベクトルストアへの格納): 生成された埋め込みベクトルとその元のテキストチャンクを、高速な類似度検索が可能なデータベース(ベクトルストア)に格納する (VectorStoresを使用)。Chroma, FAISS, Pinecone, Redisなど多くの選択肢がある。
  5. Retrieval (検索): ユーザーからの質問(クエリ)を埋め込みベクトルに変換し、ベクトルストア内で類似度の高いテキストチャンクを検索・取得する (Retrieversを使用)。
  6. Generation (生成): 取得した関連性の高いテキストチャンクをコンテキストとしてLLMに渡し、質問に対する回答を生成させる (ChainsやLCEL (LangChain Expression Language) を使用)。
このように、Document Loadersは、外部知識をLLMに供給するためのパイプライン全体の出発点であり、その後のステップの品質や効率に直接的な影響を与える重要なコンポーネントです。適切なローダーを選択し、必要に応じて設定をカスタマイズすることが、高性能なRAGシステムを構築するための鍵となります 🔑。

LangChainのDocument Loadersは、テキストファイル、PDF、CSV、Webページ、データベース、各種APIなど、驚くほど多様なデータソースから情報を抽出し、LLMアプリケーションで利用可能な標準形式に変換するための強力なツール群です。

本記事では、Document Loadersの基本的な概念から、主要なローダー (TextLoader, CSVLoader, DirectoryLoader, WebBaseLoader, PyPDFLoader, JSONLoaderなど) の使い方、そして遅延読み込みやメタデータカスタマイズといった応用的なテクニックまでを解説しました。

Document Loadersを効果的に活用することで、LLMに最新の情報やドメイン固有の知識を与え、より正確で文脈に即した応答を生成するRAGシステムなどの高度なアプリケーションを構築することが可能になります。LangChainは活発に開発が進んでおり、対応するデータソースや機能は日々拡充されています。ぜひ公式ドキュメントも参照しながら、皆さんのプロジェクトに最適なDocument Loaderを見つけて活用してみてください!🚀

コメント

タイトルとURLをコピーしました