Late chunking: что это и как проверить поиск — метод построения эмбеддингов фрагментов, при котором весь документ сначала проходит через трансформер целиком, а разбиение на чанки и усреднение применяются после. Проверка поиска сводится к сравнению одинаковых документов и вопросов в двух схемах с записью document_id и chunk_id.
Порядок операций в обычном и позднем разбиении
Обычная схема кодирования начинает с разбиения. Текст делится на предложения, абзацы или куски по предельной длине заранее, до обращения к модели. Затем эмбеддинг-модель применяется к каждому фрагменту по отдельности, повторно. Для получения одного вектора на чанк многие модели используют mean pooling по токеновым представлениям. Так описывает наивный конвейер статья Jina AI о late chunking.
Позднее разбиение меняет порядок. Сначала трансформерный слой модели применяется ко всему тексту или к максимально большой его части. На выходе получается последовательность векторных представлений для каждого токена, и каждое несёт информацию из всего текста. Затем mean pooling применяется к каждому чанку этой последовательности, и эмбеддинг чанка учитывает контекст всего документа. Границы фрагментов нужны в обеих схемах. Разница в том, когда они используются: в наивной — прямо в предобработке, в поздней — после получения токеновых представлений от трансформера.
Отсюда и название метода. Слово «late» указывает на момент применения границ после кодирования. В таблице сравнения из статьи Jina AI наивное разбиение даёт независимые одинаково распределённые эмбеддинги чанков, а позднее — условные, где каждый чанк зависит от остальных. Контекст соседних чанков в наивной схеме теряется, и эвристики вроде перекрывающегося окна лишь смягчают потерю.
Практический вывод для инженера: позднее разбиение требует, чтобы модель отдавала представления по токенам. Если интерфейс возвращает один финальный вектор на вход, применить метод не получится. Порядок операций зависит от архитектуры доступа к представлениям модели.
| Шаг | Обычное разбиение | Позднее разбиение |
|---|---|---|
| Разбиение | До кодирования | После кодирования |
| Вход модели | Отдельный чанк | Весь документ |
| Выход модели | Вектор чанка | Векторы токенов |
| Mean pooling | По чанку сразу | По диапазону чанка |
| Зависимость чанков | Независимые | Условные |
Почему нужен доступ к токеновым представлениям до pooling
Mean pooling усредняет векторы токенов в один вектор чанка. Если pooling выполняется внутри модели и наружу выходит только финальный вектор, промежуточные представления недоступны. Позднее разбиение работает именно на этом промежуточном слое: оно получает токеновые представления из общего контекста и усредняет их по диапазону фрагмента.
Документация проекта Chonkie описывает свой LateChunker так: сначала весь текст кодируется, затем текст делится по рекурсивным правилам, а эмбеддинг каждого чанка получается усреднением соответствующих частей представления полного документа. Формулировка «single embedding» здесь требует уточнения: документ проходит кодирование один раз, затем из токеновых представлений формируются векторы отдельных чанков. Токеновые представления внутри остаются доступны для последующего усреднения по диапазонам.
Вторая статья Jina AI перечисляет признаки, по которым модель или API можно проверить на пригодность. Первый: отдаёт ли интерфейс один финальный эмбеддинг на предложение вместо токеновых представлений — тогда позднее разбиение, скорее всего, не поддержать. Второй: держит ли модель контекст минимум в 8192 токена и подтверждена ли работа на длинных контекстах. Третий: используется ли mean pooling по умолчанию — модели с CLS или max pooling несовместимы с методом.
Для самостоятельно размещённых моделей доступ к слою до pooling обычно есть. Для веб-API он зависит от провайдера. Проверка сводится к одному вопросу: доступны ли векторы по токенам до финального усреднения. Ответ определяет, применим ли метод в вашем конвейере.
Различие late chunking, late interaction и контекстной приставки
Три подхода решают разные задачи и работают на разных уровнях.
Late chunking меняет момент разбиения при построении эмбеддингов. Он даёт один вектор на чанк, но этот вектор учитывает контекст всего документа. Хранение не растёт: размер эмбеддинга остаётся тем же.
Late interaction, известный по моделям семейства ColBERT, сохраняет представления по токенам и на этапе поиска сравнивает их попарно. На этом уровне меняется способ сопоставления запроса и документа. Векторов становится много, и требования к хранилищу другие.
Контекстная приставка — подход Contextual Retrieval от Anthropic. Вторая статья Jina AI описывает его как способ, при котором каждый чанк отправляется в LLM вместе с полным документом, а модель добавляет к чанку релевантный контекст. Это обогащение контекста: глобальный контекст явно вписывается в каждый чанк с помощью языковой модели. Такой путь дороже по стоимости, времени и объёму хранения. Jina AI отмечает, что в качественном исследовании их работы контекстный поиск Anthropic показал результаты, близкие к позднему разбиению, а сам метод опирается на механику encoder-only трансформера.
| Подход | Что меняется | Вектор на чанк | Дополнительное хранение |
|---|---|---|---|
| Late chunking | Момент разбиения | Один | Нет |
| Late interaction | Сопоставление при поиске | Много токенов | Растёт |
| Контекстная приставка | Текст чанка | Один | Растёт |
Выбор зависит от того, что вы оптимизируете. Позднее разбиение встраивается в существующий конвейер эмбеддингов и не требует переобучения модели. Контекстная приставка требует вызовов LLM на каждый чанк. Late interaction меняет слой поиска целиком.
Пример Ориона и сохранение document_id/chunk_id
Возьмём учебный документ D-orion: «Проект Орион выпускает настольный планировщик. Его календарь поддерживает офлайн-работу. Мобильная версия проекта Орион требует подключения к сети». Контрольный фрагмент C-offline: «Его календарь поддерживает офлайн-работу». Вопрос: «Какая версия Ориона поддерживает офлайн-работу?»
В обычной схеме фрагмент C-offline кодируется отдельно. Местоимение «его» остаётся без привязки к «настольному планировщику», потому что определение предмета находится в другом предложении. В поздней схеме токеновые представления берутся из общего контекста D-orion, а затем усредняются по диапазону C-offline. Связь между «его» и планировщиком сохраняется в векторе.
Ожидаемый источник ответа — фрагмент вместе с определением настольного планировщика. Мобильную версию приписывать этому фрагменту нельзя: в тексте сказано, что она требует подключения к сети, и офлайн-работа к ней не относится. Второй вопрос, «Поддерживает ли Орион Linux?», требует знания, которого в документе нет, и заданного ответа не имеет.
Карточка сравнения хранит одинаковые исходные документы, вопросы и границы смысловых фрагментов для обеих схем. В неё записывают document_id, chunk_id, исходный текст, реальные token_start и token_end после токенизации и ссылку на исходник. Конкретные индексы токенов определяются выбранным токенизатором, и в примере они не задаются заранее. Ниже — заготовка карточки, которую заполняют после реального запуска.
import os
import json
# Учебная карточка сравнения. Поля результата заполняются после запуска.
card = {
"document_id": "D-orion",
"document_text": (
"Проект Орион выпускает настольный планировщик. "
"Его календарь поддерживает офлайн-работу. "
"Мобильная версия проекта Орион требует подключения к сети."
),
"chunks": [
{
"chunk_id": "C-offline",
"text": "Его календарь поддерживает офлайн-работу",
"token_start": None, # заполнить после токенизации
"token_end": None, # заполнить после токенизации
"source_ref": "D-orion#C-offline",
}
],
"questions": [
{
"question": "Какая версия Ориона поддерживает офлайн-работу?",
"expected_source": "C-offline + определение настольного планировщика",
"found_chunk_id": None, # заполнить после запуска
"answer_correct": None, # заполнить после запуска
},
{
"question": "Поддерживает ли Орион Linux?",
"expected_source": None, # знания в документе нет
"found_chunk_id": None,
"answer_correct": None,
},
],
}
path = os.environ.get("CARD_PATH", "orion_card.json")
with open(path, "w", encoding="utf-8") as f:
json.dump(card, f, ensure_ascii=False, indent=2)
Поля token_start, token_end, found_chunk_id и answer_correct остаются пустыми до реального прогона. Заполнять их догадками нельзя: индексы зависят от токенизатора, а верность ответа — от фактического поиска. Учебный пример показывает структуру карточки и не является измеренным преимуществом метода.
Границы токенов, окно модели и ограничения длинного документа
Границы фрагментов в позднем разбиении задаются после кодирования, но сама токенизация происходит раньше. Поэтому token_start и token_end нужно получать из того же токенизатора, которым пользуется модель. Смещение на один токен сдвигает диапазон усреднения и меняет вектор чанка.
Окно модели задаёт предел. Первая статья Jina AI описывает модели с поддержкой до 8192 токенов, что соответствует примерно десяти стандартным страницам текста. Вторая статья называет для jina-embeddings-v3 окно в 8192 токена. Документ, который не помещается в окно целиком, придётся делить на части, и контекст между частями сохраняется не полностью.
Документация Chonkie показывает параметр chunk_size со значением по умолчанию 2048 токенов и минимальную длину предложения min_characters_per_chunk со значением 24. Эти значения относятся к конкретному инструменту и его настройкам по умолчанию. Для своего документа их подбирают отдельно.
Вторая статья Jina AI отмечает, что метод устойчив к неточным границам: разбиение по фиксированной длине в токенах, соединённое с поздним усреднением, обходит наивное разбиение с семантическими границами. При этом устойчивость к плохим границам не отменяет их значимости для читаемости результатов человеком и языковой моделью. Оптимизацию границ можно вести ради читаемости, а потерю контекста закрывает сам метод.
Отдельное ограничение — направление внимания. Вторая статья Jina AI разбирает распространённое заблуждение: условные эмбеддинги чанков зависят не только от предыдущих чанков. Матрица внимания у encoder-only трансформера полностью связная, поэтому зависимость двунаправленная. Каждый токен учитывает все остальные токены в пределах окна, включая последующий текст. Для jina-embeddings-v3 это до 8191 других токенов. Это же объясняет устойчивость к точному расположению границ.
План сравнения одинаковых документов и вопросов
Сравнение строится так, чтобы единственным различием между прогонами была схема разбиения. Документы, вопросы и границы смысловых фрагментов остаются одинаковыми.
Порядок действий:
- Зафиксировать корпус документов и набор вопросов. Для каждого вопроса заранее указать эталонный источник — фрагмент, который содержит ответ.
- Определить границы фрагментов и записать их как диапазоны в токенах после токенизации выбранной моделью.
- Прогнать обычную схему: кодировать каждый фрагмент отдельно, получить вектор на чанк.
- Прогнать позднюю схему на той же модели: кодировать документ целиком, усреднить токеновые представления по тем же диапазонам.
- Для каждого вопроса найти ближайшие чанки в обоих индексах и записать найденный chunk_id.
- Сверить найденный chunk_id с эталонным источником и отметить верность ответа.
- Записать в карточку document_id, chunk_id, token_start, token_end и ссылку на исходник.
Таблица для собственной проверки:
| Вопрос | Эталонный источник | Найденный chunk_id | Верность ответа |
|---|---|---|---|
| Какая версия Ориона поддерживает офлайн-работу? | C-offline + определение планировщика | ||
| Поддерживает ли Орион Linux? | знания в документе нет |
Результаты остаются незаполненными до реального запуска. Метрику выбирают заранее: в исследовании Jina AI на наборе BEIR использовался nDCG@10, а рейтинг чанков переводился в рейтинг документов с сохранением первого вхождения. Такой способ подходит, когда важен документ целиком. Если важна точность фрагмента, оценивают попадание найденного chunk_id в эталонный источник.
Вопрос без ответа в документе — отдельная строка проверки. Он показывает, не приписывает ли система ответ фрагменту, который его не содержит. В примере с Орионом вопрос про Linux не имеет эталонного источника, и найденный чанк не должен считаться верным ответом.
Когда улучшать исходный текст и поиск до смены метода
Смена схемы разбиения — не первое действие. Вторая статья Jina AI разделяет две задачи: определение точек разбиения и потерю контекста внутри чанка. Авторы считают их почти ортогональными и решаемыми по отдельности, а приоритет отдают второй. Позднее разбиение закрывает потерю контекста и снижает требования к точности границ.
До смены метода стоит проверить исходный текст. Если документы короткие, потеря контекста между фрагментами мала, и выигрыш от позднего разбиения будет ограничен. Первая статья Jina AI показывает, что улучшение коррелирует со средней длиной документа: чем длиннее документы, тем заметнее разница. На коротких текстах разрыв между схемами небольшой.
Второй шаг — проверить сам поиск. Если запросы плохо сформулированы или эталонные источники не размечены, сравнение схем ничего не покажет. Сначала нужен набор вопросов с известными ответами и указанными фрагментами.
Третий шаг — проверить модель. Вторая статья Jina AI приводит результат ablation-исследования: модель остаётся самым значимым фактором, и нет случая, когда слабая модель с поздним разбиением обходит сильную модель без него. Смена модели даст больше, чем смена схемы разбиения на слабой модели.
Четвёртый шаг — оценить стоимость. Позднее разбиение не требует дополнительного обучения и не увеличивает размер эмбеддинга. Контекстная приставка требует вызовов LLM на каждый чанк и дополнительного хранения. Если бюджет ограничен, позднее разбиение дешевле. Обучение с учителем для метода описано во второй статье Jina AI: используются тройки из запроса, документа и релевантного фрагмента, а обучение идёт с контрастивной функцией потерь вроде InfoNCE. Это дополнительная опция для задач вопросно-ответного поиска.
Частые вопросы
Что такое late chunking простыми словами?
Это способ получить вектор для каждого фрагмента документа так, чтобы вектор учитывал контекст всего документа. Сначала весь текст проходит через эмбеддинг-модель, затем фрагменты выделяются, а их векторы получаются усреднением токеновых представлений по нужному диапазону. Границы применяются после кодирования, отсюда слово «late» в названии.
Чем late chunking отличается от обычного разбиения?
В обычной схеме текст делится на фрагменты заранее, и каждый кодируется отдельно. В поздней схеме сначала кодируется весь документ, а разбиение применяется к последовательности токеновых векторов. В результате эмбеддинги чанков в поздней схеме условны и учитывают контекст соседей, тогда как в обычной они независимы.
Какие модели поддерживают late chunking?
Метод применим к любой модели с длинным контекстом, которая использует mean pooling. Вторая статья Jina AI называет jina-embeddings-v2, jina-embeddings-v3 и nomic-v1. Документация Chonkie указывает, что её LateChunker работает с моделями SentenceTransformer. Модели с CLS или max pooling несовместимы с методом.
Как проверить, подходит ли мой поиск для late chunking?
Соберите одинаковые документы, вопросы и границы фрагментов для двух схем. Прогоните обычное и позднее разбиение на одной модели, найдите ближайшие чанки и сверьте их с эталонными источниками. Запишите document_id, chunk_id и реальные token_start и token_end. Результаты сравнения покажут, есть ли разница на ваших документах.
Нужно ли дообучать модель для late chunking?
Дополнительное обучение не требуется: метод применяется к готовым моделям с длинным контекстом и mean pooling. Вторая статья Jina AI описывает опциональное дообучение на тройках из запроса, документа и релевантного фрагмента с контрастивной функцией потерь. Оно может улучшить результаты в задачах вопросно-ответного поиска, но не является обязательным шагом.
