Chunking для RAG: как делить документы без потери смысла
Сделайте каждый retrieved passage ограниченной и проверяемой evidence-unit
Semantic boundaries, явный overlap, source metadata и контролируемый retrieval
Авторская CHUNK-7 architecture с failure modes и production acceptance criteria
chunking для RAG, семантический chunking, деление документов, RAG система, AI база знаний и CHUNK-7

$ chunk rag --contract CHUNK-7
> receive: source / version / owner / access
> parse: heading / table / caption / locator
> bound: semantic unit / exception / parent
> connect: overlap / adjacent context / lineage
> evaluate: normal / denied / changed / no-answer
> route: index / quarantine / correctionПроблема: chunking — это контракт retrieval, а не счётчик символов
Chunking определяет, какое evidence retriever может вернуть для ответа. Слишком широкий chunk смешивает несколько несвязанных утверждений — embedding становится размытым. Слишком маленький теряет оговорку, исключение, заголовок таблицы или предыдущее определение, без которых фраза меняет смысл. Лимит символов или токенов полезен как технический guardrail, но сам по себе не является semantic policy.
Начинать нужно с вопроса, на который система должна отвечать, и с evidence, которое сможет проверить человек. Для policy поддержки вместе могут понадобиться правило, исключения, дата действия, owner и locator источника. Для инструкции — шаг, prerequisites и warning. Поэтому хороший chunk — ограниченная evidence-unit: у него одна главная цель, достаточно локального контекста и устойчивый путь к исходному документу.
Это уже следующий вопрос после выбора RAG вообще. Если задачу решает rule, template, прямой lookup в системе или решение ответственного человека, сначала нужен меньший путь. Граница описана в гайде по RAG-системам; здесь предполагается, что retrieval оправдан, и разбирается, как сделать corpus понятным для него.
CHUNK-7: контролируемая архитектура chunking
CHUNK-7 отделяет parsing документа от качества retrieval и состоит из семи стадий:
- RECEIVE — принять source со стабильным ID, version, owner, access rule и receipt.
- PARSE — сохранить структуру: title, headings, paragraphs, lists, tables, captions и page/section locators.
- BOUND — разделить материал в осмысленных переходах: heading, procedure, table или явное exception.
- CONNECT — оставить небольшой явный overlap либо parent-section reference между соседними chunks.
- ENRICH — добавить source, version, section path, locator, language, access и lifecycle metadata.
- EVALUATE — проверить representative questions, changed-source, denied-access и no-answer cases.
- ROUTE — отправить ambiguous, oversized, stale или structurally damaged material owner, а не индексировать молча.
Модель не решает, какой документ authoritative. Это делает ingestion contract. Chunking должен сохранить это решение, а не превратить его в безымянный текст. В записи chunk важны не только text и embedding:
type Chunk = {
id: string;
sourceId: string;
sourceVersion: string;
sectionPath: string[];
locator: { page?: number; heading?: string; start: number; end: number };
text: string;
previousChunkId?: string;
nextChunkId?: string;
accessRule: string;
lifecycle: "current" | "superseded" | "quarantined";
};Без section path и locator нельзя проверить retrieved paragraph. Без lifecycle и access ответ может выглядеть обоснованным, но опираться на устаревший или запрещённый материал.
Границы определяет смысл документа
Если у источника есть hierarchy, используйте её. Heading вместе с первым объясняющим paragraph обычно полезнее окна на произвольные 900 токенов. Держите вместе связанные элементы: rule и его exception; instruction и ограничивающий warning; table, title и explanatory notes; definition и определяемые terms.
Не нужен единый «правильный» размер. Нужны target range и явные exception rules. Короткое definition может жить отдельно. Длинную procedure можно разделить по numbered steps, оставив каждому child parent title. Плотную table иногда стоит нормализовать в несколько retrieval units, сохранив title, column headers, document version и row locator. Scanned PDF с ненадёжным порядком текста нужно quarantine до repair: дробление повреждённого текста лишь сделает его легче находимым.
Overlap нужен для continuity, а не для дублирования corpus. Оставляйте его только там, где следующий chunk зависит от предыдущего: transition sentence, общее definition или heading. Он должен быть inspectable. Если одна и та же фраза находится во множестве chunks, top-k начинает переоценивать её и скрывает пробелы coverage.
Контракты parser, index и answer layer
Parser должен возвращать structure, а не только plain text: heading depth, list order, table boundaries, captions, page breaks и extraction confidence. Chunker применяет к ней versioned policy. Index получает chunks вместе с filters по tenant, access scope, language, document type, lifecycle и source version. Answer layer получает выбранные chunks с source locators и правилом, когда evidence недостаточно.
Детерминированные проверки нужно оставить вне модели. Например, отклонять chunk без source ID или access rule, chunk, начинающийся посреди heading, либо материал, у которого current version уже superseded. Модель может предложить semantic split для review, но не должна обходить contract.
require(source.id && source.version && source.owner && source.accessRule);
require(parsed.structurePreserved && chunk.sectionPath.length);
require(chunk.text.length <= policy.maxLength || chunk.exceptionApproved);
require(chunk.lifecycle === "current");
indexable = policy.approved && evaluator.coveragePassed && !chunk.quarantined;Для более широкого разбора delivery полезны кейсы: там систему оценивают через contracts, ownership и verification, а не через feature checklist.
Failure modes, которые маскируются под работающий RAG
Слепые fixed-size windows. Находятся подходящие слова, но conclusion приходит без scope. Исправление: split по structural boundaries и parent context.
Отсоединённые tables и captions. Извлекается строка без column definition или note о применимости. Исправление: table-aware chunks с title, headers и locators.
Избыточный overlap. Почти одинаковые passages захватывают top-k. Исправление: минимальный overlap и тест на duplicate retrieval.
Metadata как постобработка. Есть text, но нет access, version или origin. Исправление: metadata — precondition индексации, а не поздняя enrichment job.
Тихое обновление source. Новая version индексируется рядом со старой. Исправление: lifecycle transition должен быть явным; test обязан доказать, что superseded chunks не выдаются.
Оптимизация только embedding score. Benchmark выглядит хорошо, но reviewer не находит governing paragraph. Исправление: измерять answer support, source precision, no-answer behavior и скорость correction вместе с retrieval relevance.
Production-проверка перед rollout
До изменения всего corpus соберите небольшой evaluation set: обычные вопросы, вопросы с контекстом на границе chunks, ответ из table, denied-access, changed-source и вопросы, где нужен no-answer. Для каждого результата смотрите сам returned chunk, parent section, version, access filter и source locator.
Сравнивайте candidate policy с текущим baseline. Не обещайте универсальный «лучший размер chunk»: полезна только policy, которая улучшает traceable evidence для конкретного corpus и задачи, не ломая permissions и lifecycle. Оставьте rollback path: version chunk-policy, source snapshot, evaluation result и owner correction.
Практический результат — не больше chunks. Это retrieval layer, который может объяснить, почему именно этот passage, из этой version, по этому access rule разрешён как evidence для ответа.
require(source.id && source.version && source.owner && source.accessRule);
require(parsed.structurePreserved && chunk.sectionPath.length);
require(chunk.lifecycle === "current" && evaluator.coveragePassed);
indexable = policy.approved && !chunk.quarantined;