Files
deepwork/README.md
T
Alex CubeandClaude Opus 5 44f07c8af9 DeepSeek через deepseek-api; память шагов переехала из чата в стенограмму
Транспорт: был дочерний процесс ask.mjs из ollama_orchestro с обменом через
временные файлы, стал POST /api/chat к сервису deepseek-api. Он держит вкладку
браузера и отдаёт наружу протокол Ollama, поэтому здесь про браузер больше
не знают: ушли проверка существования моста, временные файлы шагов вместе
с флагом --keep-temp и четыре осиротевших импорта.

Главное не в транспорте. Раньше шаги 2..N уходили с флагом --new, и ход
разговора помнил сам чат DeepSeek. Сервис памяти не имеет принципиально:
каждый запрос он кладёт в чат целиком, затирая прошлый. Поэтому стенограмму
ведём здесь — growTranscript подклеивает к отправленному тексту прошлый ответ
модели между метками === ТВОЙ ПРЕДЫДУЩИЙ ОТВЕТ === и === END ===, и следующим
шагом уезжает всё сразу.

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

--check теперь различает три состояния: сервис отвечает и браузер поднят;
сервис жив, но браузера ещё нет; сервис не запущен. Плашка в ui.html тоже.

Настройки: bridge/chat/cdpPort заменены на deepseekHost и deepseekModel.

Проверено живым многошаговым прогоном: шаг 1 — 2094 символа, ответ
NEED: deepwork.mjs; шаг 2 — 28 447 символов стенограммы, ответ дословно верный
и по вопросу из ПЕРВОГО сообщения, то есть память шагов работает. Два шага
за 17 с. --selftest пройден, --check зелёный.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 13:40:09 +03:00

12 KiB

deepwork

Цикл с инструментами поверх сервиса deepseek-api.

DeepSeek через веб-чат видит только то, что положили ему в вопрос. Здесь у него появляется обратное направление: он возвращает блок-запрос, программа его исполняет и досылает результат следующим сообщением в тот же чат. Обычный агентский цикл, только транспорт — текст.

Как пользоваться

Обычный путь — пульт: бросить папку проекта на start-deepwork.cmd. Откроется страница: файлы галочками, поле вопроса, ход работы построчно, кнопка «Записать» у каждого пришедшего файла.

Пульт открывается в браузере ПО УМОЛЧАНИЮ, а не в отладочном: там он занял бы вкладку, и мост перестал бы видеть DeepSeek. Порт 8787 — рядом с пультом orchestro (8777), оба могут работать разом.

Тот же цикл из консоли:

cd c:\мой\проект
node c:\#Projects\deepwork\deepwork.mjs "вопрос" --files a.js,b.js --apply
node c:\#Projects\deepwork\deepwork.mjs ui        тот же пульт вручную

Работает в текущей папке: её файлы показывает, в неё же пишет правки.

Инструменты

Только чтение и только внутри текущей папки:

Блок в ответе Что делает
=== NEED: путь, путь === прислать файлы целиком
=== LIST: **/*.mjs === список файлов по маске
=== FIND: текст === где встречается текст, с номерами строк

Запуска команд (RUN) нет намеренно: исполнять строки из ответа, пришедшего из интернета, без белого списка и подтверждения нельзя.

Лимиты на результат шага: NEED — 200 000 символов суммарно, FIND — 100 попаданий, LIST — 300 путей. Обрезанное помечается прямо в тексте.

Как устроено

У веб-версии DeepSeek нет API — только вкладка в браузере. Разговором с ней занимается отдельный сервис deepseek-api, а нам показывает протокол Ollama. Здесь про браузер не знают вовсе: обычный POST /api/chat.

Разговор помним мы, а не DeepSeek. Сервис без памяти: каждый запрос он кладёт в чат целиком, затирая прошлый. Поэтому стенограмма растёт здесь — growTranscript подклеивает к отправленному тексту прошлый ответ модели (между метками === ТВОЙ ПРЕДЫДУЩИЙ ОТВЕТ === и === END ===) и результаты запросов, и следующим шагом уезжает всё сразу.

Раньше эту роль играл флаг --new у моста, и помнил всё сам чат. Своя стенограмма надёжнее: видно в одном месте, что именно уедет модели, а чужой потребитель, вклинившийся между нашими шагами, ничего не портит — мы всё приносим заново. Плата — вопрос растёт на длину прошлого ответа; при steps: 4 это единицы килобайт против 22 КБ первого сообщения.

Цикл останавливается, когда запросов в ответе нет, когда кончились шаги или когда пришёл тот же самый запрос второй раз подряд (петля).

Настройка

config.json (в репозиторий не попадает, образец — config.example.json):

{ "deepseekHost": "http://localhost:11500", "deepseekModel": "deepseek:think", "steps": 4 }

Без файла берётся http://localhost:11500.

Проверка

node deepwork.mjs --selftest   внутренние проверки, ничего не отправляют
node deepwork.mjs --check      сервис на месте, вкладка жива

--check различает три состояния: сервис отвечает и браузер поднят; сервис жив, но браузера ещё нет (встанет на первом же вопросе); сервис не запущен.

Запускается сервис своим start.cmd в папке deepseek-api — браузер он поднимает сам.

Переезд на deepseek-api (26.08.2026)

Транспорт сменился: был дочерний процесс ask.mjs из ollama_orchestro с обменом через временные файлы, стал POST /api/chat к сервису deepseek-api. Заодно ушли временные файлы шагов (и флаг --keep-temp), проверка существования моста и запуск браузера — всё это теперь не наша забота.

Память шагов переехала из чата DeepSeek в growTranscript (см. «Как устроено»). Проверено живым прогоном: шаг 1 — 2094 символа, ответ NEED: deepwork.mjs; шаг 2 — 28 447 символов стенограммы, ответ дословно верный и по вопросу из ПЕРВОГО сообщения. Два шага за 17 с.

Что проверено

  • --selftest: разбор запросов, отказ пути за пределы папки, маски * и **, снятие тройных кавычек и подписи языка. Строка вида === NEED: … === внутри блока === FILE: === за запрос не принимается — иначе правка файла с описанием формата запускала бы запрос сама.
  • Цикл целиком на заглушке вместо моста: приставка едет в первом сообщении, второй вызов уходит с --new, результаты NEED и FIND доезжают, отступы в записанном файле сохраняются, прежняя версия уходит в backup/.
  • Пропавший файл в NEED возвращает «файла нет» и цикл не роняет.
  • --steps 1 отдаёт ответ с пометкой «шаги кончились», а не зависает.
  • Битый config.json — понятная ошибка, а не молчаливый откат к умолчаниям.

Живые прогоны на DeepSeek, 11.08.2026

Задача одна и та же: из папки ollama_orchestro спросить, где решается, что ответ дописан до конца, дав в вопросе только ui.html — то есть заведомо не тот файл. Правильный ответ (waitForAnswer в ask.mjs) достижим только через инструменты.

Прогон Шагов Что вышло
1. приставка только в начале сообщения 1 запросов нет. «Точный код в серверном файле» и три догадки
2. + напоминание вплотную к вопросу 4 нашёл, но 2 круга ушли на угадывание имён файлов
3. + дерево папки в первом сообщении 2 запросил ask.mjs сразу, ответил по существу

Два вывода, оба стоили живого прогона:

  • Инструкция в начале сообщения не работает. Между приставкой и вопросом легли 22 КБ файла, и DeepSeek инструменты просто не заметил. Помогло дословное повторение перед самим вопросом (TAIL_REMINDER).
  • Дерево папки надо давать сразу. Без него DeepSeek угадывал имена (server.js, bridge.js — таких в проекте нет), потом просил LIST *.js (проект на .mjs, пусто), потом LIST **/*. Дерево — 600 символов против двух кругов по минуте.

Пульт, 11.08.2026

Сквозной прогон через страницу: вопрос без единого приложенного файла — «какая функция решает, что ответ дописан?». 36 секунд, три шага. DeepSeek запросил пятью блоками FIND сразу (waitForAnswer, streaming, done, stop_reason, finish_reason), затем NEED: ask.mjs, и ответил точно: waitForAnswer, строка 675. Журнал шагов обновлялся на странице по ходу.

Обрезанные ответы: причина найдена и устранена в мосте (11.08.2026)

В третьем прогоне ответ оборвался посреди блока кода на // Бы. Оказалось, это не «конец определён раньше времени» и не кнопка Continue.

Браузер работал в фоне. У неактивного окна страница не отрисовывается, а DeepSeek рисует блоки кода лениво: пока блок не нарисован, в DOM от него лежат первые две строки, а остального нет вовсе — его не достать ни innerText, ни textContent. Прокрутка не помогает (проверено проходом по всему ответу шагами: ни одного лишнего символа).

Помогает единственное — заставить браузер отрисовать страницу, и через CDP это делает Page.captureScreenshot. Замеры на живых ответах:

до отрисовки после
ответ про TaskQueue 2068 24 362
ответ про EventBus 3136 30 817
ответ про Router (уже с правкой) 258 32 045

Правка в ollama_orchestro/ask.mjs: revealAnswer() перед съёмом текста делает дешёвый скриншот (jpeg, качество 1) и перечитывает ответ, до трёх раз, пока текст растёт. Картинка выбрасывается.

Вдобавок browser-launch.ps1 запускает браузер развёрнутым и с запретом фонового режима (--disable-backgrounding-occluded-windows, --disable-renderer-backgrounding, --disable-background-timer-throttling, --disable-features=CalculateNativeWinOcclusion). Это не замена скриншоту: свёрнутое окно Windows не рисует вообще.

Вторая причина обрезания, найденная там же, — кнопка Continue: DeepSeek режет длинный ответ по своему лимиту и предлагает дописать, а генерация при этом честно завершается. Теперь waitForAnswer жмёт её и ждёт дальше; продолжение уходит в тот же блок ответа. Сквозная проверка — ответ на 47 414 символов с целыми кодом и тестами.

Обе правки в ollama_orchestro, deepwork получает их даром — мост вызывается подпроцессом.