
Эта публикация закрывает цикл из двух материалов. Здесь — информация для тех, кто уже наигрался с управлением контекстом и немного покрутил RAG/агентов, но пока не успел выстроить в голове системную картину. А в первой статье мы обсудили простые правила хорошего промпта для новичков.
Привет, на связи Настя из Cloud.ru. В прошлый раз поговорили о простых материях: контексте, расширенном промпте и ролях. А в этой части обсудим, что делать, если хорошего промпта уже недостаточно: RAG, файнтюнинг, работу с ИИ-агентами. Ну и ограничения, в которые ИИ упрется, даже если вы все сделали правильно. Если вы уже спотыкались о кривой чанкинг, ответили для себя на вопрос, что лучше — BM25, векторный поиск или гибрид, настраивали RAGAS и пробовали скрестить это все с простенькими агентами, которые сами ходят в базу знаний и дергают пару-тройку инструментов, — вам здесь будет скучновато, лучше почитать что-нибудь позабористей от моих коллег.
Ну а чтобы вы в мою панамку не пихали всякого неприличного (как в прошлый раз) в этот крестовый поход я взяла с собой двух коллег-рецензентов из числа дата-сайентистов Стаса (@gridstan) и Володю (@lovets18). Они разбираются в этих ваших RAG и дообучениях намного лучше меня, и проследили, чтобы сюда не просочилось ничего обывательского.
Deus vult!
ИИ не телепат — строим RAG
Для начала пример: два нью-йоркских адвоката писали судебный иск и попросили ChatGPT обычным zero-shot-промптом найти им прецеденты. Нейросеть сгенерировала великолепные, десять из десяти логичные дела, которых никогда не существовало. Адвокаты не проверили тексты и принесли эту галлюцинацию в федеральный суд. Итог — публичное унижение на весь мир и штраф.
Вывод: модель не знает вашу документацию и кейсы, потому что ее обучали на вебе, а не на вашей вики. Чтобы это починить, мы не переучиваем модель — это долго, дорого и в большинстве случаев не нужно.
Поэтому вместо попыток научить модель всему обычно строят RAG-пайплайн: пользователь задает вопрос → система ищет релевантные документы → передает их в контекст модели → модель отвечает на основе найденных фрагментов.

Как это работает пошагово:
Сырые данные → подготовка
Есть ваши исходники: PDF‑регламенты, DOCX, презентации, страницы из Confluence и аналогов. Их можно выгружать и парсить с помощью, например, Apache Tika, встроенных парсеров в Elasticsearch или облачных сервисах.
Чанкинг — нарезка на куски
Длинные документы режем на небольшие смысловые фрагменты: по заголовкам, разделам, пунктам. Это можно делать через LangChain, LlamaIndex или скриптами на Python с поддержкой этих библиотек.
Эмбеддинги и векторная база
Каждый фрагмент превращается в вектор (эмбеддинг) с помощью модели типа OpenAI embeddings, BGE, GTE, Cohere и т.п., и складывается в векторное хранилище — Qdrant, Weaviate, Milvus, pgvector в PostgreSQL или Elasticsearch/OpenSearch.
Запрос пользователя и поиск
Вопрос пользователя тоже преобразуется в вектор и ищет похожие по смыслу куски в векторной базе. Это тот же Qdrant/Weaviate/Elasticsearch, к которым вы обращаетесь через API.
LLM и ответ
Найденные фрагменты вместе с вопросом отправляются в LLM (например, через API к облачной или к своей развернутой LLaMA/Mistral‑модели). Модель читает эти куски и формирует ответ уже на основе ваших документов, а не только своей предобученной памяти.
На словах отлично, демо на конференциях показывают идеальный путь: чистая база, понятный вопрос, релевантный ответ… Но мы бы не писали эту статью, если бы ковровая дорожка к успеху не прикрывала десяток-другой граблей. На практике выясняется, что:
ассистент уверенно ссылается на документ, в котором написано совсем не то;
на очевидный вопрос «где регламент по отпускам» модель отвечает «не знаю», хотя регламент лежит в базе;
ответ получается обрывочным: половина из одного раздела, половина из другого;
иногда помощник работает мгновенно, иногда думает по двадцать секунд;
счет за месяц приходит в два раза больше ожидаемого.

Когда RAG оказался враг
Документы порезаны как попало. Регламент — связный текст, где смысл одного абзаца зависит от соседних. Если при разбивке на чанки система разрезала его автоматически посередине параграфа, ассистент получит только обрывок и использует его для ответа.
Для пользователя это выглядит как галлюцинация, хотя нужные данные в базе были — система просто не смогла собрать их воедино.

Поиск находит не то. Если ваши документы написаны на русском с обилием профессионального жаргона, а поисковая модель обучалась преимущественно на общем английском, она будет промахиваться. Запрос «оформить командировку» не найдет документ «служебная поездка», потому что для системы это не близкие понятия.
В топ попадает похожее, но не нужное. Система находит пять документов, формально похожих на запрос, но по смыслу не отвечающих на него. Модель добросовестно отвечает по тому, что ей дали, и выдает уверенный, но неправильный ответ. Ошибку видно только если знать правильный ответ заранее.
Стоимость и скорость. Пилот на пятидесяти документах летает мгновенно и почти ничего не стоит. Продакшен на пятидесяти тысячах — другая история. Об этом тоже стоит думать заранее, а не когда придет первый счет.
Как это лечить
Чинятся эти проблемы вполне конкретной инженерной работой.
Чанкинг
Режем документы по структуре (заголовки, разделы, абзацы), а не просто каждые 500 токенов. Можно использовать парсеры, которые понимают формат документа: PDF/Word/HTML в связке с Unstructured, LangChain Document Loaders или Databricks Vector Search, где есть структурный или рекурсивный чанкинг.
Подбор модели
Используем embedding‑модели, которые нормально понимают русский и ваш домен. Для многоязычных кейсов — BGE‑M3, Jina v5‑text, Harrier‑OSS, Cohere embed‑v4, OpenAI text‑3‑large и другие модели из MTEB/ruMTEB‑лидербордов.
Для проверки можно взять 50–100 реальных запросов, руками сопоставить их с «правильными» документами и посмотреть, попадает ли он в первые результаты поиска (например, в топ‑5 или топ‑10). Если нужный документ часто оказывается ниже или вообще не появляется, значит модель эмбеддингов или настройки поиска подобраны неудачно — их нужно менять и прогонять тот же тест еще раз.
Расширение запросов
Добавляем синонимы и альтернативные формулировки: «командировка» → «служебная поездка», «бизнес‑трип», «перемещение между офисами» и т.п. Это можно делать небольшой LLM‑моделью, которая переписывает запрос несколькими способами, или правилами/списками терминов, если у вас уже есть словарь по домену.
В проде часто используют схему: bi‑encoder embeddings + keyword‑поиск (BM25) + query expansion — чтобы покрывать и точные совпадения, и смысловую близость.
Реранкинг
Сначала делаем быстрый черновой поиск (например, векторный поиск или BM25) и берем не один, а, скажем, 30 наиболее подходящих документов. Потом прогоняем их через более «умную» модель, которая умеет сравнивать запрос и текст целиком, а также выстраивать их по реальной смысловой близости.
В итоге в контекст LLM попадают не просто формально похожие, а действительно самые релевантные 5–10 фрагментов.
Контроль контекста
Ограничиваем и структурируем то, что попадает в модель. Лучше передавать 3–6 лучших фрагментов, сгруппированных по документам, с понятными заголовками и метаданными: название документа, дата, версия, раздел.
Если контекст длинный, можно дозагружать соседние чанки из того же документа, но при этом сохранять структуру «документ → раздел → пункт», чтобы модель не сшивала в один ответ несвязанные куски из разных мест.
Оценка качества
Смотрим не только на то, нравится ли нам ответ модели, но и на то, хорошо ли работает поиск. Для этого делаем небольшой тестовый набор вопросов с указанием «правильных» документов и регулярно прогоняем его при любых изменениях в пайплайне.
Плюс обращаем внимание на живые сигналы: где пользователи чаще жмут «ответ не помог», какие вопросы чаще всего уходят к живому оператору, какие документы постоянно открывают вручную после неудачного ответа ассистента. Это валидные индикаторы того, что именно в retrieval что‑то сломано.
А про что тогда файнтюнинг
Большие языковые модели обучают один раз на огромных корпусах текстов — книгах, сайтах, коде, документации. На выходе получается общая модель: она что‑то знает про мир, но не понимает конкретно ваши правила.
Файнтюнинг нужен как раз для того, чтобы научить эту модель вести себя так, как нужно вам и работать на ваших примерах. Но чем это отличается от RAG?
RAG отвечает за доступ к знаниям: какие документы мы нашли, какие факты подложили модели в контекст. Файнтюнинг — за поведение поверх этих знаний: как именно модель строит ответ, в каком тоне и по каким правилам. Для фактических данных (регламенты, тарифы, условия) файнтюнинг обычно плохой выбор: они меняются, и каждое изменение превращается в переобучение. Гораздо дешевле и надежнее держать такие вещи в RAG, а файнтюнингом настраивать «как говорить», а не «что знать».
С чем именно он работает:
Стиль и тон. Чтобы ассистент писал как корпоративный юрист или саппорт, а не как блогер или коллега.
Формат. Строгие шаблоны ответов, структуры, JSON, блоки «что произошло / что делать / ссылки».
Правила поведения. Не отвечать, если нет данных, всегда ссылаться на документ, а также сначала задавать уточняющие вопросы и т.п.
Устойчивость. Не ломаться от опечаток, выдерживать одно и то же поведение в тысячах разных диалогов.
Хорошо работающий стек обычно выглядит так: RAG подтягивает актуальные документы и факты, а файнтюнинг делает так, чтобы модель на их основе отвечала в нужном тоне, формате и по понятным правилам.
И кстати: если раньше дообучение означало поднятие инфраструктуры и закупку железа, то сейчас это можно делать с помощью облачных мощностей и через простой интерфейс, без сложного кода.
Промптинг агентов: как командовать ИИ-помощником
Промпт, RAG, файнтюнинг — это все про то, как модель отвечает. Но за последнее время появился отдельный класс задач, где модель должна не отвечать, а делать: править код, запускать команды, лезть в файлы. Это территория ИИ-агентов, под капотом которых работает Tool Calling (вызов функций). И промпты для них пишутся по-другому.
Если вы еще ни разу не пробовали работать с агентами: крайне рекомендую начать! Не потому что это супер-хай-тек, а потому что вы получаете невероятный опыт пребывания в роли начальника, который управляет целым штатом джунов-сотрудников. Из преимуществ: вы чувствуете себя богом, воля которого закон, и все делается само, достаточно только поставить задачу. Но есть и минусы.

Первый заключается в том, что агенты делают все так, как вы сказали. Вот буквально: как вы сказали и ни шагу в сторону здравого смысла, даже если он там очень нужен. Когда спустя несколько итераций задача не выполнена как надо, к вам начинают закрадываться подозрения, что проблема здесь не в агентах, а в вас, что неприятно. Иногда с ними как в суде: все, что вы скажете, будет использовано против вас. Второй возможный минус: иногда агент слишком проактивен и креативен в выполнении задачи. Что-то идет не по регламенту? Ерунда, ваше благородие, перепишем мы этот ваш регламент — вот теперь красота!
К счастью, агенты — это всего лишь зацикленная языковая модель, которой вместе с промптом передали JSON-список доступных инструментов (Tools), например: read_file, run_bash_command, git_commit. Так что сколько бы вы на нее ни токсили и ни заставляли переделать, дефенестрация в реальной жизни вам не грозит. А вот превышение лимитов и соответствующие счета — да. Поэтому лучше на всякий случай разобраться сразу, как поставить задачу так, чтобы стать довольным за меньшее количество итераций.
Итак, когда модель понимает, что для решения задачи ей нужно действие, она вместо текста генерирует JSON с названием функции и ее аргументами. Ваш бэкенд (или IDE с Cursor) ловит этот JSON, физически выполняет скрипт на компьютере и отдает результат обратно в модель.
Промпт для чата и промпт для агента, который работает в Cursor, Claude Code или у себя в CI — это разные вещи:
Чат-промпт работает в режиме «вопрос — ответ». Если что-то пошло не так, пользователь увидит и поправит.
Агент работает автономно через Tool Calling: он читает код, редактирует файлы, запускает команды, иногда удаляет то, что не должен был удалять. Контроля меньше и цена ошибки выше.
Поэтому промпт для агента — это в первую очередь инструкция.
Роль и зона ответственности. Четко ограничьте, какими инструментами модель может оперировать. Явно укажите «ты — backend-инженер, работаешь только в директории services/payments, не трогаешь миграции и конфиги инфры». Чем уже область, тем меньше шансов, что агент пойдет править что-то соседнее.
Декомпозиция вместо размашистых задач. «Реализуй авторизацию» — плохая постановка задачи. Вы можете сказать: «Да я уже N раз так делал, и все было норм». Охотно верю, но я попадалась на этот крючок примерно сто раз (у меня уже все жабры в труху), поэтому просто поверьте: это скорее счастливое исключение из правила. Обычно агенты теряют фокус на больших целях, начинают параллельно делать пять вещей и заваливают все задачи. Качественная постановка — это пронумерованный план: например, сначала роуты, потом миддлвар, потом тесты, потом миграции. По шагу за раз, с проверкой после каждого.
Chain-of-Thought — заставьте рассуждать вслух. Тут вы скажете: «Но ведь большинство современных моделей делают reasoning по умолчанию». И будете правы. Более того, разработчики OpenAI рекомендуют для reasoning-моделей использовать простые и прямые инструкции, а не заставлять их расписывать ход мыслей на каждом шаге.
Проблема в том, что CoT не всегда показывает реальную трассу размышлений: иногда модель просто объясняет уже принятое решение постфактум. Поэтому не стоит воспринимать такие рассуждения как точный лог работы агента.
Тем не менее в некоторых случаях полезно попросить агента кратко объяснять свои действия. Если что-то пойдет не так, вы получите не только результат, но и примерную картину того, на каком этапе он свернул не туда. Главное — не требовать подробного CoT всегда и везде. Гораздо важнее задать модели понятный рабочий контракт: цель, ограничения, критерии успеха, условия остановки и формат итогового отчета.
Стоп-условия. Явно прописывайте, когда нужно остановиться и спросить человека.
«Если не хватает данных — не выдумывай, спроси».
«Если тесты падают после правки — откати и покажи лог».
«Если задача требует удаления файлов — остановись и запроси подтверждение».

Формат отчета. «По завершении пришли резюме: список измененных файлов, что работает, что не работает, какие тесты упали». Без этого вы можете час разбираться, что именно произошло, даже если видны затронутые файлы.
Сборка всех этих правил в реальном промпте для агента, который чинит баг по тикету, может выглядеть так:
Ты — backend-инженер, работаешь в репозитории платежного сервиса.
Скоуп: только директория services/payments.
НЕ ТРОГАЙ: migrations/, infra/, .github/.
ЗАДАЧА: исправь баг из тикета PAY-1423 (описание ниже).
ПЛАН ДЕЙСТВИЙ:
1. Прочитай тикет и сформулируй гипотезу о причине.
2. Найди релевантные файлы по ключевым словам из тикета.
3. Воспроизведи баг через существующие тесты или напиши новый.
4. Внеси минимальное изменение для исправления.
5. Прогони все тесты в services/payments.
6. Если тесты зеленые — оформи коммит. Если красные — откати правку.
ДЛЯ КАЖДОГО ШАГА:
- THOUGHT: объясни, что делаешь и почему
- ACTION: один конкретный вызов инструмента
- OBSERVATION: результат
- Не переходи к следующему шагу без зеленого OBSERVATION
СТОП-УСЛОВИЯ:
- Если для исправления нужно менять схему БД — остановись, спроси.
- Если правка затрагивает >5 файлов — остановись, покажи план.
- Если не уверен в причине бага — остановись, не правь наугад.
ОТЧЕТ В КОНЦЕ:
- Гипотеза о причине бага
- Список измененных файлов
- Какие тесты добавлены/изменены
- Что осталось непроверенным
ТИКЕТ:
[вставить тело тикета]
Да, придется потратить время, но зато после такого промпта агент не удалит миграцию и не закроет тикет одной строчкой «fixed». Здесь и кроется ответ, почему одни команды говорят, что агенты — это магия, а другие — головная боль. Разница часто не в модели, а в инструкции.
Промпт работает. RAG найдет нужное. Файнтюн зашьет стиль. Агент сделает работу. И при всем этом арсенале остается категория задач, в которых модель будет ошибаться — независимо от того, насколько вы хороши в промптинге.
Где ИИ не помощник, даже если вы гуру промптов
Базовая логика и подсчет букв
Любимый жанр в соцсетях: «спросил у ИИ, сколько букв "r" в слове strawberry, он ответил две».

Причин здесь две.
Первая — токенизация. Модель видит не буквы, а токены — куски слов длиной обычно в несколько символов. Слово strawberry для нее — это не последовательность из десяти букв, а, грубо говоря, два-три токена вроде straw + berry или str + aw + berry. Когда вы спрашиваете «сколько букв r», модель честно пытается ответить, не имея прямого доступа к буквенному составу.
Вторая причина — модель может слишком сильно полагаться на шаблоны. Загадка про запаянную кружку в интернете встречается в тысяче вариаций со стандартными формулировками. Модель видит знакомый паттерн и тащит шаблонный ответ, не вчитываясь в конкретику задачи. Слово «запаянная» она пропустила, потому что в обучающих текстах оно встречалось редко и веса на него низкие. Это та же история, что и со стереотипами в картинках.
Современные reasoning-модели такие задачи решают лучше, потому что заставляют себя рассуждать пошагово и перепроверять. Но и они спотыкаются на нестандартных формулировках. Поэтому если ваша задача требует точного результата (подсчет, проверка условия, разбор по символам) — выносите ее в код через Tool Calling, а не уговаривайте модель «стараться лучше» и уж тем более не хамите ей. Сами знаете, почему.

Точная математика
LLM не калькулятор. Когда он складывает семизначные числа или считает проценты в большом массиве данных, то может ошибиться. Решение все то же — выносить в Tool Calling: модель формулирует запрос, отдельный код-интерпретатор считает.
Актуальные данные
Модель зачастую не знает курс валют, цену билета, погоду на завтра, если она не ходит в интернет (но иногда ходит, пример: Sonar). Ее знания — это срез интернета на момент обучения. Но здесь все просто: добавляем поиск в интернете или подключенные API.
Стабильное поведение от запуска к запуску
Один и тот же промпт даст разные ответы. Это свойство вероятностной генерации: модель семплирует следующий токен, а температура и top-p управляют разбросом. Если снизите температуру, то выдача станет почти детерминированной. Но строгая воспроизводимость все равно не гарантирована из-за особенностей GPU, dynamic batching, tensor parallelism, маршрутизации запроса на другой бэкенд и т.д.
Долгая память
Даже миллион токенов не делает модель помнящей все. Внутри длинного диалога у трансформеров есть эффект lost in the middle: модель лучше всего использует начало и конец контекста, а важные детали из середины или документа просто теряет. Решение: конспектирование контекста, явные сводки, RAG поверх истории. В Claude Code, например, даже по простым вопросам лучше заходить через загрузку файлов. Так он хотя бы будет ссылаться на контекст.
Чувствительные темы
Большинство коммерческих моделей не ответят на вопросы безопасности, оружия, запрещенных веществ, деликатного контента и 18+. Из примечательного. Многие из нас уже привыкли использовать языковые модели как второе мнение при походах к врачу. Обычно LLM-ки не возражают представить себя на месте условного гастроэнтеролога. Некоторые даже лестно обращаются «коллега». Но вот недавно я попросила консультацию от лица специалиста в области ABA-терапии и модель спасовала:

Короче, если у вас задача — пентест-скрипт или анализ дарквеб-логов (я не хочу знать, откуда они у вас), готовьтесь либо к отказам, либо к разворачиванию опенсорс-моделей.
Но в целом кормить чувствительными данными модели — так себе идея. Данные уходят за периметр компании, и хотя крупные вендоры по умолчанию не обучаются на API-трафике, для чувствительной информации этого мало — нужен Enterprise с Zero Data Retention или локальное развертывание.

Если есть что добавить или с чем не согласиться — приглашаю в комменты.
