RAG · Retrieval-augmented generation · урок 1 · 13 мин

RAG offline: ingestion, metadata, chunking

RAG offline: ingestion, metadata, chunking

ТемыRAG

Зачем

После урока ты сможешь рассказать первую, offline-половину любого RAG: как документ попадает в систему, какую карточку он получает и как его режут на куски. Это первая половина ответа на самый частый вопрос технического раунда «walk me through a RAG pipeline», и именно здесь чаще всего рождается неверный ответ, который потом списывают на модель.

Зачем RAG и почему у него две половины

Модель знает только то, что было в её обучающих данных, и не знает тарифов конкретного банка. Если её спросить, она угадает — уверенно и гладко. RAG (retrieval-augmented generation, генерация с опорой на найденные документы) превращает экзамен «по памяти» в экзамен «с открытой книгой»: перед ответом система находит нужные страницы, кладёт их в context (всё, что модель видит в этом запросе) и просит отвечать только по ним, со ссылками.

У RAG две половины. Offline (заранее, до вопросов) — подготовить библиотеку. Online (на каждый вопрос) — найти и ответить. Самый полезный факт о RAG: половины ломаются независимо. Этот урок — про offline: ingestion, metadata, chunking.

Ingestion: от файла до чистого текста

Ingestion (приём данных) — зонтичное слово для всего входа: забрать документ, разобрать, очистить, нарезать, снабдить карточкой. По шагам:

  1. Connector (подключение к источнику) забирает документы оттуда, где они живут: SharePoint, Confluence, файловое хранилище.
  2. Parsing (разбор) вытаскивает из PDF, Word и HTML текст и таблицы. Здесь ломается больше всего: таблица в PDF превращается в кашу из чисел, колонтитулы попадают в середину абзаца, скан без OCR (распознавания текста на картинке) даёт пустую страницу.
  3. Clean (очистка): sanitization убирает то, что нельзя хранить (персональные данные, секреты, скрытые инструкции), normalization приводит одно и то же к одному виду (цифры, формы Unicode, даты). У каждой из них отдельный урок.
  4. Chunk (нарезка) и metadata — о них ниже.

Решение инженера здесь — проверять parsing глазами на худших документах, а не на лучших. Десять самых сложных PDF (таблицы, сканы, двухколоночная вёрстка) скажут больше, чем тысяча простых.

Metadata: карточка, по которой фильтрует код

Metadata (метаданные, «данные о данных») — не сам текст, а сведения о нём: продукт, язык, версия, владелец, дата вступления в силу valid_from, дата окончания valid_to и ACL (access control list, список тех, кому документ можно показывать). Это карточка книги в каталоге: по ней отсеивают до того, как читать.

Главная мысль урока: дата вступления в силу — поле metadata, а не фраза в тексте. Фразу «действует с 1 марта 2026 года» модель может не заметить, неверно понять или перепутать с соседним документом. Поле valid_from проверяет код, одинаково каждый раз. Без этих полей вопрос «какая комиссия действует сейчас?» код не решит вообще — решать будет модель, наугад.

Отсюда же правило обновлений. Re-indexing (переиндексация): документ изменился — его chunks обновляют, а старую версию не удаляют, а закрывают датой valid_to. У клиента с договором 2024 года может действовать именно старая версия тарифа. Удалишь её — потеряешь правильный ответ для него.

Каждый chunk несёт ещё и технические поля: doc_id, chunk_id, version. Они нужны не поиску, а диагностике: когда ответ окажется неверным, trace (журнал одного запроса) покажет, какие именно куски и каких версий видела модель.

{"chunk_id": "fees-auto-2026-v3#12", "doc_id": "fees-auto-2026",
 "version": 3, "product": "auto_loan", "language": "ru",
 "valid_from": "2026-03-01", "valid_to": null,
 "acl": ["retail_staff", "customers"], "section": "4. Досрочное погашение"}

Chunking: размер, overlap и таблицы

Chunk (кусок документа) — фрагмент, который ищут и кладут в context целиком. Режут по двум причинам: чтобы поиск был точным и чтобы в context попадало только нужное. Типичный размер — несколько сотен tokens (кусочков слов, которыми считает модель), часто с overlap (перекрытием соседних кусков).

Компромисс такой:

Chunk Что выигрываем Что теряем
Маленький (100–200 токенов) точное попадание, мало шума в context кусок теряет смысл без соседей: «она составляет 1%» — кто она?
Большой (1 000+ токенов) больше смысла в одном куске вектор размывается по нескольким темам, context забивается лишним
Overlap = 0 нет дублей фраза или условие, разрезанные границей, пропадают из обоих кусков
Большой overlap границы безопасны индекс растёт, в выдаче повторы

Стратегии по возрастанию сложности: fixed-size (фиксированное число tokens), recursive (резать сначала по абзацам, потом по строкам), по структуре документа (заголовки, пункты, таблицы), semantic (по смене темы, ещё экспериментальный приём). Pinecone в своём разборе советует начинать с fixed-size и сравнивать на своих запросах 128–256 tokens против 512–1 024.

Главная ловушка — таблица тарифов. Граница посередине оставляет в одном куске названия комиссий, в другом суммы, и ни один не отвечает на вопрос. Правило: таблицы, списки и пункты договора держать целыми. Если таблица больше chunk — резать по строкам и повторять заголовок таблицы в каждом куске.

Ещё один приём — строка контекста в начале chunk: «Тариф по автокредиту, редакция 2026 года, раздел 4». Она лечит главную беду маленьких кусков: chunk снова понятен сам по себе. Об этом отдельный урок про contextual retrieval.

Пример с цифрами

PDF тарифов на 50 страниц — около 20 000 токенов (Anthropic для оценки берёт 200 000 токенов ≈ 500 страниц). Режем по 400 токенов с overlap 50: шаг 350 токенов, (20 000 − 400) / 350 = 56 шагов, итого 57 chunks. Но в документе есть таблица из шести строк на 600 токенов. Граница fixed-size почти наверняка пройдёт через неё, и последние строки окажутся в соседнем chunk без заголовка «Комиссия / Сумма / Условие». Решение: таблица — отдельный chunk целиком на 600 токенов, даже если он больше нормы. Размер — ориентир, смысл — правило.

Что решает инженер

Три решения offline-половины, которые потом не исправить никакой инструкцией в prompt (тексте запроса к модели): какие поля metadata обязательны (без даты действия и ACL документ не принимается), как режутся таблицы, и что пишется в каждый chunk для будущей диагностики. Всё это проверяется до первого вопроса пользователя.

Схема

Термины · Key terms

RAG (retrieval-augmented generation)
генерация с опорой на найденные документы
ingestion
приём данных: забрать, разобрать, очистить, нарезать, снабдить карточкой
connector
подключение к источнику документов
parsing
разбор файла в чистый текст и таблицы
OCR
распознавание текста на картинке или скане
metadata
сведения о документе: продукт, язык, версия, даты действия, доступ
ACL (access control list)
список тех, кому документ можно показывать
chunk
кусок документа для поиска, обычно сотни tokens
overlap
перекрытие соседних chunks
re-indexing
обновление chunks изменившегося документа, старая версия закрывается датой

Сделай сам

Набросай на бумаге страницу тарифа с таблицей из шести строк (комиссия, сумма, условие). Покажи стрелками, как режешь её на chunks, и подпиши у каждого chunk поля metadata: product, language, valid_from, valid_to, ACL. Отдельно отметь, что будет с таблицей при fixed-size нарезке по 400 токенов.

Обсуди с тьютором: покажи фото наброска и спроси, какое поле metadata ты забыл и что сломается в online-половине из-за его отсутствия.

Проверь себя · ответ под вопросом

1Почему дата вступления в силу — поле metadata, а не фраза в тексте документа?
Поле проверяет код, одинаково при каждом запросе, до того как модель что-то увидит. Фразу в тексте модель может пропустить или спутать с соседним документом. Без поля вопрос «какая версия действует» решает модель, то есть наугад.
2Что теряется при overlap = 0 и что — при слишком большом chunk?
При overlap = 0 условие или фраза, разрезанные границей, теряют смысл в обоих кусках. Слишком большой chunk смешивает несколько тем в одном векторе, поэтому хуже находится, и тратит context на лишний текст.
3Что такое строка контекста, добавленная к chunk, и какую беду она лечит?
Короткая строка в начале куска: откуда он, какой продукт, год, раздел. Она лечит потерю смысла у маленького chunk, который вне документа непонятен («она составляет 1%»). Такой кусок лучше находится и векторным поиском, и поиском по словам.

Interview questions · US / UK

1Walk me through the offline half of a RAG system. What happens before the first user question?
Strong answer: connector → parsing (tables and scans break) → sanitize and normalize → chunk with tables kept whole → metadata per chunk (version, valid_from/valid_to, language, ACL) → embed and index; plus chunk IDs and versions for traces.
2Your fee schedule changes every quarter. How do you handle versions in the index?
Strong answer: never overwrite; close the old version with valid_to; filter by the date that matters in retrieval code (today or the contract date); incremental re-indexing from a change feed; a test for both date cases.
3How do you choose a chunk size? Follow-up: what would you do with a 30-row fee table?
Strong answer: it is a measured trade-off, not a constant; compare a few sizes on real queries with recall@k; small chunks lose context, big ones blur the vector; tables split by rows with the header repeated, never mid-row.

Ключевые фразы · Say it aloud

Материалы