Содержание
Коротко
Автор вакансийной платформы описал, что меняется, когда RAG выходит за пределы демонстрации и начинает обрабатывать больше 10 тысяч объявлений в сутки. RAG — это подход, при котором большая языковая модель получает найденные по смыслу фрагменты данных перед тем, как сформировать ответ или оценку.
Главные сложности оказались не в самой модели. На точность и счёт влияют разбиение документов, качество исходных данных, хранение векторов и возможность увидеть тихие сбои в цепочке обработки.
Что произошло
Платформа сопоставляет вакансии с профилями кандидатов. Сначала текст объявления очищают и приводят к единой форме, затем делят на фрагменты, строят для них векторные представления и ищут ближайшие по смыслу. После этого LLM оценивает релевантность найденной вакансии.
Автор отказался от фрагментов фиксированного размера: они разрывали связанные требования и условия. Лучше сработало рекурсивное разбиение — сначала по абзацам, затем по строкам и предложениям — с небольшим перекрытием между соседними фрагментами. Так важная фраза на границе не исчезает из поиска.
Особенно полезной оказалась предварительная нормализация. Разные системы найма возвращают описания в разных форматах, иногда с HTML. Если не очистить и не унифицировать входящие данные до разбиения, пустые или искажённые фрагменты доходят до поиска незаметно.
Почему это важно
Разбиение текста — не второстепенная настройка. Оно определяет, какой контекст попадёт в поиск, сколько векторных представлений придётся построить и насколько много кандидатов передать модели для дорогой финальной оценки.
Для векторных представлений автор сравнил облачную модель OpenAI и локальную модель на той же машине. Локальный вариант был дешевле, но хуже различал специфические требования вакансий; экономия на построении векторов превращалась в более шумную выдачу и лишние обращения к LLM.
Хранение в pgvector рядом с данными PostgreSQL также решило операционную проблему. Отдельное векторное хранилище удобно для быстрого прототипа, но требует поддерживать синхронизацию. В одной базе объявление и его векторы записываются одной транзакцией, поэтому после сбоя не возникает двух расходящихся версий данных.
На практике
Начинать стоит с формы собственных документов, а не с универсального размера фрагмента. Вакансия, договор и карточка товара имеют разную внутреннюю структуру; границы разбиения должны её сохранять.
Для крупного потока полезно разделить быстрый поиск и дорогую оценку моделью. Автор сократил расходы за счёт пакетной отправки запросов, кэша для повторяющихся профилей и выбора более доступной модели для типовых ролей.
- Нормализуйте входящий текст до разбиения: удаляйте HTML, приводите переносы строк и проверяйте обязательные поля.
- Измеряйте качество найденных фрагментов на реальных запросах, а не только на удобных примерах из разработки.
- Храните векторы вместе с основными данными, если это упрощает транзакции и резервное копирование; отдельно проверьте настройку индекса и время поиска.
- Ограничивайте дорогие вызовы LLM: объединяйте задания в пакеты, кэшируйте результаты и применяйте более сильную модель только там, где это оправдано.
- Добавьте идентификатор прохождения для каждого документа и записывайте число фрагментов, время построения векторов, ошибки и итоговую оценку.
Наблюдаемость должна появиться до роста нагрузки. В описанном случае только связанное журналирование показало, что некоторые источники формировали пустые фрагменты: процесс не падал, но вакансии пропадали из результатов поиска.
Итог
Опыт автора показывает, что устойчивый RAG строится прежде всего вокруг данных и эксплуатации. Выбор модели или векторного индекса важен, но не компенсирует неочищенный ввод, неверные границы фрагментов и отсутствие измерений.
Практичный порядок работы — сначала сделать данные однородными, затем проверить качество поиска, контролировать дорогие обращения к модели и наблюдать весь путь документа. Тогда масштабирование не превращает рабочую демонстрацию в непрозрачный источник расходов.

