Право на суждение. Агентность как принцип проектирования ИИ-систем
Право на суждение. Агентность как принцип проектирования ИИ-систем

Полная версия

Право на суждение. Агентность как принцип проектирования ИИ-систем

Язык: Русский
Год издания: 2026
Добавлена:
Настройки чтения
Размер шрифта
Высота строк
Поля
На страницу:
5 из 6

В демонстрации этот слой невидим. На заготовленных вопросах роутер попадает в цель, ветки отрабатывают гладко, система выглядит понятливой — потому что демо и составляют из запросов, под которые ветки писались. Изъятие проступает только на живом трафике, на запросах, которых при разметке категорий никто не держал в голове; и проступает не как ошибка модели, а как молчаливое несовпадение между тем, что́ спросили, и тем, к какой полке это отнесли. Систему потом чинят, добавляя ветки, — но каждая новая ветка лишь передвигает границу неизвестного, а суждение о том, что за этой границей, обратно модели не отдаёт.

То, что на архитектурной схеме подписано «intent router» или «orchestration layer», в таксономии изъятия читается одной строкой: здесь заморожено суждение «какую способность применить». Обвязка, выглядевшая нейтральной инфраструктурой — просто «как система устроена», — оказывается первым и, как правило, самым ранним местом, где у модели забрали право решать. Проявитель сработал на первом же слое: то, что казалось водопроводом, оказалось вынесенным суждением.

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

3.2. «Повторить ли»: одноходовые конвейеры

Модель ответила. Ответ, положим, слаб: извлечение подняло не тот документ, и на его основе получилась гладкая, уверенная и неверная реплика. Возникает решение — принять этот ответ или зайти ещё раз: иначе поискать, иначе сформулировать, перепроверить себя. Чьё это суждение — повторить ли?

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

На схеме это выглядит обезоруживающе просто:

обработать (запрос):

контекст = извлечь (запрос)

ответ = модель (запрос, контекст) # ровно один проход

вернуть ответ

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

Одноходовость не всегда носит имя. Явный max_steps=1 — лишь самый честный её вид, тот, что признаётся в конфигурации. Гораздо чаще второй заход отсутствует молча: конвейер собран как прямая последовательность вызовов, и место для возврата в нём просто не предусмотрели — не потому, что решили его запретить, а потому, что о нём не задумались. Такая система одноходова оттого, как она написана, а не оттого, что кто-то выбрал предел. И то и другое замораживает одно и то же суждение «повторить ли» — с той разницей, что явный предел хотя бы виден на ревью и может быть оспорен, а неявная линейность выглядит не решением, а просто «так устроен код».

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

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

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

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

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

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

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

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

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

Так одна строка — max_steps=1 или её неявный эквивалент в виде линейной трубы без ветвления — оборачивается вторым видом таксономии: изъятием суждения «повторить ли». На архитектурной схеме это даже не компонент, а свойство её формы: отсутствие стрелки, ведущей назад.

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

3.3. «Как ответить»: форс-вызовы, жёсткие схемы, шаблоны

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

Всё чаще — не модели. Форма ответа задаётся раньше, чем возникает содержание, которое в неё предстоит уложить. Ответ отливают в форму до того, как есть что отливать, — и делают это тремя привычными способами.

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

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

Тонкость форс-вызова в том, что он изымает не столько сам вызов, сколько суждение о его уместности — а с ним и право не действовать. Способность вовремя не вызвать инструмент, промолчать, обойтись рассуждением — такое же суждение, как и способность вызвать, и оно исчезает первым. Модель, обязанная на каждом ходу что-то предпринимать, теряет возможность сказать «здесь делать ничего не надо» — а ведь именно этот ответ нередко и есть правильный.

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

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

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

И шаблон подводит именно там, где случай лишь притворяется типовым. Вопрос по ключевым словам совпал с частым, система выдала заготовленный абзац — а у пользователя ровно тот редкий поворот, из-за которого стандартный совет неверен. Модель, дай ей сформулировать, уловила бы оговорку в вопросе; шаблон её не слышит, потому что он вообще не слушает — он срабатывает по совпадению, а не по смыслу.

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

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

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

У жёсткой формы, как и у прочих изъятий, причина честная. Структурированный вывод нужен, чтобы ответ модели можно было передать дальше по программе — в базу, в интерфейс, в смежный сервис, которые ждут ровно определённых полей и споткнутся о свободный текст. Форс-вызов гарантирует, что нужный шаг случится, а не будет пропущен на живом трафике. Шаблон обеспечивает единый выверенный тон и юридически проверенные формулировки там, где импровизация недопустима. Это не капризы: за каждым приёмом стоит реальное требование системы к предсказуемости выхода, и требование законное.

И потому важно: изъятие здесь только названо, приговор ему не вынесен. Из того, что схема или форс-вызов замораживают суждение о форме, не следует, что их надо изгнать: и то и другое способно служить иначе — оформлять суждение модели, а не занимать его место. Схема может задавать контракт на структуру, не диктуя содержания; вызов может быть гарантирован так, что модель всё равно остаётся автором того, что этот вызов несёт. Но это уже разговор о возврате — о том, как удержать пользу формы, не изымая решения. Здесь, в таксономии, фиксируется только сам факт: в этих трёх приёмах суждение о выражении вынесено за модель. Где проходит граница между формой, что служит суждению, и формой, что встаёт на его место, — вопрос отдельный, и до него дойдёт черёд.

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

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

3.4. «Что помнить»: внешнее управление памятью и суммаризация

Разговор с системой длится, задача обрастает историей, и рано или поздно всего накопленного становится больше, чем модель может держать перед собой разом. Что-то придётся оставить, что-то убрать, что-то сжать. Кто-то должен решить, что из прошлого всё ещё важно. Чьё это суждение — что помнить?

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

Чужая рука наводит порядок в чужой памяти. Хозяин памяти — модель, которой предстоит на неё опереться; распоряжается ею внешний код, который сам этой памятью пользоваться не будет. Рука решает, какие записи оставить на виду, какие свернуть в одну строку, какие смахнуть со стола, — и делает это по своим правилам, а не по нуждам того, кто потом будет вспоминать.

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

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

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

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

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

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

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

На страницу:
5 из 6