Привет, ХабрПосле знакомства с Codex CLI от OpenAI я решил провести практический тест: можно ли в российском ИИ-ландшафте собрать ChatGPT-подобный login UX для Привет, ХабрПосле знакомства с Codex CLI от OpenAI я решил провести практический тест: можно ли в российском ИИ-ландшафте собрать ChatGPT-подобный login UX для

Агентность на практике: Codex CLI и российский AI-ландшафт

2026/03/04 16:15
11м. чтение
Для обратной связи или замечаний по поводу данного контента, свяжитесь с нами по адресу crypto.news@mexc.com

Привет, Хабр

После знакомства с Codex CLI от OpenAI я решил провести практический тест: можно ли в российском ИИ-ландшафте собрать ChatGPT-подобный login UX для агентного CLI — запускаю клиент, логинюсь, сразу работаю с инференсом.

Сначала разберу рынок и авторизацию: как я сравнил Яндекс и Сбер, и почему для нужного UX Яндекс оказался проще в реализации. А потом покажу самое вкусное: что пришлось чинить в runtime inference, чтобы агент вообще не умирал на первом ходе.

TL;DR

  • Для agentic CLI необходим воспроизводимый мост IDP login -> API token -> inference.

  • У Яндекса такой мост в публичной документации есть: OAuth -> IAM -> AI Studio API.

  • У Сбера в персональном сценарии (GIGACHAT_API_PERS) доступ к GigaChat API идёт через Client ID/Client Secret -> /api/v2/oauth, то есть через app-credentials контур.

  • Для гипотезы "сделать ChatGPT-like login UX в Codex CLI под российский инференс" путь через Яндекс оказался единственным вариантом, где связка identity → compute собирается без архитектурного разрыва.

  • Бонус: Яндекс уже работает через Responses API, поэтому не пришлось насильно возвращать Codex в легаси chat/completions.

  • Самая жесть была не в авторизации, а в рантайме: content_filter, устаревший формат tool-call сообщений и необходимость жёсткой нормализации контекста.

Какой UX я считал целевым

Мне нужен был простой критерий: после пользовательского логина CLI должен сразу получать рабочий compute-доступ от имени этого пользователя.

Без ручной проклейки ключей, без отдельного контура app-credentials, без разрыва между логином и инференсом.

Сравнение провайдеров по доступу к AI Compute API

Yandex: связка пользовательского входа и AI API реально собирается

По документации Yandex Cloud:

  • для AI Studio API нужен IAM token в Authorization: Bearer;

  • OAuth-токен пользователя можно обменять на IAM token.

Практически это даёт воспроизводимую цепочку:

  1. Пользователь проходит OAuth-логин.

  2. CLI получает OAuth token со scope cloud:auth.

  3. CLI меняет его на IAM token.

  4. CLI работает с AI Studio API в нужном облачном folder context.

Требования тут понятные и явно описаны в документации:

  • нужно зарегистрировать приложение в Yandex ID;

  • указать целевой scope cloud:auth;

  • у пользователя должен быть доступ к облачному аккаунту (включая ролевой доступ и биллинг).

Но ключевое для меня было то, что связка "человек залогинился -> CLI получил рабочий compute-доступ" в принципе есть.

Sber (физлица): контур для GIGACHAT_API_PERS другой

По публичной документации GigaChat API для персонального сценария:

  • используется scope=GIGACHAT_API_PERS;

  • access token выдаётся через POST /api/v2/oauth;

  • до этого нужен Authorization key (из Client ID + Client Secret / ключа проекта);

  • токен короткоживущий (30 минут).

На практике для CLI это означает раздельные auth-контуры: пользовательский вход по SberID и доступ к compute API не образуют единую цепочку.

В публично описанном контуре отсутствует прямой механизм обмена пользовательского IDP-токена на compute-токен для GigaChat API.

Для персонального agentic UX уровня с непрерывным auth-контуром это и есть ключевое ограничение. Проще говоря: вошёл как пользователь, а дальше всё равно отдельная возня с app-ключами.

Почему в моей гипотезе сработал Yandex

Моя цель была узкой: сделать Codex CLI под российский инференс с login UX, максимально похожим на ChatGPT-поток.

Яндекс позволял проверить гипотезу сразу, потому что в документации есть явный и воспроизводимый мост между пользовательским OAuth и рабочим API-токеном.

Что пришлось менять в Codex CLI

Чтобы довести UX до рабочего состояния, пришлось пройти через несколько слоёв архитектуры Codex CLI.

1) TUI onboarding и конфиг провайдера

TUI onboarding
TUI onboarding

Что сделал:

  • добавил конфигурируемые OAuth-провайдеры;

  • вынес их в config.toml (oauth_providers);

  • собрал onboarding-экран в TUI для настройки и старта логина;

  • зафиксировал scope cloud:auth, чтобы не ловить случайно кривые конфиги.

Ключевые файлы:

  • codex-rs/tui/src/onboarding/auth.rs

  • codex-rs/tui/src/onboarding/onboarding_screen.rs

  • codex-rs/core/src/config/mod.rs

  • codex-rs/core/config.schema.json

2) OAuth runtime в отдельном login-слое

Конфигурация Yandex OAuth провайдера
Конфигурация Yandex OAuth провайдера

Что сделал:

  • универсальный старт OAuth-логина;

  • генерация authorize URL;

  • извлечение code;

  • обмен code на токены.

Отдельно поддержал два режима:

  • verification_code — нужен в текущем сценарии;

  • loopback — оставил как универсальный путь для провайдеров, где возможен локальный callback.

Ключевые файлы:

  • codex-rs/login/src/oauth.rs

  • codex-rs/login/src/lib.rs

  • codex-rs/login/src/server.rs

3) Нормальный lifecycle токенов в AuthManager

Yandex OAuth провайдер сконфигурирован
Yandex OAuth провайдер сконфигурирован

Что сделал:

  • добавил OauthAuthData в auth storage;

  • встроил OAuth в общий AuthManager;

  • добавил refresh-поток, чтобы сессия не рассыпалась после перезапуска/протухания access token.

Ключевые файлы:

  • codex-rs/core/src/auth/storage.rs

  • codex-rs/core/src/auth.rs

  • codex-rs/core/tests/suite/auth_refresh.rs

4) Провайдерный слой: довести логин до реального compute-доступа

Это самый важный кусок: логин сам по себе ничего не даёт, пока рантайм не получает корректный IAM + folder context.

TUI с доступом к AI API
TUI с доступом к AI API

Что сделал:

  • обмен OAuth token на IAM token;

  • резолв активной folder;

  • интеграция этого контекста в общий auth/runtime путь.

Ключевой момент: OAuth-логин не должен заканчиваться на уровне UI. Его нужно доводить до полноценного compute-контекста внутри рантайма Codex.

Здесь вынес всю специфическую логику в отдельный файл:

  • codex-rs/core/src/auth/yandex.rs

5) Нативная интеграция провайдера и моделей

Чтобы это было похоже на "родной" опыт, добавил провайдера как встроенный вариант и связал модельные пресеты с folder-aware slug.

Выбор моделей Yandex
Выбор моделей Yandex

Ключевые файлы:

  • codex-rs/core/src/model_provider_info.rs

  • codex-rs/core/src/models_manager/manager.rs

  • codex-rs/core/src/models_manager/model_presets.rs

Что получилось в итоге по UX

На стороне пользователя поток стал таким:

  1. Запускаю codex.

  2. Выбираю нужного провайдера.

  3. Прохожу вход в браузере.

  4. Возвращаюсь в CLI и ввожу verification_code.

  5. CLI получает рабочий контекст и может идти в модель.

Субъективно это уже близко к нужному login first, work immediately, ради которого я и затевал первую часть эксперимента.

Инференсы Yandex моделей в рантайме Codex CLI

После логина всё выглядело красиво ровно до первого живого запроса. Дальше началась суровая реальность: вместо нормального агентного цикла я наблюдал сообщения от цензора Yandex с фразой CONTENT FILTER снова и снова. После этого разваливающийся tool-loop уже не удивлял.

6) Борьба с цензором и логирование запросов

На самом деле content_filter я увидел при первом же запросе: Codex отправляет отдельно developer сообщение со страшным словом shell да еще и в XML тегах. Не проблема, если склеить все developer сообщения в одно, то начинает проходить.

Но цензор только разминался. При втором запросе я снова словил content_filter. Почему? Зачем? Поэкспериментировав с историей, решил отдельно логировать сырые запросы в инференс на провайдере Yandex:

  • log_yandex_responses_request_payload(...) в codex-rs/core/src/yandex_context_normalizer.rs;

  • вызов этого логирования в build_responses_request(...) (codex-rs/core/src/client.rs).

Что это дало: в логах сразу видно role, text_len, превью каждого ResponseItem. И там быстро всплыл корень боли.

7) Content filter бил не «случайно»: мы вторым запросом забивали контекст

По логам сессий в ~/.codex-yandex/log/codex-tui.log:

  • в проблемных запросах в input уходил огромный блок # AGENTS.md instructions ..., который честно собирал рантайм по всему проекту для контекста агента (text_len порядка 18095);

  • после этого шли ретраи и финал с reason: content_filter.

То есть фильтр бил не по содержанию промпта, а по форме и объёму префикса контекста.

Чтобы это стабилизировать, появился normalize_responses_input_for_provider(...):

  • схлопывание стартовых служебных сообщений в компактный префикс;

  • вынос developer-контента в единый блок (склейка);

  • временный хак: вырезать AGENTS-полотно (то что вообще делает агента Codex агентом), но сохранить <environment_context>.

Код: merge_initial_context_messages(...) и strip_agents_md_context_hack(...) в codex-rs/core/src/yandex_context_normalizer.rs.

8) Модель иногда отдавала tool calls в legacy-тексте, а не в структурных событиях

В ряде rollout-ов assistant присылал не нормальный function_call, а текст вида:

  • [TOOL_CALL_START]exec_command ...

  • или fenced-блоки с update_plan/exec_command.

Без нормализации это ломает агентный цикл, потому что рантайм ждёт поддержку tool API и структурный tool-call item.

Я не в первый раз встречаюсь с таким поведением (этим часто грешат китайские модели), поэтому не удивился и просто добавил преобразование в normalize_responses_output_items_for_provider(...):

  • парсинг [TOOL_CALL_START]...;

  • парсинг fenced tool-блоков;

  • ремонт кривого JSON (например \\' в аргументах shell);

  • генерация валидных ResponseItem::FunctionCall с call_id формата yandex-legacy-*.

9) Почему пришлось отключить параллельные tool calls

В Codex возможность параллельных вызовов инструментов определяется флагом parallel_tool_calls. Он подтягивается из model_info.supports_parallel_tool_calls (см. run_sampling_request(...) и сборку запроса в client.rs).

Проблема в том, что на старте Yandex-модель определялась как unknown slug (Unknown model gpt://.../yandexgpt/rc). А для неизвестных моделей Codex по умолчанию ставит supports_parallel_tool_calls = false.

В итоге цикл автоматически перешёл в последовательный режим. И, что интересно, это неожиданно стабилизировало поведение агента: пропали ситуации, когда модель в одном ответе генерировала несколько tool-команд и рантайм начинал спотыкаться о формат событий. Мы ведь помним, что Yandex нарушает tool API на второй-третий запрос, а поиск серии tool calls в ответе модели не выглядит стабильным решением.

По факту вывод простой: при интеграции стороннего провайдера лучше сначала добиться стабильного serial tool-loop. Параллелизм имеет смысл включать только тогда, когда формат ответов и событий гарантированно чистый и предсказуемый.

10) Отдельный кайф: Yandex уже на Responses API

Самый приятный бонус интеграции: не пришлось тащить костыли под legacy chat/completions, который команда Codex уже успела отключить в приложении. Откатывать логику ребят было бы странно.

В провайдере сразу зафиксировано:

  • wire_api = "responses" (create_yandex_provider в codex-rs/core/src/model_provider_info.rs);

  • supports_websockets = false и обычный HTTP streaming путь.

Это сильно упростило архитектуру: я чинил только нормализацию и совместимость формата, а не весь транспортный слой Codex.

Что получилось после стабилизации

После всех правок пайплайн стал воспроизводимым:

  1. OAuth/login и IAM/folder context собираются автоматически.

  2. Запросы не валятся пачкой в content_filter по тем или иным причинам.

  3. Legacy tool-call текст приводится к структурным функциям, и агентный цикл живёт.

  4. Inference работает на Responses API без отката в старый режим.

Итого: самая сложная часть оказалась не в добавлении логина Yandex, а в адаптации Codex рантайма к реальным возможностям инференса.

Послевкусие

Интеграция заработала технически. Login собирается. IAM подтягивается. Responses API работает. Tool-loop после нормализации не разваливается на первом шаге.

Но ощущение "всё заработало" так и не появилось.

Формально интегрировать инференс — не значит получить полноценный агентный рантайм. Вопрос в агентных критериях. Если их нет, никакая обвязка не сделает систему агентной.

Я для себя сформулировал минимальный набор таких критериев.

1. Непрерывный auth-контур

Логин пользователя должен напрямую давать compute-доступ. Без разрыва между identity пользователя и инференсом.

Если после входа начинается отдельная возня с ключами — это не агентный UX.

2. Предсказуемость фильтрации

Инъекции — реальная угроза. Но когда инференс начинает реагировать на служебные теги или слова вроде shell как на атаку, это ломает инструментальный сценарий.

Защита не должна разрушать базовые инженерные use-case.

3. Контекстная устойчивость

Если несколько циклов reasoning приводят к деградации инструкций, потере политики или невозможности нормально использовать AGENTS.md / SKILL.md, это уже архитектурное ограничение.

Агентность невозможна без устойчивого управления контекстом.

4. Соблюдение tool API

Агентный рантайм строится на контракте. Если модель периодически "падает" в legacy-текст вместо структурных tool-calls, контракт нарушается. Тогда рантайм начинает чинить модель, вместо того чтобы выполнять задачу.

5. Следование инструкциям под нагрузкой (со звездочкой)

Самая тонкая тема — это не фильтр и не tool API, а устойчивость следования инструкциям в многошаговом цикле.

В моих экспериментах после 2–3 итераций reasoning модели начинали терять строгость: забывать ограничения, игнорировать формат, "переизобретать" правила, которые уже были заданы в начале сессии (установка python зависимостей через uv — это еще то приключение).

Я давно гоняю разные инференсы под агентной нагрузкой — от простых CLI-циклов до прогонов под агентные бенчмарки. И там всегда возникает ощущение: эта модель "держит форму" или нет. Появляется субъективное чувство — "твоя" она или нет.

Но когда модель обнуляется уже после пары циклов — это перестаёт быть субъективным ощущением. Это измеримый эффект деградации инструкционного слоя под нагрузкой. И речь не о качестве ответа и не о креативности.

Речь о способности сохранять контракт:

  • помнить ограничения,

  • соблюдать формат,

  • продолжать начатую стратегию,

  • не разрушать уже построенный контекст.

Если модель не удерживает инструкционную рамку в пределах одной агентной сессии, никакая обвязка её не спасёт. Агентность — это не "умный ответ", это стабильное поведение в агентном цикле. И это, по сути, один из главных маркеров зрелости агентного слоя.

Соответствуют ли российские инференсы агентным требованиям?

YandexGPT Pro 5.1 (её я тестировал в Codex; Alice AI LLM позиционируется как чат-модель — tool-контракт и агентный reasoning она держит заметно хуже)

  • Непрерывный auth-контур собрать можно — плюс.

  • Responses API есть — плюс.

  • Tool API формально поддерживается — плюс (с поправкой на нормализацию и легаси-адаптацию).

  • Устойчивость контекста и поведение фильтра — спорный момент.

  • Долгоживущий reasoning без деградации — нестабильно.

Инфраструктурно Yandex сейчас ближе к агентному UX. Поведенчески ограничения начинают проявляться уже в многошаговом цикле.

GigaChat-2-Max (тестировался на стандартном Codex CLI через роутер)

  • Непрерывного auth-контрура нет — первый критерий не выполняется.

  • Контекст больше (заявленные 128k против ~32k у YandexGPT Pro 5.1) — плюс.

  • Tool API соблюдается чище, чем у Yandex — плюс.

  • Но деградация инструкционного слоя под нагрузкой ощущается сильнее.

То есть формально контракт может выглядеть аккуратнее, но после пары циклов reasoning модель начинает "расплываться" быстрее.

И это не раздача оценок, а попытка трезво посмотреть на зрелость агентного слоя через один и тот же рантайм.

Агентность — не про OAuth и не про наличие tool-calls. Это способность модели удерживать инструкционную рамку, не деградировать после нескольких итераций и соблюдать контракт с рантаймом. Если этого нет, мы получаем не агента, а чат с инструментами.

Codex CLI - это универсальный агентный рантайм. На его базе можно строить и бизнес-агентов, и исследовательские пайплайны, и автоматизацию. Но если модель не удерживает контракт в цикле, сфера применения не имеет значения — деградация всё равно проявится.

Что дальше?

С самого начала мне хотелось не оценки инференсам раздавать, а провести воспроизводимый эксперимент.

Интеграция показала: разные провайдеры живут в разных эпохах API.

  • У GigaChat — контракт раннего OpenAI, который уже даже не fully compatible.

  • У Yandex до Responses API был свой, мягко говоря, нестандартный completions.

  • У Codex — строгий рантайм, который ждёт нормальный Responses и корректный tool-контракт.

Чтобы не переписывать CLI под каждого вендора, я давно вынес адаптацию в отдельный слой — роутер. Сначала он жил в проде как утилитарная прокладка между разными контрактами. Потом я пересобрал его в чистую Rust-реализацию, чтобы можно было нормально экспериментировать с инференсами в Codex.

Роутер принимает Responses API и chat/completions, нормализует события, сглаживает различия провайдеров и позволяет подключать разные инференсы к Codex без переписывания CLI.

Проект роутера здесь:
https://github.com/olegische/xrouter

Fork Codex с Yandex здесь:
https://github.com/xrouter-ru/codex-yandex-provider

Если хотите проверить агентные критерии сами — подключайте через роутер Yandex, GigaChat или любой другой инференс и прогоняйте свои сценарии в реальном агентном цикле.

Такие эксперименты важны не только для пользователей. Они сталкивают провайдеров с реальными многошаговыми задачами, а не с синтетическими демо-кейсами. Под такой нагрузкой быстрее всего проявляются ограничения и растёт зрелость экосистемы.

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу crypto.news@mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Цены на криптовалюту