LLM-приложение состоит из модели и частей вокруг неё: приём запроса с правами, поиск по документам, шаблоны запросов, инструменты и действия, проверка ответа, эталонная выборка для оценки качества, журнал со стоимостью, лимиты и кэш. Модель стоит посередине, но качество и безопасность определяют остальные части.

Поломка приложения может быть не в модели, а вокруг неё. Устаревший индекс, правка шаблона без прогона эталона, кэш, отдающий чужой ответ, повторное действие при повторном запросе: такие поломки не видны без журнала и эталона. Поэтому поломку сначала ищут в журнале и на эталоне, а замену модели оставляют напоследок. Услуга описана на странице «Разработка LLM-приложений».

Коротко. Начинайте с эталона из реальных вопросов: без него нельзя сравнить ни две модели, ни две версии шаблона. Права применяются внутри запроса к индексу и перед каждым действием, ключ кэша включает права и версии, в журнал пишутся версии и номера фрагментов, а не тексты. Стоимость считайте по принятым ответам, а не по запросам.

Путь одного запроса: от вопроса до ответа

Рассмотрим ассистента по документам, который может ещё и выполнять действия. Запрос проходит девять шагов, и вероятностный по природе только вызов модели: остальные шаги обычный код, если в них не встроены свои модели.

  1. Приём. Проверяются вход пользователя, его роль и права, лимит запросов; ввод очищается.
  2. Поиск. Вопрос превращается в поисковый запрос, поиск идёт по смыслу и, если эталон это подтверждает, по точным словам; он ограничен правами пользователя внутри запроса к индексу, а не фильтруется после выдачи: закрытые фрагменты не должны попадать даже в промежуточный результат.
  3. Сборка запроса к модели из версионированного шаблона: инструкция, найденные фрагменты, вопрос. Длина контекста ограничена.
  4. Вызов модели с тайм-аутом; ответ нужен в заданной структуре.
  5. Инструменты. Если модель просит действие, слой политик проверяет, разрешено ли оно; дорогие действия ждут подтверждения человека.
  6. Проверка ответа: структура по схеме, ссылки только на найденные источники, порог по релевантности найденных фрагментов, фильтры.
  7. Ответ пользователю, отказ «в базе нет ответа» или передача человеку.
  8. Запись в журнал: идентификатор запроса, версии шаблона и модели, номера найденных фрагментов и их редакций, время, расход.
  9. Учёт стоимости по пользователю и тарифу.

Обычный код можно покрыть тестами. Если в поиске или проверке тоже стоят модели (переписывание вопроса, переранжирование, классификатор атак, оценка ответа другой моделью), они ошибаются так же, и их тоже проверяют на эталоне. Поэтому вокруг модели ставят проверки на входе и выходе. Порог в проверке ответа считают по оценке поиска или по вероятности класса, а не по самооценке модели: фраза «я уверена» не откалибрована.

Принцип поиска перед ответом описан в статье «Что такое RAG», а вопрос о том, дообучать ли модель вместо этого, разобран в статье «RAG или дообучение LLM».

Компоненты: что получает, что отдаёт и чем проверяется

Таблица описывает типовое приложение; в вашем может не быть какой-то части, например инструментов, если приложение только отвечает.

КомпонентВход → выходЧто решают заранееЧем проверяется
Приём и праваЗапрос с учётными данными → пользователь, роль, набор прав, остаток лимитаОткуда берутся роли и кто их назначает; что видит каждая рольПрогон из-под каждой роли с попыткой получить чужое
ПоискВопрос → фрагменты с номером документа и редакцииКак резать документы, нужен ли поиск по точным словам, как часто обновлять индексДоля вопросов эталона, для которых нужный фрагмент найден
Шаблоны запросовИнструкция, фрагменты и вопрос → запрос к модели в пределах длины контекстаФормат ответа, предельная длина, номер версииПрогон эталона при каждой правке
МодельЗапрос → текст или структура и объём израсходованного текстаСвоя в контуре или внешний API; версия закреплена, если это позволяет поставщикЭталон на обоих вариантах, замер задержки в худших случаях
ИнструментыПросьба модели о действии → результат или отказ политикиКакие действия разрешены, какие требуют подтверждения, чем отменяютсяТестовый набор ситуаций, попытка действия вне роли
Проверка ответаОтвет и найденные фрагменты → пропустить, пометить или заменить отказомСхема ответа, порог по оценке поиска, правила для ссылокВопросы вне базы: должен быть отказ
Эталон и оценкаВерсия приложения → метрики по поиску, ответу, отказу и действиямКто размечает, какая часть выборки отложена, что блокирует выкаткуПрогон при каждой смене модели, шаблона, индекса или порога
ЖурналИтог каждого шага → запись без текстов запроса и ответаЧто писать, сколько хранить, кто читаетМожно ли по записи понять, из чего собран запрос: версия документа, фрагмент, шаблон
Лимиты и кэшЗапрос и ключ → ответ из кэша или допуск к моделиИз чего состоит ключ кэша, срок записей, поведение при отказе моделиПроверка ключа кэша; нагрузочный прогон при выводе в эксплуатацию

Устройство индекса для поиска по смыслу разобрано в материале про векторные базы данных.

Оценка качества на эталонной выборке

Без эталона нельзя сказать, как изменилось качество приложения после любой правки. Эталон собирается из реальных запросов, а не из удобных. Перед включением в эталон из вопросов убирают персональные данные и коммерческую тайну: эталон хранится в репозитории, а если ответы оценивает внешняя модель, вопросы уходят поставщику.

Что входит в эталон

  • вопросы пользователей с известными ответами и указанием документа, из которого ответ берётся;
  • вопросы вне базы, на которые приложение обязано отказаться;
  • трудные случаи: две редакции документа, сокращения, номера приказов, вопросы из нескольких частей;
  • для приложений с действиями: ситуации, где верное действие, неверное действие и отказ;
  • набор атак со вставленными инструкциями.

Что считаем отдельно

Одна общая оценка скрывает, где ломается. Поиск оценивают по тому, найден ли нужный фрагмент, ответ — по верности и наличию ссылки, отказ — на вопросах вне базы, инструменты — по выбору действия и его параметров. Тогда по падению понятно, какая часть виновата.

Как проверять

Часть выборки используют при настройке, часть откладывают и не показывают. Прогон повторяют при каждой смене модели, шаблона, индекса или порога, и новая версия не выходит в работу, если на эталоне она хуже допустимого порога, записанного заранее. Оценка ответов другой моделью удобна для массового прогона, но её сверяют с разметкой человека на части выборки. Выбор порога, ниже которого приложение отказывается отвечать, разобран в статье «Порог уверенности модели», методика для распознавания документов — в статье «Как измерить точность распознавания документов».

Защитные проверки: вход, источники, выход и действия

Защита стоит в четырёх местах: на входе, на источниках, на выходе и на действиях. Ни одно из них не надёжно по отдельности, поэтому ставят все четыре, а набор атак из эталона прогоняют после каждой смены модели или шаблона.

Вход

Лимиты на пользователя и длину ввода, очистка, обнаружение явных попыток подмены инструкций. Это не полная защита, а первый фильтр.

Источники

Документы, письма и страницы, которые попадают в индекс или контекст, могут содержать вставленные инструкции, поэтому их считают недоверенным вводом: источники проходят очистку, а модель не получает права на действия из-за инструкции в документе. Механика атаки разобрана в статье «Что такое prompt injection».

Выход

Ответ проверяют по схеме, ссылки сверяют с найденными источниками: это отсекает выдуманные источники, но не искажённый пересказ настоящих, его ловят эталон и выборочная проверка людьми. Ссылки наружу пропускают по белому списку, а текст ответа не вставляют в страницу или команду без экранирования.

Действия

Модель только предлагает действие, исполняет его код под отдельной учётной записью с минимальной ролью. Слой политик сверяет действие с ролью, проверяет параметры и лимит, а для дорогих действий возвращает «нужно подтверждение» вместо результата. Повторная доставка запроса не создаёт дубль: у операции есть ключ идемпотентности, составленный из реквизитов, например из номера и даты документа. Отдельный выключатель, не зависящий от модели, отключает инструменты. Подробно — в статье «Как ограничить ИИ-агента». Перед запуском пройдите чек-лист безопасности LLM-системы.

Журнал без текстов запросов

Журнал нужен, чтобы по записи понять, из чего собран запрос: версия документа, фрагмент, шаблон. Точно воспроизвести ответ он не позволяет: модель может ответить иначе. Но запросы пользователей могут содержать фамилии, телефоны и номера документов, и журнал с текстами сам становится хранилищем персональных данных. Поэтому записывают структуру, а не текст.

Что пишем

  • идентификатор запроса, псевдоним пользователя и роль; если по псевдониму можно выйти на человека, журнал защищают как хранилище персональных данных;
  • версии шаблона, модели, индекса и порога;
  • номера найденных фрагментов и документов с редакцией, а не их тексты; версии документов в индексе хранятся весь срок хранения журнала, иначе по номеру не узнать, что видела модель;
  • результат каждой проверки, итог (ответ, отказ, передача человеку), выбранные инструменты и их результат;
  • время по шагам, расход текста и признак попадания в кэш.

Что не пишем

Тексты запросов и ответов по умолчанию не хранятся. Если для разбора ошибок они нужны, их пишут в отдельное хранилище с автоматическим маскированием фамилий, телефонов и номеров. Маскирование находит не всё, поэтому такое хранилище считают содержащим персональные данные: короткий срок, ограниченный доступ, выборочная проверка масок.

Журнал держат в вашем контуре и в вашей базе: так вы сами управляете сроком хранения и доступами, а персональные данные граждан РФ обычно должны храниться на серверах в России. Срок и круг лиц с доступом утверждает ваша сторона; вопросы статуса оператора и оснований обработки готовит юрист заказчика, мы консультаций не даём.

Версии шаблонов запросов и модели

Шаблон запроса — такой же код, как остальной. Он хранится в репозитории, получает номер версии, проходит проверку и откатывается так же, как программа.

  • Шаблон отделён от кода и собирается из частей: инструкция, формат ответа, примеры. Одну часть можно менять, не трогая другие.
  • Версия записывается в журнал, поэтому любой ответ можно связать с шаблоном и моделью, которые его породили.
  • Правка проходит эталон. Выкатка блокируется, если качество на эталоне упало больше допустимого порога, записанного заранее. На небольшой выборке один вопрос заметно двигает долю, а ответы модели меняются от прогона к прогону, поэтому спорные различия проверяют повторным прогоном и разбором ответов.
  • Меняют одно за раз: либо шаблон, либо модель, либо индекс. Иначе непонятно, что дало изменение.
  • Версию модели закрепляют там, где это возможно. Своя модель меняется, когда решили вы. Закреплённую версию внешнего API поставщик со временем снимает, и вернуться на неё нельзя, поэтому срок отслеживают, а переход планируют заранее, с прогоном эталона.

Общий интерфейс к модели упрощает замену, но не делает её бесплатной: шаблоны, формат вызова инструментов и поведение у разных моделей отличаются, так что замену планируют как обновление, с правкой шаблонов и прогоном эталона. Критерии выбора между своей моделью и внешним API разобраны в статье «Своя LLM или ChatGPT API».

Управление стоимостью запросов

Стоимость запроса складывается из объёма текста на входе и выходе, выбранной модели, повторов и нагрузки на оборудование, если модель своя. Цены зависят от поставщика и оборудования, поэтому здесь их нет.

Что измеряем

Расход на каждый шаг, на пользователя и на тариф, долю повторных запросов и долю отказов. Считайте стоимость принятого ответа: весь расход за период, включая отказы и повторы, делённый на число принятых ответов. Что считать принятым, решают заранее: отметка пользователя или отсутствие повторного вопроса.

Чем управляют

  • Размер контекста: ограничивают число и длину найденных фрагментов.
  • Короткие шаблоны: лишние инструкции и примеры убирают, проверяя на эталоне.
  • Кэш одинаковых запросов. Ключ включает набор прав пользователя (не только роль), версии индекса, шаблона и модели, иначе кэш отдаст чужой или устаревший ответ. Кэш хранит тексты ответов, поэтому к нему применяют правила журнала с текстами; ответы, после которых выполнялись действия, не кэшируют.
  • Разные модели для разных шагов: классификацию и извлечение полей отдают модели поменьше, сложные ответы оставляют сильной.
  • Лимиты и тайм-ауты: на пользователя, на тариф, на число повторов.

Порядок для любой меры один: прогон эталона до и после; если качество упало больше допустимого порога, мера отменяется, даже если расход снизился. Цену владения целиком, включая оборудование и сопровождение, разбирает статья «Сколько стоит разработка ИИ на заказ», а расчёт памяти для своей модели — статья «Сколько видеопамяти нужно для LLM».

Симптом, причина, что проверить

Найдите симптом в первом столбце и проверьте пункты из третьего по порядку. Начинайте с журнала: версии и номера фрагментов показывают, что нашёл поиск и что получила модель.

СимптомВероятная причинаЧто проверить
Ответ уверенный, но неверныйНужный фрагмент не найден, найден обрезанным или противоречит другому (две редакции), либо модель отступила от найденногоЖурнал найденных фрагментов; попал ли нужный фрагмент в запрос к модели целиком; нет ли в выдаче двух редакций; есть ли документ в индексе; работает ли отказ ниже порога
Ответ по отменённой редакцииСтарый документ остался в индексеДата изменения и статус документа в индексе, правила обновления
Пользователь видит чужой документПрава применяются после поиска или не применяются; ключ кэша не включает набор правФильтр по правам внутри запроса к индексу; прогон из-под каждой роли; состав ключа кэша
После правки ответы изменилисьШаблон выкатили без прогона эталонаВерсия шаблона в журнале; сравнение прогонов до и после
Номер приказа или аббревиатура не находятсяТолько поиск по смыслу, неудачная нарезкаПодключён ли поиск по точным словам; как разрезан документ
Разбор ответа падаетМодель вернула ответ не по схемеДоля ответов вне схемы; изменение версии модели; строгость формата в шаблоне
Приложение встаёт на пикеНет тайм-аутов и лимитов, повторы множат нагрузкуТайм-аут вызова модели; лимиты на пользователя; число повторов
Расход растёт без роста нагрузкиДлинный контекст, повторы, кэш не работаетРазмер найденного контекста; доля повторных запросов; расход по шагам
Агент создал дубльПовторный запрос без ключа идемпотентностиКлюч идемпотентности; журнал действий; повторная доставка входа
Качество упало, код не менялсяИзменились данные или поведение модели у поставщикаВерсия модели в журнале; прогон эталона; свежие примеры входа

Что делаем мы и с чего начать

Мы берём один контур или один участок и сверяем его с этой схемой: какие части уже есть, каких нет, что можно оставить как есть. Услуга описана на странице «Разработка LLM-приложений», выбор типа решения — на странице «Разработка ИИ на заказ».

Начните с эталона: соберите реальные вопросы с ответами (из почты, чата поддержки, обращений) и отметьте, какие из них должны вызывать отказ. На небольшой выборке различия между версиями не показательны, поэтому размер набора уточняется на обследовании. Если нужна помощь, обследование стоит 200 000 ₽ за один-два процесса, оно вычитается из первого этапа, если продолжаете с нами, а цены других ступеней — на странице тарифов. На выходе схема приложения со списком недостающих частей по порядку важности, эталонный набор, решение по выбору модели и вилка бюджета. Исходный код, шаблоны запросов и эталон лежат в вашем репозитории с первого коммита.

Частые вопросы

Из чего состоит LLM-приложение?

Из приёма запроса с правами, поиска по документам, шаблонов запросов, модели, инструментов, проверки ответа, эталонной выборки, журнала со стоимостью, лимитов и кэша. Модель одна из частей, и качество с безопасностью зависят от остальных.

Что делать, если модель недоступна или отвечает слишком долго?

Ограничить ожидание тайм-аутом и число повторов, затем переключиться на запасной путь: другая модель за тем же интерфейсом, ответ из кэша там, где это безопасно, или честный отказ с передачей человеку. Повторы без предела превращают сбой одной модели в нагрузку на всю систему. Запасную модель прогоняют на том же эталоне.

Как оценивать качество LLM-приложения?

На эталонной выборке реальных вопросов с известными ответами. Часть выборки откладывается и не показывается при настройке, а прогон повторяется при каждой смене модели, шаблона, индекса или порога. Поиск, ответ, отказ и действия оцениваются отдельно.

Что писать в журнал, если в запросах персональные данные?

Структуру: идентификатор запроса, версии, номера найденных фрагментов, результаты проверок, время и расход. Тексты запросов по умолчанию не хранятся; если они нужны для разбора ошибок, их пишут в отдельное хранилище с масками, коротким сроком и ограниченным доступом внутри вашего контура. Маскирование находит не всё, поэтому такое хранилище считают содержащим персональные данные.

Как версионировать шаблоны запросов?

Как код: хранить в репозитории, давать номер версии, проверять на эталоне до выкатки, записывать версию в журнал и уметь откатить. Менять за раз что-то одно: шаблон, модель или индекс.

Как снизить стоимость запросов без потери качества?

Ограничить размер контекста, сократить шаблоны, кэшировать одинаковые запросы, отдать простые шаги модели поменьше и поставить лимиты. Каждую меру нужно проверять на эталоне, потому что экономия, которая роняет качество, обходится дороже.

Нужен ли кэш и чем он опасен?

Нужен, если вопросы часто повторяются: кэш снижает расход и задержку. Но он опасен неверным ключом: если ключ не включает права пользователя и версии индекса, шаблона и модели, кэш отдаст чужой или устаревший ответ. Кэш хранит тексты ответов, поэтому ему нужны те же правила, что журналу с текстами: короткий срок и ограниченный доступ.

Какую модель выбрать: свою или внешний API?

Для данных ограниченного доступа выбирают свою модель в контуре: облачные модели для таких данных чаще всего не подходят по требованиям заказчика. Для данных без ограничений и быстрого старта подходит внешний API, если его разрешила ваша служба безопасности. Окончательный выбор делает прогон одного эталона на обоих вариантах с замером задержки в худших случаях.