Files
deepwork/README.md
T
Alex CubeandClaude Opus 5 263ad14810 Пульт: выключатель инструментов, помощь с git, память прошлого ответа
Три вещи, которых не хватало по сравнению с пультом DeepShim, — теперь
здесь, и DeepShim можно закрывать.

1. Выключатель инструментов. Снят — приставка, дерево папки и напоминание
   про NEED/LIST/FIND не едут вовсе, круг ровно один: обычное "спросил
   по этим файлам, получил ответ". Замер на одной задаче: 229 символов
   вопроса против 1886. В консоли это флаг --no-tools.

2. Помощь с git: ряд всплывает, только если в папке нет репозитория или
   в .gitignore не хватает строк. Главная - backup/, куда ложатся прежние
   версии файлов перед записью. Перенесено из пульта DeepShim.

3. "Помнить прошлый ответ": галочка, по которой прошлый ответ уезжает
   вместе со следующим вопросом. Разговор из нескольких вопросов, которого
   раньше не было: стенограмма велась внутри одного вопроса и на этом всё.

Попутно два дефекта, оба найдены при проверке:

- Разбор флагов съедал аргумент после булева флага: "deepwork --apply вопрос"
  превращался в apply="вопрос", а вопрос пропадал. Молчало, пока булев флаг
  стоял последним. Та же грабля уже ловилась в ask.mjs; добавлен BOOL_FLAGS.

- Блок-запрос вплотную к прозе не распознавался. DeepSeek регулярно пишет
  "Запрашиваю поиск константы.=== FIND: MAGIC ===" одной строкой, разбор
  требовал начала строки, и круг уходил впустую. Якорь начала снят, якорь
  конца оставлен: упоминание формата посреди фразы запросом не считается.
  От запросов внутри === FILE: === бережёт вырезание этих блоков, а не якорь.

Проверено вживую: без инструментов - 229 символов, один круг, верный ответ;
с инструментами - FIND распознан, два круга, верный ответ; git - repo:false
и пять недостающих строк, после кнопки repo:true и missing пуст; память -
"удвой это число" вернуло 80834 против бессмыслицы без галочки.
--selftest дополнен двумя случаями на разбор запросов.

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

437 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# deepwork
**DeepSeek, который сам читает вашу папку с кодом.**
Обычный веб-чат видит только то, что вы вставили в вопрос. Спросишь «почему
падает вот эта функция» — и в ответ получишь три догадки, потому что остального
кода он не видел.
Здесь у него появляется обратное направление. DeepSeek отвечает не текстом,
а **запросом**: «пришлите мне файл `server.js`», «покажите, где встречается
`waitForAnswer`». Программа исполняет запрос, досылает данные, и так по кругу,
пока он не соберёт всё нужное и не ответит по существу.
```
вы: "почему падает загрузка?"
DeepSeek ──► "=== NEED: upload.js ==="
│ │
│ программа читает файл и досылает
│ │
▼ ▼
DeepSeek ──► "=== FIND: parseBody ==="
│ │
▼ ▼
DeepSeek ──► ответ по существу + исправленный файл
```
Читает он только вашу папку и только на чтение. Правки возвращает файлами,
которые вы записываете нажатием кнопки — или не записываете.
---
## Оглавление
1. [Что понадобится](#что-понадобится)
2. [Установка по шагам](#установка-по-шагам)
3. [Как пользоваться](#как-пользоваться)
4. [Что он умеет запрашивать](#что-он-умеет-запрашивать)
5. [Правки файлов](#правки-файлов)
6. [Настройка](#настройка)
7. [Если не работает](#если-не-работает)
8. [Безопасность](#безопасность)
9. [Как это устроено внутри](#как-это-устроено-внутри)
10. [Что проверено вживую](#что-проверено-вживую)
---
## Что понадобится
| Что | Зачем | Как проверить |
|---|---|---|
| **Windows** | скрипт запуска написан под неё | — |
| **Node.js 22 или новее** | на нём написана программа | `node --version``v22.…` и выше |
| **Сервис `deepseek-api`** | через него идёт разговор с DeepSeek | см. шаг 1 |
Node.js, если его нет, ставится с [nodejs.org](https://nodejs.org) — берите
версию LTS. После установки **закройте и откройте заново** командную строку,
иначе `node` в ней не найдётся.
---
## Установка по шагам
### Шаг 1. Поднять deepseek-api
У веб-версии DeepSeek нет API — только вкладка в браузере. Разговором с ней
занимается отдельная программа
[deepseek-api](https://git.08h.ru/alex/deepseek-api): она держит вкладку
и отдаёт наружу обычный HTTP.
Поставьте и запустите её по инструкции в её README — это отдельные пять шагов,
включая вход в аккаунт DeepSeek. Возвращайтесь сюда, когда заработает.
> **Проверка:**
> ```cmd
> curl http://localhost:11500/api/tags
> ```
> должен вернуть список из трёх «моделей». Окно `deepseek-api` при этом
> остаётся открытым — пока оно живо, работает и сервис.
### Шаг 2. Скачать deepwork
```cmd
cd C:\#Projects
git clone https://git.08h.ru/alex/deepwork.git
```
Без git — скачайте ZIP с той же страницы и распакуйте. Папка может лежать где
угодно; рядом с чем-то конкретным её класть не нужно.
> **Проверка:**
> ```cmd
> cd C:\#Projects\deepwork
> node deepwork.mjs --selftest
> ```
> Ответ `самопроверка пройдена`. Ничего никуда не отправляется.
### Шаг 3. Проверить связку
```cmd
node deepwork.mjs --check
```
Три возможных ответа:
| Что видно | Что это значит |
|---|---|
| `готов: браузер поднят, вкладка DeepSeek открыта` | всё на месте, можно работать |
| `не готов (HTTP 503)` | сервис жив, но браузер ещё не поднят — встанет сам на первом вопросе |
| `не отвечает: …` | `deepseek-api` не запущен, вернитесь к шагу 1 |
**Готово.** Настраивать больше нечего: адрес `http://localhost:11500` вшит
как значение по умолчанию.
---
## Как пользоваться
### Способ 1: пульт (обычный путь)
**Перетащите папку своего проекта мышкой на файл `start-deepwork.cmd`.**
Откроется страница в браузере:
- слева файлы папки — отметьте галочками те, что точно нужны (можно ни одного);
- поле вопроса;
- ход работы построчно: видно, что DeepSeek запросил и сколько это заняло;
- у каждого пришедшего файла кнопка «Записать».
Три переключателя рядом с кнопкой «Спросить»:
| | Что делает |
|---|---|
| **инструменты** | разрешить модели самой запрашивать файлы. Снимите — получится обычный «спросил по этим файлам, получил ответ»: один круг, без беготни по папке. См. [когда выключать](#когда-выключать-инструменты) |
| **шагов** | сколько кругов позволить. Без инструментов не нужен и гаснет |
| **помнить прошлый ответ** | прошлый ответ уедет вместе со следующим вопросом. Так можно вести разговор: «а теперь перепиши это на async» |
Внизу слева иногда всплывает ряд про **git** — он появляется, только если
в папке нет репозитория или в `.gitignore` не хватает строк. Главная из них —
`backup/`: туда ложатся прежние версии файлов перед записью, и однажды они
уедут в коммит.
Пульт открывается в браузере **по умолчанию**, а не в том, которым управляет
`deepseek-api`: иначе он занял бы там вкладку, и вкладка с чатом потерялась бы.
Порт 8787.
Остановить — `Ctrl+C` в чёрном окне.
### Способ 2: командная строка
Работает в **текущей** папке: её читает, в неё же пишет правки.
```cmd
cd C:\мой\проект
node C:\#Projects\deepwork\deepwork.mjs "почему падает загрузка файла?"
```
Полезные флаги:
| Флаг | Что делает |
|---|---|
| `--files a.js,b.js` | положить эти файлы в вопрос сразу |
| `--steps 6` | сколько кругов позволить (по умолчанию 4) |
| `--apply` | записать пришедшие файлы на диск (без него только покажет) |
| `--no-tools` | не давать модели инструменты: один круг, только отмеченные файлы |
| `--chat search` | режим с поиском в интернете вместо обычного |
| `--check` | проверить связку и выйти |
| `--selftest` | внутренние проверки, ничего не отправляя |
### Когда выключать инструменты
Инструменты хороши, когда **непонятно, что читать**: «почему падает загрузка»
в чужом проекте. Модель сама найдёт нужное.
Выключать стоит, когда вы **точно знаете нужные файлы**. Тогда:
- ответ приходит за один круг вместо двух-трёх;
- вопрос выходит вчетверо короче — приставка, дерево папки и напоминание
не едут вовсе (замер: 229 символов против 1886 на той же задаче);
- модель не ходит по папке без нужды, и наружу не уезжает лишний код.
### Чего ожидать по времени
Один круг — 7–15 секунд. Обычная задача укладывается в два-три круга, то есть
полминуты. Если DeepSeek запросил много файлов, вопрос на следующем круге
вырастает до десятков килобайт — это нормально.
---
## Что он умеет запрашивать
Только чтение и только внутри текущей папки:
| Блок в ответе | Что делает |
|---|---|
| `=== NEED: путь, путь ===` | прислать файлы целиком |
| `=== LIST: **/*.mjs ===` | список файлов по маске |
| `=== FIND: текст ===` | где встречается текст, с номерами строк |
**Запуска команд (`RUN`) нет, и не будет.** Исполнять строки, пришедшие
из интернета, без белого списка и подтверждения нельзя — а белого списка,
который был бы и полезен, и безопасен, придумать не вышло.
Лимиты на один круг: `NEED` — 200 000 символов суммарно, `FIND` — 100
попаданий, `LIST` — 300 путей. Обрезанное помечается прямо в тексте, чтобы
DeepSeek видел, что данные неполные.
Цикл останавливается, когда запросов в ответе больше нет, когда кончились
круги или когда пришёл тот же самый запрос второй раз подряд — это петля,
данные ему уже присылали.
---
## Правки файлов
Исправленный код DeepSeek возвращает блоками:
```
=== FILE: src/upload.js ===
```
...содержимое целиком...
```
=== END FILE ===
```
Записываются они **только по вашей команде** — кнопкой «Записать» в пульте
или флагом `--apply` в консоли. Без этого программа просто перечислит, что
пришло.
**Прежняя версия не пропадает:** перед записью она уезжает в папку `backup/`
внутри проекта. Файлы вне рабочей папки не пишутся вовсе — путь проверяется.
---
## Настройка
Нужна редко. `config.json` рядом с программой (в репозиторий не попадает,
образец — `config.example.json`):
```json
{
"deepseekHost": "http://localhost:11500",
"deepseekModel": "deepseek:think",
"steps": 4,
"timeout": 240
}
```
| Ключ | По умолчанию | Смысл |
|---|---|---|
| `deepseekHost` | `http://localhost:11500` | адрес сервиса `deepseek-api` |
| `deepseekModel` | `deepseek:think` | режим чата: `think`, `search` или `vision` |
| `steps` | `4` | сколько кругов позволить |
| `timeout` | `240` | секунд на один круг (сверх этого сервису дают ещё две минуты на очередь) |
| `uiPort` | `8787` | порт пульта |
Без файла всё работает на значениях по умолчанию. А вот **битый** `config.json`
не молчит: программа скажет об ошибке, а не откатится тихо к умолчаниям —
иначе правка в настройках не действует, и понять почему невозможно.
---
## Если не работает
| Что видно | В чём дело | Что делать |
|---|---|---|
| `deepseek-api не отвечает по адресу …` | сервис не запущен | Запустите `start.cmd` в папке `deepseek-api` |
| `--check` говорит `не готов (HTTP 503)` | сервис жив, браузера ещё нет | Ничего: браузер встанет на первом же вопросе |
| `deepseek-api ответил 500` | что-то со вкладкой | `curl http://localhost:11500/api/probe` покажет, что там видно |
| **Ответ пришёл обрезанным**, код оборван на середине | окно браузера свёрнуто | Разверните его. Свёрнутое окно Windows не отрисовывает, а DeepSeek рисует блоки кода лениво — см. [ниже](#обрезанные-ответы-причина-найдена) |
| `шаг 1: запросов нет, готово`, а ответ — догадки | DeepSeek поленился спросить и придумал | Спросите конкретнее либо дайте файл сразу через `--files`. Так бывает; напоминание в конце вопроса помогает, но не всегда |
| `тот же запрос повторно — петля, выхожу` | он не принял присланные данные | Обычно значит, что запрошенного файла нет или он пуст. Посмотрите журнал шагов |
| `шаги кончились, а запросы ещё идут` | задача крупнее четырёх кругов | `--steps 6`, но сначала подумайте, не проще ли разбить вопрос |
| `DeepSeek занят: «Server is busy»` | лимиты на стороне DeepSeek | Подождите минуту |
| `node не является внутренней или внешней командой` | Node не установлен или консоль открыта до установки | Поставьте Node, **закройте и откройте заново** консоль |
| Чаты в DeepSeek пропадают сами | тот же профиль браузера использует другая программа | Дайте `deepseek-api` отдельный профиль |
---
## Безопасность
- **Ваш код уходит на серверы DeepSeek.** Не только присланные файлы: он сам
запрашивает то, что считает нужным, из всей рабочей папки. Для закрытых
проектов это не годится.
- **Ключей и паролей в папке быть не должно.** `.env`, `config.json` с токенами,
приватные ключи — всё это он может запросить, и это уедет наружу. Убирайте
такое из рабочей папки заранее.
- **Правки не применяются сами.** Только кнопкой или флагом `--apply`,
и прежняя версия всегда сохраняется в `backup/`.
- **За пределы рабочей папки программа не пишет** и не читает: путь
проверяется, `..` не проходит.
- **Пульт слушает только этот компьютер** (`127.0.0.1`), из сети к нему
не подключиться.
---
## Как это устроено внутри
### Разговор помним мы, а не DeepSeek
Сервис `deepseek-api` **без памяти**: каждый запрос он кладёт в чат целиком,
затирая прошлый. Поэтому стенограмму ведёт `deepwork` — функция
`growTranscript` подклеивает к отправленному тексту прошлый ответ модели
(между метками `=== ТВОЙ ПРЕДЫДУЩИЙ ОТВЕТ ===` и `=== END ===`) и результаты
запросов, и следующим кругом уезжает всё сразу.
Раньше память держал сам чат DeepSeek. Своя стенограмма надёжнее: видно
в одном месте, что именно уедет модели, а чужая программа, вклинившаяся между
нашими кругами, ничего не портит — мы всё приносим заново. Плата — вопрос
растёт на длину прошлого ответа; при четырёх кругах это единицы килобайт
против 22 КБ первого сообщения.
### Что уезжает в первом сообщении
1. Приставка из `agent-prompt.md` — кто он и какие блоки может вернуть.
2. **Дерево рабочей папки.** Без него первый живой прогон ушёл на угадывание
имён файлов, см. замеры ниже.
3. Файлы, отмеченные галочками (или переданные через `--files`).
4. **Напоминание про инструменты — вплотную к вопросу.** Не в начале: между
приставкой и вопросом ложатся килобайты файлов, и инструкция теряется.
5. Сам вопрос.
### Файлы
| Файл | Что в нём |
|---|---|
| `deepwork.mjs` | всё: цикл, инструменты, разбор ответов, пульт |
| `agent-prompt.md` | приставка к первому сообщению |
| `ui.html` | страница пульта |
| `config.json` | настройки (не в репозитории) |
| `start-deepwork.cmd` | запуск пульта перетаскиванием папки |
Зависимостей нет — только то, что есть в Node 22.
---
## Что проверено вживую
### Внутренние проверки (`--selftest`)
Разбор запросов, отказ пути за пределы папки, маски `*` и `**`, снятие тройных
кавычек и подписи языка. Строка вида `=== NEED: … ===` **внутри** блока
`=== FILE: ===` за запрос не принимается — иначе правка файла с описанием
формата запускала бы запрос сама. Пропавший файл в `NEED` возвращает «файла
нет» и цикл не роняет. `--steps 1` отдаёт ответ с пометкой «шаги кончились»,
а не зависает.
### Из чего выросли два правила, 11.08.2026
Задача одна и та же: спросить, где решается, что ответ дописан до конца, дав
в вопросе **заведомо не тот файл**. Правильный ответ достижим только через
инструменты.
| Прогон | Кругов | Что вышло |
|---|---|---|
| 1. приставка только в начале сообщения | 1 | **запросов нет.** «Точный код в серверном файле» и три догадки |
| 2. + напоминание вплотную к вопросу | 4 | нашёл, но два круга ушли на угадывание имён файлов |
| 3. + дерево папки в первом сообщении | **2** | запросил нужный файл сразу, ответил по существу |
- **Инструкция в начале сообщения не работает.** Между приставкой и вопросом
легли 22 КБ файла, и DeepSeek инструменты просто не заметил.
- **Дерево папки надо давать сразу.** Без него он угадывал имена (`server.js`,
`bridge.js` — таких в проекте нет), потом просил `LIST *.js` (проект
на `.mjs`, пусто), потом `LIST **/*`. Дерево — 600 символов против двух
кругов по минуте.
### Пульт, 11.08.2026
Вопрос без единого приложенного файла — «какая функция решает, что ответ
дописан?». 36 секунд, три круга: пять блоков `FIND` сразу, затем `NEED`
на найденный файл, и точный ответ с номером строки. Журнал кругов обновлялся
на странице по ходу.
### Обрезанные ответы: причина найдена
Один из прогонов оборвался посреди блока кода на `// Бы`. Это не «конец
определён раньше времени» и не кнопка `Continue`.
**Браузер работал в фоне.** У неактивного окна страница не отрисовывается,
а DeepSeek рисует блоки кода лениво: пока блок не нарисован, в DOM от него
лежат первые две строки, а остального нет вовсе — его не достать ни
`innerText`, ни `textContent`. Прокрутка не помогает.
Помогает единственное — заставить браузер отрисовать страницу. Замеры на живых
ответах:
| | до отрисовки | после |
|---|---|---|
| ответ про TaskQueue | 2068 | 24 362 |
| ответ про EventBus | 3136 | 30 817 |
| ответ про Router | 258 | **32 045** |
Вторая причина обрезания — кнопка **Continue**: DeepSeek режет длинный ответ
по своему лимиту и предлагает дописать, а генерация при этом честно
завершается. Сквозная проверка после починки — ответ на 47 414 символов
с целыми кодом и тестами.
Обе поломки лечатся в `deepseek-api`, а не здесь: отрисовка и кнопка `Continue`
живут в его `browser.mjs`. Отсюда остаётся одно требование — **не сворачивать
окно браузера**.
### Блок-запрос вплотную к прозе, 26.08.2026
DeepSeek регулярно дописывает запрос в конец фразы, без перевода строки:
```
Мне нужны данные для ответа. Запрашиваю поиск константы MAGIC.=== FIND: MAGIC ===
```
Разбор требовал, чтобы блок начинал строку, и такой запрос не видел: цикл
решал, что запросов нет, и заканчивал работу догадкой. Круг впустую.
Якорь начала строки снят, якорь конца оставлен: упоминание формата посреди
фразы («верни `=== NEED: файл ===` и я пришлю») запросом по-прежнему не
считается. От запросов внутри блоков `=== FILE: ===` бережёт не якорь,
а вырезание этих блоков перед разбором — так было и раньше.
Проверено на живой задаче: до правки — один круг и «запросов нет»; после —
`FIND: MAGIC`, второй круг, верный ответ.
### Переезд на deepseek-api, 26.08.2026
Раньше `deepwork` вызывал мост `ask.mjs` из проекта DeepShim дочерним процессом
и гонял текст через временные файлы. Теперь это `POST /api/chat`, а память
кругов переехала из чата в стенограмму (см. выше).
Проверено живым прогоном: круг 1 — 2094 символа, ответ `NEED: deepwork.mjs`;
круг 2 — 28 447 символов стенограммы, ответ дословно верный и по вопросу
из **первого** сообщения. Два круга за 17 с.
---
## Лицензия
MIT.