Завантажувачі документів у LangChain: як файли й сторінки потрапляють у RAG-пайплайн
Як завантажувачі перетворюють PDF, текст, CSV і вебсторінки на об'єкти Document, який PDF-завантажувач обрати і коли ліниве завантаження заощаджує пам'ять на великих папках.
LLM не може сама походити вашими папками й прочитати файли. Генерація з доповненим пошуком (RAG) з'явилася тому, що моделі не знають ваших приватних даних, не бачать нічого свіжого й часом вигадують, коли бракує контексту. Рішення — підтягувати потрібний текст у момент запиту. Але перед цим PDF, вебсторінка, CSV чи Word-файл узагалі мають якось потрапити в пайплайн. Це робота завантажувача документів: його легко не помітити, а зробити правильно варто.
Що робить завантажувач
Завантажувач читає дані з певного джерела й приводить їх до формату, який розуміє LangChain. По суті, це перекладач. Ваші матеріали можуть лежати будь-де:
- Звичайні текстові файли
- 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
}
)
Підходить для каталогів товарів, даних клієнтів, складських залишків, фінансових записів і схожих бізнес-даних.
Три речі, які варто запам'ятати
- Будь-який RAG-пайплайн починається із завантаження. До чанкінгу, ембедингів і пошуку дані мають потрапити всередину.
- Різним джерелам потрібні різні завантажувачі: текстовим файлам — TextLoader, PDF — PyPDFLoader, сайтам — WebBaseLoader, CSV — CSVLoader, папкам — DirectoryLoader.
- Майже все зрештою стає об'єктом Document, і ця спільна форма дає решті пайплайну працювати однаково.
Повний перелік завантажувачів є в документації з інтеграцій LangChain.
Коли документи завантажено, наступна задачка — як їх розбити: за символами, словами, реченнями, абзацами чи токенами, і якого розміру мають бути шматки для 100-сторінкового файлу. Наріжете задрібно — втратите контекст. Завелико — витягуватимете шум. Це рішення заслуговує на окремий уважний розбір, бо це мистецтво різати документи, не ламаючи RAG-систему.