Skip to content
dsh.fish
Bundle

dsh-thin-search

Free web search for DeepSeek Harness: zero-cost keyless engines (Bing/DuckDuckGo/SearXNG/AnySearch) with automatic fallback, platform search, web_fetch, and a web settings card. No DeepSeek model or Anthropic-format API calls — plain HTTP only.

Source
loommii
License
MIT
Updated
Updated 12 hours ago

Readme

# dsh-thin-search

[![node](https://img.shields.io/badge/node-%E2%89%A520-339933)](package.json)
[![dsh](https://img.shields.io/badge/dsh-%E2%89%A50.1.5--alpha.1-4c8dff)](https://github.com/deepseek-ai/deepseek-harness)
[![dsh-plugin](https://img.shields.io/badge/dsh--plugin-blue)](https://github.com/deepseek-ai/deepseek-harness)

**DeepSeek Harness 免费搜索插件 —— 零成本、零 API key、零模型调用。**

DSH 官方网页搜索强制依赖 DeepSeek V4 Flash 模型(Anthropic 格式),每次搜索都消耗一次模型调用。本插件用纯 HTTP 抓取免费搜索引擎(Bing / DuckDuckGo / SearXNG / AnySearch),**不调用任何模型、不需要任何 API key**,任何第三方网关/中转用户都能直接联网搜索。

[中文](#中文) · [English](#english)

---

## 中文

### 为什么需要它

DSH 官方自带的网页搜索插件(`dsh-web-search-deepseek`)会强制调用 DeepSeek V4 Flash 模型,并以 Anthropic 格式(`/anthropic/v1/messages` + `web_search_20250305` 工具)发起请求:

- 使用第三方 DeepSeek V4 Flash 网关时,对方不一定提供 Anthropic 协议,搜索直接不可用;
- 即使可用,每次搜索都会消耗一次模型调用,需要额外付费。

**本插件用普通 HTTP 抓取免费搜索引擎,完全不调用模型,任何网络环境都能用,搜索本身零成本。**

### 功能一览

| 功能 | 说明 |
|---|---|
| 🆓 **零成本搜索** | 全部引擎免费、无需 API key、无需注册 |
| 🚫 **零模型调用** | 只发普通 HTTP 请求,不消耗任何 token,不依赖 Anthropic 协议 |
| 🔁 **自动回退** | 任一引擎失败(限流/反爬/网络错误/0 结果)自动尝试下一个,结果注明实际生效引擎 |
| 🌐 **多引擎** | AnySearch(默认)/ Bing / DuckDuckGo / SearXNG,支持自定义 SearXNG 实例 |
| ⚙️ **网页设置页** | 设置 → 搜索引擎:引擎选择、官方搜索切换、增强开关、平台开关、缓存时长 |
| 💬 **弹出式切换** | 聊天框输入 `/thin-search-engine`,点选即切换引擎 |
| 🧪 **引擎测试** | `free_search_test` 工具 + 设置页"测试引擎"按钮 |
| 🕒 **时效** | 按时间范围限定搜索结果,如「最近一周的新闻」 |
| 🔎 **平台搜索** | `platform_search`:GitHub / V2EX / Bilibili / Reddit / Hacker News / Stack Overflow / Wikipedia / npm(公开 API,零 key) |
| 🧠 **搜索增强** | 可选(默认关):LLM 把口语化输入规范化为搜索关键词,改善中文问句效果;⚠️ 开启后消耗 Token;支持自定义提示词 |
| 🚀 **网络代理** | 内置 HTTP/HTTPS 代理(undici ProxyAgent),仅本插件生效,不污染 DSH 全局 |
| 📦 **结果缓存** | LRU 50 条,TTL 0-5 分钟可配,防免费引擎限流 |
| 🤝 **干净集成** | 实现官方 `WebSearchProvider` seam,与官方插件共存,patch 自动接管 `web.searchProvider` |

### 引擎

| id | 引擎 | 说明 |
|---|---|---|
| `anysearch` | AnySearch AI | **默认引擎**,对中文自然语言问句理解最好,免费无 key |
| `bing` | Bing | 免费,速度快,中文优化(`bingMarket: zh-CN`) |
| `searxng` | SearXNG 元搜索 | 免费,多个公开实例自动切换,支持自定义实例 |
| `ddg` | DuckDuckGo HTML | 免费,共享 IP 上可能限流(自动回退) |
| `ddg-lite` | DuckDuckGo Lite | 免费,同上 |

### 安装

```sh
# 从 npm 安装(发布后)
dsh plugin --profile web add dsh-thin-search

# 或从 GitHub 安装
dsh plugin --profile web add github:loommii/dsh-thin-search

# 或本地源码(开发/自用)
git clone git@github.com:loommii/dsh-thin-search.git
dsh plugin --profile web add /path/to/dsh-thin-search
```

安装后**重启 `dsh web`** 生效。插件通过 patch 自动把 `web.searchProvider` 指向本插件,无需手动配置。

#### 卸载

```sh
dsh plugin --profile web remove dsh-thin-search
```

卸载后**重启 `dsh web`**,即可恢复 DSH 官方默认搜索。

> 依赖说明:对 `@deepseek-ai/dsh-settings` 和 `@deepseek-ai/dsh-tools` 使用 `peerDependencies`,DSH 运行时必须使用安装树中的唯一实例。请用 `dsh plugin --profile <profile> add ...` 安装,不要把 DSH 核心包复制进 profile 的本地 `node_modules`。

### 快速开始

安装重启后,在任意会话直接对 agent 说"帮我搜索……"即可 —— 默认引擎 AnySearch,开箱即用。

### 使用

#### 网页设置页(推荐)

打开 **设置 → 搜索引擎**:

- **搜索提供方**:免费引擎(默认)/ 官方 DeepSeek 搜索 —— 切换会写 profile 的 `cordis.patch.yml`,**重启 DSH 后生效**(页面会提示)
- **搜索引擎**:下拉框切换,保存即生效(默认 `anysearch`)
- **搜索增强**:**默认关闭**。开启后会用 LLM 把口语化输入规范化为搜索关键词(如「今天美元兑换日元的汇率多少?」→「美元 日元 汇率」),改善 Bing 等引擎对中文问句的效果。⚠️ **开启后每次搜索会消耗 Token**;关闭则完全免费、零模型调用。支持自定义增强系统提示词:在"搜索增强"卡片中,文本框始终显示当前实际生效的提示词(内置默认或自定义),可直接编辑;"恢复默认"按钮会覆盖为内置默认提示词
- **增强模型**:可选指定用于搜索增强的 LLM 模型(下拉列出 DSH 中已配置的全部模型)。留空 = 跟随 DSH 默认模型;指定模型失败时自动回退默认模型
- **网络代理**:可选填 HTTP/HTTPS 代理地址(如 `http://127.0.0.1:7890`),用于访问需要外网的引擎。V2Ray / Clash 需开启 HTTP 端口;不支持 SOCKS5。留空 = 直连,保存即生效
- **平台搜索**:勾选启用的平台(`platform_search` 工具按此过滤)
- **结果缓存时长**:0 关闭缓存,1-5 分钟
- **测试引擎**:直测当前引擎可用性


#### 聊天框切换引擎(/thin-search-engine)

输入 `/thin-search-engine` 弹出引擎选择窗口(和 `/model` 一样的交互),点选即切换,当前引擎会标记出来。命令只改首选引擎,搜索仍走自动回退链。

#### 配置文件(`~/.dsh/settings.yaml`)

```yaml
thin-search:
  provider: anysearch        # anysearch / bing / ddg / ddg-lite / searxng
  searchEnhance: false        # 搜索增强:LLM 规范化搜索词(默认关,开启消耗 Token)
  enhanceModel: ""            # 增强模型 "provider:model";空 = 跟随 DSH 默认模型
  proxy: ""                   # HTTP/HTTPS 代理,如 http://127.0.0.1:7890;空 = 直连
  bingMarket: zh-CN           # Bing 市场
  region: cn-zh               # DuckDuckGo 区域(可选)
  searxngInstances:           # 自定义 SearXNG 实例(可选)
    - https://your-instance.example
  platforms:                  # platform_search 启用列表
    - github
    - v2ex
    - bilibili
    - reddit
    - hn
    - stackoverflow
    - wikipedia
    - npm
  cacheTtl: 5                 # 结果缓存分钟数(0-5)
```

### 网络代理(国内用户)

Bing / AnySearch 在多数网络环境可直接访问;DuckDuckGo / SearXNG / GitHub 等可能需要代理。本插件内置 HTTP/HTTPS 代理支持(基于 undici `ProxyAgent`),**无需设置系统环境变量**:

- **设置 → 搜索引擎 → 网络代理**:填 `http://127.0.0.1:7890`(V2Ray / Clash 的 HTTP 端口),保存即生效,无需重启
- 或直接写 `~/.dsh/settings.yaml` 的 `thin-search.proxy`
- 留空 = 直连

**作用域**:代理只作用于本插件的 11 处引擎请求,不设置全局 dispatcher、不修改 DSH 全局 fetch,其他插件和 DSH 本身不受影响。

> ⚠️ **已知限制**:DuckDuckGo 对代理/数据中心出口 IP 有反爬(HTTP 202 风控页),公共 SearXNG 实例对代理 IP 普遍限流(HTTP 429)。代理场景下推荐 **Bing / AnySearch**(实测稳定)。如自建 SearXNG 实例,填入 `searxngInstances` 即可。

### 工具

#### free_search_test

对 agent 说"测试一下所有搜索引擎",它会调用 `free_search_test` 工具并报告:

```
Search engine test:
- bing: OK (3 results, e.g. "DeepSeek Harness developer preview: Everything is a plugin")
- anysearch: OK (3 results, ...)
- ddg: FAIL - DuckDuckGo anti-bot challenge (HTTP 202): DDG blocks datacenter/proxy egress IPs. Try Bing instead.
```

#### 时效(advanced_search)

对 agent 说「最近一周的新闻」「最近 3 天的消息」,搜索即自动限定在对应时间段,返回较新的结果。

| 形式 | 示例 | 含义 |
|---|---|---|
| 固定档 | `day` / `week` / `month` / `year` | 1 / 7 / 30 / 365 天 |
| 自定义相对值 | `12h`、`3d`、`2mo`、`1y` | 最近 N 小时/天/月/年 |
| 绝对日期 | `2026-07-01` | 该日期(含)之后发布的结果 |

实现上,支持时间范围的引擎(SearXNG / DDG / DDG Lite)会把天数映射到最近似档位(`≤2 天 → day`,`≤14 天 → week`,`≤90 天 → month`,否则 `year`);Bing / AnySearch 无对应参数会忽略。带 `timeRange` 时支持时间范围的引擎自动排到回退链前面。

#### platform_search(平台搜索)

`platform_search` 支持 GitHub / V2EX / Bilibili / Reddit / Hacker News / Stack Overflow / Wikipedia / npm,全部走公开 API(零 key)。可在设置页勾选启用哪些平台。

### 工作原理

- `lib/index.js`(host 端):实现 `WebSearchProvider`(`id: thin-search` / `available()` / `search()`),统一引擎路由 + 自动回退 + 时间过滤解析 + 结果缓存;注册 `thin-search` settings namespace;提供 `/api/dsh-thin-search-settings` 读写桥(describe / mutate / raw-search,仅限 loopback);注册 `free_search_test`、`platform_search`、`advanced_search` 工具;动态注入引擎清单到系统提示词
- `lib/client.js`(浏览器端):React 设置页(挂 `settings.section` 插槽,设置 → 搜索引擎)+ `/thin-search-engine` 弹出式命令(`commandUi` popupSelect)
- `lib/proxy.js`:undici `ProxyAgent` 封装,单例缓存 + `fetchWithProxy`,仅本插件使用
- `cordis.patch.yml`:插件 loader 配置,patch 同时把 `web.searchProvider` 设为 `thin-search`(保留 `fetchProvider: http`)

### 兼容性

| 项目 | 要求 |
|---|---|
| DSH | `>= 0.1.5-alpha.1`(依赖 `@deepseek-ai/dsh-settings` / `dsh-tools` / `dsh-web` / `dsh-llm` 均 `>= 0.1.5-alpha.1`) |
| Node.js | `>= 20` |

> **从 v0.3.0 升级**:dsh v0.1.5-alpha.1 起,客户端命令契约 `CommandContribution.description` 由 `string` 改为**惰性求值函数** `() => string`(核心运行时在渲染候选行时直接调用 `contribution.description()`)。v0.3.0 传的是字符串,会抛 `TypeError`;由于该异常发生在整个 `command` 斜杠源的 `candidates()` 内部,**全部官方命令会一起从 `/` 菜单消失**。v0.4.0 已修复并同步上移版本下限。

> **同时修正(v0.4.0)**:三个自带工具(`free_search_test` / `platform_search` / `advanced_search`)的 `output.render` 原先返回裸字符串,而核心契约要求返回 `ContentBlock[]`(`{ type: "text", text }`)。已改为符合契约的数组形式。
>
> **说明(避免误读)**:这**不是**一次线上故障修复。这三个工具始终声明了 `finalizeContent`,核心会在结果落库前先套用它把字符串转成 `ContentBlock[]`,因此裸字符串从未真正进入 `tool-result.content`——实测全部会话日志(截至 2026-09-11,269 个会话文件)中,这三个工具共 120 次真实调用(111 成功 / 9 失败),落库结果 100% 是数组,无一例外,也从未出现过相关 TypeError。本次改动属于契约对齐与纵深防御(若日后移除 `finalizeContent`,或核心在规范化之前新增校验,裸字符串才会暴露)。输出文本与改动前逐字节一致,无行为变化。

> **从 v0.1.x 升级**:dsh v0.1.2-alpha.2 对 settings seam 做了破坏性重构——`installSettingsSection` / `settingsNamespace` 被移除,改用 `SettingsProvider.installSection`(见 dsh-v0.1.2-alpha.2+)。插件 v0.2.0 起仅支持 dsh ≥ 0.1.2-alpha.2;仍在 dsh 0.1.1 的用户请使用插件 v0.1.1。

### 与官方插件的关系

- 官方 `web-search-deepseek`(`deepseek-official`)与本插件可共存;`searchProvider` 指向谁就用谁
- 本插件默认通过 patch 接管搜索(无需 key、零成本);**设置页"搜索提供方"开关**一键切回官方(写 profile patch 的 `searchProvider: deepseek-official`,需重启 DSH 生效),也可手动改 `~/.dsh/profiles/<profile>/cordis.patch.yml`
- 本插件**不包含**任何模型调用逻辑,也不读取 `DEEPSEEK_API_KEY` —— 第三方网关用户可直接使用

### License

MIT

---

## English

### Why

DSH's official web-search plugin forces a DeepSeek V4 Flash model call in Anthropic format (`/anthropic/v1/messages` + `web_search_20250305`). If you use a third-party gateway, it may not speak the Anthropic protocol — and even when it does, every search costs a model turn.

**This plugin scrapes free search engines with plain HTTP (Bing / DuckDuckGo / SearXNG / AnySearch). No model calls, no Anthropic protocol, no API keys — searching costs nothing and works with any gateway.**

### Features

- **Zero cost** — all engines free, no keys, no registration
- **Zero model calls** — plain HTTP only; no tokens consumed
- **Auto fallback** — any engine failure (rate limit / anti-bot / network / 0 results) automatically tries the next free engine, with a `Note: ... using ...` marker
- **Engines**: `anysearch` (default, best for Chinese natural-language queries) · `bing` (fast, zh-CN) · `searxng` (multi-instance, custom instances) · `ddg` · `ddg-lite`
- **Search enhancement** — optional (default OFF): rewrites natural-language queries into compact keywords via the current default LLM. ⚠️ Consumes tokens while ON; OFF keeps searching fully free. Supports custom system prompt: the textarea always shows the effective prompt (built-in default or custom), editable; "Reset to default" overwrites with the built-in default prompt
- **Web settings page** — Settings → Search Engine
- **Popup command** — `/thin-search-engine` in chat
- **Engine test** — `free_search_test` tool + "Test engine" button
- **Fresh results** — ask for "last week's news" or "the latest updates" and get recent results instead of stale ones
- **Platform search** — GitHub / V2EX / Bilibili / Reddit / Hacker News / Stack Overflow / Wikipedia / npm
- **HTTP(S) proxy** — built-in (undici ProxyAgent), plugin-scoped only; works with V2Ray / Clash HTTP ports; no SOCKS5
- **Result caching** — LRU 50, TTL 0-5 min
- **web_fetch preserved** — official `dsh-web-fetch-http` stays enabled
- **Clean integration** — official `WebSearchProvider` seam; coexists with official plugins

### Install

```sh
dsh plugin --profile web add dsh-thin-search        # from npm (once published)
dsh plugin --profile web add github:loommii/dsh-thin-search   # from GitHub
```

Restart `dsh web` after installing. The plugin's patch points `web.searchProvider` to itself automatically.

To uninstall:

```sh
dsh plugin --profile web remove dsh-thin-search
```

Restart `dsh web` afterwards to restore DSH's official search.

### Usage

Settings page: **Settings → Search Engine** (provider switch, engine, enhancement, platforms, cache TTL, proxy). Chat command: `/thin-search-engine`. Config file: `~/.dsh/settings.yaml` under `thin-search:` (see the Chinese section above for the full schema).

### Proxy notes

Set `thin-search.proxy` (e.g. `http://127.0.0.1:7890`) in the settings page or `~/.dsh/settings.yaml` — no env vars needed. HTTP/HTTPS only; no SOCKS5. Only this plugin's requests go through the proxy; DSH and other plugins are untouched.

> **Known limits**: DuckDuckGo anti-bot (HTTP 202) and public SearXNG rate limits (HTTP 429) apply to proxy/datacenter egress IPs. Prefer **Bing / AnySearch** behind a proxy. Self-hosted SearXNG instances can be added via `searxngInstances`.

### Compatibility

| Item | Requirement |
|---|---|
| DSH | `>= 0.1.5-alpha.1` |
| Node.js | `>= 20` |

> **Upgrading from v0.3.0**: as of dsh v0.1.5-alpha.1, the client command contract `CommandContribution.description` changed from `string` to a **lazy thunk** `() => string` (the core runtime calls `contribution.description()` while rendering menu rows). v0.3.0 passed a string, which threw a `TypeError`; because that throw happens inside the whole `command` slash source's `candidates()`, **every official command disappeared from the `/` menu**. Fixed in v0.4.0, with the version floor raised accordingly.

> **Also corrected in v0.4.0**: the three bundled tools (`free_search_test` / `platform_search` / `advanced_search`) returned a bare string from `output.render`, while the core contract requires `ContentBlock[]` (`{ type: "text", text }`). They now return conformant arrays.
>
> **Note (to prevent misreading)**: this was **not** a production bug fix. All three tools have always declared `finalizeContent`, which the core applies before a result is persisted, converting the string to `ContentBlock[]` — so the bare string never actually reached `tool-result.content`. Across every session log on the author's machine (269 session files, as of 2026-09-11) these three tools were invoked 120 times (111 succeeded, 9 failed), and 100% of the persisted results are arrays; the related `TypeError` never occurred. This change is contract alignment and defence in depth (it would only surface if `finalizeContent` were removed, or if the core added a check before normalization). Rendered text is byte-identical to before — no behavioural change.

> **Upgrading from v0.1.x**: dsh v0.1.2-alpha.2 shipped a breaking settings-seam refactor — the module-level `installSettingsSection` / `settingsNamespace` helpers were removed in favor of `SettingsProvider.installSection`. Plugin v0.2.0 requires dsh ≥ 0.1.2-alpha.2; stay on plugin v0.1.1 if you are still on dsh 0.1.1.

### License

MIT

Install

dsh plugin --profile web add github:loommii/dsh-thin-search

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source