Три вещи, которых не хватало по сравнению с пультом 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>
437 lines
26 KiB
Markdown
437 lines
26 KiB
Markdown
# 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.
|