Введение
За последние несколько месяцев я применял агентов на базе LLM в проектах, над которыми работал. Когда пользователь формулировал запрос на естественном языке, агент выбирал необходимые инструменты для выполнения запросов, проверял результаты, а затем либо генерировал ответ, либо задавал дополнительный вопрос, если требовалось дальнейшее принятие решения. В этой статье я обобщаю свой опыт создания оболочки агента, которая оборачивает модель и управляет вызовами инструментов и ходом диалога, а также опробования нескольких различных моделей.
Сначала я рассматривал оболочку как универсальный уровень управления, который должен был обеспечивать надежную работу любой подключенной модели. Поэтому всякий раз, когда локальная модель нарушала схему или выполняла некорректный вызов инструмента, я добавлял парсеры, повторные попытки и правила рассуждения. В тот момент это казалось разумной реакцией, поскольку я последовательно блокировал отдельные сбои.
Однако, когда я многократно запускал одну и ту же оболочку, меняя только модель, результаты различались. Локальные открытые модели неоднократно выдавали ошибки формата, некорректные аргументы, пустые ответы и чрезмерные задержки перед завершением общей задачи. В отличие от них, gpt-5.6 наиболее надежно выполняла задачу от начала до конца при использовании тех же инструментов и того же запроса. Разница была незначительной лишь на первый взгляд; на деле она была настолько существенной, что определяла, можно ли вообще использовать систему.
|
Компенсирующая оболочка оказалась не универсальной защитной сеткой, а кодом, адаптированным к распределению сбоев наблюдавшихся мной моделей. По мере улучшения модели этот код становится ненужным. Более того, компенсации, настроенные под предыдущую модель, могут мешать простому пути выполнения новой модели. |
|---|
Это не означает, что вся оболочка исчезает. Необходимо различать компенсирующую оболочку, которая компенсирует недостатки модели, и оболочку, отвечающую за системные аспекты, такие как подтверждения, разрешения, бюджеты и аудит. В этой статье я повторно рассматриваю компенсирующий код, который действительно создал, и объясняю, почему выбор хорошей модели — это архитектурное решение, а не просто вопрос затрат, находящийся за пределами кода.
1. Почему я выбрал локальные модели и как появилась оболочка
В начале проекта были очевидные причины отдать приоритет локальным моделям. Нам требовалась конфигурация, при которой бизнес-данные не покидали бы организацию, у нас уже был доступен внутренний сервер инференса, а затраты на вызовы можно было сократить. Я решил, что если модель и провайдера можно переключать через конфигурацию, код приложения останется неизменным.
В упрощенном виде, с исключением идентификаторов проекта, которые нельзя раскрывать за пределами организации, конфигурация выглядела так:
Конфигурация для переключения провайдера и модели
agent:
provider: ${LLM_PROVIDER:local}
endpoint: ${LLM_ENDPOINT}
model: ${LLM_MODEL}
capabilities:
tool-calling: true
structured-output: false
Если смотреть только на конфигурацию, переключение моделей кажется таким же простым, как изменение одной строки. На практике это было не так. Первая локальная модель заменяла названия полей, объявленных в схеме, другими словами, а вторая генерировала ответ до завершения необходимых запросов или отвечала слишком долго. Другая модель использовала некорректные аргументы инструментов или возвращала пустой ход без содержимого и вызовов инструментов.
Каждый раз при возникновении сбоя я добавлял код, чтобы модель могла добиться успеха с другой попытки. Я принимал синонимы названий полей, выводил некорректные типы из количества вариантов, повторно отправлял пустые ответы и увеличивал число шагов в соответствии с привычками вызовов одной из моделей. Количество отдельных ошибок уменьшалось, но оболочка все больше накапливала специфические для моделей знания.
В этом процессе была важная ловушка. Каждое изменение проходило тесты и устраняло реальную ошибку. Поэтому при рассмотрении только кода все эти изменения выглядели необходимой защитной логикой. Однако необходимость возникала не из контракта продукта, а из поведения использовавшейся в тот момент модели. Это был код, обоснование которого могло исчезнуть вместе со сменой модели.
2. Компенсирующая оболочка, которую я действительно добавил
Хотя добавленная мной логика компенсации принимала разные формы, все ее варианты можно описать одним предложением: «Поскольку модель ошибалась таким образом X, код исправляет это таким образом Y». Я объединил типичные случаи в обобщенный код, который можно раскрыть за пределами организации.
2.1 Я принимал названия полей схемы как синонимы
Инструмент для отправки пользователям дополнительных вопросов требовал такие поля, как questionId, question и inputType. Однако некоторые модели отправляли id или key вместо questionId, а type или kind вместо inputType. При разборе как есть весь вопрос пропускался, поэтому я создал функцию, которая последовательно проверяла несколько названий.
Парсер, принимающий варианты названий полей
private String firstText(Map<String, Object> values, String... names) {
for (String name : names) {
String value = text(values.get(name));
if (!value.isBlank()) {
return value;
}
}
return "";
}
String questionId = firstText(values, "questionId", "id", "key");
Проблема заключалась в том, что список синонимов не был взят из спецификации. Он появился в результате последовательного добавления слов после их обнаружения в журналах выполнения. Даже одна и та же модель в разных запусках генерировала разные слова, а смена модели порождала еще больше вариантов. Парсер становился шире, но контракт — более неоднозначным.
2.2 Код выводил некорректные типы входных данных
Даже после определения названий полей значения по-прежнему оставались проблемой. Я разрешал только фиксированный набор значений, таких как Text, Radio и Select, но модели генерировали выражения вроде multiple_choice, dropdown и checkbox. Чтобы не отбрасывать вопрос, я нормализовал строки и, если тип все еще оставался неизвестным, определял тип пользовательского интерфейса по количеству вариантов.
Логика нормализации и вывода значений, сгенерированных моделью
String normalized = raw.toLowerCase(Locale.ROOT).replaceAll("[^a-z]", "");
QuestionType declared = switch (normalized) {
case "text", "freetext", "string" -> QuestionType.Text;
case "radio", "singlechoice", "choice" -> QuestionType.Radio;
case "select", "dropdown", "list" -> QuestionType.Select;
case "multiselect", "multiplechoice", "checkbox" -> QuestionType.MultiSelect;
default -> null;
};
return declared != null ? declared : inferFrom(options);
Эта логика позволяла отобразить пользовательский интерфейс, но создавала риск произвольной интерпретации приложением неоднозначности, вызванной моделью. Некорректный вывод мог сохраниться как обычный ввод, и даже когда новая модель отправляла правильное значение, старый резервный вариант продолжал действовать. Это было полезно для краткосрочного восстановления, но не подходило для долгосрочного контракта.
2.3 Я повторил контракт в промпте вместо использования структурированного вывода
В некоторых локальных средах выполнения включение структурированного вывода приводило к тому, что генерация внешне завершалась нормально, но тело ответа возвращалось пустым. В итоге я отключил встроенное обеспечение формата, заново записал JSON-схему и инструкцию «выводи только JSON» в промпт и заставил парсер проверять результат.
Пример обхода контроля формата с помощью промпта и парсера
request.disableNativeSchema();
request.addInstruction("Respond with JSON only.");
request.addInstruction(renderSchema(responseSchema));
Response parsed = parser.parse(modelResponse);
validator.validate(parsed);
Этот подход сразу заработал, но продублировал контракт в трех местах. Схема инструмента, текст промпта и защитные правила парсера представляли одно и то же содержимое по-разному. Изменение любого из них требовало изменения остальных, и даже после улучшения модели это дублирование не исчезало автоматически.
2.4 Я обрабатывал пустые ходы и повторяющиеся ошибки с помощью повторных попыток
После успешного выполнения нескольких запросов к инструментам система иногда возвращала ответ, в котором и содержимое, и вызовы инструментов были пустыми. Чтобы из-за одного последнего хода не потерять все уже полученные наблюдения, я добавил путь, который повторно отправлял пустые ответы и формировал итоговый ответ на основе оставшейся информации.
Восстановление после пустого ответа и бюджет выполнения
private static final int MAX_EMPTY_RESPONSE_RETRIES = 2;
if (response.hasNoContent() && response.hasNoToolCalls()) {
retryOrFinishWithCollectedEvidence();
}
RunBudget budget = RunBudget.of(maxSteps, maxDuration, maxTokens);
Сами по себе повторные попытки могут быть необходимой защитой. Однако количество повторов и число шагов, разрешенных в рамках одного выполнения, я определял исходя из частоты сбоев и привычек вызовов модели в тот момент. Для одной модели эти значения были недостаточными, а для другой — чрезмерными; одна и та же константа означала совершенно разные затраты в зависимости от модели.
2.5 Промпт также превратился в журнал сбоев конкретной модели
Рос не только код, но и системный промпт: в него добавлялись такие инструкции, как «точно копируй идентификаторы», «не создавай заполнители» и «не делай выводов до проверки результатов инструментов». Большинство этих инструкций возникло из-за реального сбоя, произошедшего однажды.
По мере удлинения промпта становилось трудно отличить инструкции, являвшиеся политиками продукта, от компенсаций для конкретной модели. Постоянно указывая даже на поведение, с которым новая модель уже хорошо справлялась, мы затушевывали приоритет важных политик. Это также свидетельствовало о том, что оболочка была подогнана под модель.
3. Что произошло, когда я изменил только модель в той же оболочке
3.1 Метод сравнения
Чтобы проверить гипотезу, я изменил только модель, сохранив неизменными системный промпт, схему инструментов, данные запросов и бюджет выполнения. Я многократно запускал сложные запросы, аналогичные использовавшимся в реальных операциях. Задача включала проверку необходимой информации с помощью нескольких инструментов и задание вопросов по пунктам, требовавшим решения пользователя.
Я проверял, завершается ли общая задача от начала до конца без прерываний.
Я проверял, соответствуют ли названия инструментов и аргументы схеме.
Я проверял, возникали ли ошибки формата ответа, пустые ходы и повторные попытки.
Я проверял, значительно ли различались результаты при повторном выполнении одного и того же запроса.
Я проверял, сохранялись ли нормальные результаты даже без вмешательства оболочки.
В первом варианте документа я представил некоторые второстепенные метрики в числовом виде. Однако при проверке выяснилось, что эти значения не отражали должным образом реальные впечатления или общий успех. Например, даже если один объект JSON с вопросом соответствовал формату, было бы трудно назвать задачу успешной, если предыдущий вызов инструмента завершился ошибкой и общая задача не была выполнена. Поэтому в этом документе я не стал создавать новые числовые показатели по памяти, а представил только результаты, фактически воспроизведенные в ходе повторных запусков, в виде статусов.
3.2 Фактические наблюдения
Таблица 1. Результаты повторных запусков в одной и той же оболочке при изменении только модели
|
Пункт наблюдения |
gpt-oss:120b |
gemma4:31b |
qwen3.6:35b |
gpt-5.6 |
|---|---|---|---|---|
|
Задача полностью выполнена |
Повторяющиеся ошибки во время выполнения затрудняли стабильное завершение. |
Ошибки вызовов инструментов и пустые ответы возникали неоднократно. |
Сбои неоднократно происходили из-за задержек ответа и ошибок формата. |
Среди повторных запусков именно эта модель наиболее стабильно выполняла задачу от начала до конца. |
|
Вызовы инструментов |
Она продолжала выполнять вызовы, соответствующие схеме. |
Она часто пропускала необходимые вызовы. |
Происходили незавершённые остановки. |
Она продолжала выполнять вызовы, соответствующие схеме. |
|
Формат ответа |
Иногда требовалась проверка формата. |
Требовалось восстановление после пустых ответов. |
Требовались повторные попытки и проверка формата. |
Ответы были стабильными после одной или двух повторных попыток. |
|
Разброс выполнения |
В результатах и ходе выполнения наблюдался относительно небольшой разброс. |
Формат ответа отличался. |
Формат ответа отличался. |
Результаты и ход выполнения отличались меньше всего. |
|
Общая оценка |
В текущих условиях развернуть её в производственной среде было сложно. |
В текущих условиях развернуть её в производственной среде было сложно. |
В текущих условиях развернуть её в производственной среде было сложно. |
Только эта модель стабильно соответствовала критериям фактического развертывания. |
Поскольку количественные журналы для каждого запуска не сохранялись в едином формате для всех моделей на тот момент, я не стал рассчитывать новые показатели успешности или среднее время, которые могли бы оказаться неточными. В таблице зафиксированы только неоднократно наблюдавшиеся закономерности сбоев и оценки фактической пригодности к развертыванию.
Результаты подавляюще свидетельствовали в пользу gpt-5.6. Когда другую модель удавалось исправить в одной области, в другом месте снова возникала ошибка, и при каждом запуске закономерность сбоев менялась. В отличие от неё, gpt-5.6 наиболее надёжно проходила весь сценарий — от вызовов инструментов до финального вопроса — на том же тестовом контуре. На результаты сильнее повлияли собственные возможности модели по использованию инструментов и соблюдению контрактов, чем объём написанного мной кода вознаграждения.
Особенно важным было различие не в том, могла ли модель один или два раза выдать ответ, соответствующий формату, а в том, могла ли она неоднократно полностью выполнить задачу. Локальные модели также добивались частичного успеха. Однако функция, предоставляемая пользователям, требует завершения всего сценария. Если оценивать модели по этому критерию, различия между ними становились гораздо очевиднее.
Тестовый контур подтягивал более слабые модели до определённого уровня, но не устранял распределение сбоев. Добавление синонимов порождало новые синонимы; увеличение числа повторных попыток повышало время и стоимость; а увеличение бюджета шагов приводило к более длительному повторению некорректных вызовов. Логика вознаграждения была эффективной, но её ограничения также стали очевидны.
3.3 Что означает соответствие тестового контура модели
Входными данными для компенсационного тестового контура служат не требования продукта, а наблюдаемые сбои модели. Эти наблюдения относятся к конкретной модели, конкретной версии, конкретному промпту и конкретному серверу инференса. Поэтому логика вознаграждения, преобразующая эти наблюдения в код, также привязана к тем же условиям.
|
Модель завершалась сбоем X. → Тестовый контур компенсирует это с помощью Y. → Тестовый контур соответствует модели, демонстрировавшей сбой X. |
|---|
Оборачивание недетерминированных результатов в детерминированный код может создать впечатление стабильности системы. Однако при изменении модели меняется и распределение результатов. Ошибки, часто возникавшие в старой модели, могут не появиться в новой, а шаблоны вызовов новой модели могут отличаться от последовательности, предполагаемой старой логикой вознаграждения. В итоге код, считавшийся универсальным слоем, превращается в эмпирическую модель, приближающую поведение конкретной модели.
С этой точки зрения замена модели — это не просто смена провайдера. Это попытка заново оценить связанность тестового контура с моделью. Если подключить новую модель, оставив существующий код вознаграждения без изменений, ненужные резервные ветки могут изменить корректные значения, а ненужные повторные попытки — добавить задержки. После обновления модели сначала следует рассмотреть возможность удаления кода, а уже потом — добавления нового.
4. Что исчезло после перехода на gpt-5.6
Главное изменение после перехода на gpt-5.6 заключалось не просто в том, что она эффективнее проходила существующий тестовый контур. Сами ситуации, требовавшие компенсации, существенно сократились. По мере того как модель всё чаще сохраняла точные имена полей и форматы аргументов, выбирала необходимые инструменты и выполняла задачу целиком, обоснование кода восстановления в общем цикле становилось слабее.
Таблица 2. Изменения в компенсационном тестовом контуре до и после замены модели
|
Элемент компенсации |
Модели с повторяющимися ошибками |
После применения gpt-5.6 |
|---|---|---|
|
Синонимы имён полей |
Она последовательно перебирала несколько псевдонимов. |
Она использовала имена, определённые в схеме, без изменений, поэтому в большинстве случаев это стало не нужно. |
|
Вывод типа входных данных |
Она выводила неизвестные значения из строковых правил и количества вариантов. |
Она использовала допустимые типы, благодаря чему резервную ветку вывода можно было удалить. |
|
Повторение схемы в промпте |
Она заново объясняла контракт длинными предложениями. |
Контракт можно было напрямую сосредоточить на схемах инструментов и ответов. |
|
Восстановление после пустого ответа |
Пути повторной попытки и сохранения состояния срабатывали часто. |
Основной путь стабилизировался, что позволило свести их к обработке исключительных сбоев. |
|
Корректировки шагов для конкретной модели |
Мы увеличили константы в соответствии с характерными шаблонами вызовов. |
Количество ненужных вызовов уменьшилось, что позволило применять более простую политику бюджета. |
Здесь утверждение, что что-то «больше не требуется», не означает, что весь код был немедленно удалён. Прежде чем действительно удалять его, необходимо провести регрессионные тесты с новой моделью и убедиться, что результаты остаются стабильными при последовательном отключении логики компенсации по одному элементу за раз. Важно то, что изменился фокус проверки — не на добавлении дополнительной компенсации, а на подтверждении того, что существующую компенсацию можно удалить.
Хорошая модель не просто повышает точность ответов. Она также сокращает количество ветвей парсера, повторных попыток, настроек для конкретной модели, анализов журналов сбоев и комбинаций регрессионных тестов. Локальная модель может быть дешевле, если учитывать только стоимость одного вызова, но вывод меняется, если включить затраты на разработку, расследование сбоев и поддержку кода компенсации. В реальном проекте выбор gpt-5.6 снизил общие затраты и риски.
Ещё один вывод заключался в том, что оценка модели не должна заканчиваться оценкой качества одного ответа. Для агентов производительность складывается из выбора инструментов, точности аргументов, сохранения состояния между несколькими ходами, восстановления после сбоев и итогового ответа. Преимущество gpt-5.6 заключалось не только в том, что она писала более качественные предложения, но и в том, что она доводила до конца весь граф выполнения.
5. Обвязки, которые всё же должны остаться
Даже если хорошая модель сокращает количество компенсирующих обвязок, она не берёт на себя ответственность системы. Какой бы точной ни была модель, она не должна читать данные без разрешения, выполнять труднообратимые изменения без одобрения или препятствовать отслеживанию использованных доказательств и результатов выполнения.
5.1 Вопросы для различения компенсации и ответственности
При классификации кода я использовал следующий вопрос: «Был бы этот код всё ещё необходим, если бы модель идеально соблюдала контракт?» Если ответ был «нет», это была компенсирующая обвязка; если ответ был «да», это было ближе к системной ответственности.
Таблица 3. Граница между компенсирующими обвязками и обвязками, несущими ответственность
|
Категория |
Компенсирующая обвязка |
Обвязка, несущая ответственность |
|---|---|---|
|
Основание возникновения |
Ошибка, наблюдаемая в конкретной модели. |
Политика продукта, безопасность и эксплуатационная ответственность. |
|
Зависимость от модели |
Высокая. Зависит от модели и версии. |
Низкая. Сохраняется даже при смене провайдера. |
|
Типичные примеры |
Разбор синонимов, вывод значений, повторные попытки после пустых ходов и константы для конкретной модели. |
Проверки авторизации, этапы одобрения, ограничения бюджета и записи аудита. |
|
После применения хорошей модели |
Удалить после регрессионной проверки или свести к адаптеру. |
Оставить как есть и усилить тесты. |
|
Обработка сбоев |
По возможности не исправлять это незаметно; сделать проблему явной. |
При нарушении политики остановить выполнение и записать причину. |
5.2 Одобрение пользователем и границы изменений должны сохраниться
Мы не считали запросы только для чтения и фактические изменения эквивалентными вызовами инструментов. Мы автоматизировали процесс до этапа, на котором агент проверяет информацию и объясняет свой план, но требовали явного одобрения пользователя в тот момент, когда изменяются данные или результаты публикуются во внешних системах. Эта граница не связана с производительностью модели.
Процесс одобрения, обобщённый за счёт удаления внутренних имён
사용자 요청
-> 읽기 전용 조회
-> 변경 계획과 영향 제시
-> 사용자 승인
-> 실제 변경 실행
-> 결과와 근거 기록
Мы также сохранили практику, при которой система предоставляет пользователю вопросы или запросы на одобрение в заданной структуре, вместо того чтобы позволять модели переписывать их. Это не просто компенсация на случай, если модель может неправильно указать имена полей; это принцип продукта, отделяющий решения пользователя от сгенерированного текста.
5.3 Бюджеты, разрешения и записи аудита должны сохраниться
Необходима структура, устанавливающая ограничения на количество шагов, время и токены, которые может использовать одно выполнение. Однако сами числовые значения могут зависеть от скорости модели и способа вызова, поэтому их следует вынести в конфигурацию. Политика наличия ограничения — это обвязка, несущая ответственность, а значение, адаптированное под конкретную модель, — компенсирующая настройка.
Мы также не оставляем модели списки разрешённых инструментов или проверки авторизации. Ситуация, в которой у пользователя нет разрешения на выполнение запроса, и ситуация, в которой результат запроса пуст, имеют для пользователя совершенно разный смысл. Даже если модель хорошо объясняет различие, именно система должна определять, разрешён ли вызов.
Наконец, мы должны записывать, какие инструменты вызывались для каких запросов и какие результаты послужили основанием для ответа. Если в рабочей среде возникает проблема и сохраняется только последнее предложение модели, мы не можем восстановить причину. Записи аудита не становятся менее важными по мере повышения качества модели; напротив, они становятся ещё важнее по мере расширения области фактического использования.
6. Как проектировать выбор моделей и обвязки совместно
После этого опыта мы изменили процесс, при котором сначала выбирали слабую модель, а затем компенсировали её недостатки обвязкой. Сначала мы оцениваем модели-кандидаты с помощью тонкого исполнителя, содержащего только минимальный контракт и механизмы безопасности, а затем выбираем модель, способную довести типичные сценарии до завершения. После этого мы компенсируем только те ошибки, которые продолжают повторяться, изолированно в адаптере.
6.1 Сначала измеряйте общий успех выполнения задачи
Если смотреть только на долю успешного однократного разбора JSON или успешного выполнения одного вызова инструмента, легко переоценить реальную пригодность к использованию. Необходимо одновременно учитывать, достигнут ли желаемый пользователем результат, сохраняется ли результат согласованным при повторении того же запроса и понятна ли причина сбоя, когда он происходит.
Выполняйте типичные рабочие сценарии от начала до конца, а не короткими фрагментами.
Используйте для каждой модели одинаковые промпт, инструменты, данные и бюджет.
Записывайте ошибки аргументов инструментов, пустые ходы, повторные попытки и затраченное время вместе с итоговым статусом успеха.
Сопоставляйте рядом результат при включённой обвязке и результат при отключённой логике компенсации.
После смены модели проверьте, можно ли удалить существующую компенсацию, прежде чем добавлять новую.
6.2 Ограничивайте код компенсации адаптером
Когда логика компенсации, специфичная для конкретной модели, размещается в общем цикле выполнения, становится трудно понять, для какой модели существует этот код. Лучше держать компенсацию внутри адаптера провайдера или модели, а общий цикл должен исходить из того, что соблюдается четкий контракт. Если контракт невозможно выполнить, безопаснее в долгосрочной перспективе завершить работу с диагностируемой ошибкой, чем молча выводить решение.
Если добавления компенсации избежать невозможно, в комментариях и тестах следует зафиксировать название модели, версию, условия воспроизведения и условия удаления. Тогда при переходе на следующую модель можно будет сразу определить кандидатов на удаление. Для кода компенсации нужно описывать не только его функцию, но и основания для определения срока, в течение которого он остается действительным.
6.3 Учитывайте затраты на сопровождение в стоимости модели
В таблице выбора модели обычно указывают только цены на токены и затраты на серверы инференса. Однако в реальных проектах затратами также являются время на анализ сбоев, реализацию и тестирование кода компенсации, задержки из-за повторных попыток и вероятность эксплуатационных инцидентов. Даже если локальная модель имеет низкую стоимость вызова, общая стоимость может оказаться выше, если вам постоянно приходится исправлять ее ошибки.
В моем случае gpt-5.6 оказала большее влияние за счет упрощения рабочего процесса разработки, чем за счет снижения стоимости моделей. Сократился объем работы по добавлению новой ветви каждый раз, когда удавалось воспроизвести сбой, и я смог сосредоточиться на основных политиках и контрактах инструментов. Выбор высокопроизводительной модели стал способом снизить сложность кода и эксплуатации, а не просто улучшить качество.
7. Практический контрольный список, составленный после применения
В настоящее время при внедрении или замене модели я проверяю следующее по порядку.
Убедиться, что кандидатная модель надежно выполняет репрезентативные сценарии от начала до конца.
Фиксировать сбои, классифицируя их по следующим категориям: формат вывода, выбор инструмента, точность аргументов, поддержание состояния и задержка.
Не компенсировать проблемы самой модели в общей бизнес-логике.
Изолировать логику компенсации в адаптерах и конфигурации, специфичных для модели, и фиксировать рядом с ней условия удаления.
Сохранять согласование, авторизацию, бюджеты выполнения и аудиторские записи как политики, независимые от модели.
При обновлении модели сначала рассмотреть возможность удаления ненужного кода обвязки, прежде чем добавлять новую функциональность.
Если количественные журналы недоступны, не придумывать числа на основе памяти; следует сообщать только воспроизводимые явления.
Цель этого контрольного списка — не устранить обвязку. Она заключается в том, чтобы отделить ответственность модели от ответственности системы. Если позволить коду бесконечно поглощать дефекты модели, обвязка будет продолжать разрастаться и превращаться в промежуточный слой, оптимальный ни для одной модели.
Заключение
Сначала я думал, что хорошая обвязка сможет устранить различия между моделями. На практике значительная часть обвязки представляла собой код, в котором были отражены дефекты одной конкретной модели. В тот момент этот код решал проблемы, но он не был универсальным правилом, которое оставалось бы необходимым при использовании других моделей.
Когда я подключил одну и ту же обвязку к нескольким моделям, производительность gpt-5.6 оказалась подавляюще высокой, тогда как в других моделях ошибки продолжали возникать. Этот опыт помог мне понять, что производительность модели — это не просто характеристика компонента; она определяет размер и форму обвязки. Чем больше кода накапливается для компенсации слабой модели, тем сильнее система подстраивается под эту модель.
|
Хорошая модель не устраняет обвязку целиком. Она сокращает компенсирующий код обвязки, который восполнял дефекты модели, и оставляет только ту часть обвязки, за которую система должна нести ответственность до конца, например согласование, авторизацию, бюджетирование и аудит. |
|---|
Поэтому при выборе модели не следует сравнивать только стоимость вызовов. Также нужно учитывать общую долю успешно выполненных задач, воспроизводимость ошибок, объем кода компенсации, эксплуатационные риски и время, которое разработчики тратят на исправление последствий. Причину выбора gpt-5.6 для проекта можно объяснить по тем же критериям. Использование высокопроизводительной модели не только улучшило результаты, но и сделало систему проще и понятнее.
Даже если в будущем модель снова изменится, сначала я планирую задавать один и тот же вопрос: код, который я собираюсь добавить, относится к ответственности системы или является временной компенсацией недостатков текущей модели? Документирование этого различия стало для меня критерием, который быстрее всего подсказывает, что удалить, а что сохранить при переходе на следующую модель.
IAN