Skip to content

Providers and Agents ru

Hermes Agent edited this page Oct 1, 2026 · 1 revision

Провайдеры и агенты

English | 中文 | 日本語 | 한국어 | Español | Português | Русский

Четыре сетевых протокола (apiStyle)

lib/llm-client.js — слой протоколов. Все четыре стрима делят один скелет openSseStream() (fetch / классификация ошибок / согласование бюджета / abort / SSE-обрамление / финализация); каждый протокол хранит только buildRequest + чистые парсеры событий. Извлечение SSE-полезной нагрузки data: единожды-источниковано (sseDataPayload, склейка multi-data-line по спеке):

apiStyle Эндпоинт Примечания
chat /v1/chat/completions также парсит DeepSeek-стиль reasoning_content
responses /v1/responses нативная мультимодальная запись input_*
anthropic /v1/messages явный cache_control на двух брейкпоинтах (system + последнее сообщение) — у Anthropic нет неявного префиксного кэширования
runs Hermes /v1/runs агентский протокол: одобрения / уточнения / инструменты / события мышления

thinking: 'inline' | 'omit' (дефолт omit) управляет тем, едет ли текст размышлений ОДНИМ блоком <thinking> инлайн в дельта-стриме; только основной чат и detail thread используют inline. Поле reasoning.available в runs — реплей ответа, а не мышление — echo-guard обязан его выбрасывать (ADR-0004).

Лестница turn-request

Форма запроса по провайдеру пересобирается четыре раза в основном чате (первичная / пересборка при переполнении / продолжение / перезапись таймкодов), унифицирована как createTurnRequest (prepare / rebuildFrom / continueWith / rewriteWith) — chat-handler держит КОГДА, лестница владеет КАК. Detail thread намеренно её не использует (семантика изолированных сессий, ADR-0007).

Бюджет вывода и усечение

  • Дефолт max_tokens 32768; серверы, которые ОТВЕРГАЮТ сверхлимитный бюджет с 400, получают один ретрай renegotiateOutputCap с потолком, распарсенным из текста ошибки.
  • finish_reason === 'length' → ОДИН тихий проход продолжения (антиповторная инструкция, дельты глотаются); всё ещё обрезано → DONE несёт outputTruncated, панель тостит + показывает кнопку «продолжить» в один клик. Поля ручного max_tokens намеренно НЕТ — «ручка, требующая знания конкретной модели, — дизайнерский баг».

Четыре агента

Агент Канал Сессии
Hermes /v1/runs серверные (sessionId в storage); части текущего хода используют канонические text/image_url, conversation_history — только строки (422 строгого слоя, проверено 2026-09-24)
OpenCode HTTP opencode serve серверные; случайный порт по умолчанию → рекомендуйте фиксированный --port
OpenSquilla локальный шлюз ws://…/ws одна сессия шлюза на разговор; прикрепления >60K символов загружаются как документ page-context.md; allowlist origin — см. Модель безопасности
Agent Bridge локальный демон @xiaohuzai/agent-bridge адаптирует codex/claude/pi/gemini за одним HTTP-протоколом; подписочные логины работают как источники моделей

Общий слой lib/agent-turn.js: агентский ход отправляет ТОЛЬКО текущий ход пользователя + хвостовой page-context прогон (транскрипт живёт на сервере; история никогда не пересылается). Изображения идут через pickTurnImages (≤8 изображений / бюджет URL ≤3MiB; гейты attach-времени и send-времени зеркалят друг друга). Переключение на агентского провайдера посреди разговора предлагает 「带上当前对话继续」 = одноразовый backfill (транскрипт простым текстом, потолок хвоста 200K символов).

Cross-entry хэндофф: именование и ID сессий на стороне агента (2026-10-01)

Для агентских провайдеров транскрипт уже живёт на сервере (агентские ходы шлют только текущий ход) — «пусть UI самого агента подхватит» требует обнаруживаемости, а не перемещения данных. Две части:

  • Автоименование: после успешного хода Hermes одноразовый PATCH /api/sessions/{id} (API исходного upstream; сервер санитизирует заголовки и отвергает точные конфликты) ставит заголовок в browsa: + первая строка пользовательского текста текущего хода (потолок 48 символов). Семантика «штамп один раз» (hermesSessionTitled_<provider>, тот же жизненный цикл, что у id сессии): штампуются и успех, и 4xx (конфликт заголовков не должен превращаться в цикл ретраев на каждый ход); без штампа остаются только транспортные сбои (status 0) — до следующего успешного хода. У PATCH таймаут 10с, и его await соревнуется с потолком 2.5с, чтобы DONE никогда не зависал. Каналы на сегодня: Hermes (PATCH /api/sessions/{id}) и bridge/codex (POST /threads/{id}/title демона → app-server thread/name/set, проверено вживую на codex 0.149.1 — имя сохраняется в state DB codex, и codex resume резолвится по имени или id; вердикты видимости в Picker для четырёх bridge-агентов (проверено по исходникам 2026-10-01): codex ✓ (предикат пикера has_user_event = 1 AND title <> '' — реальные ходы ему удовлетворяют; глобальная state DB, без cwd-скоупинга; именуется через POST /threads/{id}/title демона → app-server thread/name/set). claude ✓ — поскольку transcriptFix: 'claude' моста переписывает самоштампованный entrypoint: sdk-* в cli на диске после каждого завершившегося хода (agent-bridge #56, подтверждено на Mac) — клиентского канала переименования нет (claude-agent-acp титулует сам), resume работает из пикера или по ID. pi ✓ (ACP-сессии попадают в собственное cwd-скоупнутое хранилище pi, которое читает пикер; переключатель скоупа + именованный фильтр; нативный RPC pi set_session_name пишет запись имени session_info — pi-acp выставляет её только как команду /name внутри хода, к мосту пока не подключено). gemini ✓ (список исключает только kind:"subagent"; сессии моста — kind:"main"; хранилище ~/.gemini/tmp/<cwd>/chats/, со скоупом по cwd; канала именования нет — заголовок берётся из первого сообщения пользователя). opencode/squilla не проверены. Выделенные сессии detail thread намеренно НЕ титулуются.)
  • Отображение ID сессии: storage.getAgentSessionInfo единолично владеет формами сессионных ключей по видам (bridge ключуется по эндпоинту через activeModel || baseUrl); панель сессий рисует строку «Сессия агента» (короткий id + копирование) над списком, скрытую для LLM-провайдеров / когда сессии нет.

Реле одобрений / уточнений

Одобрения инструментов агента и запросы уточнений релеятся через lib/handlers/approval-relay.js (основной чат ключуется tabId, detail thread — subId). Форма pending-записи и ЕСТЬ интерфейс диспетчеризации. На стороне UI turn-chrome.js — оформление хода, общее для обеих поверхностей (индикатор ожидания, прогресс инструментов, карточки одобрений/уточнений, чип использования).

UI выбора провайдера (устоявшиеся правила)

  • Дропдаун перечисляет только СКОНФИГУРИРОВАННЫХ провайдеров, доступные первыми (стабильная сортировка); ноль сконфигурированных → один задизейбленный плейсхолдер.
  • Если сохранённый activeProvider не сконфигурирован, первый сконфигурированный автовыбирается И сохраняется (починка состояния, не предпочтение); первый успешный ping тоже автопереключает (один раз).
  • Мультимодельные провайдеры: ID моделей через запятую; по одному пункту дропдауна на модель; resolveChatModel уважает activeModel, только пока тот принадлежит этому провайдеру.
  • Обе группы (LLM / агент) рендерятся карточками с табами (группа агентов свёрнута по умолчанию; порядок табов [bridge, opencode, hermes] закреплён тестом).

Экономика CAPABILITY_HINTS (ADR-0010 — не предлагать заново ужимание)

Системный промпт каждого CHAT-хода = пользовательский systemPrompt + строка языка ответа + CAPABILITY_HINTS + CHOICE_REQUEST_HINT; он должен оставаться байт-стабильным префиксом (поключевое гейтирование по ходам ломает KV-кэш промпта с позиции 0). Каждая оставшаяся запись окупилась реальным багом рендеринга — не «ужесточайте» why-пункт, не прочитав его историю в AGENTS.md. Лечение пропущенного формата — лучший хинт, а не рантайм-детекция.


Авторитетные версии: Providers-and-Agents (англ.) / Providers-and-Agents-zh (кит.) — снимок первичного перевода ИИ, синхронизирован 2026-10-01.

Clone this wiki locally