Головна / Статті / Завантажувачі документів у LangChain: як файли й сторінки потрапляють у RAG-пайплайн

Завантажувачі документів у LangChain: як файли й сторінки потрапляють у RAG-пайплайн

Як завантажувачі перетворюють PDF, текст, CSV і вебсторінки на об'єкти Document, який PDF-завантажувач обрати і коли ліниве завантаження заощаджує пам'ять на великих папках.

1360 слів

LLM не може сама походити вашими папками й прочитати файли. Генерація з доповненим пошуком (RAG) з'явилася тому, що моделі не знають ваших приватних даних, не бачать нічого свіжого й часом вигадують, коли бракує контексту. Рішення — підтягувати потрібний текст у момент запиту. Але перед цим PDF, вебсторінка, CSV чи Word-файл узагалі мають якось потрапити в пайплайн. Це робота завантажувача документів: його легко не помітити, а зробити правильно варто.

Що робить завантажувач

Завантажувач читає дані з певного джерела й приводить їх до формату, який розуміє LangChain. По суті, це перекладач. Ваші матеріали можуть лежати будь-де:

  • PDF
  • Звичайні текстові файли
  • CSV-файли
  • Сайти
  • Документи Word
  • Бази даних

Усе, що йде після завантажувача, очікує одну стандартну форму, і завдання завантажувача — закрити цей розрив.

Чому PDF не можна одразу різати на чанки

Початківці часто уявляють процес так:

PDF → Chunking → Embeddings

Насправді все складніше. PDF — це не текст. Усередині шрифти, зображення, дані про верстку, таблиці, колонтитули й метадані. Чанкер працює лише з текстом, тож спочатку хтось має витягти слова. Реальний процес ближчий до такого:

PDF → Document Loader → Text Extraction → Chunking → Embeddings

Завантажувач витягує текст і пакує його в спільний формат.

Завантажувачі, які трапляються найчастіше

У LangChain є завантажувачі для безлічі джерел. Вхід змінюється, принцип — ні: прочитати звідкись і видати документи LangChain. Ось ті, до яких ви звертатиметеся найчастіше:

  • TextLoader працює зі звичайними .txt-файлами.
  • PyPDFLoader працює з PDF.
  • WebBaseLoader працює з вебсторінками та URL.
  • CSVLoader працює з CSV-файлами.
  • DirectoryLoader працює з багатьма файлами в папці.

Один тип на виході: Document

Хоч який завантажувач працює, зазвичай ви отримуєте список об'єктів Document. У кожному є текст і трохи відомостей про те, звідки він узявся:

Document(
    page_content="Employees are eligible for 24 paid leaves per year.",
    metadata={
        "source": "employee_policy.pdf",
        "page": 5
    }
)

Найважливіші два поля. page_content — сам текст, а metadata описує джерело, тут це ім'я файлу й номер сторінки. Така стандартна форма зручна: наступним етапам байдуже, звідки прийшли дані. І PDF-завантажувач, і веб-завантажувач віддають Document, а етап чанкінгу обробляє їх однаково.

TextLoader

Найпростіший варіант читає звичайні текстові файли. Спершу імпортуємо його:

from langchain_community.document_loaders import TextLoader

Потім вказуємо файл, завантажуємо й виводимо результат:

loader = TextLoader("notes.txt")documents = loader.load()print(documents)

Отримуємо список:

[
    Document(...)
]

Навіть для одного файлу повертається список — так інтерфейс лишається однаковим. Кожен елемент — це Document.

Беріть його, коли дані вже у вигляді тексту: логи, нотатки, текстові вивантаження чи чисті датасети. Але він зовсім простий. Він не розбирає PDF, не читає таблиці, не розуміє верстку й не витягує зображення. Якщо джерело не текстове, обирайте щось інше.

PyPDFLoader

PDF — мабуть, найчастіше джерело в RAG-проєктах, і PyPDFLoader — звична відправна точка. Імпортуємо:

from langchain_community.document_loaders import PyPDFLoader

Далі завантажуємо звіт:

loader = PyPDFLoader("annual_report.pdf")documents = loader.load()

Припустімо, у файлі 25 сторінок. Рахуємо результат:

len(documents)

і отримуємо:

25

Кожна сторінка стає окремим Document. Дивимося на перший:

documents[0]

і бачимо ось що:

Document(
    page_content="Page 1 text...",
    metadata={
        "source": "annual_report.pdf",
        "page": 0
    }
)

Тож 25-сторінковий PDF дає 25 документів. Під капотом завантажувач використовує бібліотеку PyPDF. З простими PDF він дає раду, але спотикається на складній верстці, багатоколонкових сторінках, таблицях і сканах — тому вибір PDF-завантажувача важливий.

Як обрати PDF-завантажувач

Одного завантажувача, найкращого для всіх файлів, немає. Приблизна мапа:

  • PyPDFLoader пасує до простих текстових PDF.
  • PDFPlumberLoader — для PDF із таблицями та структурованою версткою.
  • PyMuPDFLoader швидкий і добрий як універсальний варіант.
  • UnstructuredPDFLoader розрахований на складні корпоративні документи.
  • AmazonTextractPDFLoader закриває скани й завдання, де багато OCR.

Робоче правило: почніть із PyPDF. Якщо витягнутий текст виглядає погано, переходьте на PyMuPDF або Unstructured. Якщо документ — скан, використовуйте OCR-сервіс на кшталт Textract.

DirectoryLoader

Завантажити один файл — дрібниця. Завантажити вручну 5000 PDF чи 10 000 текстових файлів — уже ні, і для цього є DirectoryLoader. Імпортуємо:

from langchain_community.document_loaders import DirectoryLoader

Потім передаємо йому папку й glob-шаблон:

loader = DirectoryLoader(
    "data/",
    glob="**/*.pdf"
)documents = loader.load()

Так він підхопить усі PDF у каталозі. Уявіть таку структуру:

data/
├── report1.pdf
├── report2.pdf
├── report3.pdf

Завантажувач проходить кожним відповідним файлом і завантажує його, і ви отримуєте:

[
    Document(...),
    Document(...),
    ...
]

Для великої папки це можуть бути сотні чи тисячі документів.

Проблема величезних папок

Уявіть 20 000 PDF або 100 000 текстових файлів і такий виклик:

documents = loader.load()

LangChain одразу читає все в пам'ять. Це жадібне завантаження. Воно просте й нормально працює на невеликих наборах даних, але з'їдає багато пам'яті й уповільнює старт.

Жадібне завантаження проти лінивого

Є альтернатива:

loader.lazy_load()

У жадібному варіанті все завантажується одразу:

documents = loader.load()

а повертається:

List[Document]

Лінивий варіант видає документи лише тоді, коли вони потрібні:

documents = loader.lazy_load()

і повертає генератор:

Generator[Document]

Згадайте стримінг відео. Сервіс не завантажує весь фільм, перш ніж ви натиснете «play», він віддає дані на вимогу. Ліниве завантаження влаштоване так само.

load() пасує для малих і середніх колекцій та для прототипів. lazy_load() — для великих колекцій і середовищ з обмеженою пам'яттю.

WebBaseLoader

Не всі знання лежать у файлах. Іноді вони на сайті, і тут допомагає WebBaseLoader. Імпортуємо:

from langchain_community.document_loaders import WebBaseLoader

Потім передаємо URL:

loader = WebBaseLoader(
    "https://python.langchain.com"
)documents = loader.load()

Усередині він розбирає HTML через BeautifulSoup і витягує текст. Це підходить для блогів, документації, статей, баз знань і сайтів компаній.

Найкраще він працює зі статичними сторінками. Сайти, перевантажені JavaScript, динамічний контент і рендеринг на клієнті завдають йому клопоту. Для таких випадків є SeleniumURLLoader: він спершу рендерить сторінку в браузері, а вже потім витягує текст.

CSVLoader

Структуровані дані часто надходять у CSV. Візьмімо невеликий файл зі співробітниками:

employee_id,name,department
1,John,Engineering
2,Sarah,HR

Імпортуємо завантажувач:

from langchain_community.document_loaders import CSVLoader

І завантажуємо файл:

loader = CSVLoader(
    file_path="employees.csv"
)documents = loader.load()

Кожен рядок перетворюється на Document: назви стовпців і значення записуються текстом, а номер рядка зберігається в метаданих:

Document(
    page_content="""
employee_id: 1
name: John
department: Engineering
""",
    metadata={
        "source": "employees.csv",
        "row": 0
    }
)

Підходить для каталогів товарів, даних клієнтів, складських залишків, фінансових записів і схожих бізнес-даних.

Три речі, які варто запам'ятати

  1. Будь-який RAG-пайплайн починається із завантаження. До чанкінгу, ембедингів і пошуку дані мають потрапити всередину.
  2. Різним джерелам потрібні різні завантажувачі: текстовим файлам — TextLoader, PDF — PyPDFLoader, сайтам — WebBaseLoader, CSV — CSVLoader, папкам — DirectoryLoader.
  3. Майже все зрештою стає об'єктом Document, і ця спільна форма дає решті пайплайну працювати однаково.

Повний перелік завантажувачів є в документації з інтеграцій LangChain.

Коли документи завантажено, наступна задачка — як їх розбити: за символами, словами, реченнями, абзацами чи токенами, і якого розміру мають бути шматки для 100-сторінкового файлу. Наріжете задрібно — втратите контекст. Завелико — витягуватимете шум. Це рішення заслуговує на окремий уважний розбір, бо це мистецтво різати документи, не ламаючи RAG-систему.