Читать бесплатно онлайн книгу Право на суждение. Агентность как принцип проектирования ИИ-системавтор Сергей Кирницкий
Сергей Кирницкий
Право на суждение
Агентность как принцип проектирования ИИ-систем
Шрифты предоставлены компанией «ПараТайп»
Оформление обложки Created with Grok
© Сергей Кирницкий, 2026
В книге рассматривается проектирование систем на основе больших языковых моделей через призму распределения суждений между приложением и моделью. Вводится понятие изъятия агентности, выстраивается его таксономия, анализируются условия возврата суждений модели и сопутствующие риски: недетерминизм, безопасность, стоимость. Предлагается критерий проектирования и процедура аудита существующих систем. Издание рассчитано на инженеров и архитекторов ИИ-систем.
ISBN 978-5-0070-7172-7
Создано в интеллектуальной издательской системе Ridero
Оглавление
Введение. Право на суждение
Где-то в вашей системе есть строка кода, которую давно никто не открывал. В ней записано число — скажем, четыре: сколько фрагментов доставать из базы знаний на каждый вопрос пользователя. Или правило: модели разрешён один проход, второго не будет. Или развилка: вопросы об оплате — в эту ветку, всё остальное — в ту. Или схема, в которую ответ обязан уложиться, каким бы ни оказался вопрос. Строка появилась при проектировании; писали её, возможно, не вы, а того, кто писал, может уже не быть в команде. С тех пор система пережила смену модели, пару миграций и один редизайн — а строка всё там же, и прямо сейчас она выносит решение за очередного пользователя: что для него релевантно, сколько попыток заслуживает его задача, каким его вопросу вообще позволено быть. Почему число именно такое, никто уже не помнит; спросить не у кого, да и незачем — система работает, жалоб немного, панели мониторинга зелёные, и именно поэтому строку никто не открывает. Но интересно не «почему четыре». Интересен вопрос, который я буду задавать каждому такому месту: чьё это суждение? Кто его на самом деле выносит — модель, которая читает живой вопрос в эту секунду, или код, решивший всё однажды и с тех пор ни разу не передумавший?
Долгое время честный ответ был: код — и он был правильным. Прежнюю модель приходилось вести за руку: она теряла нить на втором шаге, путала инструкцию с данными, выдавала правдоподобную форму вместо верного содержания. Отдавать ей решения о собственном пути было не смелостью, а безответственностью, и инженерная культура вокруг моделей закономерно сложилась как культура недоверия — разумного, заслуженного, многократно подтверждённого продакшеном. Интеллект системы жил в коде: в правилах и ветвлениях, в конвейерах обработки, в маршрутизации запросов. Вокруг того, как обойтись наименьшим участием модели, выросла целая дисциплина: пусть модель делает узкую, проверяемую работу, а думает — код. Модель занимала в этой конструкции одну клетку — классифицировала, размечала, отвечала на заранее подготовленный запрос — и возвращала результат туда, где все остальные решения давно были приняты за неё. Чем меньше от неё зависело, тем спокойнее спала команда.
Это устройство мира кончилось — и кончилось быстрее, чем успели измениться привычки. Интеллект системы сместился из статичной логики приложения в саму модель. Сегодняшняя модель — из лучших, доступных в своём поколении, — держит длинную инструкцию и не теряет её к десятому шагу, удерживает план и пересматривает его по ходу, вызывает инструменты и читает их результаты, замечает собственную ошибку и пробует иначе, работает с контекстом, который прежде не поместился бы целиком. Существенно одно: та работа суждения, которую раньше приходилось вынимать из модели и записывать в код, потому что модель её не тянула, теперь всё чаще оказывается тем, что модель делает лучше кода. И тогда прежняя оболочка из страховки превращается в помеху: она уже не компенсирует слабость — она не даёт проявиться силе. А та строка об этом не знает. Способность модели выросла на поколения; права, выданные ей архитектурой, остались прежними. Для инженера это редкий случай: в системе уже лежит незадействованный ресурс — способность, к которой никто не выписал прав. Обычно выигрыш приходится добывать по крохам, вытачивая проценты из конвейера; здесь он заперт одной строкой.
Этот разрыв — между тем, что модель может, и тем, что ей позволено, — и есть предмет книги, начиная с её названия. Каждый раз, когда внешняя логика решает за модель то, что та могла бы решить сама — что искать, что предпринять, повторить ли попытку, что удержать в памяти, действовать ли, — суждение у модели изымается: выносится один раз, на этапе проектирования, и застывает в коде. Я называю это изъятием агентности, а застывшее решение — замороженным суждением; строгие определения подождут — пока достаточно имён. Замороженное суждение не ошибается. Оно просто не пересматривается — даже когда мир вокруг изменился, а модель, за которую его когда-то заморозили, теперь вынесла бы его лучше. Отсюда и название книги — право на суждение: способность сама по себе не выдаёт прав. Права в системе выдаёт архитектор, и модель будет выносить ровно те суждения, которые он ей оставил, — сколько бы поколений способности ни прошло мимо этой строки. Книга о том, где это право вернуть — потому что способность до него доросла, — и где, так же осознанно, не давать.
Такие замороженные суждения рассыпаны по любой зрелой системе, и почти все они невидимы, потому что выглядят как просто код — как норма, не требующая оправданий. Жёстко заданное число фрагментов, которые всегда достаются из базы, — это решение о том, что релевантно, вынесенное за модель и с тех пор не пересмотренное. Классификатор, раз и навсегда постановивший, какими бывают вопросы пользователей, — решение о природе задачи, застывшее в чьей-то старой таксономии. Единственный разрешённый проход там, где задача просит второго взгляда, — решение о том, сколько усилий заслуживает ответ. Ни одно из этих мест не выглядит как отнятое суждение; все они выглядят как настройки, значения по умолчанию, здравый смысл. В этом и трудность: изъятие не оставляет следов на поверхности — его не видно ни в демо, ни в интерфейсе, ни в метриках, пока запросы похожи на предусмотренные. Всё начинается с навыка замечать такие места: видеть в строке конфигурации не параметр, а решение — чьё-то, старое и до сих пор действующее.
Упрёка тем, кто эти строки писал, здесь нет. Тот, кто обернул слабую модель плотной оболочкой, решал реальную задачу своего времени и решал её правильно. Модель, терявшая нить на втором шаге, не могла вести многоходовый поиск — значит, поиск разумно было свести к одному ходу и задать снаружи; модель, отвечавшая уверенно даже на нерелевантной выборке, не годилась в судьи собственного контекста — значит, контекст решали за неё. Это была не леность мысли, а инженерная честность: точная подгонка архитектуры под то, что модель тогда умела. Само по себе изъятие — законный приём, и отменять его никто не предлагает; упрёк адресован не людям и не старым архитектурам, а инерции — решениям, которые были верны при одних способностях модели и молча пережили их рост. Порок начинается не там, где суждение забирают у модели, а там, где его забирают не выбором, а привычкой. И снять этот упрёк важно раньше любых переворотов: трудно пересматривать выбор, за который тебя не перестали винить.
Главный тезис заявлю сразу и без доказательств. Свобода модели — правильный дефолт проектирования; ограничение — осознанное исключение. Это не призыв дать свободу всему, а дисциплина различения: видеть, какое суждение отдать модели, а какое сознательно оставить приложению, — и настаивать лишь на порядке, в котором эти вопросы задаются, — сначала «что оставить модели», потом «что забрать», а не наоборот. Повод вернуть суждение всегда конкретен: модель способна вынести его лучше замороженного кода — не по определению, а на живых запросах, где код промахивается. Конкретна и цена: вместе с суждением модель получает свободу ошибиться иначе и право пойти путём, которого инженер не закладывал. Разрешите ей повторить попытку, когда первый ответ не сошёлся, — и она вытащит запрос, на котором одноходовый конвейер сдавался; но заплатить придётся лишним раундом и маршрутом, который не расписать заранее. Настоящая ставка возврата — не проценты к метрике, а класс запросов, прежде недосягаемый. А есть решения, которые модели не стоит отдавать вовсе, — недоверенный ввод, недопустимая цена ошибки, требование строгой воспроизводимости. Разморозить суждение — не жест доверия и не идеология, а инженерное решение с поводом и ценой; обе величины останутся на виду до самого конца — ни выгода не спрячется за осторожностью, ни цена за энтузиазмом. Тезис, умалчивающий о своей цене, — лозунг, а не метод.
Я пишу для тех, кто эти системы строит: для инженеров и архитекторов ИИ-систем, техлидов, продакт-инженеров — для всех, кто отвечает за то, где в системе проходит граница между кодом и моделью. От читателя потребуется знакомство с большими языковыми моделями на уровне пользователя API — запрос и ответ, вызов инструмента, контекстное окно; глубокого машинного обучения не нужно, потому что речь не о том, как модели устроены внутри и как их обучают. Предмет — то, что мы строим вокруг готовой модели, и решения, которые в этой обвязке кто-то за кого-то выносит: достаёт ли модель нужные документы сама или ей их подкладывают; разрешён ли второй заход, если первый не дал ответа; чья версия важного остаётся в памяти — модели или того, кто сжал контекст за неё; где систему остановить, а где отпустить. Это не введение в языковые модели с нуля, не туториал под конкретный инструмент и не книга об оркестрации множества агентов между собой — это книга про одну развилку, которая проходит через каждую ИИ-систему, и про то, как проходить её не вслепую. Скорее всего, вы уже выпустили в мир систему, где каждое из этих решений принято, — вопрос лишь в том, кем и глядя ли.
Сквозь книгу проходит одна карта — карта суждений: несколько типовых решений, которые ИИ-система либо выносит за модель, либо доверяет ей, — что и где искать, что делать дальше, повторить ли попытку, как ответить, что помнить, действовать ли самостоятельно. Каждое пройдёт три станции: где его изымают, где его можно вернуть и чего этот возврат стоит. По той же карте движется сквозной пример — ассистент поддержки на базе документации: система, которую хоть раз строил или видел почти каждый и в которой естественно живут все решения карты. Сначала это знакомая версия с изъятой агентностью: подготовленный за модель контекст, классификатор намерений, один проход, шаблонный ответ. Потом она упрётся в потолок на запросе, которого никто не предусмотрел. Потом её перепроектируют, суждение за суждением возвращая модели то, что было заморожено. Потом посчитают цену этой свободы и проверят систему на прочность там, где свобода опасна. И под конец по ней пройдут с чек-листом, как по чужой системе, которую нужно честно оценить. Один и тот же бот через всю книгу — чтобы было видно, как одно и то же решение сначала замораживают, а потом размораживают, и что меняется на каждом шаге.
Осталось одно обещание. Здесь не будет рецептов под конкретный инструмент, протокол или модель; не будет версий, бенчмарков и имён — ничего, что устареет к следующему поколению. Это не осторожность, а позиция: метод переживает модели. Отсюда же — сдержанность со словарём, которым поле описывает само себя. Имена его дисциплин приходят слоями и сменяют друг друга: то, что вчера звалось prompt-инжинирингом, сегодня зовётся context-инжинирингом, а завтра получит новое имя для тех же, по сути, решений. Разговор сознательно пойдёт уровнем выше — на суждениях, а не на терминах сезона; у каждого текущего имени есть мост к тому, как ту же вещь называю я, и эти мосты собраны в глоссарии, чтобы читатель, пришедший с любым словарём, легко нашёл соответствие. Знать имя сезона полезно; строить на нём метод — значит привязать метод к сроку годности имени. Вопрос же «чьё это суждение?» не требует новой модели, чтобы его задать, и не устареет вместе со старой: он останется, когда сегодняшние библиотеки, протоколы и имена дисциплин сменятся неузнаваемо. Сегодня этот вопрос звучит так: пора перестать выбирать путь за модель и позволить ей самой находить лучший путь к задаче — там, где она на это способна, и ровно настолько, насколько способна. Где проходит это «настолько», как найти его для своей системы и чем за него платить — вся остальная книга. Начнём с того, чтобы научиться видеть суждения там, где мы привыкли видеть код, — с той самой строки, которую давно никто не открывал.
Часть I. Изъятие агентности
Две системы могут быть неотличимы снаружи. Один и тот же интерфейс, та же база знаний, та же модель под капотом. Две команды показывают демо своего ассистента поддержки; обе уверенно отвечают на два десятка типовых вопросов; обе выглядят готовыми к продакшену. Разница между ними не проявляется в демо. Она проявляется тремя неделями позже — на двадцать первом вопросе, том самом, который никто не прописал заранее.
Один ассистент на этом вопросе спотыкается ровно и предсказуемо: находит не тот раздел базы, отвечает уверенно и мимо, и не может сказать «мне принесли не то». Другой — переспрашивает базу иначе, замечает, что первый результат не отвечает на вопрос, и находит путь там, где его не прокладывали. Компоненты у них одинаковые. Различаются они тем, кто внутри выносит решения.
Именно это различие — предмет книги, и прежде всего его нужно научиться видеть. Возьмём типичную поддержку на базе знаний, docs-бот, знакомый почти каждому, кто строил что-то на языковых моделях. В самой распространённой его версии почти каждое содержательное решение принято не моделью. Что искать по запросу пользователя — решает механизм извлечения, отдающий модели готовую выборку. Какого рода это запрос и в какую ветку его направить — решает роутер намерения на входе. Стоит ли попробовать ещё раз, если найденное не отвечает на вопрос, — не решает никто: конвейер одноходовый, второй попытки в нём просто нет. Как оформить ответ — задаёт шаблон. Модель в этой системе делает ровно одно: формулирует текст поверх того, что ей уже выбрали, направили и разрешили. Она — последнее звено, а не то, где живёт интеллект.
Каждое из этих решений когда-то было принято — один раз, на этапе проектирования, — и с тех пор не пересматривалось. Не потому, что оно всегда верное, а потому, что его вынесли заранее и заморозили в коде. Замороженное суждение* — это решение, которое кто-то вынес однажды при проектировании системы и застыло в её логике: оно больше не зависит ни от запроса, ни от того, что вернул поиск, ни от того, справляется модель или нет. Замороженное суждение не ошибается в том смысле, в каком ошибается живое. Оно просто не пересматривается.*
Когда суждение, которое модель могла бы вынести сама, выносит за неё внешняя логика и замораживает, — агентность изъята. Это и есть паттерн: изъятие агентности. Инъекционный RAG, роутер на входе, одноходовый конвейер, шаблонный ответ — не четыре независимых технических решения, а четыре точки одного паттерна. И этот паттерн — не экзотика и не признак плохой инженерии. Он повсюду: в поиске, в маршрутизации, в памяти, в том, разрешено ли системе действовать. Спешить осуждать его не стоит, и лекарство подождёт — тем более что не всякое изъятие в нём нуждается. Нужна оптика, в которой знакомый продакшен вдруг читается как список чужих замороженных решений. Дискомфорт этого узнавания — рабочий: пока не увидишь паттерн целиком, обсуждать, что с ним делать, преждевременно.
Глава 1. Что такое агентность в ИИ-системе
Мы легко говорим, что один ассистент «умнее» другого. Но если выложить оба на стол и разобрать по частям, окажется, что части одинаковы: тот же класс модели, та же база, тот же способ доступа к ней. Разница, которую все чувствуют и которая через месяц решит судьбу обоих продуктов, в этой описи компонентов не значится. У неё пока нет имени.
Слово «агентность» напрашивается, но оно перегружено. Для одних это автономные системы, действующие без человека; для других — почти сознание; для третьих — модный ярлык на всём, что вызывает инструменты. Ни одно из этих значений не годится как рабочее: с ними нельзя спроектировать систему, потому что они описывают ауру, а не устройство. Прежде чем строить на понятии агентности, его нужно очистить до чего-то, что можно положить в основу решения.
Расчистка начинается с одной оси и одного определения. Ось: интеллект решения в системе живёт либо в статичной логике приложения, либо в модели — и почти никогда посередине для каждого отдельного решения. Определение: агентность — это мера, в которой суждения выносит модель, а не то, что застыло вокруг неё. Но и то и другое становится инструментом лишь после того, как различие увидено на конкретной системе: определение, пришедшее раньше разницы, запоминается как формулировка, а не как инструмент.
1.1. Интеллект системы: в приложении или в модели
Вернёмся к двум ассистентам поддержки и дадим им конкретную задачу. Пользователь пишет: «Обновил тариф вчера вечером, а лимиты на API остались старые — это нормально или что-то сломалось?». В базе знаний нет статьи с таким заголовком. Есть статья про то, как меняются тарифы, есть отдельная — про то, что изменения лимитов применяются в начале следующего расчётного периода, и есть третья — про типичные задержки синхронизации. Ответ живёт на стыке трёх статей, и ни одна из них по отдельности его не содержит.
Первый ассистент устроен так: запрос пользователя целиком уходит в поиск, поиск возвращает три самых похожих фрагмента, эти фрагменты подставляются в подсказку модели, модель пишет ответ. Запрос был про «лимиты остались старые», и поиск честно принёс самое похожее — статью про изменение лимитов. Модель получает этот фрагмент как данность и на его основании уверенно сообщает: лимиты меняются в начале следующего периода, всё в порядке, ждите. Ответ звучит гладко и почти наверняка неполон: он не различает штатную задержку и настоящий сбой, потому что в принесённой выборке этого различения нет, а спросить, поискать иначе или усомниться в выборке модель не может. Ей нечем: решение о том, что искать и что считать релевантным, уже принято — механизмом извлечения, до неё.
Стоит задержаться на том, чего именно лишена первая система. Не знаний — статья про сбои лежит в той же базе, до неё просто не дотянулись. Не сообразительности — модель одна и та же в обеих системах. Она лишена способности заметить, что отвечает не на тот вопрос. Ей отдали выборку, которую она не запрашивала, и она отвечает на вопрос, который эта выборка задаёт, а не тот, что задал пользователь. Проблема не в том, что система знает мало, а в том, что она не видит границ своего знания в этом конкретном случае, — и уверенность ответа от этого не страдает. Гладкость и правота здесь развязаны.
Второй ассистент устроен иначе. Поиск дан ему не как подкладка под ответ, а как инструмент, которым распоряжается модель. Она видит вопрос, замечает в нём две разные возможности — «штатная задержка» и «сбой», — и это её суждение, а не ветка, выбранная роутером. Она ищет по первой, получает статью про начало расчётного периода, ищет по второй, получает статью про задержки синхронизации, сопоставляет их с деталью «вчера вечером» и отвечает так, как ответил бы человек, знающий базу: скорее всего, это штатная задержка до начала следующего периода, и вот как проверить, что это не сбой. Тот же интерфейс, та же база, тот же класс модели. Разными их сделало одно: во втором случае суждение о том, что здесь вообще происходит и где это искать, вынесла модель.
Важно сразу уточнить масштаб этой оси. Она приложена не к системе целиком, а к каждому её решению по отдельности. Неверно спрашивать «агентна ли эта система» так, как спрашивают, на каком она написана языке: система — не точка на оси, а связка решений, и каждое лежит на своей точке. В одном и том же ассистенте решение «как переформулировать запрос к поиску» может быть отдано модели, а решение «отправлять ли исходящее письмо клиенту без подтверждения человека» — намеренно заморожено в приложении. Это не противоречие и не половинчатость. Это норма: у зрелой системы карта решений пёстрая, и её пестрота — не признак недоделанности, а результат отдельных выборов, каждый со своей причиной. Тумблера «свобода/контроль», делящего систему надвое одним щелчком, не существует; есть десятки мелких решений, и каждое лежит там, куда его положили.
Ось эта не про поддержку и не про поиск — она проступает везде, где приложение обёрнуто вокруг модели. Возьмём систему, извлекающую из входящих документов структурированные данные: контрагент, сумма, срок оплаты. В одном варианте разработчик заранее задаёт жёсткую схему полей, а модель лишь заполняет клетки — что считать «суммой» и «сроком», решено до неё, схемой. В другом модель сама разбирается, что в этом документе есть значимого, и предлагает структуру под конкретный документ. Первый вариант надёжно провалится на документе непредусмотренного вида — там, где значимое поле в схему не заложено, его просто некуда записать; второй с таким документом справится, но заплатит меньшей предсказуемостью формы ответа. Те же две стороны оси, тот же размен — на совсем другом материале. Ось универсальна именно потому, что описывает не предметную область, а место, где принято решение.
Разложим, что именно различает эти системы. Не качество модели — она одна и та же. Не полнота базы — статьи те же самые. Не интерфейс, не язык, не оформление. Различает их место, где вынесено решение. В первой системе решение «что здесь релевантно» вынесено заранее и снаружи — логикой приложения, которая всегда делает одно и то же: берёт запрос, отдаёт в поиск, подставляет верхние результаты. Во второй то же решение вынесено моделью в момент задачи, с учётом её конкретики. Это и есть ось, на которую ложится любая ИИ-система: интеллект системы — это то, где принимаются её содержательные решения: в статичной логике приложения или в модели.
Слово «статичная» здесь несущее. Логика приложения не глупа — она бывает весьма изощрённой. Но она вынесена один раз и не зависит от конкретного запроса: она сделает с двадцать первым вопросом ровно то же, что с первым, даже если двадцать первый требует другого. Модель, напротив, выносит решение всякий раз заново, глядя на то, что перед ней. Оба режима легитимны. Есть решения, которые правильно застывают в приложении: их незачем принимать заново каждый раз, и цена ошибки живой модели тут выше выгоды. Решение о том, что данные одного пользователя нельзя показать другому, правильно заморожено в коде — доверять его живому суждению модели на каждом запросе — не гибкость, а риск, и его застылость здесь — не долг, а защита. И есть решения, застывание которых обходится дорого, — как «что здесь релевантно» у первого ассистента. Вопрос не в том, какой режим лучше вообще. Вопрос в том, какое конкретное решение в каком режиме должно жить.
Есть простой способ определить, по какую сторону оси лежит конкретное решение: посмотреть, меняется ли оно, когда меняется вход. Замороженное решение к входу глухо — оно применяет одну и ту же процедуру к типовому входу и к небывалому. Решение, оставленное модели, вход слышит: на непохожем запросе оно может выйти иначе, потому что выносится под него. Отсюда и слово «интеллект» в названии оси — не как похвала и не как метафора, а почти буквально: интеллект решения тем выше, чем сильнее оно приспосабливается к конкретике задачи, а не воспроизводит заранее заданную форму. Приложение может быть сколь угодно изощрённым как программа и при этом нести нулевой интеллект решения — если это решение одно на все входы. Сложность кода и интеллект решения — разные величины, и их легко перепутать: система с тысячей правил маршрутизации выглядит умной, но каждое из правил заморожено, и на входе за пределами тысячи она беспомощна.
И вот здесь ось из наблюдения превращается в ось проектирования. Где живёт интеллект решения — не свойство, доставшееся системе от природы, и не следствие выбранной модели. Это сумма выборов её авторов, сделанных решение за решением, часто по инерции, часто не глядя. Когда команда первого ассистента писала «запрос → поиск → верхние результаты → модель», она не формулировала это как «отберём у модели суждение о релевантности». Она писала очевидный, стандартный, рекомендованный отовсюду пайплайн. Изъятие произошло не как злой умысел и даже не как решение — как форма по умолчанию. Стартовый шаблон, первый попавшийся пример из документации, готовый рецепт «как сделать RAG» — все они уже расставили решения по оси заранее, и расставили в одну сторону: суждение — приложению, текст — модели. Разработчик получает эту раскладку в наследство вместе с первой строкой кода и чаще всего не замечает, что она вообще была раскладкой, а не единственным способом. Но от того, что оно было незаметным, изъятие не перестало быть выбором. Кто-то расставил на этой оси каждое решение системы, и большинство систем стоят там, где стоят, не потому, что там их место, а потому, что туда их поставила привычка.
Цена этого выбора не видна на предусмотренном. На первых двадцати вопросах — тех, под которые пайплайн и настраивали, — замороженное решение о релевантности работает не хуже живого: выборка та, что нужно, ответ верный, демо гладкое. Разрыв открывается на двадцать первом — на запросе, которого проектировщик не предвидел, потому что предвидеть все запросы нельзя. Здесь замороженное решение делает то же самое, что делало всегда, — и именно постоянство оказывается ошибкой. Модель, которой оставили это суждение, на непредусмотренном запросе хотя бы пробует другой ход; модель, у которой его отобрали, уверенно отвечает на вопрос, который ей подменили выборкой. Место, где вынесено решение, ничего не стоит до тех пор, пока система остаётся внутри предусмотренного, — и начинает стоить сразу за его границей. А граница предусмотренного проходит не там, где её ждут: реальный поток запросов почти всегда шире тестового набора, на котором систему признали готовой.
Различие между двумя ассистентами теперь можно показать пальцем: оно в том, по какую сторону оси лежат их ключевые решения. Но «показать пальцем» — ещё не «назвать». У меры, в которой решения отданы модели, должно быть имя, а у самого понятия — определение, достаточно точное, чтобы на него можно было опереться.
1.2. Агентность как контроль над суждением
Различие между двумя ассистентами держится на одном: кто выносит содержательные решения внутри задачи. В первой системе их выносит приложение и подаёт модели готовыми; во второй значительную часть выносит сама модель. Эту меру и называют агентностью. Агентность — мера, в которой модель сама выносит суждения внутри решения задачи, а не получает их готовыми извне.
Определение стоит читать медленно, потому что каждое слово в нём отсекает частое недоразумение. «Мера» — потому что агентность не переключатель «есть/нет», а величина: у одной и той же системы одни суждения отданы модели, другие заморожены, и общий уровень складывается из этой раскладки. «Сама выносит» — потому что речь именно о вынесении решения, а не о его исполнении: модель, дописывающая текст поверх чужого выбора, ничего не выносит, хотя работает. «Внутри решения задачи» — потому что нас интересуют суждения, из которых складывается путь к ответу, а не поведение системы во внешнем мире. И «суждения» во множественном числе — потому что их в любой задаче много, и каждое можно рассматривать отдельно.
При этом суждения не равны по весу, и мера агентности складывается не из их числа, а из их значимости. В любой задаче есть решения, от которых зависит исход, и решения проходные. Отдать модели выбор формулировки и заморозить за приложением выбор того, что вообще искать, — это низкая агентность, сколько бы мелких решений модель ни принимала попутно: несущее суждение вынесено не ею. Обратное тоже верно: система может оставлять модели немного решений, но именно те, что определяют путь, — и быть при этом высокоагентной. Поэтому «сколько суждений отдано модели» — вопрос не арифметический. Считать нужно не штуки, а вес: какие из решений, определяющих судьбу ответа, вынесены моделью, а какие застыли до неё. Одно существенное суждение, оставленное модели, меняет систему сильнее десятка косметических.
Какого рода эти суждения? Что здесь вообще спрашивают и в какую сторону это решать. Где искать ответ и по каким словам. Достаточно ли найденного или стоит зайти иначе. Нужно ли переспросить, уточнить, разбить задачу на части. Каким должен быть ответ, чтобы он отвечал именно на этот вопрос. Ни одно из этих решений не про действие во внешнем мире — все они про то, как система движется к ответу. И каждое из них может быть либо оставлено модели, либо вынесено за неё заранее. Агентность — это про то, сколько таких суждений и насколько существенных остаётся за моделью.
Здесь неизбежен вопрос, на котором книга могла бы поскользнуться: в каком смысле у модели вообще «есть суждение»? Модель не размышляет за столом и ничего не взвешивает в человеческом смысле; говорить, что она «решает» или «понимает», — соскользнуть в мистификацию, от которой эта книга держится подальше. Но и другая крайность — отказать модели в суждении вовсе — сделала бы разговор невозможным: тогда пришлось бы описывать вторую систему языком «активаций» и «распределений», в котором различие между двумя ассистентами просто не выразить. Выход — рабочая рамка. Суждение модели здесь — рабочая рамка, а не утверждение о внутреннем мире: способность на конкретном входе выдать один исход, а не другой, обоснованно с точки зрения задачи. Когда вторая система «замечает», что вопрос допускает две трактовки, и ищет по обеим, — это наблюдаемое поведение, которое проще и точнее всего описать как вынесенное суждение. Мы не заглядываем модели в голову; мы называем то, что видим на выходе, тем словом, которое позволяет об этом рассуждать и проектировать.
Проверить эту рамку можно тем же грубым способом, каким различали стороны оси: подменить вход — только смотреть теперь не на исход, а на путь к нему. Дайте первой системе запрос про тарифы, потом запрос про сбой — путь один и тот же: поиск по похожести, верхние результаты, ответ поверх них; менялся не путь, а лишь текст на его конце. Дайте те же два запроса второй — и путь разойдётся: разные переформулировки, разное число обращений к базе, разный порядок. Там, где вход меняет не только ответ, но и ход к нему, суждение вынесено на месте; там, где ход неизменен, — оно вынесено заранее и заморожено. Рамка не требует заглядывать внутрь модели: разницу видно снаружи, по следу, который система оставляет, решая задачу.
Эта рамка — не уступка удобству, а необходимое условие всего разговора об агентности. Без неё каждое утверждение вида «здесь суждение вынесла модель» пришлось бы либо переводить в мистику, либо разворачивать в абзац оговорок; с ней можно говорить прямо — и не приписывать модели ничего сверх наблюдаемого.
У контроля над суждением есть точный образ. Представьте дорогу и машину на ней. Приложение — тот, кто строил дорогу: заранее, для всех будущих поездок сразу, проложил ровно те повороты, которые предусмотрел. Модель с агентностью — тот, кто едет: выбирает поворот здесь и сейчас, глядя на то, что перед ним. Пока маршрут совпадает с предусмотренным, разницы не видно — оба едут по одной трассе. Разница проступает там, где нужного поворота дорога не содержит: строитель уже ушёл и переложить полотно не может, а водитель ещё здесь и может свернуть. Контроль над суждением — это руль в руках того, кто едет, а не того, кто когда-то проектировал дорогу. Изъять агентность — значит забрать руль и оставить водителю только газ: скорость останется его, направление — уже нет.
Из этого образа сразу видно, чем агентность не является, — и два смешения стоит снять прямо в определении, пока они не приросли к термину. Первое: агентность — не автономия. Автономия — про надзор: действует ли система сама или под рукой человека, нужно ли подтверждение на каждый шаг. Это отдельная ось, и она пересекается с нашей как угодно. Можно построить систему с высокой агентностью и полным надзором: модель выносит все суждения о том, как решать задачу, но каждый её вывод проходит через человека перед применением. Это и есть human-in-the-loop — режим, в котором человек утверждает выводы системы перед их применением. Тот же docs-бот легко представить в этом режиме: модель сама разбирает обращение, ищет по базе, составляет полное решение — но не отправляет его клиенту, а кладёт на стол оператору как черновик. Все содержательные суждения здесь за моделью, ни одного действия наружу без человека. Агентность высокая, автономии — ноль. И можно построить систему автономную и почти безагентную: она крутится без человека сутками, но всё, что она делает, разложено по замороженным правилам, а модель лишь заполняет пустые места. «Без надзора» и «сама выносит суждения» — про разное, и путать их — значит спорить об одной оси, думая, что говоришь о другой.
Второе смешение: агентность — не действия. Соблазн измерять агентность тем, сколько система делает во внешнем мире — сколько вызывает инструментов, отправляет писем, меняет записей, — понятен, но обманчив. Действия — это то, что система совершает наружу; агентность — то, кто внутри решил их совершить и почему. Система, которая рассылает сотни писем по жёсткому сценарию, действует много и не решает почти ничего: все суждения о том, кому и что писать, вынесены до неё. И наоборот: система, которая не касается внешнего мира вовсе — только читает, ищет и рассуждает, — может нести очень высокую агентность, если каждое суждение о том, куда двигаться в задаче, выносит сама. Тот же обман прячется в счёте вызовов инструментов. Система, дёргающая десяток инструментов по заранее прописанной цепочке, выглядит деятельной, но каждый вызов в ней предрешён — модель лишь идёт по проложенному списку. Система, вызывающая один инструмент, но выбравшая его сама, под конкретную задачу, несёт больше агентности, чем первая при всей её суете. Мерить агентность объёмом действий — всё равно что мерить мышление количеством произнесённых слов.
Определение получилось короткое и, кажется, чистое: мера, в которой суждения внутри задачи остаются за моделью. Но слово «агентность» пришло в инженерный обиход не пустым — оно тащит за собой шлейф чужих значений, от философских до маркетинговых, и пока этот шлейф не отцеплен, определением нельзя пользоваться как инструментом: оно будет всякий раз стягиваться к тому, что читатель уже привык вкладывать в слово. Прежде чем строить, территорию вокруг термина нужно расчистить — по одному ложному толкованию за раз.
1.3. Чем агентность НЕ является
Слово пришло не пустым. «Агент», «агентность», «агентный» звучат в поле давно и успели обрасти значениями, которые к рабочему определению отношения не имеют, но липнут к нему при первом же разговоре. Пока эти значения не отцеплены, обсуждение любого проектного решения будет сворачивать в спор о терминах. Поэтому — расчистка: три ложных толкования и разметка границ, за которыми термин перестаёт значить то, что нужно книге.
У этих трёх — общее устройство. Каждое входит в разговор через свою дверь: одно из философии, где «агент» тянет за собой сознание; другое из идеологии, где агентность звучит как свобода-абсолют; третье из страха, где она слышится как отмена правил. Двери разные, а итог один — проектный вопрос подменяется чем-то посторонним, и обсуждение, кому отдать конкретное суждение, так и не начинается. Закрыть эти двери стоит до того, как термин пойдёт в работу.
Первое: агентность — не сознание. Рабочая рамка называет суждением наблюдаемую способность модели выдать на конкретном входе один обоснованный исход, а не другой; она ничего не говорит о внутреннем мире и намеренно в него не лезет. Смешение с сознанием опасно с обеих сторон. Кто принимает его всерьёз, начинает ждать от модели человеческого — устойчивых намерений, понимания последствий, ответственности — и либо переоценивает систему, доверяя ей то, чего она не тянет, либо, разочаровавшись, отказывает ей в суждении вовсе и возвращается к языку, на котором различие двух ассистентов не выразить. И то и другое уводит от инженерной задачи в спор, у которого здесь нет ни места, ни нужды: чтобы решить, кому отдать суждение о релевантности, не требуется знать, есть ли у модели внутренний мир. Требуется знать, выносит ли она это суждение лучше замороженного правила. Вопрос проектный, а не метафизический, и книга держит его таким.
Как это смешение выглядит вживую, видно по мелочам языка и решений. Команда начинает говорить «модель поняла, что клиент раздражён», «она решила, что так будет лучше», — и незаметно переносит на систему доверие, которое человек оказывает человеку. За словами приходят решения: раз «понимает» — можно убрать проверку, раз «хочет как лучше» — можно не перепроверять её выбор. Замените в тех же фразах «поняла» на «выдала исход, согласующийся с раздражением в тексте», и соблазн исчезает: становится видно, что доверять тут нужно не намерению, а надёжности исхода на этом классе входов — а это уже вопрос, на который есть инженерный ответ. Рамка суждения без сознания не обедняет разговор; она возвращает его на землю.
Стоит оговорить, чего это отсечение не делает. Оно не выносит приговора вопросу, способна ли модель к чему-то похожему на понимание в принципе, — этот вопрос книга оставляет тем, чья он тема, и не берётся ни утверждать, ни отрицать. Мы обходим этот спор не потому, что знаем ответ, а потому, что он не входит в условие задачи.
Второе: агентность — не автономия ради автономии. Тезис «отдавать суждения модели по умолчанию» легко прочитывается как призыв «отпустить всё» — снять ограничения, довериться модели во всём, отдать ей руль вместе с тормозами. Это не тезис книги. Дефолт — не абсолют: сказать «по умолчанию суждение остаётся за моделью» не значит «всегда и любое». Вся дисциплина подхода — про то, где этот дефолт сознательно нарушают: какое суждение осмысленно оставить приложению и почему. Манифест максимальной свободы обошёлся бы без второй половины; книга без неё не имеет смысла.
Полезно уточнить, что вообще значит здесь «по умолчанию». Дефолт — это не разрешение и не индульгенция, а место, откуда стартует рассуждение. Проектировщик, держащий агентность дефолтом, не освобождён от обоснований — он обязан обосновать каждое изъятие, тогда как при обратном дефолте молча обосновывать пришлось бы каждый возврат. Меняется не строгость, а сторона, на которой лежит бремя доказательства. Сказать «оставляем суждение модели, если нет причины забрать» — не то же самое, что «оставляем всё и не спрашиваем»: причины бывают, и веские. Дефолт задаёт, с чего начинать думать, а не позволяет думать не начинать.
Цена этого смешения двойная. Тот, кому оно нравится, слышит разрешение убрать всё и строит систему, которая на непредусмотренном срывается в непредсказуемое. Тот, кого оно пугает, слышит призыв к анархии и запирает всё обратно, теряя ровно ту способность находить путь, ради которой агентность и возвращают. Оба спорят с манифестом, которого книга не писала.
Спор этот легко услышать на любом проектном обсуждении, где «агентность» произнесли без определения. Один инженер понимает её как «уберём лишние рамки, пусть модель решает» и уже прикидывает, что вырезать. Другой слышит в этом безрассудство и упирается: «в проде так нельзя, нужен контроль». Они спорят громко и мимо, потому что оба приняли толкование «агентность = свобода без границ» — только один за него, другой против. А вопрос, ради которого стоило собраться, — какое именно суждение эта система выносит лучше замороженного правила, а какое разумнее оставить снаружи, — не прозвучал ни разу. Определение с отцепленным «ради автономии» этот спор снимает: обсуждать становится нечего в идеологии и есть что в инженерии.
Третье: агентность — не «работа без правил». Здесь смешение самое цепкое, потому что кажется очевидным: раз суждение отдано модели, значит, рамки сняты. Но отдать суждение и снять рамку — разные операции. Правила, границы, ограничения никуда не исчезают; меняется их место — они встают вокруг суждения, а не вместо него. Разница практическая, не риторическая. Второй ассистент, тот, что сам решает, где искать, всё равно живёт в рамках: он не дотянется до данных другого клиента, не отправит наружу того, что требует подтверждения, не выйдет за пределы того, к чему ему открыт доступ. Эти границы очерчивают поле, внутри которого суждение остаётся за моделью, — но самого суждения не отменяют. Изъять суждение — значит решить за модель, что здесь релевантно; поставить границу — значит оставить ей решать, но очертить, где она может искать и что вправе сделать с найденным. Первое замораживает выбор, второе — оставляет его живым внутри очерченного поля.
Проще всего увидеть разницу, если заметить, что правила и агентность вообще лежат на разных осях. У первого ассистента, того самого замороженного, ровно те же внешние ограничения, что у второго: он тоже не дотянется до чужих данных, тоже не отправит наружу без подтверждения, тоже заперт в своей области доступа. Ограничений у него не меньше — а агентности нет вовсе. Значит, «есть правила» и «есть суждение» не связаны: можно иметь все мыслимые границы и нулевую агентность, а можно — высокую агентность внутри тех же границ. Толкование «агентность = отсутствие правил» сжимает эти две оси в одну и потому неверно в самой посылке. Как ставить такие границы, не изымая суждения, — отдельный разбор цены и защиты; здесь достаточно снять само уравнение «агентность = отсутствие правил».
Территория расчищена. Термин, определённый и очищенный, больше не тянет за собой ни сознания, ни анархии, ни отмены рамок: осталась мера, в которой суждения внутри задачи вынесены моделью. Но определение — ещё не метод. Знать, что такое агентность, и уметь увидеть, где она изъята в конкретной системе, — разные умения. Чтобы из чистого понятия получился рабочий инструмент, его нужно превратить в процедуру: не «что такое агентность вообще», а «чьё вот это решение — здесь, в этой строке архитектуры».
1.4. Единица анализа: одно решение — чьё суждение?
Понятие есть, оно очищено — но пользоваться им как есть неудобно. «Уровень агентности системы» — величина слишком крупная, чтобы за неё ухватиться: она усреднённая, размазанная по всей архитектуре, и на вопрос «а что здесь не так» отвечает в лучшем случае «в целом маловато». Инженеру нужно не «в целом», а «вот здесь». Чтобы понятие заработало, масштаб взгляда придётся сменить: перестать смотреть на систему как на набор компонентов и начать видеть её как набор решений.
Это смена оптики, и она непривычна. Архитектуру принято разглядывать по частям: вот механизм извлечения, вот роутер, вот модель, вот шаблон ответа, вот хранилище памяти. Части удобны — их видно на схеме, у них есть имена и границы. Но части ничего не говорят о том, где живёт интеллект: механизм извлечения бывает и подкладкой под ответ, и инструментом в руках модели, и по коробке этого не отличить. Различие прячется не в компонентах, а в решениях, которые сквозь эти компоненты проходят. Поэтому единица анализа здесь — не компонент, а решение. Суждение здесь — единица анализа: одно решение внутри задачи, у которого есть исход и есть тот, кто этот исход выносит. Не «поиск» как модуль, а конкретное решение «что искать по этому запросу»; не «память» как хранилище, а решение «что из прошлого сюда относится».
Почему компонент для этого не годится, видно на любом из них. Возьмите тот же механизм извлечения. В первой системе он владеет решением о релевантности — берёт запрос и сам определяет, что подложить модели. Во второй тот же по названию механизм не владеет ничем: он лишь исполняет запросы, которые формулирует модель, а решение, что искать, осталось за ней. Компонент один и тот же, чертёж один и тот же, а владелец решения — противоположный. То же с памятью: «модуль памяти» может решать за модель, что из прошлого релевантно, а может просто хранить и отдавать по запросу модели — снаружи это один прямоугольник на схеме. Смотреть на компоненты — значит смотреть на коробки, внутри которых решение может лежать любой стороной. Смотреть на решения — значит сразу видеть ту сторону, которая всё определяет.
Как только систему разложили на отдельные решения, к каждому можно приложить один вопрос: чьё это суждение — приложения или модели? У каждого решения есть владелец суждения — то, что фактически выносит исход: приложение, заморозившее его на этапе проектирования, или модель, выносящая его на месте. Вопрос звучит просто, почти наивно, но именно он превращает разглядывание архитектуры в метод. Приложите его к первому ассистенту, решение за решением. Что искать по запросу? Решает механизм извлечения — владелец приложение. В какую ветку направить обращение? Решает роутер — владелец приложение. Повторить ли поиск, если найденное не отвечает на вопрос? Не решает никто, потому что конвейер одноходовый, — суждение заморожено в самой форме пайплайна, владелец приложение. Как оформить ответ? Задаёт шаблон — владелец приложение. Пройдитесь тем же вопросом по второму — и колонка владельцев меняется: что искать — модель, какого рода это вопрос — модель, повторить ли — модель, как ответить — модель. Один и тот же список решений, разные владельцы. Вот теперь различие двух систем не только видно, но и записано: не «этот умнее», а «у этих двух по-разному распределены владельцы одних и тех же суждений».
У этой записи есть немедленная практическая отдача: она показывает, где на самом деле лежит проблема. Когда первый ассистент отвечает мимо, привычный диагноз звучит как «плохой поиск» — и инженер идёт крутить механизм извлечения. Но карта владельцев говорит другое: решение о релевантности здесь вынесено приложением, и дело не в том, что поиск плохо ищет, а в том, что суждение о том, что искать, отобрано у модели. Диагноз по компонентам указывает на деталь; диагноз по решениям — на само решение и его владельца. Это разные адреса, и чинить, не различая их, — значит подолгу настраивать то, что не было причиной. Метод не обещает готового лекарства, он делает меньшее и более важное: точно называет, о чьём суждении идёт речь, — а без этого любое лечение бьёт наугад.
У этого вопроса есть свойство, делающее его несущим: он приложим к любому решению в любой системе и всегда даёт определённый ответ — исход либо застыл в коде, либо выносится моделью, третьего места ему нет. И потому «чьё это суждение?» работает не только на разборе чужих ошибок, но и как рабочий инструмент проектировщика над собственной системой: у каждого решения в системе есть владелец суждения, и этот вопрос всякий раз его называет.
Работает он в обе стороны времени. Назад — как диагноз: приложенный к готовой системе, он вскрывает, где суждение уже отобрано, и делает видимой раскладку, сложившуюся сама собой. Вперёд — как инструмент проектирования: заданный до того, как написана строка кода, тот же вопрос перестаёт быть вскрытием и становится развилкой. «Чьё будет вот это суждение?» — спрошенное вовремя, оно возвращает автору выбор, который иначе сделался бы за него формой по умолчанию. Разница между двумя ассистентами возникла не потому, что кто-то из авторов ответил на этот вопрос неправильно, а потому, что один из них его не задал вовсе — и раскладку за него выбрал шаблон. Задать вопрос заранее — уже половина дела: изъятие, замеченное в момент проектирования, перестаёт быть случайным, даже если в итоге его оставляют.
Остаётся последнее, и оно важнее всего предыдущего. Владелец суждения — не свойство, приросшее к решению от природы. Это выбор. Кто-то — команда, автор пайплайна, стартовый шаблон, привычка поля — определил для каждого решения, застынет оно в приложении или останется за моделью. Чаще всего этот выбор не был сделан как выбор: его никто не проговаривал — просто написали стандартный пайплайн, в котором релевантность уже отобрана. Спросите инженера, унаследовавшего такую систему: «кто решил, что модель не должна повторять поиск, если найденное не подходит?» — и честный ответ будет «никто». Так решила форма: одноходовый конвейер не содержит места для второй попытки, и отсутствие этого места — тоже вынесенное суждение, только вынесла его не команда, а шаблон, из которого система выросла. Но незамеченный выбор — всё равно выбор, и у него всё равно есть автор и момент.
Метод на этом останавливается — и это намеренно. Назвать владельца суждения не значит осудить его. Часть замороженных владельцев стоит там по хорошей причине, и забирать у них решение было бы ошибкой; другие застыли по инерции и держатся только историей. Различить эти два случая — отдельная и более поздняя работа; вопрос «чьё это суждение?» её не делает, он лишь кладёт перед ней предмет. Он атом, а не вся процедура: полный разбор системы соберётся из множества таких вопросов и обрастёт критериями, но начинается всё с одного — приложенного к одному решению. Сначала увидеть владельца, потом судить о нём; в обратном порядке не выходит.
И как только на систему смотришь так — как на список решений, каждое со своим владельцем и каждый владелец назначен чьим-то выбором, — знакомый продакшен начинает читаться иначе. Не «вот моя архитектура», а «вот тридцать решений, и по каждому кто-то однажды выбрал, чьим оно будет; помню ли я, чтобы выбирал?».
Вопрос «чьё это суждение?» превращает разглядывание системы в разбор. Пока система — набор компонентов, о ней можно говорить только общими словами: удачная, неуклюжая, умная, тупая. Стоит разложить её на решения и над каждым спросить, чей исход, — и она перестаёт быть картинкой и становится списком: столько-то суждений, у каждого владелец, у каждого владельца — история назначения. Именно этот список, а не схема из прямоугольников, и есть настоящее устройство системы.
Два ассистента, с которых всё началось, теперь различимы до конца. Снаружи они по-прежнему одинаковы — тот же интерфейс, та же база, тот же класс модели. Но их различие больше не приходится списывать на неуловимое «один умнее». У них разные владельцы одних и тех же суждений: там, где первый заморозил решение в приложении, второй оставил его модели. Всё, что чувствовалось в открывающем контрасте и не имело имени, оказалось именно этим — картой владельцев, разложенной по-разному.
И остаётся вопрос, обращённый уже не к двум вымышленным ботам, а к системам читателя. Сколько суждений в них заморожено — и заметил ли он момент, когда это произошло? Большинство изъятий не обставлялись решением: они пришли формой по умолчанию и с тех пор не пересматривались. Значит, где-то в знакомой архитектуре стоят замороженные суждения, о которых никто не помнит, что когда-то выбрал их заморозить. Найти их — уже работа; но начинается она не с инструмента, а с одного вопроса, приложенного к каждому решению по очереди.
разбирай систему не на компоненты, а на суждения — у каждого решения в системе есть владелец, и это выбор, а не данность.
Глава 2. Флагманский случай: инъекционный RAG
Команда полгода улучшает извлечение. Меняет модель эмбеддингов на более точную, дробит документы на чанки поумнее, добавляет реранкер, который переупорядочивает найденное перед тем, как отдать его модели. На каждой итерации метрики сходства растут, а качество ответов бота упирается в стену, которую не видно на графиках. Стена не там, где её ищут. Она не в качестве компонентов — все они делают ровно то, чего от них ждали. Она в том, что модель, которая пишет ответ, ни разу не была спрошена, что именно ей искать.
Кто в системе владеет поиском — приложение или модель? Пока этот вопрос не задан, его ответ выглядит настолько само собой разумеющимся, что кажется не решением, а свойством мира: конечно, поиском занимается пайплайн, для того он и построен. Но это решение. Оно вынесено один раз, на этапе проектирования, и с тех пор не менялось. И у него есть цена — не в точности выборки, а в том, какие суждения оно отняло у модели и что стало невозможным вместе с ними.
Модель в такой системе отвечает по выборке, которой не заказывала, — так, как если бы выборка была исчерпывающей. Возразить против принесённого ей нечем: она не знает, что было доступно и чего не хватает. Это не глупость и не дефект обучения. Это слепота к собственному незнанию, встроенная в архитектуру: модель не видит границы того, что ей дали, потому что саму границу проводили без неё.
2.1. Инъекция против агентного извлечения
Возьмём привычную схему, по которой работает большинство ассистентов на базе знаний. Пользователь задаёт вопрос. Приложение превращает его в вектор, идёт в векторную базу — хранилище, где документы лежат как числовые представления смысла, — находит несколько наиболее близких фрагментов, при желании переупорядочивает их реранкером и вклеивает получившийся текст в промпт перед вопросом пользователя. Только теперь вызывается модель. Она видит вопрос и рядом — блок «вот релевантные документы», как будто он всегда там был. На этом блоке она и строит ответ.
Назовём эту механику по имени. Инъекция (инъекционный RAG) — схема, в которой приложение находит и вставляет контекст до вызова модели, а модель получает его как данность. Контекст именно впрыскивается — снаружи, готовым, без участия того, кто будет им пользоваться. RAG (retrieval-augmented generation) — генерация, дополненная извлечением; инъекционный RAG — та его разновидность, где извлечение целиком вынесено из модели в код вокруг неё.
Эта схема — не чья-то ошибка и не признак небрежности. Она стала стандартом по хорошей причине: когда модель нельзя было надёжно попросить самой сходить за нужным документом — сформулировать разумный запрос, не заблудиться, не выдумать источник, — единственным способом дать ей факты было положить факты рядом заранее. Приложение брало поиск на себя, потому что модель его не потянула бы. Инъекция — рациональный ответ на реальную задачу; узнать в ней свой продакшен не стыдно. Стыдно другое — не заметить, что решение, принятое под те условия, продолжает действовать, когда условия изменились. Но об этом уместно говорить, только предъявив сперва, что именно это решение замораживает.
У этой механики есть точный глагол. Инъекция обходит суждение модели о поиске: решение о том, что и где искать, принимается в обход того, кто будет отвечать. Не против модели — мимо неё. Приложение не спорит с моделью о выборке и не показывает ей альтернатив; оно просто ставит модель перед фактом. К моменту, когда модель включается в работу, поиск уже состоялся, его результат заморожен, и переиграть его нельзя — не потому что запрещено, а потому что механика не предусматривает такого хода. Модель здесь — последнее звено конвейера, а не его распорядитель.
Чтобы увидеть это не как схему, а как повседневность продакшена, проследим за одним вопросом к боту поддержки. Пользователь пишет: «После обновления перестал приходить вебхук на платёж — куда копать?» Приложение переводит эту фразу в вектор и уходит в базу зн
