DeepSeek, притворяющийся Ollama

У веб-версии DeepSeek нет API: ни ключа, ни эндпойнта, только вкладка
в браузере. Этот сервис держит такую вкладку и отдаёт наружу протокол
Ollama, поэтому любая программа, которая уже умеет ходить в Ollama,
подключается к DeepSeek сменой одного адреса.

  GET  /api/tags        три "модели" по числу режимов чата;
                        503, пока браузер с чатом не поднят
  GET  /api/version
  POST /api/chat        images в сообщении уводят в чат со зрением
  POST /api/generate
  POST /api/embeddings  501: веб-чат векторов не отдаёт

Внутри — ollama_orchestro/ask.mjs дочерним процессом: он ничего не
экспортирует и выполняется при импорте, это программа командной строки.
Браузер поднимается напрямую из Node, а не через browser-launch.ps1 из
DeepShim: тот рассчитан на человека за консолью и на занятом профиле
спрашивает Read-Host, а у сервиса некому отвечать. Флаги окна оттуда
перенесены: у неактивного окна DeepSeek не дорисовывает блоки кода.

Чего нет: настоящего потока (формат соблюдается, но ответ приходит
целиком), temperature и num_predict, эмбеддингов, параллельных запросов —
вкладка одна, запросы идут очередью по одному.

Сервис слушает все адреса и ходит в DeepSeek под вашим логином. Только
доверенная сеть; сузить до этого компьютера — host в config.json.

Проверка перевода формата без браузера: node test.mjs

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Alex Cube
2026-08-26 12:49:03 +03:00
co-authored by Claude Opus 5
commit f5cf061fcc
8 changed files with 874 additions and 0 deletions
+488
View File
@@ -0,0 +1,488 @@
# deepseek-api
**DeepSeek, притворяющийся Ollama.**
У веб-версии DeepSeek нет API: ни ключа, ни эндпойнта — только вкладка
в браузере, куда человек руками пишет вопросы. Эта программа открывает такую
вкладку, пишет в неё за вас и отдаёт наружу обычный HTTP — тот самый протокол,
на котором говорит [Ollama](https://ollama.com).
Смысл: **любая программа, которая уже умеет работать с Ollama, начинает
работать с DeepSeek после смены одного адреса.** Ни строчки кода в ней менять
не нужно.
```
ваша программа ──HTTP──> deepseek-api ──> браузер ──> chat.deepseek.com
(думает, что │
говорит с Ollama) <──────────── ответ ───────────────┘
```
Платить не нужно — используется ваш обычный аккаунт DeepSeek. Взамен всё
медленно (секунды, а не миллисекунды) и работает только пока открыт браузер.
---
## Оглавление
1. [Что понадобится](#что-понадобится)
2. [Установка по шагам](#установка-по-шагам)
3. [Проверка, что всё работает](#проверка-что-всё-работает)
4. [Как подключить свою программу](#как-подключить-свою-программу)
5. [Что умеет и чего не умеет](#что-умеет-и-чего-не-умеет)
6. [Настройка](#настройка)
7. [Если не работает](#если-не-работает)
8. [Безопасность](#безопасность)
9. [Как это устроено внутри](#как-это-устроено-внутри)
---
## Что понадобится
| Что | Зачем | Как проверить, что есть |
|---|---|---|
| **Windows** | скрипты запуска написаны под неё | — |
| **Node.js 22 или новее** | на нём написана программа | `node --version` → должно быть `v22.…` и выше |
| **Браузер на движке Chromium** | Яндекс.Браузер, Chrome, Edge — любой | — |
| **Аккаунт DeepSeek** | бесплатный, регистрация на [chat.deepseek.com](https://chat.deepseek.com) | — |
| **Проект DeepShim** | в нём живёт код, который умеет управлять вкладкой | ставится на шаге 1 |
Node.js, если его нет, ставится с [nodejs.org](https://nodejs.org) — берите
версию LTS, кнопка слева. После установки **закройте и откройте заново**
командную строку, иначе `node` в ней не найдётся.
---
## Установка по шагам
Всего шагов семь. Делайте по порядку, каждый заканчивается проверкой —
не переходите к следующему, пока предыдущий не дал ожидаемого результата.
### Шаг 1. Скачать обе программы
Нужны две: эта и DeepShim, в котором лежит код управления браузером.
Положите их **рядом, в одну папку**:
```
C:\#Projects\
deepseek-api\ <- эта программа
ollama_orchestro\ <- DeepShim
```
```cmd
cd C:\#Projects
git clone https://git.08h.ru/alex/deepseek-api.git
git clone https://git.08h.ru/alex/deepshim.git ollama_orchestro
```
Папка DeepShim должна называться именно `ollama_orchestro` — так она указана
в настройках по умолчанию. Хотите иначе — поправите путь на шаге 6.
Без git можно просто скачать ZIP с тех же страниц и распаковать.
> **Проверка:** в `C:\#Projects\deepseek-api\` лежит `server.mjs`,
> в `C:\#Projects\ollama_orchestro\` — `ask.mjs`.
### Шаг 2. Завести отдельный профиль браузера
Браузером будет управлять программа, поэтому работать в нём вам не нужно —
и не стоит: он будет самопроизвольно открываться и переключать вкладки.
Заведите под него **отдельный профиль** — это просто пустая папка,
браузер сам её заполнит:
```cmd
mkdir C:\Browsers\deepseek
```
Имя любое, запомните путь — понадобится на следующем шаге.
### Шаг 3. Настроить DeepShim
В папке `ollama_orchestro` создайте файл `config.json`:
```json
{
"profileDir": "C:\\Browsers\\deepseek",
"browserExe": "C:\\Program Files\\Yandex\\YandexBrowser\\Application\\browser.exe",
"port": 9333
}
```
Три значения:
- **`profileDir`** — папка из шага 2.
- **`browserExe`** — путь к браузеру. Типичные:
| Браузер | Путь |
|---|---|
| Яндекс.Браузер | `C:\Program Files\Yandex\YandexBrowser\Application\browser.exe` |
| Google Chrome | `C:\Program Files\Google\Chrome\Application\chrome.exe` |
| Microsoft Edge | `C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe` |
- **`port`** — порт, по которому программа управляет браузером. `9333` подходит,
меняйте только если он у вас занят.
**Обратные слэши в JSON удваиваются**`C:\\Browsers\\deepseek`, а не
`C:\Browsers\deepseek`. С одинарными файл не прочитается.
> **Проверка:** откройте `config.json` в браузере (перетащите файл в окно).
> Если показалась структура — JSON верный. Если ошибка — ищите пропущенную
> запятую или одинарный слэш.
### Шаг 4. Запустить браузер и войти в DeepSeek
```cmd
cd C:\#Projects\ollama_orchestro
start-browser.cmd
```
Откроется браузер с чистым профилем на странице DeepSeek. **Войдите
в аккаунт.** Логин сохранится в профиле — больше делать это не придётся.
> **Проверка:** в окне командной строки написано
> `READY. … is listening on port 9333.`
> Если написано `Port 9333 did not answer` — см. [Если не работает](#если-не-работает).
**Окно браузера не сворачивайте и не закрывайте.** Свёрнутое окно Windows
не отрисовывает, и программа не сможет прочитать ответ. Достаточно перевести
его на другой рабочий стол или просто оставить позади других окон.
### Шаг 5. Создать три рабочих чата
DeepSeek выбирает режим (обычный / с поиском в интернете / со зрением)
**в момент создания чата**, и потом переключить его нельзя — тумблеры
исчезают после первого сообщения. Поэтому под каждый режим нужен свой чат.
Создадутся они сами:
```cmd
cd C:\#Projects\ollama_orchestro
node ask.mjs init
```
Команда откроет три новых чата, задаст в каждом по вопросу и запишет их
номера в `chats.json`. Займёт минуту-две, окно браузера в это время будет
само щёлкать — так и надо, не мешайте.
> **Проверка:** в конце напечатается что-то вроде
> ```
> Сохранено в chats.json:
> think expert af016e20-…
> search default+… 8021bbc1-…
> vision vision 14663b36-…
> ```
### Шаг 6. Настроить deepseek-api
Файл `config.json` в папке `deepseek-api` уже готов:
```json
{
"port": 11500,
"host": "0.0.0.0",
"bridge": "C:/#Projects/ollama_orchestro/ask.mjs",
"chatUrl": "https://chat.deepseek.com/",
"defaultRole": "think",
"timeout": 300
}
```
Если вы разложили папки как в шаге 1 — **менять здесь нечего, переходите
к шагу 7.** Если DeepShim лежит в другом месте, поправьте `bridge`. Здесь
слэши обычные, прямые (`C:/…`), а не обратные.
### Шаг 7. Запустить
```cmd
cd C:\#Projects\deepseek-api
start.cmd
```
> **Проверка:** в окне появилось
> ```
> DeepSeek прикидывается Ollama: http://localhost:11500
> Модели: deepseek:think, deepseek:search, deepseek:vision
> ```
Это окно должно оставаться открытым — пока оно живо, работает и сервис.
Останов — `Ctrl+C` или просто закрыть окно.
**Готово.** Дальше — проверка.
---
## Проверка, что всё работает
Откройте **вторую** командную строку (первая занята сервисом) и спросите
что-нибудь:
```cmd
curl http://localhost:11500/api/tags
```
Ожидается список из трёх «моделей»:
```json
{"models":[{"name":"deepseek:think",},{"name":"deepseek:search",},{"name":"deepseek:vision",}]}
```
Теперь настоящий вопрос:
```cmd
curl http://localhost:11500/api/chat -d "{\"model\":\"deepseek:think\",\"stream\":false,\"messages\":[{\"role\":\"user\",\"content\":\"Ответь одним словом: столица Японии?\"}]}"
```
Через несколько секунд:
```json
{"model":"deepseek:think","created_at":"…","message":{"role":"assistant","content":"Токио"},"done":true,"done_reason":"stop"}
```
Если пришло — всё работает. В окне сервиса при этом видно, сколько заняло:
```
[think] 37 симв.
[think] ответ 5 симв. за 8 с
```
---
## Как подключить свою программу
Везде, где программа спрашивает **адрес Ollama**, укажите:
```
http://localhost:11500
```
Моделью выберите `deepseek:think`.
Если программа работает на **другом компьютере** в вашей сети — вместо
`localhost` укажите адрес этого компьютера, например `http://192.168.0.10:11500`.
Сервис слушает все адреса, ничего дополнительно настраивать не нужно.
### Три «модели» — это три режима
| Модель | Что это | Когда брать |
|---|---|---|
| `deepseek:think` | обычный режим (expert) | почти всегда |
| `deepseek:search` | с поиском в интернете | нужны свежие данные |
| `deepseek:vision` | со зрением | разбор картинок |
Картинку можно просто приложить к запросу, как это делает Ollama —
в поле `images` массивом base64. Тогда режим `vision` включится сам,
независимо от того, какую модель вы указали.
**Незнакомое имя модели — не ошибка.** Если программа настойчиво шлёт свою
`llama3` или `qwen3:8b`, запрос уйдёт в обычный чат. Это сделано нарочно:
чужую программу можно переключить на DeepSeek, не разыскивая в её настройках
поле с моделью.
### Пример: помощник hlamingo
В [hlamingo](https://git.08h.ru/alex/hlamingo) на вкладке **⚙** есть выбор
«Кто думает» — переключите на «DeepSeek», укажите адрес, сохраните.
---
## Что умеет и чего не умеет
### Эндпойнты
| Адрес | Что делает |
|---|---|
| `GET /api/tags` | список «моделей». **Отвечает 503, пока браузер не поднят** — по этому клиенты понимают, что сервис не готов |
| `GET /api/version` | опознавательная строка |
| `POST /api/chat` | сообщения → ответ |
| `POST /api/generate` | то же, но вопрос одной строкой |
| `POST /api/embeddings` | **501** — веб-чат векторов не отдаёт |
### Чего нет и не будет
- **Настоящего потока.** Ollama печатает ответ по словам; здесь он приходит
целиком. Формат потока при этом соблюдается (клиент получит две строки JSON
вместо многих), поэтому программы, которые ждут поток, не ломаются —
просто ответ появляется разом. Хотите один объект вместо потока — присылайте
`"stream": false`.
- **`temperature`, `num_predict` и прочих настроек генерации.** Веб-чат их
не принимает. Присланные — молча игнорируются.
- **Эмбеддингов** (векторов для поиска по смыслу). Веб-чат их не считает.
Вместо пустого вектора возвращается честная ошибка 501: молчаливая подделка
испортила бы поиск так, что вы бы этого не заметили. Если вашей программе
нужен и чат, и поиск по смыслу — оставьте поиск на настоящей Ollama.
- **Параллельных запросов.** Вкладка одна, и два запроса разом затрут друг
другу поле ввода. Запросы встают в очередь и идут по одному.
### Сколько ждать
Порядок величин, замерено на разборе фразы и генерации тегов:
| | Ollama (qwen3:8b, локально) | DeepSeek (веб) |
|---|---|---|
| короткий вопрос | 2–3 с | 8–12 с |
| первый запрос после запуска браузера | — | +15 с на подъём окна |
Если ваша программа считает, что модель обязана ответить за пару секунд, —
увеличьте в ней таймаут.
---
## Настройка
### `config.json`
| Ключ | По умолчанию | Смысл |
|---|---|---|
| `port` | `11500` | порт, на котором сервис слушает |
| `host` | `0.0.0.0` | какие адреса слушать; `127.0.0.1` — только этот компьютер |
| `bridge` | `C:/#Projects/ollama_orchestro/ask.mjs` | путь к DeepShim |
| `chatUrl` | `https://chat.deepseek.com/` | что открывать во вкладке |
| `defaultRole` | `think` | режим для незнакомых имён моделей |
| `timeout` | `300` | сколько секунд ждать **бездействия** страницы |
Про `timeout`: это не «сколько ждать ответа». Пока DeepSeek печатает, ждём
сколько угодно; срок отсчитывается от последнего признака жизни страницы.
### Переменные окружения
Перебивают файл, удобны для разовых запусков:
```cmd
set DEEPSEEK_API_PORT=11501
set DEEPSEEK_CDP_PORT=9444
start.cmd
```
Профиль браузера, путь к нему и порт управления берутся из
`ollama_orchestro/config.json` — там уже выполнен вход в DeepSeek, и второй
экземпляр этих настроек однажды разошёлся бы с первым.
---
## Если не работает
Сначала посмотрите в окно сервиса — там пишется, что происходит.
| Что видно | В чём дело | Что делать |
|---|---|---|
| `Браузер с отладочным портом 9333 не отвечает` в ответе на `/api/tags` | браузер закрыт | Ничего: он поднимется сам при первом запросе к `/api/chat`. Если не поднялся — следующая строка |
| `Браузер так и не открыл отладочный порт` | этот профиль уже открыт **без** порта управления: новый процесс отдаёт ссылку старому окну и молча завершается | Закройте **все** окна браузера с этим профилем и повторите. Проверить, что не осталось: диспетчер задач, процессы браузера |
| `в config.json не задан profileDir или browserExe` | шаг 3 пропущен или файл лежит не в той папке | `ollama_orchestro\config.json`, оба ключа заполнены |
| `мост вышел с кодом 1. Вкладка chat.deepseek.com не открыта` | вкладку закрыли руками | Откройте `chat.deepseek.com` в том же окне браузера, или перезапустите его через `start-browser.cmd` |
| Ответ приходит **обрезанным**, без кода | окно браузера свёрнуто — Windows перестаёт его отрисовывать, а DeepSeek дорисовывает блоки кода лениво | Разверните окно. Не сворачивайте: уводите на другой рабочий стол или за другие окна |
| Ответ приходит **чужой**, не на ваш вопрос | вкладка не успела обновиться | Повторите запрос. Если повторяется — перезапустите браузер |
| В окне мелькнуло `правки в этом чате кончились, завожу новый` | исчерпан лимит в шесть правок одного сообщения | Ничего. Это штатная смена чата, см. [Чаты изнашиваются](#чаты-изнашиваются-и-меняются-сами) |
| `EADDRINUSE: address already in use` | сервис уже запущен в другом окне | Закройте то окно либо возьмите другой порт (`DEEPSEEK_API_PORT`) |
| `node не является внутренней или внешней командой` | Node не установлен или командная строка открыта до его установки | Поставьте Node, **закройте и откройте заново** командную строку |
| Просит войти в DeepSeek при каждом запуске | профиль браузера каждый раз новый | Проверьте, что `profileDir` указывает на одну и ту же существующую папку |
Отдельная диагностика самого моста — из папки DeepShim:
```cmd
node ask.mjs probe
```
Она скажет, видна ли вкладка, найдено ли поле ввода и не просит ли сайт
залогиниться.
Проверить перевод формата, не поднимая браузер:
```cmd
cd C:\#Projects\deepseek-api
node test.mjs
```
---
## Безопасность
Прочтите, это важнее, чем кажется.
- **Сервис слушает все сетевые адреса** (`0.0.0.0`), а не только этот
компьютер. Так задумано: подключаться должны и соседние машины, ровно как
к самой Ollama. **Аутентификации нет.**
- **Он ходит в DeepSeek под вашим логином.** Любой, кто достучится до порта
11500, задаёт вопросы от вашего имени и расходует ваши лимиты.
- Поэтому: только домашняя или доверенная сеть. **Не пробрасывайте порт
наружу** и не открывайте его на публичном Wi-Fi.
- Всё, что вы отправляете, уходит на серверы DeepSeek — как если бы вы
написали это в чат руками. Для приватных данных берите локальную Ollama.
Если сервис нужен только на этом компьютере, впишите в `config.json`:
```json
"host": "127.0.0.1"
```
Тогда снаружи к нему не подключиться вовсе.
---
## Как это устроено внутри
```
POST /api/chat
├─ склеить system + user в один текст (у веб-чата нет ролей)
├─ выбрать чат по имени модели (think / search / vision)
├─ встать в очередь (вкладка одна)
├─> написать вопрос во временный файл
├─> node ask.mjs ask --file … --out … (дочерний процесс)
│ └─> Chrome DevTools Protocol
│ └─> правка ПЕРВОГО сообщения чата
│ └─> дождаться, пока DeepSeek допечатает
├─< прочитать ответ из файла
└─ отдать в формате Ollama
```
Почему правка первого сообщения, а не новое: DeepSeek отбрасывает всё,
что было после отредактированного сообщения, и чат остаётся из одного обмена.
Контекст не растёт, мусор между запросами не копится, каждый вопрос —
с чистого листа.
Почему дочерний процесс, а не импорт: `ask.mjs` — программа командной строки,
она ничего не экспортирует и выполняется сразу при подключении. Переделывать
чужой проект ради этого не стали.
Почему браузер поднимается напрямую, а не через `browser-launch.ps1`
из DeepShim: тот скрипт рассчитан на человека за консолью — на занятом профиле
он задаёт вопрос и ждёт ответа, а у сервиса некому отвечать.
### Чаты изнашиваются и меняются сами
**Одно сообщение DeepSeek разрешает править ровно шесть раз.** После шестой
правки кнопка перестаёт открывать поле ввода — и чат становится непригоден.
Ничего делать не нужно, это уже предусмотрено. На седьмом вопросе DeepShim
заводит новый чат того же режима, задаёт вопрос сразу в нём, записывает новый
номер в `chats.json` и **удаляет отработавший чат**. Снаружи не видно ничего:
тот же запрос, тот же ответ, разве что на несколько секунд дольше.
Отсюда два следствия:
- **`chats.json` — не настройка, а переменная.** Номера в нём меняются сами
по мере работы. Не правьте его руками и не удивляйтесь, если git покажет
в нём изменения.
- **В сайдбаре DeepSeek не копится мусор.** Старые чаты удаляются, плюс перед
каждым вопросом сайдбар подчищается от посторонних служебных чатов.
Если вы удалите рабочий чат вручную — тоже ничего страшного: следующий запрос
заметит пропажу и создаст новый.
Файлы:
| Файл | Что в нём |
|---|---|
| `server.mjs` | всё: HTTP, очередь, запуск браузера, вызов моста |
| `config.json` | настройки этой машины |
| `start.cmd` | запуск |
| `test.mjs` | проверка перевода формата, браузер не нужен |
Зависимостей нет — только то, что есть в Node 22.
---
## Лицензия
MIT.