От простого подхода к производственному решению RAG для отчетов SEC 10-K: разбиение на фрагменты, гибридный поиск.
Почему пайплайн RAG в учебнике потерпел неудачу при обработке 300-страничных отчетов 10-K и как это исправить: разбиение на фрагменты с учетом структуры разделов, гибридный поиск, переранжирование с использованием кросс-кодеров, проверка качества и использование метаданных.
Источники: **How I Built a RAG Pipeline on Legal & Financial Documents and What Actually Broke?**
Пайплайн, который выглядел правильно, но возвращал неверные данные
Представьте пайплайн типа RAG для обработки документов SEC 10-K, на создание которого ушло три недели: в его состав входили компоненты LangChain, хранилище векторов ChromaDB, чистый скрипт загрузки данных на Python и удобный интерфейс Streamlit. В качестве первого простого теста был задан вопрос о том, какие критически важные аудиторские аспекты касаются учета доходов. Система с полной уверенностью ответила о компенсациях руководства, хотя речь шла совершенно о другом разделе документа. Слово «галлюцинация» здесь не совсем подходит — текст действительно существует, но он взят из неверной части 300-страничного документа, причем система этого не заметила.
Внешний вид системы типа RAG и её реальная работа — это разные вещи. Ниже приведены решения по разбиению текста на фрагменты, сбои при поиске информации, эксперименты с вкладками, набор данных для оценки, сформированный из необходимости, а также версия системы, которая в итоге сократила время ручной проверки на 65%. Любой, кто разрабатывает функции на основе реальных документов, а не простых PDF-файлов, может избежать недель бесполезного обучения.
Почему документы типа 10-K трудно найти
Рассматривать документ 10-K как обычный PDF — значит недооценивать его. Документ крупной или средней публичной компании состоит из 150–400 страниц с финансовыми таблицами, сносками, ссылающимися на другие сноски, длинными юридическими формулировками, описанием рисков и комментариями руководства. Целью поиска были критически важные аспекты аудита (Critical Audit Matters, CAM), в которых аудиторы объясняют самые сложные или требующие особого суждения части своей работы.
То, что читатель хочет найти, такое как термины, связанные с рисками, и заключения аудита, распределено неравномерно. Оно сосредоточено в определённых разделах, написано специализированной лексикой и связано с цифрами, которые теряют смысл вне контекста. Документ рассматривается как набор отдельных фрагментов, и система поиска чаще всего выбирает неверные из них, что и произошло в первой версии.
Два подхода параллельно
Различия между версиями содержат все уроки.
Версия 1 соответствовала стандартному подходу: загружался PDF, он делился на фрагменты по 500 токенов с перекрытием 50 токенов, затем применялась модель OpenAI text-embedding-ada-002 для генерации эмбеддингов, данные хранились в ChromaDB, для запроса извлекались 5 лучших фрагментов, которые передавались в GPT-4 для составления резюме. Этот подход срабатывал при простых запросах к чистым документам, но при работе с объёмным 300-страничным документом он давал сбои тремя конкретными способами.
Версия 2 включает гораздо больше этапов: загрузку PDF, слой обнаружения разделов, разбиение на фрагменты с учетом типа раздела и изменяющимися размерами, меткирование метаданных для каждого фрагмента, модель вкладки с учетом контекста домена, хранение в ChromaDB с подробными метаданными, переписывание запросов, гибридный поиск (семантический плюс по ключевым словам), переранжирование, проверку качества и, наконец, генерацию результатов. Каждый из этих элементов существует по определенной причине, которая будет рассмотрена далее.
Первая проблема: фиксированные фрагменты разрывают контекст
Самая распространенная ошибка версии V1 заключалась в использовании фрагментов по 500 токенов, что приводило к разрыву структуры в тех местах, где происходило достижение этого числа. Документы формата 10-K имеют естественную структуру: они состоят из заголовка, описания, ответа аудитора и часто ссылки на финансовую запись, причем длина такого документа может достигать 600–800 токенов. Разделение на фрагменты по 500 токенов часто приводило к разрыву документа пополам — описание оказывалось в одном фрагменте, а ответ — в другом, из-за чего ни один из фрагментов не был полезен сам по себе.
Решением стало добавление слоя обнаружения разделов перед процессом разбиения на части. pdfplumber анализировал структуру документа для поиска границ разделов, заголовков, подзаголовков и краев таблиц, после чего разбиение происходило внутри разделов, а не между ними, с учетом естественных разделителей. В случае густых финансовых таблиц формировались небольшие части объемом примерно 200–300 токенов, чтобы сохранить возможность поиска деталей на уровне строк. В разделах с повествовательным текстом, таких как CAM и информация о рисках, формировались более крупные части объемом до 800–900 токенов, чтобы сохранить целостность каждой логической единицы. Уровень перекрытия оставался на уровне 10% от размера части. Уже эта одна изменение значительно улучшило релевантность результатов поиска, прежде чем были внесены какие-либо другие изменения.
Вторая проблема: эмбеддинги, не учитывающие финансовую лексику
text-embedding-ada-002 — это мощная универсальная модель, хотя она и не обучалась специально на финансовом или юридическом тексте, и это сказывается на обработке объемных аудиторских документов. Запрос о сферах принятия решений, связанных с учетом доходов, привел к получению фрагментов, касающихся доходов и принятия решений в общем смысле. Часть информации поступила из обсуждений руководства, часть — из примечаний, а небольшая часть — из не связанных с этим разделов, посвященных раскрытию рисков; сам модуль CAM для учета доходов не был найден надежным образом.
Были протестированы два варианта. Более новая модель OpenAI — text-embedding-3-large — значительно улучшила качество поиска. Гибкий подход, сочетающий плотный векторный поиск с разреженным поиском по ключевым словам с использованием алгоритма BM25 и объединяющий полученные результаты, превзошел оба метода по отдельности. Логика проста: плотный поиск находит контент, концептуально связанный с запросом, тогда как разреженный поиск находит точные формулировки. В финансовых текстах точность формулировок имеет огромное значение. Фраза «критически важный аудиторский вопрос» не является синонимом фразы «значительная аудиторская проблема» в юридической терминологии, и наличие обеих формулировок существенно повышает точность результатов. Для разреженного поиска использовалась библиотека Python rank_bm25, а простая функция слияния результатов на основе обратного ранга объединяла два списка результатов перед их повторным сортированием.
Третья проблема: отсутствие проверки, уверенные неверные ответы
Это была самая тихая, но в то же время самая опасная проблема. В версии V1 некачественные фрагменты данных, полученные в процессе поиска, всё равно заставляли большую языковую модель отвечать, причём GPT-4 очень хорошо справляется с тем, чтобы казаться уверенной в себе при недостатке контекста. Её ответы не казались явно неверными — это были правдоподобные краткие изложения того, что было предоставлено в качестве фрагментов, даже если эти фрагменты взяты из неправильной части документа.
Проблему удалось обнаружить лишь с помощью небольшого набора для оценки: 30 пар вопросов и ответов, проверенных вручную на соответствие конкретным документам, каждая из которых сопровождалась указанием точных исходных фрагментов, которые должны были быть использованы. По этим критериям версия V1 правильно находила нужные исходные фрагменты примерно в 55% случаев, а в оставшихся 45% случаях генерировала связный текст на основе нерелевантных данных.
Два изменения решили проблему. Первым был этап переранжирования: кросс-кодер от HuggingFace под названием ms-marco-MiniLM-L-6-v2 пересчитал оценки десяти полученных фрагментов, и только три лучших попали дальше к генератору. Кросс-кодеры работают медленнее, чем би-кодеры, но обеспечивают гораздо более точное ранжирование, поскольку запрос и кандидатский фрагмент считаются одним входным данным, а не двумя отдельными.
Вторым изменением стала система проверки перед генерацией. Она проверяла, содержат ли полученные фрагменты идентификаторы разделов, ожидаемые на основе метаданных момента загрузки. Например, если вопрос о CAMs возвращал фрагменты из таблиц компенсаций, проверка проваливалась, и система переписывала запрос и пыталась снова до двух раз, прежде чем отметить ответ как имеющий низкую степень достоверности. Это отличает демо-версию от производственной системы: демо-версия охватывает успешные сценарии, тогда как производственная система справляется с 45% случаев, когда такой сценарий не срабатывает.
Метаданные для каждого фрагмента
В учебниках по RAG редко упоминается одно из дизайнерских решений: наличие подробных метаданных у каждого фрагмента. Каждый фрагмент в коллекции ChromaDB содержал следующие поля:
section_type— раздел документа 10-K, откуда взят фрагмент, например CAM, информация о рисках, финансовая отчетность, примечания или обсуждение руководствомsection_title— точное название раздела над содержимымpage_range— номера страниц исходного документа, охватываемые фрагментомdocument_id— идентификатор документа в виде тикера компании и финансового годаhas_table— булево значение, указывающее на наличие табличных данныхfiscal_year— период отчетности
Метаданные выполняли две функции. Во-первых, они позволяли осуществлять фильтрованное поисковое восстановление: при поиске информации, связанной с аудитом, можно было заранее отфильтровать результаты до типа раздела critical_audit_matter перед использованием векторного поиска, что значительно сужало объем поиска. Во-вторых, они обеспечивали работу слоя цитирования в интерфейсе, поскольку каждое резюме указывало на страницы и разделы, из которых оно взято, что позволяло аудиторам немедленно проверять исходные источники.
В инструментах финансового аудита цитирование является обязательным. Аудитор, не способный подтвердить происхождение резюме, не может полагаться на него, а генерация цитат на основе метаданных превратила систему из простого инструмента для изучения в что-то, с чем пользователи могли реально работать.
Итоговая система
Полная цепочка обработки, сократившая объем работы по аудиту на 65%, состоит из трех уровней.
- Прием данных: использование pdfplumber для обнаружения разделов, динамического разбиения на части, извлечения метаданных, встраивания информации с помощью модели text-embedding-3-large, а затем хранение в ChromaDB с полными метаданными.
- Запрос: переписывание запроса с использованием LLM для повышения точности, фильтрация результатов с использованием предварительного фильтра по типу раздела при необходимости, гибридный метод поиска с использованием алгоритмов dense и BM25 для выбора топ-10 результатов, дополнительная сортировка с помощью cross-encoder для выбора топ-3 результатов, проверка качества, генерация текста с помощью GPT-4, добавление ссылок на источники и отображение результатов в интерфейсе.
- Оценка: использование набора из 30 элементов для отслеживания точности поиска и оценки качества ответов; для определения релевантности, точности и полноты информации применяется LLM-судья.
Интерфейс Streamlit предоставлял аудиторам поле для ввода запросов, генерируемое резюме, ссылки на источники, которые открывались по запросу, а также показатель уверенности, полученный в результате проверки качества.
Что нужно сделать иначе
- Сначала создайте набор данных для оценки. Его собрали в спешке после неудачи версии V1, но он должен быть готов с самого начала. Без измерений ничего не улучшится, а фраза «кажется, лучше» не является показателем.
- Заранее спроектируйте схему метаданных. На её правильную настройку ушло время, но каждый потраченный час окупился в несколько раз благодаря повышению качества поиска и появлению функций интерфейса, которые она обеспечивает. Метаданные заслуживают первостепенного внимания в архитектуре.
- Исходите из того, что парсер PDF может что-то упускать. pdfplumber — отличный инструмент, однако объединённые ячейки, повёрнутые заголовки или маркеры сносок внутри ячеек в финансовых таблицах всё ещё могут его сбивать. Фиксируйте каждую аномалию при извлечении данных и регулярно просматривайте эти записи, поскольку безвызовные сбои при обработке — самые сложные для отслеживания позже.
- Сомневайтесь во всем по умолчанию. Значения по умолчанию для размера блоков, модели встраивания и критерия top-k существуют потому, что в среднем они работают приемлемо, но тип документа может не соответствовать этому среднему случаю. Протестируйте каждый из этих параметров на реальных данных.
Результат
После внедрения система извлекала и подводила итоги критически важных аудиторских аспектов, требуя на 65% меньше усилий на ручную проверку по сравнению с предыдущим процессом. Выполняя функцию первичного рецензента, она находила соответствующие разделы и составляла структурированные краткие отчеты, благодаря чему аудиторы могли сосредоточиться на проверке и оценке, а не на извлечении и обобщении информации.
Именно этот последний момент имеет наибольшее значение. Целью никогда не было замена суждений аудиторов, а лишь устранение механических, трудоемких этапов, чтобы экспертные суждения могли быть применены там, где это действительно важно. Хорошо спроектированные системы на основе больших языковых моделей не заменяют экспертизу — они направляют ее использование.
Связанные материалы
- AegisOps: Автономный агент SRE с четырьмя уровнями памяти — Как агент LangGraph ReAct решает инциденты в Kubernetes с использованием эпизодической памяти pgvector, уровней автономии, контролируемых правилами, шестнадцати инструментов и полной истории аудита.
- Сравнение 16 архитектур RAG: какой подход подходит для каждой проблемы — Обзор шестнадцати паттернов RAG в четырех категориях, от стандартных и гибридных методов поиска до агентных, графовых и федеративных решений, с пошаговым планом разработки месяц за месяцем.