Главная / Статьи / Загрузчики документов в 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-систему.