DeepSHPlugins
dsh-plugins#web-search-searxng 專案預覽
GITHUB / COMMUNITY PROJECT

dsh-plugins#web-search-searxng

工具與整合JavaScriptMIT

把 web_search 指向自建的 SearXNG 例項而非原生搜尋路由:自動翻頁直到滿足 maxResults,並在引擎失敗時報告覆蓋不全,避免把稀少的結果誤讀成網上本就沒有內容。

5 StarsCREAIT-nl 更新時間 2026-08-26
訪問 GitHub 倉庫
dsh-plugins#web-search-searxng 專案預覽

專案介紹

中文

把 web_search 指向自建的 SearXNG 例項而非原生搜尋路由:自動翻頁直到滿足 maxResults,並在引擎失敗時報告覆蓋不全,避免把稀少的結果誤讀成網上本就沒有內容。

English

Points web_search at a self-hosted SearXNG instance instead of the native route: pages until maxResults is satisfied, and reports degraded coverage when engines fail so a thin result set is not read as a thin web.

專案資訊與收錄訊號

快照 2026-09-11
作者 / 組織
CREAIT-nl
倉庫
CREAIT-nl/dsh-plugins#web-search-searxng
GitHub Stars
5
Forks
2
開發語言
JavaScript
許可證
MIT
最近推送
2026-08-26
Manifest
資料中檢測到宣告

公開倉庫不自動等於可商用;許可證與當前相容性以原專案文件為準。Manifest 和資料評分不構成安裝安全認證。

資料評分

75/ 100
維護活躍
90
文件與實用資訊
85
生態關注度
35
安裝便捷訊號
100
專案宣告訊號
100

近 16 天仍在更新;README 含完整安裝與使用說明;標準命令一鍵安裝。

評分來自已有資料快照的五維模型,僅作瀏覽參考,不是實際使用評測或安全檢測結論。

安裝與使用

所有專案資料與許可均以對應 GitHub 倉庫為準。

dsh plugin --profile web add @creait/dsh-web-search-searxng

複製命令不會自動執行。請先閱讀倉庫文件,確認環境要求與許可證。

專案截圖

倉庫 README

GitHub 原文

已儲存的倉庫文件快照;文件抓取時間未記錄,可能與上方目錄介紹處於不同版本。安裝前請核對當前倉庫說明。

儲存庫檔案保留原文,指令與授權文字不作自動改寫。

@creait/dsh-web-search-searxng

SearXNG-backed search provider for the DeepSeek Harness ctx.web capability seam. It replaces the stock DeepSeek search route, so the model-facing web_search tool queries a self-hosted SearXNG instance instead.

Install

dsh plugin --profile web add @creait/dsh-web-search-searxng

That mounts the provider — and mounting it changes nothing yet. baseURL has no default, so an unconfigured row reports itself unavailable and ctx.web goes on selecting the shipped DeepSeek provider exactly as it did before. Two keys switch the search over, in your profile patch — $DSH_HOME/profiles/<profile>/cordis.patch.yml, where $DSH_HOME defaults to ~/.dsh:

- id: web
  config:
    searchProvider: searxng

- id: web-search-searxng
  config:
    baseURL: http://localhost:8080   # your SearXNG instance
    maxResults: 10
    engines: bing,google,wikipedia

Both are required, and they fail differently. Without baseURL the provider stays unavailable, so naming it costs you every search with WEB_PROVIDER_CONFIGURED_UNAVAILABLE. Without searchProvider two usable providers are registered and nothing chose between them, which is WEB_PROVIDER_AMBIGUOUS.

Disabling the shipped provider is a separate decision: naming one in searchProvider is what resolves the ambiguity, so web-search-deepseek can stay mounted as a fallback you can switch back to by editing one line.

- id: web-search-deepseek
  disabled: true

If you installed this before it shipped a bundle patch, your profile patch inserts the row by hand. Drop that - insert: block and keep the - id: web-search-searxng config override above: insert appends unconditionally, and a second row would register the same provider id twice, which ctx.web rejects with WEB_DUPLICATE_PROVIDER.

How the peers resolve

The plugin imports @deepseek-ai/dsh-web, @deepseek-ai/cordis and @deepseek-ai/schemastery as bare specifiers, and nothing installs them beside it. They resolve by Node's parent-directory walk from the package's realpath into $DSH_HOME/profiles/node_modules — the flat closure dsh maintains (one symlink per package in the installation's dependency graph, re-pointed on every boot by healProfilesModuleFallback).

Node resolves a symlinked package from its real location, not from the link, so that walk starts wherever the package really lives. Installing from the registry puts it under the profile and the walk lands where it should. Running a checkout instead — a link: into a working tree outside $DSH_HOME — means the walk starts in that tree, so the tree needs the peers reachable from it; the repo root README has the symlink shim that arranges exactly that.

Installing the peers locally instead is not a fix: a second copy of @deepseek-ai/dsh-web means a second WebError class and a second ctx.web service identity. That is what peerDependencies is preventing.

Config

KeyDefaultMeaning
baseURLnoneSearXNG instance, trailing slashes stripped. Empty means unavailable: the row mounts, the seam ignores it
maxResults10Sources returned; reached by paging, see below
maxPages3Ceiling on result pages fetched for one search
engines''Comma-separated engine pin, e.g. bing,google
timeRange''day \week \month \year — freshness window
categories''e.g. news, science
language''e.g. en
userAgentdeepseek-harness/0.0.1 (searxng search)Sent on every request

maxResults costs round-trips

SearXNG has no result-count parameter. It accepts count and silently ignores it — a request for three results still returns ten. The only lever is pageno, so maxResults above one page's worth is reached by paging, bounded by maxPages. A page that contributes nothing new ends the loop rather than paging into repeats, and the common case (maxResults ≤ one page) costs exactly one request.

timeRange needs an engine that implements it

A general-web engine that does not support time filtering returns nothing for a filtered query rather than ignoring the filter — and does not report itself unresponsive. bing gives ten results for nvidia gpu and zero for the same query with time_range=week. Freshness works in the news category, whose engines implement it and populate publishedDate:

config:
  categories: news
  timeRange: week

When a windowed search comes back empty the provider says so in content, rather than letting a filter artefact read as an empty web.

When a search returns nothing

A public-engine SearXNG instance degrades quietly. Engines get CAPTCHA'd, rate-limited and suspended individually, and SearXNG answers with an empty results array plus an unresponsive_engines list that the seam has nowhere to put. The search tool renders that as No results found. — which the model reads as *the web holds nothing*, and it is nothing of the kind.

On 2026-08-23 every engine in one instance's enabled general category was down at once (duckduckgo and startpage on CAPTCHA, brave suspended for too many requests, google returning nothing while not even reporting itself unresponsive). Every general web search on it returned zero results, silently.

So this provider distinguishes the two:

  • No results, no failed engines → an empty result. A genuine no-hit.
  • No results, some failed engines → WEB_PROVIDER_ERROR naming them. The

provider cannot tell an empty web from a broken search, and says so rather than letting the model conclude the stronger thing.

  • Some results, some failed engines → the results, led by a

degraded-coverage warning naming the failed engines.

engines is the lever: pin the ones your instance can actually reach, and add others back as they recover.

The content block

WebSearchResult.content is rendered by the web tool above the source list, which makes it the only channel a provider has for saying something about the search rather than about one result. Every line is conditional, because it is paid for on every call:

  • an answer SearXNG resolved outright, when there is one;
  • the degraded-coverage warning above;
  • the escalation to web_fetch — snippets are index summaries, and a question

about live state (a price, availability, a count, a status) cannot be answered from them. web_search's own guidance says this only when web_fetch is mounted in the *same* composition, which is false on the web surface, where the search tool is per-preset and the fetch half is host-plane;

  • spelling corrections and query suggestions, only when nothing was found.

Verify

scripts/smoke.sh in the repo checkout (it is not in the published tarball) drives the real provider class against a real instance and checks that all three patch rows survived composition:

DSH_PROFILE=web SEARXNG_URL=http://localhost:8080 ./scripts/smoke.sh

What breaks this

The seam this binds to (ctx.web.registerSearchProvider, WebError with WEB_PROVIDER_ERROR / WEB_ABORTED, search() -> { content?, sources, truncated }) is a pre-1.0 internal API with no compatibility guarantee. peerDependencies pins @deepseek-ai/dsh-web to ^0.1.0-rc.7, but the profile sets autoInstallPeers: false, so a mismatch is a pnpm *warning*, not a guard — at runtime the plugin resolves whatever $DSH_HOME/profiles/node_modules points at and loads against it. Treat that warning as the canary and run the smoke test after every dsh update.

Note that dsh's own dependencies float on ^0.1.0-rc.N ranges, so a fresh npm i -g @deepseek-ai/dsh can pull newer internals than the CLI's own version suggests. Verified working against dsh CLI 0.1.0-rc.7 with rc.8 internals.

使用前核對

安裝步驟、相容版本與授權範圍以原專案當前文件為準。Stars 和評分是快照中的導航訊號。

安裝與排錯 · 專案問題反饋

同類專案