--- title: OmniRoute: настройка web-комбо под агентскую работу (deepseek-web + тулы) type: concept tags: [omniroute, deepseek-web, tool-calling, combo, pi, web-cookie, qwen-web, baxia-waf] sources: [../sources/omniroute-session-2026-08-22.md] updated: 2026-08-22 --- # OmniRoute: настройка web-комбо под агентскую работу Диагностика и настройка комбо `deepseek-v4-flash-web` / `deepseek-v4-pro-web` (провайдер deepseek-web = веб-чат chat.deepseek.com через userToken, 3 аккаунта megan/yulia/vitya). Сессия 2026-08-22. ## Архитектура ``` pi (клиент) → omniroute (localhost:20128, Next.js + sqlite + MCP) → combo → deepseek-web executor → chat.deepseek.com/api/v0/chat/completion ``` - Веб-чат DeepSeek **не имеет нативного function calling** — omniroute **эмулирует**: сериализует `tools[]` в текстовый контракт `{"name":"...","arguments":{...}}` в промпт, модель пишет блоки текстом, парсер (`open-sse/translator/deepseekWebTools.ts`, 486 строк) разбирает зоопарк форматов обратно в OpenAI `tool_calls`. - `toolCalling: "emulated"` в `src/shared/constants/providers/web-cookie.ts` — маркер эмуляции (claude-web = `"none"`, grok-web = нативный `true`). - Executor: `open-sse/executors/deepseek-web.ts` (1147 строк): userToken→accessToken, PoW-челлендж, создание/удаление сессий, SSE-парсинг фрагментов (THINK/ANSWER), эмуляция тулов. - Управление: REST `http://127.0.0.1:20128/api/combos` (GET список, PUT /api/combos/[id]). MCP-тулы omniroute в pi-сессии отваливаются с `Unknown Mcp-Session-Id` — работать через HTTP напрямую. ## Найденные причины вылетов агента (по убыванию важности) ### 1. `system_message` override убивает скилы pi ⭐ `applyComboAgentMiddleware` (`open-sse/services/comboAgentMiddleware.ts`) **удаляет ВСЕ system-сообщения от pi** (скилы, AGENTS.md, инструкции) и ставит свой `system_message` комбо. Модель физически не видит скилы. **Фикс:** `PUT /api/combos/[id]` с `"system_message": ""` — пустая строка = override не применяется (`trim()`-проверка), промпт pi проходит. ### 2. `tool_filter_regex` режет инструменты ⭐ Было `^(bash|computer)$` — из 339 тулов pi оставалось 2, модель слепая (не могла звать read/write/edit/MCP). **Фикс:** `^(bash|read|write|edit|grep|glob|list|describe_image|mcp__[a-z0-9_]+)$` — базовые + все MCP (filesystem, projects_meta, interns, books_ops...). Проверено: 339→25 тулов, все имена матчатся. ### 3. search-режим модели ломает tool-calling ⭐ `deepseek-v4-flash-think-search` (суффикс `-search`) включает веб-поиск чата: при URL модель сама лезет «читать» (сообщение «Link reading is unavailable in Expert Mode» — это веб-чат, не pi), генерит токены `DEEP_SEARCH`/`SEARCHING` в стриме → валидатор качества режет → `Upstream response failed quality validation: streaming upstream error`. **Фикс:** модель в комбо → `deepseek-v4-flash-think` (без search; thinking остаётся). ### 4. `streaming upstream error` — валидатор качества `open-sse/services/combo/validateQuality.ts` `isStreamingUpstreamError()`: SSE-фрейм с `eventType === "error"` / `parsed.error != null` / `type:"error"` до контента → стрим режется, все 3 аккаунта round-robin фейлятся → «All models failed». Через API не настраивается — патч кода. ### 5. 503 `chat_admission_busy` — лимит веб-чата DeepSeek Перегрузка их серверов / слишком частые запросы (агентный цикл pi = серия быстрых запросов). Не настройка omniroute, а capacity провайдера. **Фикс:** `config.maxRetries: 4`, `config.retryDelayMs: 4000` (было 1–2 / 1500–2000). Также qwen-web падал по DNS (`EAI_AGAIN chat.qwen.ai`) — сетевой глюк, не ключ. ### 6. Пустые tool_calls (`arguments: {}`, `name: undefined`) Модель иногда генерит `{"name":"bash","arguments":{}}` без аргументов → pi-валидация падает (`command: must have required properties command`). Следствие длинного контекста + слабого контракта. Сокращение тулов (фикс №2) снижает частоту. ## Что работает после фиксов (проверено тестами) - Эмуляция tool-calling **исправна**: тест с 1 тулом и с 25 тулами (stream=true и false) возвращает корректные `tool_calls` (`{"name":"read","arguments":{"path":...}}`). - То, что выглядит как «модель пишет read("...") текстом» — это **`reasoning_content`** (мысли модели), pi их показывает, реальный tool_call при этом корректный. - Скиллы видны после удаления system_message. ## Web-провайдеры с tool calling (для кастомного провайдера) Из кода omniroute (`web-cookie.ts` + executors). Эмуляция (`"emulated"`) у: deepseek-web, qwen-web, gemini-web, chatgpt-web, kimi-web, t3-web, perplexity-web, blackbox-web, muse-spark-web, inner-ai, adapta-web. Нативный (`true`): grok-web. `"none"`: claude-web (не стоит скрейпить). Бесплатные без подписки: **deepseek-web** (✅ есть), **qwen-web** (✅ ключ есть, DNS-глюк), **gemini-web** (Google-аккаунт), **kimi-web** (свободный лимит), **grok-web** (free-тир). ## План кастомного провайдера (идея, не реализовано) OmniRoute для 2 комбо — overkill (Next.js + sqlite + дашборд + MCP). Идея: **вытащить** готовые `executor/deepseek-web.ts` + `translator/deepseekWebTools.ts` из omniroute (они работают), завернуть в минимальный OpenAI-совместимый сервер: - `POST /v1/chat/completions` + `GET /v1/models` (pi подключается как провайдер) - round-robin по аккаунтам из конфига (отдельное комбо на провайдера — **не смешивать** в одном) - **без quality validation** (убирает все streaming upstream error) - `persistSession: true` (переиспользование сессии → меньше 503) - ~300 строк обвязки + 2 готовых файла - Плюс: поддержать qwen-web / gemini-web / kimi-web / grok-web как отдельные комбо (один парсер webTools.ts, разные executors) ## Ключевые файлы omniroute - `src/shared/constants/providers/web-cookie.ts` — реестр web-провайдеров, toolCalling - `open-sse/executors/deepseek-web.ts` — executor веб-чата DeepSeek - `open-sse/translator/deepseekWebTools.ts` + `webTools.ts` — эмуляция/парсинг тулов - `open-sse/services/combo/validateQuality.ts` — валидатор качества (режет стримы) - `open-sse/services/comboAgentMiddleware.ts` — system_message override + tool_filter_regex - `src/shared/validation/schemas/combo.ts` — `updateComboSchema` (PUT-поля) - Данные: docker volume `omniroute-data` → `/app/data/storage.sqlite`, логи `/app/data/logs/application/app.log`, call_logs `/app/data/call_logs/` ## Связанное - [[admin-infra-project]] — контекст стека - [[omniroute-session-2026-08-22]] — хронология сессии (sources) ## qwen-web: стена baxia-WAF (2026-08-22, llm-web-proxy подзадача) Попытка добавить qwen-web в llm-web-proxy упёрлась в анти-бот: - **Симптом:** completion возвращает HTTP 200 с JSON-эневелопом `{"ret":["FAIL_SYS_USER_VALIDATE","RGV587_ERROR::SM::哎哟喂,被挤爆啦,请稍后重试"],"data":{"url":".../_____tmd_____/punish?x5secdata=...&action=captcha"}}` — слайдер-капча Alibaba baxia-WAF. RGV587 = punish-flow, требует решить капчу в браузере (получить x5secdata). - **Проверено (все — капча):** Node-fetch с полным свежим cookie-jar (включая token/cna/ssxmod_itna), со всеми браузерными заголовками (sec-ch-ua, sec-fetch-*, Accept: text/event-stream), со статическим bx-umidtoken / без него. Даже **настоящий Chrome** (CDP :9222, залогиненная сессия, капча разгадана вручную): chats/new **проходит** (браузерный фингерпринт), но chat/completions виснет → капча возвращается. - **Вывод:** капча привязана к device-фингерпринту запроса (bx-umidtoken минтится JS из sg-wum.alibaba.com), не к cookie. Прямой API-scraping из Node — мёртвая ветка. - **Зацепка:** «из под omniroute работает» — в **контейнере** omniroute лежит рабочая версия `open-sse/executors/qwen-web.ts` (≠ GitHub main, который портировали в lwp). Сравнить и/или взять подход оттуда (возможно решённый punish-flow / живой браузерный путь). Ключ/сессия qwen хранится в omniroute DB (provider_connections, зашифрован `enc.v1.` + STORAGE_ENCRYPTION_KEY из `/app/data/server.env`). - Статус: подзадача [llm-web-proxy-qwen-web] 🟡 paused, next-сессия — юзер покажет рабочий подход. - **2026-08-22 вечер: находка-подтверждение.** Аккаунт ForgetMeAI (FreeQwenApi, 212⭐) гоняет qwen через браузер и работает: чат-запрос исполняется **внутри страницы** (`page.evaluate(fetch(...))`) — запрос несёт настоящий bx-umidtoken + canvas-фингерпринт + same-origin → baxia-WAF пропускает. Плюс тяжёлый stealth-спуфинг (navigator/canvas-noise/задержки событий). Вывод: Node-fetch-путь мёртв окончательно, рабочий сценарий = браузерный executor (puppeteer/playwright). Референс: [[forgetmeai-free-web-chat-proxies]] + lwp `docs/reference/forgetmeai/`.