Перейти к основному содержимому

ADR 0051 — Доказанные грани capability; "для чего годится" — это курирование

Контекст​

Первая реальная синхронизация OpenRouter принесла 338 моделей на экран курирования. Группировка по производителю (Фаза 12, хаб с подгруппами) сделала список навигируемым, но не ответила на вопрос, который действительно задаётся перед ним: какая из них подходит для того, что мне нужно сейчас?

Два открытия, измеренных против живого API, очерчивают проблему.

Первое: каталог уже публиковал то, чего экран не показывал. Парсер читал id, name, context_length, pricing и supported_parameters — и отбрасывал architecture. Хуже того, синхронизация никогда не обращалась к удалённому источнику за capability модальности:

// sync-model-catalog.use-case.ts:202, antes
supportsVision: local?.supportsVision ?? false,

Значение бралось из уже записанного, а записанное рождалось false. Итог: supports_vision = false у всех 338, включая модели, у которых вся страница provider называется "vision". Колонка рождалась ложной и умирала ложной — не было пути, по которому она могла бы стать истинной.

По каталогу от 2026-08-04 OpenRouter объявляет: 181 модель принимает изображение на входе, 11 производят изображение, 213 принимают reasoning, 25 принимают аудио, 0 производят видео.

Второе: половины того, что хочется фильтровать, нет ни в одном каталоге. Запрос включал "melhores IAs por tipo — documentar, imagem, vídeo, thinking, code". Изображение и thinking provider объявляет. "Лучшее для кода" и "хорошее для документации" не публикует ни один provider — нет поля, нет соглашения, нет ничего, кроме имени модели. А выводить capability из имени — ровно то, что ADR 0041 запрещает: capability объявляется только тогда, когда suite её доказывает.

Видео — крайний случай: ноль моделей у девяти providers. Грань видео была бы фильтром, который никогда ничему не соответствует.

Решение​

Обе оси существуют, раздельно, и каждая живёт там, где живёт её истина.

1. Грань capability — что provider ДОКАЗЫВАЕТ​

Три новых поля в models, питаемые удалённым каталогом:

полеисточник в OpenRouter
supports_visionarchitecture.input_modalities содержит image
generates_imagearchitecture.output_modalities содержит image
supports_reasoningsupported_parameters содержит reasoning

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

Синхронизация теперь читает из удалённого источника с локальным fallback, в той же форме, что и supportsToolCalling:

supportsVision: remoto.supportsVision ?? local?.supportsVision ?? false,

undefined в удалённом источнике сохраняет локальное значение — отсутствие объявления не есть объявление отсутствия. Парсер опускает поле, когда provider молчит, вместо выдачи false; объявление false там стёрло бы ручное курирование при первом же изменении формата каталога у provider.

Аудио и видео остаются вне: аудио — потому что ни одна часть продукта его сегодня не потребляет, видео — потому что его не существует. Войдут, когда будет что фильтровать.

2. Курирование по использованию — что ОБНАРУЖИЛА КОМАНДА​

Закрытый словарь из пяти использований — codigo, documentacao, analise, imagem, conversa — отмечаемый по workspace, в колонке workspace_models.uses (text[]).

Живёт в workspace_models, а не в models, по той же причине, что и ADR 0049: это мнение того, кто эксплуатирует. Одна и та же модель — "та, что для кода" в одном workspace и дешёвая для разговора в другом, а право решать имеет тот, кто платит по счёту. Глобальной оси нет, потому что не существует глобального ответа.

Словарь закрытый, а не свободный текст: code, coding, Code и código на одном экране за неделю дают фильтр, который ничему не соответствует, — хуже, чем отсутствие фильтра. Новое использование входит в тип и получает миграцию, с доказательством исчерпывающести во время компиляции с обеих сторон (тот же механизм, что у llm-provider-names.ts).

text[], а не enum Postgres, как delegations.area Фазы 8: новое использование не должно требовать миграции типа.

3. Две оси не смешиваются ни в UI, ни в базе​

  • Метка грани и метка использования имеют разные тона в строке каталога, а чипы фильтров разделены разделителем.
  • Отметка использования не включает модель в селекторе. Колонка is_active имеет DEFAULT true, поэтому строка, рождённая отметкой использования, вставляется с явным isActive: false — без этого мнение о модели разрешило бы ей тратить, вопреки RN-043.
  • Смена использования не выключает включённое: is_active остаётся вне SET в ON CONFLICT.
  • Экран никогда не пишет "не читает изображение". Метка утверждает только истинное, потому что false здесь означает "provider не объявил".

Последствия​

  • Экран отвечает "какая модель подходит для этого" двумя путями: что provider доказывает и что команда обнаружила в использовании.
  • Фильтр, обнуляющий список, становится отличимым от пустого каталога — раньше экран сказал бы "зарегистрируйте учётные данные" тому, у кого они уже есть.
  • Существующий каталог получает истинные грани только при следующей синхронизации: миграция создаёт колонки с false, а синхронизация заполняет их из удалённого источника. Никакой backfill невозможен без обращения к provider, а выдумывать значение было бы снова исходным дефектом.
  • Восемь providers, не публикующих модальность, остаются с false — честно и деградируемо, как только любой из них начнёт объявлять.
  • "Лучшее для кода" никогда не будет capability в этом продукте. Если provider когда-нибудь опубликует такое поле, это будет ещё одно мнение — его собственное — и оно не заменит мнения команды.

Три проекта были отброшены, и причина каждого — последствие, которое осталось бы:

  • Выводить использование из имени модели (*-coder-* → код): догадка, переодетая в данные. Ошибается на универсальной модели, хорошей в коде, ошибается на той, у которой "coder" в имени, а команда сочла её плохой, и никто из знающих не может исправить.
  • Глобальное использование в models: повторило бы ровно тот дефект, который ADR 0049 исправил, — один workspace решает за соседа.
  • Свободный текст в использовании: фрагментирует словарь за неделю и превращает фильтр в текстовый поиск.

Что остаётся на потом​

Грань аудио и видео, когда будет что фильтровать: у аудио уже 25 моделей в OpenRouter, но ни одна часть продукта сегодня аудио не потребляет; у видео ноль у девяти providers.

Сортировать каталог по отмеченному использованию — сегодня использование только фильтрует. С зафиксированным словарём сортировка — одна строка; пока никто ничего не отметил, она сортировала бы по пустому списку.

Грани у остальных восьми providers. Только OpenRouter публикует модальность сегодня; каждый из прочих нужно исследовать по официальной документации, как Фаза 11 сделала с quirks, — наследовать парсер одного в другом запрещено.

Правила — в RN-056 и RN-057.