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

Сайдкары: веб-поиск и vision

Не все маршрутизируемые модели предоставляют hosted веб-поиск или нативный ввод изображений. opencodex восполняет эти возможности двумя сайдкарами. Каждый может работать через провайдера входа ChatGPT (forward) или через сохранённого OAuth-провайдера Anthropic. Ошибки сайдкара превращаются в ограниченные по размеру результаты инструментов или маркеры изображений, а не приводят к сбою всего хода.

Когда Codex запрашивает hosted web_search для маршрутизируемой модели вне сквозного режима, opencodex:

  1. Убирает hosted-инструмент web_search и вместо него предоставляет маршрутизируемой модели синтетический функциональный инструмент web_search(query). Исходные опции hosted-инструмента сохраняются для вызова сайдкара.
  2. Запускает маршрутизируемую модель в небольшом агентном цикле. Когда она вызывает web_search, opencodex использует выбранный бэкенд сайдкара: OpenAI выполняет hosted web_search по умолчанию с gpt-5.6-luna; Anthropic выполняет web_search_20250305 по умолчанию с claude-sonnet-5. Потоковый ответ и цитаты становятся результатом инструмента.
  3. Повторяет цикл, пока модель не ответит или суммарный бюджет реальных запросов не достигнет maxSearchesPerTurn (по умолчанию 3), после чего убирает инструмент поиска и принуждает к финальному ответу. Настоящие клиентские инструменты вроде apply_patch или shell завершают ход, чтобы эти вызовы дошли до Codex.

Каждая итерация маршрутизируемой модели запрашивает у вышестоящего провайдера stream: true, но opencodex полностью буферизует семантические события внутри, прежде чем решить, искать дальше или вернуть финальный ответ. Заранее получаются только финальные заголовки/статус первой итерации и ротации ключей по 429. Поэтому синтетические поисковые вызовы и промежуточный вывод никогда не попадают к клиенту как видимый вывод модели.

Внедряемый результат оборачивается в границу недоверенных данных, ограничивается по длине и дедуплицируется по URL источника. В ходах со структурированным выводом (json_schema / json_object) он передаётся компактным JSON, а не прозой. Для текстовых маршрутизируемых моделей поисковой модели дополнительно поручается описывать релевантные изображения словами и указывать их исходные URL.

{
"webSearchSidecar": {
"enabled": true,
"backend": "anthropic",
"model": "claude-sonnet-5",
"reasoning": "low",
"maxSearchesPerTurn": 3,
"routedModelStallTimeoutMs": 200000,
"timeoutMs": 200000
}
}

Уровень рассуждений minimal не используется, потому что hosted-бэкенд отклоняет инструменты на этом уровне. Неудачный поиск возвращается маршрутизируемой модели как ограниченный по размеру результат с ошибкой, позволяя ей ответить на основе уже имеющегося контекста.

Действуют четыре отдельных таймера. stallTimeoutSec — базовый бюджет простоя событий моста. connectTimeoutMs (по умолчанию 200000) покрывает только DNS/TCP/TLS и финальные заголовки ответа. Задаваемый только в файле конфигурации webSearchSidecar.routedModelStallTimeoutMs (по умолчанию 200000, целое 1..2147483647) ограничивает непрерывное отсутствие сырых байтов ответа для каждой итерации маршрутизируемой модели и сбрасывается при каждом непустом байте. webSearchSidecar.timeoutMs отдельно ограничивает один hosted-запрос поиска. Итоговый сторожевой таймер моста — max(base stall, connect timeout, routed-model stall, sidecar timeout) + 30 seconds. Простой маршрутизируемой модели не является общим таймаутом генерации. Сбои до начала SSE возвращают JSON с кодом, отличным от 2xx; сбои генерации после отправки заголовков ответа доставляются как SSE-событие response.failed.

Когда маршрутизируемая модель указана в noVisionModels своего провайдера, а запрос содержит изображение, opencodex описывает каждое изображение до основного вызова и заменяет его текстом. Дашборд и API управления показывают gpt-5.6-luna как текущее значение по умолчанию, а при запуске явно сохранённое устаревшее значение gpt-5.4-mini мигрирует на Luna. Если поле visionSidecar.model полностью отсутствует, путь выполнения vision всё же имеет зашитый в код фолбэк gpt-5.4-mini.

  • Изображения могут приходить из сообщений пользователя, разработчика и результатов инструментов, включая view_image из Codex.
  • Каждое изображение отправляется в настроенную нативную vision-модель с reasoning.effort: "low"; полученное описание заменяет часть с изображением на месте.
  • Описания выполняются с ограниченной параллельностью (по 3 одновременно, порядок входа сохраняется). Пользовательский контекст, передаваемый описывающей модели, ограничен 800 символами, а каждое внедряемое описание — 2 000 символами. Запрос не отправляет max_output_tokens, который бэкенд ChatGPT отклоняет.
  • URL изображений проверяются перед пересылкой: data-URL должны использовать png / jpeg / jpg / webp / gif, а объём base64-данных ограничен примерно 20 МБ. Принимаются только схемы data: и https:; удалённые https-изображения загружает бэкенд OpenAI, а не прокси.
  • Сопоставление noVisionModels игнорирует суффикс :size в стиле Ollama, поэтому запись gpt-oss покрывает и gpt-oss:120b.
  • Если описание не удалось, модель получает короткий маркер ошибки обработки. Если план сайдкара недоступен, исходное изображение удаляется, а не пересылается текстовому бэкенду.
  • maxDescriptionsPerTurn (по умолчанию 8) ограничивает число новых описаний за один ход основной модели. Попадания в кэш и дубликаты в рамках того же хода лимит не расходуют. Успешные описания data:-изображений кэшируются по бэкенду, модели, детализации, байтам изображения и контексту сообщения; изменяемые https:-изображения не кэшируются.
{
"visionSidecar": {
"enabled": true,
"backend": "anthropic",
"model": "claude-sonnet-5",
"maxDescriptionsPerTurn": 8,
"timeoutMs": 45000
}
}

Модель помечается как текстовая для конкретного провайдера:

{
"providers": {
"ollama-cloud": {
"adapter": "openai-chat",
"baseUrl": "https://ollama.com/v1",
"noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"]
}
}
}

Ключи в файле конфигурации доступны уже сейчас. Чтобы отключить любой из сайдкаров, установите ему enabled: false в config.json. Поиск и описание изображений через Anthropic OAuth переиспользуют существующий прецедент OAuth-отпечатка Claude Code, но их стоит обкатать с целевым аккаунтом и нагрузкой.

Все поля описаны в справочнике по конфигурации.