dsh-web-search-searxng
Русский | English
Бандл для DeepSeek Harness,
который даёт агенту работающий web_search без ключа вендора. Он подключает
провайдера поиска на базе SearXNG к шву веб-доступа харнесса (ctx.web) и по
умолчанию поднимает собственный контейнер SearXNG — то есть установка бандла
и есть вся настройка.
Принцип работы и отличие от «голого» SearXNG
«Голый» подход — это адаптер на тридцать строк: взять {base}/search?format=json
и вернуть results[]. Он исходит из того, что SearXNG у вас уже поднят, JSON в
нём включён, порт свободен, образ свежий, а контейнер жив. И он не отличает
«ничего не нашлось» от «движки внутри отказали»: в обоих случаях приходит HTTP 200
с пустым списком.
Наш плагин встраивает в пакет сам инстанс и надёжность:
- Установка = рабочий инстанс. Плагин тянет
searxng/searxng, берёт первый свободный порт, генерируетsettings.yml(JSON включён, лимитер выключен, секрет создаётся один раз), ждёт/healthzи останавливает контейнер при выгрузке. Отдельного шага «сначала подними SearXNG» нет; если инстанс уже есть — один параметрbaseURL, и контейнер не создаётся. - Отказ движков — это ошибка, а не пустой ответ. SearXNG отдаёт
unresponsive_engines; плагин делает второй проход (поfallbackEnginesили без сужающих фильтров) и только потом сообщает, какие движки отказали. - Временные сбои переживаются. Сеть, таймаут попытки, 429/5xx повторяются;
отмена вызывающим — никогда; худшая задержка
(retries + 1) × timeoutMs. - Повторный запрос ничего не стоит. TTL-кэш на 60 секунд: агент часто повторяет один и тот же поиск, второй раз ответ приходит из памяти.
- Результат пригоден для цитирования. Только http(s), сниппет ≤ 800
символов,
publishedAt, ответ движка (answers) какcontent— без придуманного текста.
Движки — это источники, которые SearXNG опрашивает параллельно. В сгенерированных
настройках стоит use_default_settings: true, поэтому включён набор по умолчанию
текущей версии: в категории general это brave, duckduckgo, google cse, wikipedia и
wikidata (плюс вспомогательные вроде currency, dictzone и lingva); google, bing,
qwant и yandex в конфиге присутствуют, но выключены — им нужны ключи или они
нестабильны, и включаются они в settings.yml. Полный список включённых движков
своего инстанса:
curl -s http://127.0.0.1:8888/config | jq -r '.engines[] | select(.enabled) | .name'
Чего мы не делаем: не зовём внешний API, не требуем ключей, не платим за поиск и
не сочиняем сводок — content приходит от движка. Провайдер регистрируется как
searxng, инструмент web_search остаётся штатным; пакет — обычный ESM без
зависимостей и сборки.
Требования
| Требование | Зачем |
|---|---|
Демон Docker, доступный пользователю, который запускает dsh (docker version) |
плагин сам запускает и останавливает контейнер |
| ~400 МБ диска и сеть при первом запуске | однократная загрузка образа |
| Node 22+ (харнесс и так его требует) | fetch, AbortSignal.timeout |
DSH с швом ctx.web (текущие 0.2.x) |
провайдер регистрируется в этот шов; на старых сборках ряд просто ждёт сервис и ничего не делает |
pnpm и git, доступные команде dsh |
dsh plugin ставит пакет через pnpm, спецификация github: требует git |
Нет Docker? Укажите baseURL на существующий инстанс SearXNG — тогда ничего не
запускается (см. Использовать существующий инстанс).
Установка
Пакет объявляет dsh.bundle, поэтому установка одновременно и активирует его:
# из GitHub (поддерживаемый способ)
dsh plugin --profile web add github:svoemesto/dsh-web-search-searxng
# из локальной копии (для разработки)
dsh plugin --profile web add /path/to/dsh-web-search-searxng
Внутри уже запущенного харнесса то же самое делает plugin_manager →
install_bundle с каталогом пакета в качестве target. После этого спросите
агента о чём-нибудь актуальном: web_search отвечает через ваш инстанс.
Первый запуск медленный — один раз. Плагин загружает образ, создаёт контейнер и ждёт
/healthz (до startupTimeoutMs, по умолчанию 90 с). Пока инстанс не готов,
поиск завершается ошибкой «SearXNG instance is not ready: …»; загрузка при этом
продолжается в фоне, и следующий поиск уже проходит.
Что создаётся
| Что | Где |
|---|---|
| Контейнер | dsh-searxng (имя настраивается), --restart unless-stopped, публикация только на 127.0.0.1:<свободный порт>:8080 |
| Настройки | $DSH_HOME/searxng/settings.yml (по умолчанию ~/.dsh/searxng/), пишутся один раз и никогда не перезаписываются |
| Секрет | генерируется в эти настройки; сохраняется между перезапусками и обновлениями |
| Порт | первый свободный порт начиная с port (8888), просматривается до 50 портов — машина, где SearXNG уже занят, продолжает работать |
Контейнер запускается при активации плагина и останавливается при его выгрузке
(завершение харнесса, HMR-перезагрузка, удаление плагина). Сам контейнер
сохраняется, поэтому следующий старт занимает секунды; данные и секрет остаются
в $DSH_HOME/searxng.
Что меняется в вашем профиле
Две строки патча, обе видны в dsh --profile <имя> --dump-config:
- ряд
web-search-searxng— сам провайдер (по умолчаниюport: 8888); - переопределение ряда
web:searchProvider: searxngиfetchProvider: http. Патч заменяетconfigцеликом, поэтомуfetchProviderуказан явно — иначе после установки уweb_fetchне осталось бы провайдера. Если в профиле уже сохранён оверрайд из Settings → Plugins → Web search, он сильнее патча: поиск останется за DeepSeek, хотя плагин установлен.
Контейнер с именем dsh-searxng, созданный не этим плагином, не
переиспользуется и не останавливается: плагин проверяет метку
dsh.plugin=dsh-web-search-searxng, а в чужом случае отказывается работать с
понятным сообщением (задайте другой containerName).
Настройка
Все поля необязательны, у всех есть рабочие значения по умолчанию.
| Поле | По умолчанию | Смысл |
|---|---|---|
baseURL |
(не задано) | Использовать этот существующий инстанс и ничего не запускать. Не задано → свой контейнер. |
port |
8888 |
Первый порт-кандидат для контейнера; занятые пропускаются. |
image |
searxng/searxng:latest |
Любой совместимый образ SearXNG (зеркало, закреплённый тег). |
containerName |
dsh-searxng |
Контейнер, которым владеет плагин. |
dataDir |
$DSH_HOME/searxng |
Каталог на хосте с settings.yml. |
instanceName |
DSH web search |
Подпись в собственном интерфейсе инстанса. |
startupTimeoutMs |
90000 |
Бюджет ожидания готовности (1000–600000). |
timeoutMs |
20000 |
Бюджет одного поиска. |
language |
(не задано) | Передаётся как language; не задано = значение инстанса. |
categories |
(не задано) | general, news, … |
safesearch |
(не задано) | 0, 1, 2. |
timeRange |
(не задано) | day, week, month, year. |
numResults |
(не задано) | Обрезка только когда вызывающий не передал maxResults; реальную границу держит шов. |
apiKey |
(не задано) | Передаётся как preference[api_key] для инстансов за ключом. |
retries |
1 |
Дополнительные попытки после первой, если ошибка повторяемая (0–5). |
retryDelayMs |
300 |
Пауза между попытками. |
fallbackEngines |
[] |
Движки, на которые сужается повторная попытка, например ["brave", "duckduckgo", "google cse"]; пусто — повтор идёт без language, categories, time_range. |
cacheTtlMs |
60000 |
Время жизни закэшированного результата; 0 отключает кэш. |
cacheMaxEntries |
200 |
Сколько поисков держать в кэше, прежде чем вытеснить самый старый. |
Неверные значения останавливают активацию с сообщением, называющим поле — опечатка никогда не превращается в молча неработающий поиск.
Пример строки (в патче бандла или профиля):
- insert:
- id: web-search-searxng
name: dsh-web-search-searxng
config:
port: 8888
language: ru
Использовать существующий инстанс
config:
baseURL: http://127.0.0.1:8888
Тогда контейнер не создаётся и Docker не нужен. Полезно, когда на машине уже работает SearXNG для других задач или инстанс живёт на другом хосте.
Как отображаются результаты
results[]→{url, title?, snippet?, publishedAt?}; записи без абсолютного http(s)-URL отбрасываются, пустые сниппеты опускаются (не выдумываются), сниппеты ограничены 800 символами;- первый непустой ответ движка (
answers[]) становитсяcontent— это реальный вывод движка, а не текст, сочинённый здесь; - нет результатов и непустой список
unresponsive_engines→WEB_PROVIDER_ERRORс именами движков, чтобы «полусломанный» инстанс был виден, а не выглядел как честный пустой ответ.
Ошибки несут коды шва: WEB_PROVIDER_ERROR (HTTP-ошибка, неразбираемое тело,
таймаут, сеть, неготовность) и WEB_ABORTED (отмена вызывающим).
Повторы и кэш
Попытка повторяется, когда сбой временный: сетевая ошибка, таймаут попытки,
HTTP 429/5xx или — случай, которому научил поиск текстов в Караоке — HTTP 200
с нулём результатов и непустым списком unresponsive_engines. Повтор — это не
тот же самый запрос:
- если задан
fallbackEngines, поиск сужается до этих движков (engines=bing,brave,…), а фильтры сохраняются; - иначе запрос повторяется без
language,categories,time_range— именно эти сужения способны обнулить в остальном отвечаемый запрос.
Худшая задержка — (retries + 1) × timeoutMs; с умолчаниями это 40 с, что
укладывается в 60-секундный бюджет инструмента. Отмена вызывающим не повторяется.
Одинаковые поиски кэшируются в памяти процесса: ключ включает запрос, границу
результатов, language, categories, safesearch, timeRange, список
резервных движков и адрес инстанса. Запись живёт cacheTtlMs (по умолчанию
60 с), кэш держит cacheMaxEntries поисков. Он локальный для процесса — без
файлов и без общего состояния между разными хостами Harness — и хранит только
успехи, поэтому сбой не прячет более поздний удачный поиск.
Диагностика
| Симптом | Причина и что делать |
|---|---|
docker is not available on this machine |
Запустите Docker или задайте baseURL. |
SearXNG instance is not ready: … did not become healthy |
Смотрите docker logs dsh-searxng; возможно, ещё идёт первая загрузка образа. |
no free port in 8888..8937 |
Уменьшите port или освободите порт; каждый контейнер занимает один. |
| Поиск даёт странные или скудные результаты | SearXNG агрегирует сторонние движки; docker logs показывает, какие отказали (unresponsive_engines). |
| Два харнесса на одной машине | Они делят имя контейнера и порт; второй переиспользует запущенный. Для изоляции задайте разные containerName + port. |
container "dsh-searxng" exists but was not created by this plugin |
На машине уже есть контейнер с этим именем, но без нашей метки. Задайте другой containerName в конфиге или удалите тот контейнер. |
| Удаление | Снятие бандла останавливает контейнер; чтобы вернуть диск — docker rm -f dsh-searxng и удалить $DSH_HOME/searxng. |
Безопасность
- Контейнер публикуется только на
127.0.0.1; из локальной сети он недоступен. - JSON-вывод включён, а ограничитель частоты выключен — это безопасно только
благодаря привязке к loopback. Если открываете инстанс наружу, включите
ограничитель в
settings.ymlи поставьте его за прокси. - Секретный ключ генерируется локально и хранится только в
settings.yml.
Разработка
Зависимостей и сборки нет: исходники и есть артефакт.
index.js точка входа плагина (apply, жизненный цикл, выбор режима)
src/config.js валидация и значения по умолчанию
src/container.js жизненный цикл docker: inspect / start / pull+create / health / stop
src/settings.js генерация settings.yml (+ одноразовый секрет)
src/provider.js клиент JSON-API SearXNG и маппинг результатов
test/unit.test.mjs юнит-набор (фейковый docker, без сети)
test/docker.smoke.mjs живой контейнер: ensure → search → stop → rm
Текст карточки плагина в DSH — по-русски: харнесс поставляет только локали
zh/en, поэтому русский текст лежит в locale/en.json (и в locale/ru.json),
чтобы отображаться в интерфейсе; английская документация — в README.en.md.
node --test test/unit.test.mjs # конфиг, настройки, жизненный цикл контейнера, маппинг, ошибки
node test/docker.smoke.mjs # настоящий контейнер; нужны Docker и сеть, поднимает dsh-searxng-smoke
CI гоняет юнит-набор на Node 22 и 24 на каждый push
(.github/workflows/ci.yml); проверка с контейнером — ручная
(.github/workflows/docker-smoke.yml).
Релиз
Репозиторий и есть канал распространения: шага «публикация в реестр» нет.
- Поднимите
versionвpackage.jsonи добавьте запись вCHANGELOG.md. - Закоммитьте, поставьте тег
v<version>и запушьте тег. - Создайте GitHub-релиз на этот тег (
gh release create v<version>или через веб-интерфейс).
Ставят — и закрепляют версию — прямо из GitHub:
dsh plugin --profile web add github:svoemesto/dsh-web-search-searxng
dsh plugin --profile web add github:svoemesto/dsh-web-search-searxng#v2.0.1
Чтобы плагин находили в списках сообщества, публикуйте репозиторий с темой
dsh-plugin на GitHub.
Лицензия
MIT — см. LICENSE.
No comments yet. Be the first to write one.