Контекстная инженерия для слабой локальной модели: как мы делаем среднюю модель надёжной

от автора

Принято думать, что качество ИИ-агента упирается в размер модели. Но когда модель работает локально, в закрытом контуре и на ограниченном железе, брать «побольше» особо некуда. И оказывается, что главный рычаг не модель, а контекст: что вы ей показываете, в каком порядке и как фильтруете. Причём «контекст» здесь — это сборка под то, кто спрашивает, откуда и о чём, плюс честный порог релевантности и продуманный порядок секций. На сильной облачной модели небрежный контекст прощается запасом по reasoning; на средней локальной — нет. Об этом и статья.

Почему на локальной модели контекст важнее самой модели

«Первая Форма» строит агента для закрытого контура: внешние сервисы недоступны, модель работает локально. Это задаёт правила игры: модель, которую реально развернуть на доступном железе, рассуждает скромнее топовых, чаще ошибается, держит меньший контекст. У нас это Qwen3.6-35B-A3B, но почти всё, о чём ниже, от конкретной модели не зависит.

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

Чем наивный RAG убивает слабую модель

Наивная схема «нашёл релевантные куски → склеил → отдал модели» на средней модели работает плохо по двум причинам.

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

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

Теперь поговорим о том, как мы добиваемся ценности сборки.

Как мы собираем контекст под запрос

Главное, что отличает рабочую сборку контекста от «склеить топ выдачи», — она зависит не только от текста вопроса.

Откуда спрашивают. Один и тот же вопрос, заданный из раздела «Разработка» и из раздела «Финансы», должен получить разный контекст. Мы не фильтруем выдачу постфактум, а заранее знаем раздел, откуда пришёл вопрос: подгружаем карту именно этого раздела и взвешиваем источники под него. Документация видна вся, но её релевантность считается с поправкой на то, где находится спрашивающий.

Кто спрашивает. Во-первых, права: агент отдаёт в контекст только то, к чему у спрашивающего реально есть доступ. Это и безопасность (агент не становится обходным каналом к чужим данным), и меньше шума в контексте. Во-вторых, роль: администратору и рядовому пользователю доступны разные инструменты и разный объём служебных деталей. В-третьих, короткий живой профиль автора — его текущие обязанности и проекты, собранные из актуальных данных, а не накопленная «память» о прошлых разговорах. Так модель точнее понимает, с кем говорит.

О чём вопрос. Перед основным вызовом дешёвый классификатор решает, простой это вопрос или сложный. Простому достаётся тонкий промпт и узкий набор инструментов — лишнее не мешает и не съедает окно. Сложному — полный набор и многоходовый режим. Размер и форма контекста подбираются под тип задачи, а не выдаются всем одинаково.

Как мы готовим и контролируем контекст

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

Курация — это несколько сигналов. Мы объединяем несколько ортогональных сигналов (полнотекстовые варианты плюс векторный) и ранжируем ещё и по структуре ссылок между документами — с поправкой, чтобы навигационные «хабы» вроде оглавлений не раздували авторитет всего, на что ссылаются. Само переранжирование вынесли на процессор, чтобы оно не отнимало вычислительный слот у модели.

Контроль качества и галлюцинаций. Мы не отдаём в контекст top-k всегда. Сначала есть порог релевантности: если ничего его не прошло, в контекст идёт почти ничего, а ИИ честно отвечает пользователю, что совпадения нет. Для слабой модели это критично: на мусоре, поданном как «релевантное», она галлюцинирует. У нас как раз случилась такая проблема, наш порог «высокой релевантности» какое-то время выдавал уверенный вердикт на документе, который на вопрос вообще не отвечал. Помогла перекалибровка порога, но вывод был сделан.

Порядок секций: стабильное вперёд, переменное в хвост

То, в каком порядке сложены куски контекста, — отдельное инженерное решение, и сразу по двум причинам.

  1. Внимание. Важное кладём в начало и в конец, так как середину модель, как мы уже выяснили, проваливает.

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

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

Ловим враньё внешней проверкой

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

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

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

Вывод: что вышло и где границы

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

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

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

ссылка на оригинал статьи https://habr.com/ru/articles/1054228/