Как я перешёл с openclaw на DeepSeek Harness, вернул своему VDS load average 0,03 и перестал докупать память.
Вслед за многими читателями этого сайта я в какой-то момент дошёл до стадии, когда тоже установил себе ИИ-ассистента на VDS. Поначалу всё ограничивалось поиском новостей, но время шло, и спектр работ, которые он выполняет, рос.
Не буду писать о своих задачах, для статьи они не особо важны. Скажу лишь, что в основном они здесь именно ассистентные: найти, предупредить, прочитать и т.п.
Но эйфория от того, что я, начитавшись интернетов и насмотревшись видео, поставил себе openclaw, быстро прошла. Да, функциональность у него потрясающая.
Однако реализован он — х-гм… странно. Не как асинхронное приложение (что было бы очевидно), а как-то иначе. Вследствие этого потребление ресурсов у него (как бы это сказать помягче?) радикально завышенное. Запредельное.
Наткнувшись на то, что к моему VDS раз десять пришёл в гости OOM-киллер, я сперва добавил ему своп на 2 гигабайта. Затем купил дополнительный гиг RAM к стартовому пакету. Позже добавил ещё два. Проблемы вроде и ушли, но… не до конца.
Необъяснимые случаи, когда в ответ на простой вопрос ассистенту весь хост валится или неимоверно тупит 10–15 минут, продолжились. Причём, как всегда, происходит это именно тогда, когда требуется что-то срочное.
Разбираясь с этим, я, разумеется, привлекал к решению и самого ассистента. Он делал апгрейды и даунгрейды версии openclaw, что-то там себе тюнил, патчил, уменьшал и увеличивал какие-то лимиты, но нормального функционирования добиться мы так и не смогли.
В итоге я пошёл искать что-то «такое же, но лёгкое».
Дисклеймер
Подробно в архитектуре openclaw я не копался. Как пользователю мне хотелось простого и человеческого «чтобы работало из коробки». Потому если кто-то захочет написать в комментах: «Ты — лох, надо было делать вот так!», то добро пожаловать. Я, как говорится, не претендую.
Здесь я хочу рассказать, как избавился от назойливого лог-сообщения системы:
openclaw invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0
Out of memory: Killed process 243946 (tsc) total-vm:4927976kB, anon-rss:3113548kB
Три гигабайта у одного процесса — на машине, где всего 4 гигабайта. Причём в момент, когда к нему пришли с простым вопросом.
PS: эту статью я писал сам, с помощью ИИ здесь лишь расставлены запятые. Если вдруг кажется, что текст — ИИ-слоп, то это от того, что мы скоро все начнём писать одинаково. Время такое. Хе-хе.
Как должен быть устроен правильный ассистент
Однако, несмотря на дисклеймер, не могу удержаться от того, что возьму и немного поумничаю. Да. В этой микроглаве мы немного уйдём в офтопик и поговорим о том, как эта (и подобные) штука должна быть устроена в принципе.
Правильный ассистент — это обычное асинхронное приложение, которое 99% времени тупо ждёт результатов или команд от пользователя, и перенаправляет их в сеть. А потому на 90% он должен состоять из http-клиента и очереди сообщений. Всё. Кто скажет, что я не прав — добро пожаловать в комменты!
Отсюда вытекает крайнее недоумение: как вообще можно было сделать такую программу столь требовательной к ресурсам? Не знаю! Но разработчики openclaw справились! В код, как я сказал выше, я не заглядывал, но моё замешательство здесь, считаю, вполне обоснованным.
В общем, всё было настолько плохо, что какой-то момент меня даже посещала идея сесть и написать своего агента. Но…, но…, но! Хотелось таки найти готовое решение, не рисуя очередной велосипед, коих итак тысячи…
А потому я, как и положено, обратился к поиску:
picoclaw
Первым, что удалось нарыть, был, разумеется, picoclaw. У него всё вроде бы хорошо. Функционал (для меня) тот же. Написан на Go, ресурсов почти не потребляет (вот правда: почти не потребляет!), но…, зараза, недоделан!
Даёшь ему задание «Поищи в сети книжку, которую я ещё не читал» (у ассистента есть реестр прочитанного мной и список предпочтений), а в ответ он говорит: «пошёл я в интернет», и… не возвращается!
Не знаю, как, не разбирался почему, но его workflow легко может прерваться посреди процесса. Происходит это с завидной регулярностью, а потому приходится всё время бдить в чате, дёргая его: «ты не завис?», «ты здесь?» и т.п.
Лёгкость лёгкостью, но ассистент, за которым надо следить, — это не ассистент, а домашнее животное.
DeepSeek Harness
Конечно, кроме picoclaw, я смотрел на многие иные варианты, в основном сопровождая поисковые запросы эпитетами «мало памяти», «лёгкий» и так далее: nano, pi и подобные были отвергнуты по тем или иным причинам.
Поиск занял довольно много времени, а потому до главной альтернативы — hermes — так и не добрался, хотя и собирался.
Проблема решилась сама собой — совсем другим способом. Однажды на глаза мне попала новость о DeepSeek Harness, и я решил попробовать его не в качестве ассистента, а как простого агента на десктопе.
Целью установки было программировать. Изначально в качестве замены openclaw я его не рассматривал.
Установил, понажимал кнопочки, поглядел в огромную тучу плагинов, пожал плечами: «Ещё один аналог OpenCode, Cursor и т.п.» — и собрался было уже пройти мимо, но по инерции взглянул на потребление памяти, и здесь меня торкнуло: вот оно! Пока ты ему не пишешь, CPU оно не ест. Вот вообще. А когда работает, то тоже в htop почти не наблюдается!
В итоге у меня образовалась вполне полноценная замена openclaw, которую нормально можно запускать на одногигабайтовом и даже полугигабайтовом железе!
Дальше — про то, что превращает его из «ещё одного чата» в полноценного ассистента.
Инструкция
Самое главное, что делает ассистента ассистентом — возможность работать 24/7 — у DSH есть прямо, что называется, «из коробки». Встроенный а-ля cron-шедулер позволяет удобно настраивать самостоятельную работу хоть в GUI, хоть посредством текстовых сообщений модели.
Осталось оснастить всю эту машинерию мелочью — шлюзом в Telegram и памятью.
Покопавшись по индексу доступных плагинов, я почти сразу нашёл всё, что требуется.
Подключение к Telegram (и куче других мессенджеров) обеспечил плагин: dsh-im. А систему памяти, делающую из агента ассистента, я решил строить на плагине dsh-mneme.
Итак, что мы имеем, если используем DSH:
-
Крайне надёжная система работы с моделями. Причина надёжности — ориентация ПО на программирование. Если бы эта штука глючила, как тот picoclaw, то никто бы ей не пользовался. Однако 257 тысяч звёзд на GitHub как бы неистово вопят о качестве;
-
Планировщик (аналог cron) — «на борту»;
-
Выход в мессенджеры, то есть возможность общаться с системой с любого устройства — есть;
-
Индексируемая память с возможностью ничего не забывать и «вспоминать» происходившее даже между сессиями — имеется (причём множество вариантов).
Осталось со всем этим хозяйством взять и «взлететь».
Итак, устанавливаем на VDS.
Для работы в роли ассистента, как уже сказано выше, DSH требуется узел с Linux, с одним и более CPU (одного вполне достаточно) и одним и более гигабайтом RAM (одного хватит).
1. Node.js
На новом хосте создаём пользователя и ставим ему свежий Node.js. Способ, который не требует прав суперпользователя после первой пары команд:
~root$ apt install -y sudo
~root$ adduser ai
~root$ usermod -aG sudo ai
~root$ loginctl enable-linger ai
~root$ su - ai
~ai$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash
~ai$ . ~/.nvm/nvm.sh
~ai$ nvm install --lts
~ai$ exec bash -l
~ai$ node -v
~ai$ npm install -g pnpm
~ai$ pnpm config set dangerouslyAllowAllBuilds true
2. Ставим сам DeepSeek Harness:
~ai$ npm install -g @deepseek-ai/dsh
~ai$ dsh web --port 3080
Последняя команда поднимает Web UI — это и есть основное приложение, в котором живёт агент. Открываем адрес из вывода, логинимся.
Чтобы браузер смог увидеть веб-сервер на удалённом узле, выполните проброс портов к себе (этот шаг нужен лишь на первоначальном этапе, в дальнейшем не потребуется).
~ai$ ssh -L 3080:127.0.0.1:3080 root@<адрес-вашего-сервера>
Первое, что мы увидим, — окошко «Models»: там можно выбирать провайдера и модель, однако пока откажемся от этого, отложив действие на более позднее время.
Сейчас нас интересует очень важная кнопка — Sign in: если войти через неё, а не вставлять ключ, большинство запросов к модели DeepSeek будет бесплатным.
Примечание про Sign in: желательно сделать это именно на текущем шаге, пока вы общаетесь с будущим ассистентом через адрес http://127.0.0.1:3080. Когда перед DSH уже будет установлен nginx, сделать то же будет сложнее.
3. Добавляем плагины
# Шлюз в мессенджеры
~ai$ dsh plugin --profile web add @xmanrui/dsh-im
# Память
~ai$ dsh plugin --profile web add @modusensus/dsh-mneme
# Место хранения «личности» агента
~ai$ dsh plugin --profile web add dsh-agent-profiles
Также можно сразу поставить плагины, которые помогут общаться с агентом при помощи голосовых сообщений:
# Позволит гибко настроить способ расшифровки голосовых
~ai$ dsh plugin --profile web add @dr-co/dsh-speech-gate
Примечание: dsh plugin — это обёртка над pnpm в каталоге профиля, ставит пакеты в ~/.dsh/profiles/web.
4. Настраиваем Telegram-шлюз (dsh-im).
Создаём бота у @BotFather, берём его токен и вставляем в конфиг плагина через меню: «Настройки → IM-боты → Telegram».
Заодно решаем, как бот реагирует в группах: у меня это флаг «отвечать на прямое обращение и на реплаи», иначе в общем чате он начинает встревать в каждый разговор.
5. Настраиваем ассистента (im-assistant).
Делаем это также через веб-интерфейс, меню: «Настройки → Agent Profiles».
Здесь, что называется, «из коробки» было настроено пяток разных агентов; я все их удалил, оставил лишь одного, радикально исправив его инструкции.
Здесь же в полях настроек можно просто указать инструкцию вида «Прочитай /path/to/SOUL.md», чтобы перенести, например, «душу» вашего ассистента из openclaw сюда.
6. Настраиваем память.
В принципе, сразу после установки оно уже вполне нормально настроено. Однако можно попросить агента выполнять компактизацию и индексацию чуть пореже. Это позволит экономить токены модели (именно в этом месте они платные).
7. Если есть доменное имя — ставим nginx.
Поскольку ассистент изначально слушает только localhost, постановка nginx позволит нам назначить серверу имя, включить шифрование и так далее.
Конфиг довольно простой:
~ai$ sudo apt install nginx
~ai$ sudo tee /etc/nginx/sites-available/assistant.example.com >/dev/null <<'EOF'
server {
listen 443 ssl;
http2 on;
server_name assistant.example.com;
ssl_certificate /etc/ssl/certs/assistant.example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/certs/assistant.example.com/private.pem;
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
}
EOF
~ai$ sudo ln -s /etc/nginx/sites-available/assistant.example.com
/etc/nginx/sites-enabled/
~ai$ sudo rm -f /etc/nginx/sites-enabled/default
~ai$ sudo nginx -t && sudo systemctl reload nginx
Сертификат проще всего получить тем же acme.sh:
~ai$ curl https://get.acme.sh | sudo bash
~ai$ sudo /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt
~ai$ sudo /root/.acme.sh/acme.sh --issue --standalone -d assistant.example.com
~ai$ sudo mkdir -p /etc/ssl/certs/assistant.example.com
~ai$ sudo /root/.acme.sh/acme.sh --install-cert -d assistant.example.com
--key-file /etc/ssl/certs/assistant.example.com/private.pem
--fullchain-file /etc/ssl/certs/assistant.example.com/fullchain.pem
--reloadcmd "systemctl reload nginx"
Примечание: --standalone — самый простой путь: acme.sh сам поднимает временный веб-сервер на 80-м порту, проверяет домен и уходит. Именно поэтому 80-й порт стоит держать свободным: отдадите его под что-нибудь постоянное — и придётся переезжать на DNS-валидацию (--dns dns_cf и подобное), то есть добавлять зависимость от чужого DNS-провайдера. Мне с этим возиться было лень, потому так.
8. Настраиваем автозапуск при перезагрузке.
Кладём в ~/.config/systemd/user/dsh-server.service юнит:
[Unit]
Description=DeepSeek Harness Web UI
After=network.target
[Service]
Type=simple
Environment="DSH_HOME=/home/ai/.dsh"
ExecStart=/home/ai/.nvm/versions/node/v24.22.0/bin/dsh web --no-open --public-url https://assistant.example.com/ --trusted-host assistant.example.com
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
~ai$ systemctl --user daemon-reload
~ai$ systemctl --user enable --now dsh-server
И вот мы у цели!
Средняя нагрузка, которая во времена openclaw болталась не ниже LA=2, падает до 0,1–0,03:
uptime 20:46:49 up 5 days, 2:58, 1 user, load average: 0,10, 0,03, 0,03
А функционал тот же!
Сколько это ест
Цифры сняты на моём VDS: 3 ядра, 4 ГБ RAM:
|
что |
сколько |
|---|---|
|
node сам по себе |
44 МиБ |
|
один ответ ассистента (flash или pro — одинаково) |
~308 МиБ пик |
|
ответ с вызовами инструментов |
310 МиБ пик |
|
свежий |
462 МиБ (PSS) |
|
он же через сутки работы с историей сессий |
573 МиБ |
|
плагинов в профиле |
196 записей |
|
для контраста: тот самый процесс openclaw |
3,1 ГБ, после чего OOM |
Итого полгигабайта на веб-сервер (который в среднем нужен только для настроек), плюс треть гига, пока есть активность с моделями. На гигабайтный VDS это влезает с запасом.
Ещё и модель нахаляву? Почти
В качестве бонуса эта штука может быть полностью бесплатной в плане затрат на модели. Ну, почти.
Если вы, как было предложено в инструкции, залогинитесь в deepseek через кнопочку Sign in, то большинство ваших запросов к модели будут бесплатными.
Однако есть нюанс, и он был описан выше: категоризация памяти тем же дипсиком понемногу кушает баланс. Я пока не стал ничего здесь исправлять, посчитав это мелочью, но в целом кажется, что при необходимости сюда можно подрядить какой-либо из совсем бесплатных способов — позже, возможно, покопаюсь.
Заключение
Конечно, есть некоторые косяки. Да, всё работает из коробки, но поскольку dsh — штука новая (на момент написания статьи это release candidate 0.2.1), вскрываются недоработки в различных инструментах, написанных сторонними разработчиками.
Первые два или три дня мы с ассистентом только и делали, что рассылали патчи в разные стороны:
-
улучшили систему распознавания речи (звуковых сообщений) из Telegram и IM вообще — теперь голосовое превращается в текст до того, как попадёт в модель, а если распознать не удалось, ассистент честно говорит, чего ему не хватает (#405);
-
радикально переделали отображение статуса «что сейчас происходит» при общении с моделью через Telegram (#412, #414, #425, #426);
-
добавили к телеграм-транспорту работу с лимитами: у Telegram есть бюджет отправок на чат, и без его соблюдения бот временами начинает получать ошибку выхода за установленные ограничения (#423);
-
и по мелочи: триггеры ответа в группах, чтобы бот не встревал в каждый разговор (#404);
-
заодно (где дотянулись) перевели интерфейс многих настроек на русский (#438, #439, #440).
На момент, когда я это пишу, все перечисленные PR уже влиты в апстрим, и скорость, с которой мы генерируем новые, заметно упала. То есть «косяки» здесь — не столько жалобы, сколько отчёт о том, что можно было улучшить. Кому-то надо было первым походить по граблям и оформить их в патчи. Хе-хе!
Также по ходу дела сваяли и пару плагинов:
-
dsh-speech-gate — распознавание речи для голосовых сообщений: держит реестр провайдеров (локальный whisper, облачная модель, свой скрипт) и позволяет выбрать, чем распознавать;
-
dsh-secret-form — одноразовые веб-формы для секретов: токен вставляется в браузере и уходит сразу на диск, минуя историю чата, где он остался бы навсегда.
В общем, идущий после меня сможет поставить все эти плагины и тупо всё настроить, не задавая вопросы «почему оно не так удобно, как openclaw?».
Оно теперь удобно. И даже более удобно, нежели в openclaw!
Получив первый доступ к модели, можете отдать ей ссылку на эту статью и попросить «настроить всё так же, как здесь».
Итогом будет полный функциональный аналог openclaw, нетребовательный к ресурсам и весьма надёжный.
Оплачивать четырёхгиговый VDS больше не требуется. Достаточно гигового.
Такие дела.
Автор: linux-over


