dsh-web-search-pro 0.2.1 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +227 -0
- package/README.en.md +117 -0
- package/README.md +72 -578
- package/assets/skills/dsh-web-search-pro/SKILL.md +13 -12
- package/assets/skills/dsh-web-search-pro/references/config.md +36 -0
- package/assets/skills/dsh-web-search-pro/references/sources.md +13 -7
- package/assets/skills/dsh-web-search-pro/references/troubleshooting.md +3 -1
- package/catalog/sources.v1.json +16 -14
- package/lib/actions/approval.js +50 -1
- package/lib/actions/approval.js.map +1 -1
- package/lib/actions/config.js +274 -0
- package/lib/actions/config.js.map +1 -0
- package/lib/actions/errors.js +8 -1
- package/lib/actions/errors.js.map +1 -1
- package/lib/actions/format.js +10 -0
- package/lib/actions/format.js.map +1 -1
- package/lib/actions/index-view.js +2 -2
- package/lib/actions/index-view.js.map +1 -1
- package/lib/actions/read.js +7 -3
- package/lib/actions/read.js.map +1 -1
- package/lib/actions/registry.js +3 -1
- package/lib/actions/registry.js.map +1 -1
- package/lib/actions/run.js +2 -2
- package/lib/actions/run.js.map +1 -1
- package/lib/actions/schema.js +18 -0
- package/lib/actions/schema.js.map +1 -1
- package/lib/actions/search.js +7 -2
- package/lib/actions/search.js.map +1 -1
- package/lib/actions/sources.js +76 -18
- package/lib/actions/sources.js.map +1 -1
- package/lib/actions/types.js +19 -1
- package/lib/actions/types.js.map +1 -1
- package/lib/browser-access.js +25 -0
- package/lib/browser-access.js.map +1 -1
- package/lib/catalog/recommend.js +11 -3
- package/lib/catalog/recommend.js.map +1 -1
- package/lib/catalog/schema.js +3 -1
- package/lib/catalog/schema.js.map +1 -1
- package/lib/cli/builtin-specs.js +57 -22
- package/lib/cli/builtin-specs.js.map +1 -1
- package/lib/cli/chain.js +13 -12
- package/lib/cli/chain.js.map +1 -1
- package/lib/cli/chains-spec.js +31 -11
- package/lib/cli/chains-spec.js.map +1 -1
- package/lib/cli/engine.js +19 -3
- package/lib/cli/engine.js.map +1 -1
- package/lib/cli/opencli-sites.js +83 -0
- package/lib/cli/opencli-sites.js.map +1 -0
- package/lib/cli/opencli.js +65 -17
- package/lib/cli/opencli.js.map +1 -1
- package/lib/cli/probe.js +1 -1
- package/lib/cli/probe.js.map +1 -1
- package/lib/cli/runner.js +217 -27
- package/lib/cli/runner.js.map +1 -1
- package/lib/cli/spec.js +100 -20
- package/lib/cli/spec.js.map +1 -1
- package/lib/cli/time-text.js +79 -0
- package/lib/cli/time-text.js.map +1 -0
- package/lib/cli/trial.js +57 -0
- package/lib/cli/trial.js.map +1 -0
- package/lib/client/SettingsCard.js +14 -32
- package/lib/client/SettingsCard.js.map +1 -1
- package/lib/client/agent-mark.js +27 -0
- package/lib/client/agent-mark.js.map +1 -0
- package/lib/client/card-frame.js +118 -0
- package/lib/client/card-frame.js.map +1 -0
- package/lib/client/cli-form.js +257 -0
- package/lib/client/cli-form.js.map +1 -0
- package/lib/client/field-docs.js +130 -0
- package/lib/client/field-docs.js.map +1 -0
- package/lib/client/field-keys.js +81 -0
- package/lib/client/field-keys.js.map +1 -0
- package/lib/client/field-rows.js +71 -0
- package/lib/client/field-rows.js.map +1 -0
- package/lib/client/fill.js +3 -0
- package/lib/client/fill.js.map +1 -0
- package/lib/client/form-specs.js +13 -1
- package/lib/client/form-specs.js.map +1 -1
- package/lib/client/form.js +282 -24
- package/lib/client/form.js.map +1 -1
- package/lib/client/group-advanced.js +23 -0
- package/lib/client/group-advanced.js.map +1 -0
- package/lib/client/group-agent.js +28 -0
- package/lib/client/group-agent.js.map +1 -0
- package/lib/client/group-cli.js +136 -0
- package/lib/client/group-cli.js.map +1 -0
- package/lib/client/group-common.js +29 -0
- package/lib/client/group-common.js.map +1 -0
- package/lib/client/group-judge.js +67 -0
- package/lib/client/group-judge.js.map +1 -0
- package/lib/client/group-order.js +201 -0
- package/lib/client/group-order.js.map +1 -0
- package/lib/client/group-runtime.js +33 -0
- package/lib/client/group-runtime.js.map +1 -0
- package/lib/client/group-sources.js +83 -0
- package/lib/client/group-sources.js.map +1 -0
- package/lib/client/groups-basic.js +36 -0
- package/lib/client/groups-basic.js.map +1 -0
- package/lib/client/index.js.map +1 -1
- package/lib/client/json-list.js +50 -0
- package/lib/client/json-list.js.map +1 -0
- package/lib/client/locales.js +666 -219
- package/lib/client/locales.js.map +1 -1
- package/lib/client/order.js +192 -0
- package/lib/client/order.js.map +1 -0
- package/lib/client/overlay-ops.js +46 -0
- package/lib/client/overlay-ops.js.map +1 -0
- package/lib/client/sources-table.js +1 -0
- package/lib/client/sources-table.js.map +1 -1
- package/lib/client/styles.js +74 -47
- package/lib/client/styles.js.map +1 -1
- package/lib/client/text-view.js +98 -0
- package/lib/client/text-view.js.map +1 -0
- package/lib/client/ui-kit.js +120 -0
- package/lib/client/ui-kit.js.map +1 -0
- package/lib/client/validators.js +8 -1
- package/lib/client/validators.js.map +1 -1
- package/lib/client.js +7313 -1571
- package/lib/client.js.map +1 -1
- package/lib/config-fillers.js +53 -0
- package/lib/config-fillers.js.map +1 -0
- package/lib/config-groups.js +49 -0
- package/lib/config-groups.js.map +1 -0
- package/lib/config.js +84 -108
- package/lib/config.js.map +1 -1
- package/lib/default-chain.js +66 -0
- package/lib/default-chain.js.map +1 -0
- package/lib/deps.js +1 -1
- package/lib/deps.js.map +1 -1
- package/lib/engines.js +3 -3
- package/lib/engines.js.map +1 -1
- package/lib/fetch.js +23 -4
- package/lib/fetch.js.map +1 -1
- package/lib/fiber-config.js +5 -0
- package/lib/fiber-config.js.map +1 -0
- package/lib/index.js +36 -7
- package/lib/index.js.map +1 -1
- package/lib/overlay-types.js +88 -0
- package/lib/overlay-types.js.map +1 -0
- package/lib/overlay.js +683 -0
- package/lib/overlay.js.map +1 -0
- package/lib/pipeline/candidates.js +27 -1
- package/lib/pipeline/candidates.js.map +1 -1
- package/lib/pipeline/compile.js +63 -0
- package/lib/pipeline/compile.js.map +1 -1
- package/lib/pipeline/confidence.js +64 -0
- package/lib/pipeline/confidence.js.map +1 -0
- package/lib/pipeline/fusion.js +50 -3
- package/lib/pipeline/fusion.js.map +1 -1
- package/lib/pipeline/plan.js +2 -2
- package/lib/pipeline/plan.js.map +1 -1
- package/lib/pipeline/render.js +20 -2
- package/lib/pipeline/render.js.map +1 -1
- package/lib/pipeline/run.js +17 -5
- package/lib/pipeline/run.js.map +1 -1
- package/lib/pipeline/sources-spec.js +19 -3
- package/lib/pipeline/sources-spec.js.map +1 -1
- package/lib/providers/builtin.js +3 -0
- package/lib/providers/builtin.js.map +1 -1
- package/lib/providers/deepseek.js +348 -0
- package/lib/providers/deepseek.js.map +1 -0
- package/lib/providers/http.js.map +1 -1
- package/lib/providers/keyed-meta.js +7 -0
- package/lib/providers/keyed-meta.js.map +1 -1
- package/lib/providers/platforms.js +18 -17
- package/lib/providers/platforms.js.map +1 -1
- package/lib/providers/registry.js.map +1 -1
- package/lib/restart.js +47 -0
- package/lib/restart.js.map +1 -0
- package/lib/router.js +145 -26
- package/lib/router.js.map +1 -1
- package/lib/rpc-types.js +15 -0
- package/lib/rpc-types.js.map +1 -0
- package/lib/rpc.js +118 -0
- package/lib/rpc.js.map +1 -0
- package/lib/tools.js +10 -4
- package/lib/tools.js.map +1 -1
- package/lib/types/actions/approval.d.ts +11 -0
- package/lib/types/actions/config.d.ts +54 -0
- package/lib/types/actions/format.d.ts +9 -2
- package/lib/types/actions/run.d.ts +1 -0
- package/lib/types/actions/schema.d.ts +7 -1
- package/lib/types/actions/sources.d.ts +29 -3
- package/lib/types/actions/types.d.ts +28 -2
- package/lib/types/browser-access.d.ts +17 -0
- package/lib/types/catalog/recommend.d.ts +4 -0
- package/lib/types/catalog/schema.d.ts +2 -0
- package/lib/types/cli/builtin-specs.d.ts +11 -3
- package/lib/types/cli/chains-spec.d.ts +12 -3
- package/lib/types/cli/engine.d.ts +10 -3
- package/lib/types/cli/opencli-sites.d.ts +24 -0
- package/lib/types/cli/opencli.d.ts +42 -3
- package/lib/types/cli/runner.d.ts +47 -8
- package/lib/types/cli/spec.d.ts +101 -8
- package/lib/types/cli/time-text.d.ts +12 -0
- package/lib/types/cli/trial.d.ts +19 -0
- package/lib/types/client/SettingsCard.d.ts +17 -1
- package/lib/types/client/agent-mark.d.ts +22 -0
- package/lib/types/client/card-frame.d.ts +35 -0
- package/lib/types/client/cli-form.d.ts +97 -0
- package/lib/types/client/field-docs.d.ts +15 -0
- package/lib/types/client/field-keys.d.ts +41 -0
- package/lib/types/client/field-rows.d.ts +70 -0
- package/lib/types/client/fill.d.ts +2 -0
- package/lib/types/client/form-specs.d.ts +12 -3
- package/lib/types/client/form.d.ts +79 -1
- package/lib/types/client/group-advanced.d.ts +9 -0
- package/lib/types/client/group-agent.d.ts +20 -0
- package/lib/types/client/group-cli.d.ts +52 -0
- package/lib/types/client/group-common.d.ts +12 -0
- package/lib/types/client/group-judge.d.ts +17 -0
- package/lib/types/client/group-order.d.ts +26 -0
- package/lib/types/client/group-runtime.d.ts +15 -0
- package/lib/types/client/group-sources.d.ts +46 -0
- package/lib/types/client/groups-basic.d.ts +18 -0
- package/lib/types/client/index.d.ts +8 -0
- package/lib/types/client/json-list.d.ts +21 -0
- package/lib/types/client/locales.d.ts +1079 -200
- package/lib/types/client/order.d.ts +102 -0
- package/lib/types/client/overlay-ops.d.ts +37 -0
- package/lib/types/client/styles.d.ts +56 -41
- package/lib/types/client/text-view.d.ts +22 -0
- package/lib/types/client/ui-kit.d.ts +200 -0
- package/lib/types/config-fillers.d.ts +9 -0
- package/lib/types/config-groups.d.ts +23 -0
- package/lib/types/config.d.ts +263 -225
- package/lib/types/default-chain.d.ts +63 -0
- package/lib/types/engines.d.ts +29 -1
- package/lib/types/fetch.d.ts +12 -3
- package/lib/types/fiber-config.d.ts +18 -0
- package/lib/types/index.d.ts +164 -150
- package/lib/types/overlay-types.d.ts +98 -0
- package/lib/types/overlay.d.ts +141 -0
- package/lib/types/pipeline/candidates.d.ts +17 -0
- package/lib/types/pipeline/compile.d.ts +11 -0
- package/lib/types/pipeline/confidence.d.ts +35 -0
- package/lib/types/pipeline/fusion.d.ts +23 -1
- package/lib/types/pipeline/render.d.ts +16 -0
- package/lib/types/pipeline/run.d.ts +2 -1
- package/lib/types/pipeline/sources-spec.d.ts +5 -2
- package/lib/types/pipeline/types.d.ts +37 -0
- package/lib/types/providers/deepseek.d.ts +130 -0
- package/lib/types/providers/http.d.ts +2 -1
- package/lib/types/providers/keyed-meta.d.ts +5 -0
- package/lib/types/providers/registry.d.ts +7 -0
- package/lib/types/restart.d.ts +52 -0
- package/lib/types/router.d.ts +57 -10
- package/lib/types/rpc-types.d.ts +79 -0
- package/lib/types/rpc.d.ts +74 -0
- package/lib/types/tools.d.ts +6 -0
- package/lib/types/util.d.ts +10 -0
- package/lib/util.js +10 -1
- package/lib/util.js.map +1 -1
- package/package.json +53 -44
- package/scripts/check-client-bundle.mjs +1 -1
- package/scripts/check-docs-links.mjs +107 -0
- package/scripts/cli-login-check.mjs +214 -0
- package/LOGIN.md +0 -161
- package/lib/client/RubricEditor.js +0 -47
- package/lib/client/RubricEditor.js.map +0 -1
- package/lib/client/fields.js +0 -31
- package/lib/client/fields.js.map +0 -1
- package/lib/types/client/RubricEditor.d.ts +0 -6
- package/lib/types/client/fields.d.ts +0 -65
package/README.md
CHANGED
|
@@ -1,621 +1,115 @@
|
|
|
1
1
|
# dsh-web-search-pro
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](./README.en.md) | 中文
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)的增强型网页搜索插件:多引擎搜索、平台搜索、持久化缓存和证据包,常驻上下文只有两个工具。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **证据包模式**:给 `search.run` 一句任务目标,返回的不是结果列表,而是按需求评分后的摘录和未满足的缺口,占用的上下文少得多。
|
|
8
|
+
- **多引擎 + 平台来源**:ddg / bing / Exa / Jina 等网页引擎加 GitHub、B站、YouTube、小红书、知乎、Reddit、X、RSS 等平台,统一的探测、冷却、回退与历史;DeepSeek 搜索 / 抓取是“常用”来源(按请求计费)。
|
|
9
|
+
- **登录由你自己完成**:需要登录的平台优先走 `opencli`(用你日常 Chrome 的登录态),插件没有登录窗口,也不读 Cookie。
|
|
10
|
+
- **两个常驻工具**:`web_index`(目录)→ `web_call`(执行),27 个动作按需披露,约 800 字符常驻。
|
|
11
|
+
- **持久化**:SQLite + 内存 LRU 双层缓存、查询历史、可再导入的站点提取规则。
|
|
12
|
+
- **可视化设置**:一张设置卡片管理搜索顺序、Key、自定义 CLI 和评分模型,agent 的改动以覆盖层形式出现,可一键采纳或撤销。
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|---|---|---|
|
|
11
|
-
| `0.1.11` 及更早的维护版本 | `dsh-v0.1.1-rc.2` | 旧基线;不再维护,不与新插件混装 |
|
|
12
|
-
| `0.1.15` | `dsh-v0.1.7-rc.2` + Browser `0.1.15` | 精确锁定该宿主版本;**仍停留在 DSH 0.1.x 宿主或 dsh-browser 0.1.x 的用户请使用这一版**(后续不再更新) |
|
|
13
|
-
| `0.1.17` | `dsh-v0.1.7-rc.2` ~ `dsh-v0.2.0-rc.2` + Browser `0.1.17` | 过渡版本,已不再演进;新功能不会回到这条线 |
|
|
14
|
-
| `0.2.0`(正式版) | **只支持 `dsh-v0.2.0-rc.2` 这一条线**(peer `>=0.2.0-rc.2 <0.2.1-0`)+ 可选 `@anweat/dsh-browser ^0.2.0` | 第一个 0.2.0 线版本,**破坏性变更**(见 [CHANGELOG.md](./CHANGELOG.md));711 项单元测试、94 项 bench 测试,`pnpm run test:peers` 两种解析模式检查,真实 Host(DSH 0.2.0-rc.2)验收 |
|
|
15
|
-
| `0.2.1` | 同 `0.2.0`(peer `>=0.2.0-rc.2 <0.2.1-0` + 可选 `@anweat/dsh-browser ^0.2.0`) | 独立 CLI 适配层与平台后端链,无破坏性变更(见 [CHANGELOG.md](./CHANGELOG.md));774 项单元测试、94 项 bench 测试,真实 Host(DSH 0.2.0-rc.2)验收 |
|
|
16
|
-
|
|
17
|
-
**支持范围**:DSH `>=0.2.0-rc.2 <0.2.1-0`,可选配套浏览器插件 `@anweat/dsh-browser ^0.2.0`(即 0.2.x,不含 0.3)。
|
|
14
|
+
## 兼容性
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- 其他一切(网页搜索、证据管线、非浏览器平台、缓存、历史)照常工作。
|
|
16
|
+
| 项 | 范围 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| dsh-web-search-pro | `0.2.4` |
|
|
19
|
+
| DSH 宿主(peer) | `>=0.2.0-rc.2 <0.2.1-0 \|\| >=0.2.1-rc.2 <0.2.2-0 \|\| >=0.2.2-rc.1 <0.2.3-0`;实测 `0.2.0-rc.2`、`0.2.1-rc.3`、`0.2.1-rc.4`、`0.2.1-rc.5`、`0.2.2-rc.1` |
|
|
20
|
+
| cordis(peer) | `~4.0.4 \|\| ~4.1.0`(0.2.2 线宿主带 4.1.x) |
|
|
21
|
+
| 可选配套 `@anweat/dsh-browser` | `^0.2.0`(0.2.1 线宿主上必须是 `0.2.2` 起;0.2.2 线宿主上请用 `0.2.3` 或更新) |
|
|
22
|
+
| DSH 0.1.x 宿主或 dsh-browser 0.1.x | 固定使用 `dsh-web-search-pro@0.1.15`,不再更新 |
|
|
27
23
|
|
|
28
|
-
**
|
|
24
|
+
**0.2.2 线的设置卡片通道**:宿主 0.2.2-rc.1 的 `connection.rpc.handle()` 在 cordis 4.1 下对外部插件抛错(根因在宿主 `dsh-client-connection` 的 `register()` 读 `owner.webServer`,而 cordis 4.1 按服务的定义处检查 `inject`)。插件经一个自带 `webServer` 的派生上下文调用 `handle`(`ctx.extend({ webServer: ctx.get('webServer') })`),只用公开的 Context API,不依赖宿主的私有方法;三条宿主线(0.2.0-rc.2、0.2.1-rc.5、0.2.2-rc.1)用同一写法。做法来自 [anweat/dsh-browser#49](https://github.com/anweat/dsh-browser/issues/49)(@shuaigekda)。挂载失败时插件在控制台打一行警告并照常运行(卡片的实时状态、agent 修改面板、CLI 试运行不可用)。详见 [CHANGELOG](./CHANGELOG.md) 与 [验证记录](docs/validation/2026-11-05-dsh-0.2.2.md)。
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
- 上界是 `<0.2.1-0` 而不是 `<0.2.1`:后者放行 `0.2.1-alpha.1` 这类 0.2.1 的预发布。收口后,若 0.2.1 真出现破坏,会在**组装期**直接报 `is incompatible with dsh 0.2.1` 并点名,而不是拖到用户机器上变成运行期怪错(`0.1.5 → 0.1.7` 曾一次性打断所有按 `0.1.5-alpha.1` 构建的插件)。
|
|
32
|
-
- 范围只表示「测过哪些」,不等于承诺不破坏;真正的防线是每换一个 DSH 版本重跑一遍这套验证。`pnpm run test:peers`(已并入 `pnpm verify`)用两种解析模式逐个断言受管 peer:`0.2.0-rc.2` 必须通过,`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.1-alpha.1` 必须被拒绝;并自带 `--selftest` 已知行为自校验。
|
|
26
|
+
历史版本的兼容表见 [CHANGELOG](./CHANGELOG.md#兼容与发布通道历史),peer 范围的写法见 [兼容范围](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/compatibility.md)。
|
|
33
27
|
|
|
34
28
|
## 安装
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
# 默认安装(面向 dsh-v0.2.0-rc.2 宿主;浏览器插件可选,需要读取渲染页 / 浏览器平台时一起装):
|
|
38
|
-
dsh plugin --profile web add @anweat/dsh-browser@0.2.0 dsh-web-search-pro@0.2.1
|
|
39
|
-
# 不需要浏览器能力时,只装本插件即可:
|
|
40
|
-
dsh plugin --profile web add dsh-web-search-pro@0.2.1
|
|
41
|
-
# 或本地目录 / tarball:
|
|
42
|
-
dsh plugin --profile web add ../dsh-browser ./dsh-web-search-pro
|
|
43
|
-
# 重启(web profile 关闭了 HMR):
|
|
44
|
-
dsh --profile web
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
> 两个插件都必须是 profile 的直接依赖:DSH 只激活直接依赖的 bundle layer,且标准 profile 可能设置 `autoInstallPeers: false`。不要只安装 Web Search Pro 后依赖 peer 自动补齐。
|
|
48
|
-
> pnpm 11 若拦截 Browser 的 OpenCLI 依赖安装脚本,会要求在 profile 的 `pnpm-workspace.yaml` 中明确决定 `allowBuilds: { '@jackwener/opencli': false }`(或在确实需要安装期下载 adapter 时自行审核后设为 `true`),再重试安装;隔离 profile 中禁用脚本后,已发布 Browser 的 OpenCLI 入口仍可运行。
|
|
49
|
-
> 本版只支持 `dsh-v0.2.0-rc.2` 与 Browser `^0.2.0`,宿主或浏览器插件仍在 0.1.x 线上的,请安装 `dsh-web-search-pro@0.1.15`。若你的 harness 是本地源码 checkout,版本号可能有出入——用
|
|
50
|
-
> `dsh plugin --profile web add ./<path>` 并在 profile 的 `pnpm-workspace.yaml`
|
|
51
|
-
> 里对齐版本后重装即可。
|
|
52
|
-
|
|
53
|
-
## 从旧版本升级
|
|
54
|
-
|
|
55
|
-
先确认宿主是 `dsh-v0.2.0-rc.2`,浏览器插件(如果装了)是 `@anweat/dsh-browser@0.2.0`;两者都需要作为 profile 的直接依赖。
|
|
30
|
+
**DSH CLI**(装完重启 profile):
|
|
56
31
|
|
|
57
32
|
```bash
|
|
58
|
-
|
|
33
|
+
# 只要搜索:
|
|
34
|
+
dsh plugin --profile web add dsh-web-search-pro@0.2.4
|
|
35
|
+
# 还要读取渲染页 / 浏览器平台:连同可选的浏览器插件一起装(两者都必须是 profile 的直接依赖)
|
|
36
|
+
dsh plugin --profile web add @anweat/dsh-browser@0.2.3 dsh-web-search-pro@0.2.4
|
|
37
|
+
dsh --profile web
|
|
59
38
|
```
|
|
60
39
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
> **破坏性变更(工具面)**:旧的 `web_search_pro`、`web_fetch_pro` 等 11 个工具名不再注册,也没有兼容包装。旧会话里对它们的调用会失败;把调用改成对应动作即可,对照表见[旧工具到新动作](#旧工具到新动作)。模型在 `web_call` 里写旧工具名会得到新动作名和翻译后的参数,`web_index()` 根目录也列出同一张对照。已存储的数据(历史、页面、规则、证据、账本)与 `ctx.web` provider 不受影响。其他不兼容变化与新功能见 [CHANGELOG.md](./CHANGELOG.md)。
|
|
40
|
+
**DSH Desktop**:Desktop 的 profile 由应用管理,CLI 不能操作它。打开**插件**页 → **Add plugin**,填包名(可带版本,如 `dsh-web-search-pro@0.2.4`,写法同上面 `add` 后面那段)→ Install;对话框里还能选 **Registry**。非官方插件升级 = 先卸载再重装;国内镜像(如 npmmirror)可能晚同步,装不到新版本时把 Registry 换成 `https://registry.npmjs.org`。
|
|
64
41
|
|
|
65
|
-
|
|
42
|
+
**本地目录 / tarball(开发用)**:`dsh plugin --profile web add ../dsh-browser ./dsh-web-search-pro`。
|
|
66
43
|
|
|
67
|
-
|
|
68
|
-
2. `web_call({action:"sources.status"})`:确认搜索、CLI、Agent Reach 与浏览器后端是否 ready(浏览器应显示 `ready`,不是 `legacy (unsupported)`)。
|
|
69
|
-
3. 打开 `插件 → 已安装` 中两个 bundle 各自的详情页,确认配置表单都已加载;浏览器表单负责自由度、运行时、OpenCLI 与调用缓冲。
|
|
44
|
+
更多(新用户第一次安装的注意事项、排障)见 [安装与首次使用](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/install.md);升级见 [CHANGELOG](./CHANGELOG.md#从旧版本升级)。
|
|
70
45
|
|
|
71
|
-
|
|
46
|
+
## 第一次使用
|
|
72
47
|
|
|
73
|
-
|
|
48
|
+
- **设置卡片**:`设置 → 插件 → 插件配置 → Web Search Pro`。顶部的状态摘要显示实际尝试顺序的默认引擎链、已配置 Key 的来源数、DeepSeek 是否就绪、待重启项等。
|
|
49
|
+
- **查看后端**:让模型调用 `web_call` 的 `sources.status`,每个来源 / 平台的就绪状态和被跳过的原因都在里面。
|
|
50
|
+
- **来源与密钥**:网页搜索和证据管线装完即用(ddg / bing 免 Key)。有 `DEEPSEEK_API_KEY` 时,默认搜索会先用**按请求计费**的 DeepSeek 搜索;不想用就在 `sources.disabled` 里写 `deepseek`。Exa、Jina、博查、GitHub 等 Key 在设置卡片的“来源”分组里写入(存进 DSH Credentials,不读回明文)。
|
|
51
|
+
- **开箱默认值**:默认关闭 Jev(`evidence.jevMode: off`,证据评分用本地规则,不调用任何模型,也不需要任何模型 Key);只有配置了博查 Key 才会使用博查。
|
|
52
|
+
- **可选依赖**:浏览器插件 `@anweat/dsh-browser`(`read.snapshot`、`read.fetch mode=playwright`、搜索页类平台;有 Chrome / Edge 就直接用,不用下载 Chromium);`npm i -g @jackwener/opencli` 加它的 Browser Bridge 扩展(小红书 / 知乎 / Reddit / X,用你日常 Chrome 的登录态,见 [平台登录态](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/platform-login.md));bili、yt-dlp、gh 等 CLI 由 `sources.deps` 检测。
|
|
74
53
|
|
|
75
|
-
##
|
|
54
|
+
## 快速示例
|
|
76
55
|
|
|
77
|
-
|
|
56
|
+
模型通过 `web_call({action, args})` 调用。证据包(传 `task` 或 `profile`):
|
|
78
57
|
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
"DeepSeek Harness community feedback",指定 exa、fresh=true、返回 8 条来源。
|
|
58
|
+
```json
|
|
59
|
+
{"action":"search.run","args":{"query":"node:sqlite busy timeout","task":"how to set a busy timeout in node:sqlite","profile":"docs_code"}}
|
|
82
60
|
```
|
|
83
61
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
模型通常不需要先翻目录:`web_index()` 根目录直接给出搜索与读取两个最常用调用,随包的 skill `dsh-web-search-pro` 带有证据模式的工作示例;没有 skill 服务时根目录附一段精简指南。
|
|
87
|
-
|
|
88
|
-
| 情形 | 推荐入口 | 说明 |
|
|
89
|
-
|---|---|---|
|
|
90
|
-
| 日常网页搜索 | `search.run` | 默认按配置顺序回退;需要强制刷新时传 `fresh=true` |
|
|
91
|
-
| 语义研究、社区观点 | `search.run` + `engines=exa` | 有 API Key 时走原生 Exa API;只有 Exa MCP 连接时自动经 `mcporter` 回退 |
|
|
92
|
-
| 已知 URL 的批量正文 | `read.contents` | 直接调用 Exa `/contents`,必须配置 `EXA_API_KEY` |
|
|
93
|
-
| GitHub/B站/Reddit 等平台 | `search.run` + `platform=…` | Reddit 等 OpenCLI 平台需要 Chrome 扩展在线;中文受限站点使用 AuthProfile |
|
|
94
|
-
| 登录后页面或私有论坛 | `browserBindings` + AuthProfile | Cookie 保存在本地 storageState,按域名授权,默认只读 |
|
|
95
|
-
| 页面改版、懒加载 | `platformRules` 或 RulePack | 优先改选择器;需要等待/点击/滚动时再使用有界 RulePack |
|
|
96
|
-
| 模型生成多步页面操作 | `browser_call` → `automation.run_recipe` | 只读步骤直接运行;页面交互按 dsh-browser 的 `automationMode` 决定拒绝/审批/直通 |
|
|
97
|
-
| 外部模型生成油猴脚本 | `script.validate` → `script.run_userscript` | 强制 `@match`、`@grant none`、禁用 `@require`;仅 `unrestricted` 跳过审批 |
|
|
98
|
-
| 有限泛爬取 | `crawl.crawl` | 匿名、默认同源;调用参数不能突破浏览器插件的页数/深度预算 |
|
|
99
|
-
| OpenCLI 站点适配器或浏览器桥 | `opencli.status` → `opencli.catalog` → `opencli.run` | 先发现精确 adapter;仅 `unrestricted` 跳过通用 argv 审批 |
|
|
100
|
-
|
|
101
|
-
### 平台来源
|
|
102
|
-
|
|
103
|
-
每个平台(GitHub、GitHub 代码 / Issues、B站、YouTube、V2EX、arXiv、PubMed、小红书、Twitter / X、Reddit、Instagram、Facebook、RSS、知乎、微博、豆瓣、贴吧、抖音、快手,以及你的 `customPlatforms`)都是来源注册表里 `kind: platform` 的 provider:有描述符(语言、任务类型、站点域名、所需浏览器能力 / 登录 / CLI / Token)和适配器,与网页引擎走同一条路径(探测、冷却、并发合并、`shapeSources`、重试说明、历史)。每个平台是**一个** provider,内部是一条有序的**后端链**(独立 CLI → 独立 OpenCLI → dsh-browser 的 OpenCLI / 搜索页),顺序可用 `platformBackends` 改,详见下面的 [CLI 适配](#cli-适配--cli-adapters)。`sources.status` 在同一张列表里按维度报告它们的就绪状态(浏览器安装、已绑定登录、设置开关);目录条目用 `provider` 指向同名 id。
|
|
104
|
-
|
|
105
|
-
- `search.run platform=zhihu query=…` 只搜这个平台,历史类型仍为 `platform`;`engines=zhihu` 在经典搜索里同样可用。`url`(RSS)、`authProfile`、`rulePack` 随 `platform` 传,`browserBindings` 补全后两者。
|
|
106
|
-
- **证据模式**:`platform` 可与 `task` / `profile` / `needs` / `constraints` 同用——该平台成为 S1 / S2 的显式来源,之后门限、读取前几页(有 Browser 时自动升级)、评分、证据包照常。
|
|
107
|
-
- **平台不可用**(缺浏览器 / 登录 / CLI / Token、被设置关闭、冷却中)时返回 `CAPABILITY_UNAVAILABLE`,写明缺什么,并附目录里的安装说明;**不会**悄悄改搜网页引擎。传 `allowFallback=true` 才改搜该 profile 的网页引擎,证据包的 `notes` 会写明。平台已运行但没有结果时返回空列表和平台自己的提示(多半是缺登录),不冷却。
|
|
108
|
-
- **自动规划**:平台从不被自动选入第一轮;只有任务带硬 `site` 约束且命中某平台域名(如 `zhihu.com`)并且该平台就绪时,它才排第一,并保留一个网页引擎做回退(同一上游家族只取一个,其余留给第二轮)。平台没就绪就沿用网页计划并在 `notes` 说明。`search.recommend` 对 `experience` 类任务可以推荐已就绪的平台(仍至多 3 个)。
|
|
109
|
-
- **自定义平台**随设置热更新:新增、修改、删除都会同步注册表;与已有 provider 同名的键不会覆盖内置来源,`sources.status` 的 `notes` 会列出被拒绝的键。
|
|
110
|
-
|
|
111
|
-
先运行 `sources.status` 判断后端是否 ready(要不要用哪个来源,直接问 `search.recommend`)。指定单一引擎时失败会原样返回;不指定时才会按 `engines` 顺序自动回退。所有引擎都返回空结果或不可用(没有运行时错误)时,`search.run` 返回空结果和说明,不再报错。
|
|
112
|
-
|
|
113
|
-
### 两种用法
|
|
114
|
-
|
|
115
|
-
同一套证据管线有两个入口。模型是否走哪一个,取决于它看到哪些工具;第二种能在模型“顺手用内置 `web_search`”时仍然让证据管线生效。
|
|
116
|
-
|
|
117
|
-
**(a)直接调用 `web_call search.run`。** 模型加载 skill `dsh-web-search-pro`(或按常驻提示)调用 `search.run`,传 `task` / `profile` 得到证据包(见下节)。常驻提示和 skill 描述都写明:做网页研究时优先用它,而不是 `web_search` + `web_fetch`,因为它只返回过滤后的证据,占用的上下文少得多;读页用 `read.fetch`(长页用 `offset` 续读)。
|
|
118
|
-
|
|
119
|
-
**(b)让宿主内置的 `web_search` / `web_fetch` 经过本插件。** 宿主自带这两个工具,模型常常直接用它们(实测 DeepSeek 在自然任务里就是这样,绕过了 `web_index`)。把本插件注册并**选中**为 `ctx.web` 的 provider 后:
|
|
120
|
-
|
|
121
|
-
- `web_search` 的返回内容 = 本插件的证据包(与 `search.run` 同一渲染:`resultId`、缺口 `Gaps`、“覆盖是启发式的”提示,末尾一行说明可用 `web_call history.expand` / `read.fetch` 展开),`sources` 仍是来源列表;`truncated` 只在确有来源被截掉时为真。查询即任务(`task = query`,`needs = [query]`,profile 按规则推断,推断不出为 `general`,预算为默认值)。
|
|
122
|
-
- 证据运行有自己的期限(`provider.deadlineMs`,默认 25 秒;到点返回 `PARTIAL` 的已有结果)。超时、管线报错都**退回今天的普通来源列表**,不会让内置工具失败;只有调用方取消会原样抛出。为避免递归,这条路径不会再调用 `ctx.web` 引擎(`seam`)。
|
|
123
|
-
- `web_fetch` 沿用同一套抓取管线,上限是 `fetchDefaultChars` 的两倍;被截断时 `truncated` 为真,并在正文末尾追加一行 `[Continue with web_call read.fetch url=… offset=N]`。
|
|
124
|
-
- `provider.evidence: off` 让 `web_search` 回到只返回来源列表(与旧行为完全一致)。
|
|
125
|
-
|
|
126
|
-
**怎么选中(宿主的规则,不是本插件的)。** `@deepseek-ai/dsh-web` 只在两种情况下使用某个 provider:`web` 条目里**写明了它的 id**(`searchProvider` / `fetchProvider`,环境变量 `DSH_WEB_SEARCH_PROVIDER` / `DSH_WEB_FETCH_PROVIDER` 与它们是同一个字段,不是另一条优先级链),或它是**唯一可用**的已注册 provider。没写 id 又有两个可用 provider 时,每次 `web_search` 都会以 `WEB_PROVIDER_AMBIGUOUS` 失败。所以 `registerProvider` 默认是 `false`:只注册、不选中是无害的,但默认注册会在没有写 id 的部署里让内置工具直接报错,本插件不会悄悄接管宿主默认 provider。
|
|
127
|
-
|
|
128
|
-
1. `settings.yaml` 的 `web-search-pro:` 段打开注册(启动时生效,改完重启):
|
|
62
|
+
搜单个平台(登录类平台走 opencli-first 的后端链):
|
|
129
63
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
registerProvider: true
|
|
133
|
-
providerId: web-search-pro # 默认值;与下面的 id 必须一致
|
|
134
|
-
provider:
|
|
135
|
-
evidence: auto # auto(默认)| off
|
|
136
|
-
deadlineMs: 25000
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
2. 在 profile 的 `cordis.patch.yml`(`$DSH_HOME/profiles/<profile>/cordis.patch.yml`)里把 `web` 条目指向它:
|
|
140
|
-
|
|
141
|
-
```yaml
|
|
142
|
-
- id: web
|
|
143
|
-
name: '@deepseek-ai/dsh-web'
|
|
144
|
-
config:
|
|
145
|
-
searchProvider: web-search-pro
|
|
146
|
-
fetchProvider: web-search-pro # 只想换搜索、保留宿主抓取时,写宿主抓取 provider 的 id(宿主源码里内置的是 `http`,以所用宿主版本为准),不要省略
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
或者只在启动环境里设 `DSH_WEB_SEARCH_PROVIDER=web-search-pro`(需要时再加 `DSH_WEB_FETCH_PROVIDER`)。配置里的值与环境变量同时存在时以配置为准。**不要只注册而不写 `fetchProvider`**:本插件会同时注册搜索与抓取两个 provider,若宿主的 `web` 条目没有固定抓取 provider,宿主自带的 `http` 与本插件的会同时可用,`web_fetch` 会报 `WEB_PROVIDER_AMBIGUOUS`。
|
|
150
|
-
|
|
151
|
-
3. 重启后用 `web_call sources.status` 检查:`ctx.web route` 一行会写明是否已注册、`web_search` / `web_fetch` 当前是否选中本插件、宿主固定的是哪个 id(未选中时还给出上面的配置提示)。宿主不暴露 provider 状态查询接口,所以本插件读的是 `web` 运行时已合并好的 id(读不到时只看环境变量,并在状态行里注明)。
|
|
152
|
-
|
|
153
|
-
**和其他“联网调研”skill 并存。** 用户自己安装的 skill(例如 `agent-reach`)也可能在描述里声称负责联网调研;本插件的 skill 描述只陈述自己做什么(中英文触发词:联网搜索 / 查证 / 调研 / 读取网页,web research / look up / verify / read a page),不声称对其他 skill 的优先权,也无法阻止模型选别的 skill。(b)正是为这种情形准备的:不管模型加载了哪个 skill,只要它最终调用内置 `web_search` / `web_fetch`,走的就是本插件的证据管线。
|
|
154
|
-
|
|
155
|
-
### 证据包模式(`search.run` 传 `task` 或 `profile`)
|
|
156
|
-
|
|
157
|
-
传 `task`(一句话目标)或 `profile`(`docs_code` / `news_fact` / `academic` / `experience` / `compare` / `general`)时,`search.run` 不返回结果列表,而是按 profile 选择来源(docs_code:ddg/bing/github;academic:arxiv/pubmed/ddg;experience:ddg/bing/v2ex;news_fact:ddg/bing;compare:ddg/bing/github;general:配置的 `engines`;显式 `engines` 优先),读取前 4 个保留候选的页面,分块并按每个需求评分,在字符预算(默认 6000,`budget` 可调)内挑出摘录,并列出未被满足的需求(`gaps`)。可选参数:`needs`(`;` 分隔或 JSON 数组)、`constraints`(JSON 数组 `{kind,value,strength}`;`strength` 缺省为 soft,`hard` 只在确定违反时才丢弃候选)。输出新增 `resultId`、`evidence`、`coveredNeeds`、`gaps`、`partial` 等可选字段,原有 `sources` 仍在;超过总时限(`timeoutMs` + 30 秒)时返回 `partial: true` 的已有结果。`history.expand` 传 `evidenceId` 可读回摘录所在块及其前后块(至多 4000 字符)。不传 `task` / `profile` 时行为和输出与以前完全相同。
|
|
158
|
-
|
|
159
|
-
**相关度门(S4)**:需求与候选标题/摘要语言不同(如中文需求对英文结果)时,门限判断改用跨语言对齐(需求、query、目标与实体/必含词里的拉丁词一并参与匹配),同语言保持原有的词法规则;若门限之后保留的候选少于 3 个而更多候选存在,则按融合排序补回最靠前的、未违反硬约束的候选,标为“低相关”(`sources[].lowConfidence` / `evidence[].lowConfidence`,渲染为 `(low relevance)`,`stats.lowConfidence` 计数,并在 `notes` 说明),不会因为相关度启发式而返回空包。
|
|
160
|
-
|
|
161
|
-
**有界第二轮(S8)**:第一轮之后若有关键需求(`critical`)没有被满足(`gaps` 里原因不是 `budget`),且轮数、查询数、时间都还有余量,管线最多再补搜一轮:以“需求文本 + 任务里的关键实体(entity / must_term / version 约束与 query 里的标识符)”为查询,按各 provider 编译(站点约束等照常下推),优先用第一轮没用过的 provider,其次复用已回答的;只读取新候选里最好的至多 2 页,只对缺口需求评分,再与第一轮结果合并(按 URL 去重)重新挑选。单任务查询总数(一次 provider 调用算一次,GitHub 的放宽重试合并算一次;允许第二轮时第一轮至多用 `maxQueries-1` 个 provider,留一次给第二轮,显式 `engines` 不裁剪)不超过 `evidence.maxQueries`(默认 4),轮数不超过 `evidence.maxRounds`(默认 2,设 1 即关闭);剩余时间不足 15 秒或预算已用完时跳过并在 `notes` 说明。证据包 `stats.rounds` / `stats.queries` 给出实际轮数与查询数。
|
|
162
|
-
|
|
163
|
-
块评分默认用本地词法规则。可选的博查 Jev 评分(付费,需环境变量或凭据引用 `BOCHA_JEV_API_KEY`)由 `evidence` 配置控制:`jevMode: off`(默认,不调用)、`shadow`(规则决定,Jev 评分只记录到存储 `evidence_runs.pack_json` 供对照)、`control`(且 `scorer: jev` 时由 Jev 决定;任何 Jev 失败都回退到规则并在 `notes` 里说明)、`hybrid`(规则评分全部块,Jev 只重评需求语言与块语言不一致的 (需求, 块) 对;`hybridBorderline: true` 时再加规则评分为 1 的边界对;Jev 失败保留规则评分;与 `scorer` 无关);`maxJevQuestions`(默认 64)限制每次搜索发送的 (需求, 块) 问题数。规则评分本身对中文需求与英文块做了跨语言对齐(query / 需求 / 约束里的英文词和标识符并入匹配词),默认即生效。发给 Jev 的只有一句话目标、需求文字和页面块文本。
|
|
164
|
-
|
|
165
|
-
#### Jev 提示词(rubric):可配置、有版本 / Judge prompts: configurable and versioned
|
|
166
|
-
|
|
167
|
-
**中文** 发给 Jev 的问题措辞、评分等级和长度上限是带版本的 rubric(内置 `score.support`,另有 `gate.relevance`、`gate.constraint` 备用)。默认值与离线评测(`bench/rubrics/*.v1.json`)逐字相同。要调整,在设置文件 `evidence.rubrics` 里按 rubric id 写覆盖项,无需改代码:
|
|
168
|
-
|
|
169
|
-
```yaml
|
|
170
|
-
evidence:
|
|
171
|
-
jevMode: hybrid
|
|
172
|
-
rubrics:
|
|
173
|
-
score.support:
|
|
174
|
-
version: v2 # 必填;内容有改动就必须换新版本号(不能是 v1)
|
|
175
|
-
instructions: | # 只能用 {task} {need} {candidate};必须含 {need} 和 {candidate}(≤2000 字符)
|
|
176
|
-
文本块本身是否直接陈述了需求所问的答案,而不只是提到该主题?
|
|
177
|
-
需求:{need}
|
|
178
|
-
文本块:{candidate}
|
|
179
|
-
criteria: [无关, 只提到主题, 部分回答, 直接回答且含证据] # 2–10 级,从低到高;不是 4 级时分数按比例换算到 0..3
|
|
180
|
-
maxStateChars: 200 # 20–2000,任务描述上限
|
|
181
|
-
maxCandidateChars: 1200 # 100–8000,每个文本块上限
|
|
64
|
+
```json
|
|
65
|
+
{"action":"search.run","args":{"query":"机械键盘 轴体 推荐","task":"了解知乎上对机械键盘轴体的真实经验","profile":"experience","platform":"zhihu"}}
|
|
182
66
|
```
|
|
183
67
|
|
|
184
|
-
|
|
185
|
-
- **版本规则**:改了措辞、等级或长度就换新 `version`(如 v2、v3)。每次 Jev 评分都记录 `id@version#内容哈希`:证据包 `stats.jev.rubric`、shadow 日志(`evidence_runs.pack_json` 的 `shadow.rubric`)、证据行(`evidence_blocks.rubric`);离线评测的缓存键同样包含 id、版本和全文,所以改提示词不会复用旧分数。
|
|
186
|
-
- **查看**:`sources.status` 的 `evidence.rubrics` 列出每个 rubric 当前生效的版本、是否被覆盖。
|
|
187
|
-
- **恢复默认**:删除对应的 `evidence.rubrics.<id>` 条目即可(设置页暂不提供该项的编辑界面,只读设置文件)。
|
|
188
|
-
- **先离线对照再上线**:`node --experimental-transform-types bench/src/eval-pack.ts --rubric-file bench/rubrics/variants/score.support.v2-example.json`,在同一份冻结数据上评估候选版本;不带 `--allow-jev N` 时不会发出任何请求(用法见 `bench/src/eval-pack.ts` 文件头注释)。
|
|
189
|
-
- **不让线上模型改提示词**:rubric 只能由人通过设置文件修改;插件和任何线上模型都不会自动改写或上线新版本。
|
|
190
|
-
|
|
191
|
-
**English** The question wording, grade levels and length caps sent to Jev are versioned rubrics (built in: `score.support`, plus `gate.relevance` and `gate.constraint` for future use). Defaults are byte-identical to the offline-evaluated wording. Override one under `evidence.rubrics.<id>` in settings.yaml with its own `version`, optional `instructions` (variables `{task} {need} {candidate}` only; `{need}` and `{candidate}` required), `criteria` (2-10 levels, lowest first; other than 4 levels are rescaled to 0..3), `maxStateChars`, `maxCandidateChars`. An invalid override (unknown variable, bad level count, out-of-range length, changed content under the old version label) is ignored and the built-in is used; the reason shows in `sources.status` (`evidence.diagnostics`) and the pack `notes`. Bump `version` whenever the content changes. Every Jev result records `id@version#hash` (pack `stats.jev.rubric`, shadow log, `evidence_blocks.rubric`), and the offline judge-cache keys include id, version and full text, so a changed prompt never reuses old scores. Restore defaults by deleting the `evidence.rubrics.<id>` entry (there is no settings-page editor for it yet). Compare a candidate offline first with `bench/src/eval-pack.ts --rubric-file`. Online models must never rewrite or roll out rubrics by themselves: they change only through the settings file.
|
|
192
|
-
|
|
193
|
-
#### 评分模型 provider 与用量上限 / Judge providers and usage caps
|
|
194
|
-
|
|
195
|
-
**中文** S6 的模型评分分成「协议 × provider」两层,换模型只改配置:
|
|
196
|
-
|
|
197
|
-
- **协议**:`systemone`(Jev 兼容的 `POST /v1/systemone`:博查 Jev、其他 Jev 部署、本地 Laya sidecar);`rerank`(Jina / Cohere 风格的 `{model, query, documents, top_n}` → `results[{index, relevance_score}]`,也适用于暴露同形状接口的本地 bge / Qwen reranker);`llm`(OpenAI 兼容 chat completions,温度 0、严格 JSON 校验,仅供参考/实验,默认关闭,需 `evidence.judge.allowLlm: true`)。`systemone` 与 `llm` 用 `score.support` rubric;`rerank` 以需求文本作 query。
|
|
198
|
-
- **内置 provider**:`bocha-jev`(默认;`https://jev.bocha.cn`,`bocha-jev-v1`,密钥 `BOCHA_JEV_API_KEY`,请求与旧版逐字节相同)、`typesafe-jev`(占位预设,须自行填 `baseUrl` / `model`,**未验证**)、`laya-local`(`http://127.0.0.1:8765`,无密钥;实验 r1 里在当前提示词下接近随机,用前必须自己校准)、`jina-rerank`、`cohere-rerank`(**未验证**,从未对真实服务调用过)。
|
|
199
|
-
- **自定义 provider**(`jevMode` / `judge.mode` 决定怎么用;`judge.mode` 是 `jevMode` 的中性写法,两者都设时前者优先,且 `control` 不再需要 `scorer: jev`):
|
|
200
|
-
|
|
201
|
-
```yaml
|
|
202
|
-
evidence:
|
|
203
|
-
judge:
|
|
204
|
-
mode: hybrid # off | shadow | control | hybrid
|
|
205
|
-
provider: my-reranker # 默认 bocha-jev
|
|
206
|
-
providers:
|
|
207
|
-
my-reranker:
|
|
208
|
-
protocol: rerank
|
|
209
|
-
baseUrl: http://127.0.0.1:8080/v1 # 非本机必须 https;路径可用 path 覆盖(默认 /rerank)
|
|
210
|
-
model: bge-reranker-v2-m3
|
|
211
|
-
keyRef: MY_RERANK_KEY # 可选:凭据引用或环境变量名,永远不写密钥本身
|
|
212
|
-
limits: { maxDocumentsPerRequest: 50, blockChars: 1000 }
|
|
213
|
-
calibration: # rerank 必填,见下
|
|
214
|
-
version: v1
|
|
215
|
-
points: [[0.1, 0], [0.4, 1], [0.7, 2], [0.9, 3]]
|
|
216
|
-
bocha-jev: # 与预设同名 = 覆盖预设的个别字段
|
|
217
|
-
limits: { maxQuestionsPerRequest: 16 }
|
|
218
|
-
budget:
|
|
219
|
-
perSearchInputTokens: 60000 # 默认
|
|
220
|
-
dailyInputTokens: 1000000 # 默认
|
|
221
|
-
timezone: Asia/Shanghai # 日界线时区,默认系统时区
|
|
222
|
-
providers: { laya-local: { dailyInputTokens: 200000 } } # 按 provider 覆盖,取更严格者
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
- **校准是硬要求**:reranker 的相关度分数不是等级,不同模型/语言差异很大。`calibration.points` 是 `[原始分, 等级0..3]` 的单调分段线性映射(原始分严格递增、等级不降;区间外取端点),版本号和内容哈希随结果记录;没有校准的 rerank provider 不会被使用(规则评分继续,并在 `notes` 说明)。不同 provider 的分数从不混用,离线缓存也按 provider+模型分开(rerank 缓存的是原始分,换校准无需重新请求)。`systemone` 也可选填 `calibration`(Laya 建议)。先用 `shadow` 观察,再考虑 `hybrid` / `control`。
|
|
226
|
-
- **用量账本与上限**:每次模型调用前按保守估算预留 token、调用后按服务返回的 `usage` 结算(服务不返回则按估算记账并标 `estimated`;价格未声明时金额为空,不是 0;`price` 可选声明)。预留是原子的(SQLite `usage_ledger` 表,跨搜索、跨进程,重启不清零,未结算的预留继续占用额度)。超过单次搜索或当日上限时跳过模型阶段、回退规则评分,`notes` 出现 “model budget exceeded”。`sources.status` 的 `evidence.provider` / `evidence.usage` 显示当前 provider 是否可用、今日用量与上限。注意默认单次上限 60000 输入 token 小于 `maxJevQuestions: 64` 全量发送的估算量,较大的 hybrid 搜索可能提前停在上限处。
|
|
227
|
-
- **离线评测**:`bench/src/eval-pack.ts --provider <id> [--providers-file providers.json]`、`bench/src/run-judges.ts --provider <id>`;文件格式同 `evidence.judge.providers`。
|
|
228
|
-
|
|
229
|
-
**English** S6 model scoring is split into protocol x provider; switching models is configuration. Protocols: `systemone` (Jev-compatible API: Bocha Jev, other Jev deployments, the local Laya sidecar), `rerank` (Jina/Cohere-style query-documents API, also local bge/Qwen servers) and an opt-in `llm` (OpenAI-compatible chat, temperature 0, strict JSON; off unless `evidence.judge.allowLlm`). Presets: `bocha-jev` (default, requests byte-identical to before), `typesafe-jev` (placeholder, unverified), `laya-local` (no key; near random with the current prompts, calibrate first), `jina-rerank` / `cohere-rerank` (unverified, never called live). Add your own under `evidence.judge.providers` (same-id entries override a preset's fields); `evidence.judge.mode` is the neutral name of `jevMode`. A reranker's relevance score is not a grade: `calibration.points` (monotone piecewise-linear `[raw, grade 0..3]`) is mandatory, versioned and recorded with results; provider scores are never mixed and caches are per provider+model. Every model call is reserved before and settled after in a persisted ledger (actual tokens from the API, otherwise a flagged estimate; unknown price = null, never 0), under `evidence.budget` caps (default 60k input tokens per search, 1M per day, per-provider overrides, day boundary in `timezone`). Over a cap the model stage is skipped and rule grades are used ("model budget exceeded"). `sources.status` shows the provider and today's usage. Offline: `eval-pack.ts` / `run-judges.ts --provider <id>`.
|
|
230
|
-
|
|
231
|
-
**覆盖判定(S8,可选,默认关闭)** 规则覆盖是词法判断,分辨不出“文档里没有”。`evidence.coverage: { mode: off | shadow | control, provider?, thresholds?: { weak, covered } }`:对规则声称已覆盖的每个需求,向 systemone 模型(默认 `bocha-jev`)问一道 `cover.sufficient` noul 题——“这些摘录本身是否已明确给出答案”(一次请求,经用量账本与上限)。`shadow` 只把原始概率记入 `stats.coverage`,覆盖不变;`control`:概率低于 `weak` 的需求变成缺口 `weak_support (judge)`(关键需求可触发既有的有界第二轮,不增加轮数),介于 `weak` 与 `covered` 之间的保持覆盖并标 `(judge unsure)`。只有显式设置才启用,不随 `jevMode` 自动开启;失败、超限、超时、缺 Key、缺阈值时保留规则覆盖并在 `notes` 说明。阈值只对校准过的 provider + rubric 版本有效(内置 `bocha-jev` + `cover.sufficient@v1`:weak 0.0512,covered 0.313,取自 v1 calibration 划分);换 provider、模型或 rubric 版本须自己配 `thresholds`。离线评测见 `bench/README.md` “覆盖判定评测”,数字见开发计划 M9:增益有限,所以默认保持关闭,建议先用 `shadow` 积累数据。
|
|
232
|
-
|
|
233
|
-
**English** Optional S8 coverage judge (default off). `evidence.coverage: { mode: off | shadow | control, provider?, thresholds?: { weak, covered } }` asks a systemone model (default `bocha-jev`) one `cover.sufficient` noul question per need the rules claim covered ("do these excerpts by themselves state the answer?"), metered by the usage ledger. `shadow` records raw probabilities in `stats.coverage` and changes nothing; `control` turns probabilities below `weak` into `weak_support (judge)` gaps (a critical one may start the existing bounded second round) and marks the band up to `covered` as `(judge unsure)`. Only its own setting turns it on; any failure, cap, timeout, missing key or missing thresholds keeps the rule coverage and says so in `notes`. Thresholds are only valid for the provider + rubric version they were calibrated for (shipped: `bocha-jev` + `cover.sufficient@v1`).
|
|
234
|
-
|
|
235
|
-
## 工具面(索引 → 调用)
|
|
236
|
-
|
|
237
|
-
常驻上下文只有两个工具,常驻文本(系统提示 + 工具名、描述、参数)约 800 字符(原 11 个工具约 9300 字符):
|
|
238
|
-
|
|
239
|
-
| 工具 | 作用 |
|
|
240
|
-
|---|---|
|
|
241
|
-
| `web_index({group?, action?, query?})` | 渐进披露目录:无参数列出能力组、两个最常用调用、旧名对照;`group` 列出该组动作;`action` 给出完整参数;`query` 按关键词检索(至多 8 条) |
|
|
242
|
-
| `web_call({action, args})` | 执行一个动作,返回统一信封 `{ok, action, result \| error{code,message,hint,schema?}, truncation?}`;参数校验失败(`INVALID_ARGS`)时附带该动作的精简 schema,一次即可改对;未知动作会给出相近动作名 |
|
|
243
|
-
|
|
244
|
-
使用指引放在随包提供的 skill `dsh-web-search-pro`(证据模式优先、先读 `gaps` 再下结论、`search.recommend` 只用 1–2 个来源、`read.fetch` 的 `offset` 续读、约束、预算、浏览器交接;参考文件含来源与 Key 配置、评分 provider 与 rubric、排障)。skill 通过可选的 `ctx.skills` 注册,**不写入必需的 `inject`**;宿主没有 skill 服务时,`web_index()` 根目录附一段精简指南兜底。系统提示只保留一行入口(有 dsh-browser 时再加一行指向 skill `dsh-browser` / `browser_index`)。
|
|
245
|
-
|
|
246
|
-
### 动作
|
|
247
|
-
|
|
248
|
-
| 组 | 动作 | 作用 |
|
|
249
|
-
|---|---|---|
|
|
250
|
-
| `search` | `run` | 多引擎搜索 + RRF 融合 + 内存/SQLite 双层缓存 + 历史;传 `task` / `profile` 得证据包;传 `platform` 搜单个平台(GitHub/B站/YouTube/V2EX/小红书/Twitter/Reddit/IG/FB/RSS + 知乎/微博/豆瓣/贴吧/抖音/快手,登录态走 Playwright;RSS 用 `url` 传 feed、`query` 可选过滤;平台与网页引擎走同一条注册表执行路径,可与 `task`/`profile` 同用得到证据包,见“平台来源”) |
|
|
251
|
-
| | `recommend` | 按任务推荐至多 3 个来源(已就绪优先,其余给出缺失条件),不发搜索请求 |
|
|
252
|
-
| `read` | `fetch` | 可读化抓取(Jina → HTTP+规则抽取 → Playwright 兜底)+ 快照缓存与 `offset` 续读;`auto` 按质量升级:每次结果先判为 content / shell / js_shell / login_wall / captcha / error,只有 shell、js_shell、login_wall 且 dsh-browser 就绪时才升级到 Playwright(captcha 与错误页不会,短而有实质内容的事实页不算空壳),取质量最好的一次并在 `attempts` 里记录各后端结果;不会自动安装任何东西;显式 mode 不升级,只复用同后端缓存 |
|
|
253
|
-
| | `contents` | 原生 Exa `/contents` 批量正文抓取(1-100 URL,需 Exa Key) |
|
|
254
|
-
| | `snapshot` | Playwright HTML + 文本落盘;`screenshot=false` 时不生成 PNG(需要 dsh-browser) |
|
|
255
|
-
| `history` | `list` / `replay` / `expand` / `export` / `delete` | 持久历史:过滤列出 / 按 id 回放 / 读回证据摘录及前后块 / 导出 JSON / 删除一条查询 |
|
|
256
|
-
| `sources` | `status` | 无副作用后端探测、失败/冷却诊断与 CLI 状态(Twitter 项同时检查 `twitter` 命令、设置开关和凭据环境变量,`note` 说明缺什么) |
|
|
257
|
-
| | `deps` / `install` | 检测 / 安装搜索后端的外部依赖(bili/yt-dlp/twitter/agent-reach/mcporter;每项探测的是后端真正执行的命令);浏览器依赖由 dsh-browser 管理 |
|
|
258
|
-
| `rules` | `list` / `upsert` / `remove` / `import` / `export` | 持久化按站提取规则(脚本猫式);export 会写出可再导入的版本化 JSON rule pack |
|
|
259
|
-
| `cache` | `clear` / `stats` | 按时间/引擎清缓存 / 存储统计 |
|
|
260
|
-
|
|
261
|
-
每个动作的参数与输出 schema 是封闭的(未声明字段不会出现,除必需字段外都是可选字段)。
|
|
262
|
-
|
|
263
|
-
### 旧工具到新动作
|
|
264
|
-
|
|
265
|
-
旧的 `web_*` 工具名**已不存在**,也没有兼容包装;模型调用旧名会得到新动作名与翻译后的参数。映射表在 `src/actions/legacy.ts`,测试逐项验证每个旧参数仍可达。
|
|
266
|
-
|
|
267
|
-
| 旧工具(参数) | 新动作 |
|
|
268
|
-
|---|---|
|
|
269
|
-
| `web_search_pro` | `search.run`(`query`、`task`、`profile`、`needs`、`constraints`、`budget`、`engines`、`count`、`fresh`、`multi`、Exa 选项原样保留) |
|
|
270
|
-
| `web_platform_search` | `search.run`,传 `platform`(另有 `url`、`authProfile`、`rulePack`;现在也支持 `fresh`) |
|
|
271
|
-
| `web_fetch_pro` | `read.fetch` |
|
|
272
|
-
| `web_exa_contents` | `read.contents` |
|
|
273
|
-
| `web_snapshot` | `read.snapshot` |
|
|
274
|
-
| `web_history`(过滤) | `history.list` |
|
|
275
|
-
| `web_history replay=<id>` | `history.replay {id}` |
|
|
276
|
-
| `web_history action=expand evidenceId=<id>` | `history.expand {evidenceId}` |
|
|
277
|
-
| `web_history export=true` | `history.export`(同样的过滤参数) |
|
|
278
|
-
| `web_cache_clear`(`olderThanDays` / `engine`) | `cache.clear` |
|
|
279
|
-
| `web_cache_clear queryId=<id>` | `history.delete {id}` |
|
|
280
|
-
| `web_rule action=list\|upsert\|remove\|import\|export` | `rules.list` / `rules.upsert` / `rules.remove` / `rules.import` / `rules.export` |
|
|
281
|
-
| `web_search_stats` | `cache.stats` |
|
|
282
|
-
| `web_backend_status`(默认) | `sources.status` |
|
|
283
|
-
| `web_backend_status action=recommend` | `search.recommend` |
|
|
284
|
-
| `web_deps`(默认 / `action=check`) | `sources.deps` |
|
|
285
|
-
| `web_deps action=install` | `sources.install` |
|
|
286
|
-
|
|
287
|
-
迁移说明:
|
|
288
|
-
|
|
289
|
-
- 没有 dsh-browser 的会话里,`web_*` 旧名的调用会报“工具不存在”(宿主层错误,插件无法拦截);新会话的模型通过系统提示、skill 或 `web_index()` 找到新名字。
|
|
290
|
-
- 用户在宿主里对旧工具名设置的“总是允许”规则不再生效;`web_index` 无副作用,`web_call` 的审批由本插件按**动作**判断(见下)。
|
|
291
|
-
- `web_history` 的 `replay` / `export` / `action=expand` 不再与列表过滤混在一次调用里,每个动作只做一件事。
|
|
292
|
-
- 输出路径变化:`search.recommend` 直接返回推荐本身(不再包在 `recommend` 字段里);`sources.install` 返回 `install` 结果(不再带空的 `backends`)。
|
|
293
|
-
|
|
294
|
-
### 审批、并发与超时(按动作)
|
|
295
|
-
|
|
296
|
-
- **审批**:`sources.install`(安装外部命令)、`cache.clear`、`history.delete`、`rules.upsert` / `rules.remove` / `rules.import`(改本地存储)会请用户确认;其余动作直通。原先这些规则按工具名写在 dsh-browser 的 `tools/pre-execute` 钩子里,而 `web_call` 对它是不透明的,所以规则现在由本插件解析 `web_call` 的动作后施加,并在 dsh-browser 存在时读取它的 `automationMode`:`read-only` 拒绝;安装在 `unrestricted` 直通;本地写入在 `standard` 询问、`autonomous` / `unrestricted` 直通。**没有 dsh-browser 时模式未知,按 `standard` 处理(询问)**——这比旧行为(无 dsh-browser 时完全不审批)更严格。
|
|
297
|
-
- **并发**:`web_call` 的并发安全性由参数里的动作决定:只读动作可并行,修改状态的动作独占(`sources.install` 现在也独占,旧的 `web_deps` 因为一个标志同时覆盖 check 与 install 而全部可并行)。
|
|
298
|
-
- **超时**:每个动作保持旧工具的上限(搜索与快照 `timeoutMs` + 60 秒,读取与 Exa 正文 + 30 秒,依赖检测/安装 + 180 秒,历史与状态等 10–20 秒);`web_call` 的宿主上限取其中最长者,各动作自己的截止时间到期后返回 `DEADLINE`(修改状态的动作会说明结果未知)。调用方的取消信号会传给执行器。
|
|
299
|
-
|
|
300
|
-
### 工具面形态
|
|
301
|
-
|
|
302
|
-
`toolSurface: indexed | flat`(设置文件顶层,启动时生效,默认 `indexed`)。`flat` 把同一份动作注册表投影成每个动作一个工具,名字为 `web_<组>_<动作>`(如 `web_search_run`、`web_read_fetch`),**只用于对照与调试**,不是旧工具的兼容层,常驻体积回到全部注入的量级。两种形态共用同一个分发函数,信封、错误码与审批完全一致。
|
|
303
|
-
|
|
304
|
-
### 输出预算(所有出口)
|
|
305
|
-
|
|
306
|
-
每个把正文交给模型的出口都有默认上限;超出部分留在本地存储,按 ID 或偏移续读,不会因为 UI 折叠而仍把全文送进上下文。
|
|
307
|
-
|
|
308
|
-
| 出口 | 默认预算 | 配置项 | 超出时 |
|
|
309
|
-
|---|---|---|---|
|
|
310
|
-
| `search.run`(证据包) | 摘录总计 6000 字符,每 URL 至多 2~4 块 | 参数 `budget`(至多 30000) | 溢出的需求列在 `gaps`;`history.expand` 传 `evidenceId` 读回摘录所在块及前后块 |
|
|
311
|
-
| `read.fetch` | 20000 字符 | `fetchDefaultChars`(1000–500000);参数 `maxChars` | 输出 `truncated`、`nextOffset`、`totalChars`,并提示 “more: call read.fetch with offset=N”;`offset` 从已存的页面快照续读,命中缓存时不重新抓取 |
|
|
312
|
-
| `read.contents` | 每 URL 8000、全部 URL 合计 30000 字符;总量不足时较短的文本原样保留、剩余额度均分给较长的 | `exaContentsPerUrlChars`、`exaContentsTotalChars` | 每条结果带 `truncated`、`totalChars`,并给出 `read.fetch offset` 的续读提示 |
|
|
313
|
-
| ctx.web 抓取 Provider(内置 `web_fetch`) | `fetchDefaultChars` 的两倍(默认 40000);`WebFetchRequest` 没有大小参数 | `fetchDefaultChars` | `truncated` 如实反映是否被截断,正文末尾追加 `[Continue with web_call read.fetch url=… offset=N]` |
|
|
314
|
-
| ctx.web 搜索 Provider(内置 `web_search`,`provider.evidence: auto`) | 证据包同上(默认摘录 6000 字符);来源条数取宿主给的 `maxResults`(1–20) | `provider.evidence`、`provider.deadlineMs` | 期限到点返回 `PARTIAL` 的已有证据包;失败退回普通来源列表;`truncated` 仅在确有来源被截掉时为真 |
|
|
315
|
-
| `read.snapshot` 文本、`history.replay` 的页面文本 | `fetchDefaultChars` | `fetchDefaultChars` | 文末带截断标记;全文仍在存储里,用 `read.fetch url=… offset=N` 读取 |
|
|
316
|
-
| `search.run`(普通列表与平台搜索) | 每条摘要 500 字符,条数由 `count` 限制 | `searchMaxResults` | 摘要截断 |
|
|
317
|
-
|
|
318
|
-
抓取时页面至少读取并存储 100000 字符(更大的 `offset + maxChars` 会读更多,上限 500000),所以续读来自 SQLite 快照;超过已存部分的 `offset` 会自动用更大的上限重新读取。
|
|
319
|
-
|
|
320
|
-
## 浏览器脚本与自动化分层
|
|
321
|
-
|
|
322
|
-
`@anweat/dsh-browser ^0.2.0` 提供三类脚本入口:
|
|
323
|
-
|
|
324
|
-
1. **内置只读脚本**:`article-clean`、`links`、`jsonld`、`forms`,适合稳定抽取;先用 `script.catalog`(`browser_call`)查看。
|
|
325
|
-
2. **Recipe**:最多 25 步的结构化 Playwright 操作,支持 wait/click/fill/type/press/select/check/hover/scroll/extract/assert/screenshot;交互步骤由自动化模式决定审批。
|
|
326
|
-
3. **外部 UserScript**:适合外部模型生成站点专项逻辑。先 `script.validate` 查看 SHA-256、域名范围与能力提示,再 `script.run_userscript`(均经 `browser_call`);它在页面主世界运行,并非安全沙箱。
|
|
327
|
-
|
|
328
|
-
工具自由度由 dsh-browser 的 `automationMode` 控制:`read-only` 隐藏或拒绝页面及 Web Search Pro 写操作;`standard`(默认)对交互、写 Recipe、外部脚本、OpenCLI、缓存/规则变更和安装操作审批;`autonomous` 直通页面交互、写 Recipe 以及本地缓存/规则变更,但安装、外部脚本和通用 OpenCLI 仍审批;`unrestricted` 为隔离测试 profile 提供无审批运行。所有模式仍保留域名、参数、大小和步骤上限校验,并始终应用 dsh-browser 的调用缓冲、退避与爬取预算。
|
|
329
|
-
|
|
330
|
-
OpenCLI 用于已有站点 adapter 或复用 Chrome 登录会话。推荐顺序是 **`opencli.catalog` 查精确 adapter → network/extract → DOM 操作**;先运行 `opencli.status`。`opencli.run` 接受 argv 数组而非 shell 字符串,可覆盖 adapter、显式 session 的 `browser state/find/get/click/fill/type/select/keys/wait/extract/network` 等命令;仅 `unrestricted` 跳过审批。
|
|
331
|
-
|
|
332
|
-
更完整的 AuthProfile、脚本元数据与 OpenCLI 示例见 [LOGIN.md](./LOGIN.md)。
|
|
333
|
-
|
|
334
|
-
## 配置
|
|
335
|
-
|
|
336
|
-
三层,越靠前越日常:
|
|
337
|
-
|
|
338
|
-
1. **DSH 可视化面板**:打开 `设置 → 插件 → 插件配置 → Web Search Pro`(各分组见下节“设置面板”)。修改先保留为本地草稿,点击“保存”后写入 `settings.yaml` 并热更新,支持放弃修改和逐字段恢复部署值。
|
|
339
|
-
|
|
340
|
-
- Exa、Jina、GitHub、博查与各需 Key 来源的密钥通过 DSH Credentials 写入,面板只显示“已配置/未配置”,不会把明文密钥读回浏览器,也不会把密钥值写进 `settings.yaml`。
|
|
341
|
-
- `platformRules`、`customPlatforms`、`browserBindings`、Playwright、自定义评分 provider 等使用 JSON 对象编辑器;格式、数值范围或服务端校验不通过时会阻止保存,并在字段下方给出与服务端一致的错误信息。
|
|
342
|
-
- 浏览器工具的审批自由度由 `dsh-browser.automationMode` 管辖,调用缓冲由 `dsh-browser.usagePolicy` 管辖;用 `browser_call` 的 `runtime.status` 查看当前状态。Web Search Pro 面板只管理搜索插件自己的后端开关,不会绕过浏览器插件的审批或资源策略。
|
|
343
|
-
- 更新带客户端面板的插件版本后需要重启 Web profile,让 DSH 客户端模块扫描器重新装载 `client.js`。
|
|
344
|
-
|
|
345
|
-
2. **`$DSH_HOME/settings.yaml` → `web-search-pro:` 段**(热重载,改完即生效):
|
|
346
|
-
|
|
347
|
-
```yaml
|
|
348
|
-
web-search-pro:
|
|
349
|
-
toolSurface: indexed # indexed(默认,只常驻 web_index / web_call)| flat(每个动作一个工具,仅对照与调试;启动时生效)
|
|
350
|
-
exaApiKeyEnv: EXA_API_KEY # 推荐:运行环境或凭据服务,不把密钥写入配置
|
|
351
|
-
jinaApiKeyEnv: JINA_API_KEY
|
|
352
|
-
bochaApiKeyEnv: BOCHA_SEARCH_API_KEY # 博查 Key 的凭据 / 环境变量名;缺省再试 BOCHA_JEV_API_KEY
|
|
353
|
-
# keyedSources: # 付费来源(可选,见“付费 / 需 Key 的来源”):tavily / brave / linkup / serper / metaso / zhipu / baidu-qianfan
|
|
354
|
-
# tavily: { apiKeyEnv: TAVILY_API_KEY } # 或 apiKey(字面量,不推荐)/ baseUrl;没有 Key 的来源保持不可用
|
|
355
|
-
engines: [ddg, bing, exa, seam, jina]
|
|
356
|
-
parallelEngines: false
|
|
357
|
-
# registerProvider: false # true = 把本插件注册为 ctx.web provider(还需在 web 条目里选中,见“两种用法”)
|
|
358
|
-
# provider: { evidence: auto, deadlineMs: 25000 } # 内置 web_search 经本插件时是否返回证据包(auto | off)与其期限
|
|
359
|
-
evidence: # 证据包模式的块评分;默认完全不调用 Jev
|
|
360
|
-
scorer: rule # rule | jev
|
|
361
|
-
jevMode: off # off | shadow | control | hybrid
|
|
362
|
-
hybridBorderline: false # 仅 hybrid:同时重评规则边界对
|
|
363
|
-
maxJevQuestions: 64
|
|
364
|
-
maxRounds: 2 # 证据包补搜轮数上限;1 = 关闭第二轮
|
|
365
|
-
maxQueries: 4 # 单任务搜索查询总数(含第一轮;一次 provider 调用算一次)
|
|
366
|
-
autoProviders: true # 按任务语言提前就绪的来源(你配置了 Key 的来源,英文无 Key 时的 Exa);false = 只用 profile 表
|
|
367
|
-
# sourcePolicy: default # default | anonymous-only(自动规划绝不用需要 Key / 账号 / 登录的来源,见“来源策略”)
|
|
368
|
-
# rubrics: ... # 可选:覆盖 Jev 提示词,见下文“Jev 提示词(rubric)”
|
|
369
|
-
# judge: ... # 可选:选择/自定义模型 provider,见下文“评分模型 provider 与用量上限”
|
|
370
|
-
# budget: ... # 可选:模型输入 token 上限(单次搜索 / 每日),默认 60000 / 1000000
|
|
371
|
-
# sources: # 来源偏好与请求额度(全部可选,默认什么都不设)
|
|
372
|
-
# priority: [bocha, exa] # 就绪且适合任务语言 / profile 的来源按此顺序排最前
|
|
373
|
-
# disabled: [bing] # 自动规划与 engines 列表都不用
|
|
374
|
-
# budget: { bocha: { total: 1000, daily: 50 } } # 请求上限;用完被跳过并回退,不报错
|
|
375
|
-
ttlSeconds: 3600
|
|
376
|
-
searchMaxResults: 8
|
|
377
|
-
browserBindings:
|
|
378
|
-
zhihu:
|
|
379
|
-
authProfile: china-community
|
|
380
|
-
rulePack: zhihu-enhanced
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
3. **cordis.yml `config:`**(部署级默认值,见 `cordis.patch.yml`)。
|
|
384
|
-
4. **环境变量 / 凭据**:`$EXA_API_KEY`、`$JINA_API_KEY`、`$BOCHA_SEARCH_API_KEY`(或 `$BOCHA_JEV_API_KEY`)(`exaApiKeyEnv`/`jinaApiKeyEnv`/`bochaApiKeyEnv` 引用)。这些引用名与密钥都可以在面板“来源”分组里设置。
|
|
385
|
-
|
|
386
|
-
## 设置面板
|
|
387
|
-
|
|
388
|
-
面板按以下分组折叠显示(中英文随 DSH 语言;搜索、凭据、运行时默认展开)。嵌套选项(`evidence.*`、`provider.*`、`keyedSources.*`)由面板整体写回各自的顶层字段,不认识的同级键原样保留。
|
|
389
|
-
|
|
390
|
-
| 分组 | 内容 |
|
|
391
|
-
|---|---|
|
|
392
|
-
| 搜索策略 | 默认引擎顺序、默认结果数、并行融合 |
|
|
393
|
-
| 网络 | `allowProxyFakeIp`(Clash/TUN fake-IP 症状说明)、`timeoutMs` |
|
|
394
|
-
| 工具面 | `toolSurface`:`indexed`(默认)/ `flat`(仅调试),启动时生效 |
|
|
395
|
-
| 内置 web 工具路由 | `registerProvider`、`providerId`、`provider.evidence`(auto / off)、`provider.deadlineMs`,以及选中本插件所需的 profile patch 片段(随 `providerId` 变化) |
|
|
396
|
-
| 证据管线 | `evidence.autoProviders`、`maxRounds`、`maxQueries`,`fetchDefaultChars`、`exaContentsPerUrlChars` / `TotalChars`。`minKeep` 与证据包字符预算没有配置项(内置默认 / `search.run` 的 `budget` 参数) |
|
|
397
|
-
| 评分模型 | `evidence.judge.mode`(off / shadow / control / hybrid,同时写旧键 `jevMode` / `scorer` 保持一致)、`hybridBorderline`、provider 下拉(内置预设 + 自定义 id)、`maxJevQuestions`、`allowLlm`;覆盖判定 `evidence.coverage` 的模式、provider、阈值;用量上限 `evidence.budget`(单次 / 每日 / 时区 / 按 provider);自定义 provider JSON(与服务端同一套校验,拒绝在定义里放密钥,只放 `keyRef` 名) |
|
|
398
|
-
| 提示词(rubric) | 列出内置 rubric 与生效版本;逐个覆盖(版本、模板、等级、字符上限),按 `rubrics.ts` 的规则校验(未知变量、必需变量、等级数、版本必须不同于内置);“恢复默认”删除覆盖 |
|
|
399
|
-
| 来源 | 来源策略(`evidence.sourcePolicy`、`sources.priority`、`sources.disabled`)、博查(Key 引用名、接口地址、长摘要)、七个需 Key 的来源(Key 引用名、写入式密钥、接口地址)、每个来源的请求额度(`sources.budget.<id>.total` / `daily`,已用 / 剩余见 `web_call sources.status`)、`searxngUrl`、`openalexMailto`,以及匿名来源参考表;各来源的实时就绪状态由 `web_call sources.status` 显示 |
|
|
400
|
-
| 服务凭据 / 运行时与后端 / 高级 | Exa、Jina、GitHub 凭据引用与密钥;CLI、OpenCLI、Agent Reach、Playwright、**平台后端顺序 `platformBackends`、自定义 CLI 适配 `cliAdapters`(JSON,校验与服务端共用)**;缓存、排序加权、平台规则、自定义平台、浏览器绑定 |
|
|
401
|
-
|
|
402
|
-
校验与服务端共用同一批纯函数(`pipeline/rubrics-spec.ts`、`judges/providers-spec.ts`、`judges/calibration-spec.ts`、`coverage.ts`、`budget-spec.ts`),它们不引入 Node 模块,客户端 bundle 仍只依赖宿主模块表里的少数模块(`pnpm run test:client-bundle` 检查)。
|
|
403
|
-
|
|
404
|
-
## CLI 适配 / CLI adapters
|
|
405
|
-
|
|
406
|
-
**中文** 平台不再几乎只靠 dsh-browser 内置的 OpenCLI:本插件有一层声明式的 CLI 适配(`src/cli/`)。一个适配规格 `CliAdapterSpec` 写明:命令名(`bins`,包名可与命令名不同,记在 `packageNote`)、探测契约(`probe`:`--version`、最低版本、`--help` 里必须出现的子命令 / 参数)、搜索命令(`search.argv` 是**数组**,只有 `{query}`、`{count}` 两个占位符,永不经过 shell)、输出格式(`json` / `ndjson` / `yaml` / `text`)与字段映射、`emptyWhen` / `notLoggedInPatterns`、环境变量白名单(`env.required` / `passthrough`,只写名字)、超时与输出上限。执行器按 UTF-8 解码,超时 / 取消 / 输出超限都有结构化错误:`CLI_NOT_FOUND`、`CLI_CONTRACT_MISMATCH`、`CLI_NOT_LOGGED_IN`(提示“请自行运行 `<cli> login`”)、`ENGINE_EMPTY`、`CLI_FAILED`;错误文字里不会出现环境变量的值。
|
|
407
|
-
|
|
408
|
-
**只读保证**(内置和用户自定义规格同样适用):规格必须声明 `allowedSubcommands`,argv 里用到的子命令都得在其中,且不得命中全局拒绝表(`login`、`logout`、`post`、`comment`、`delete*`、`like`、`follow`、`favorite`、`upvote`、`save`、`subscribe`、`publish`、`ask`、`article`、`pin`、`setup`、`init`、`install`、`update` 等);会读取浏览器 Cookie 或把凭据放进参数的 flag(`--cookie-source`、`--cookies-from-browser`、`--token`、`--password`……)一律拒绝。插件**永不运行任何登录命令**,也不代用户登录:需要登录的 CLI 由你自己运行它的 `login`;没有已保存登录的 xhs / rdt / zhihu 不会被运行(它们在没有登录时会自己去读浏览器 Cookie)。子进程只拿到最小环境(PATH、HOME、代理、证书,加规格点名的变量),不继承你整个环境。
|
|
409
|
-
|
|
410
|
-
**探测**只在本地:找命令、跑 `--version` 和规格里的 `--help`,检查版本与契约,缓存 60 秒;**不搜索、不登录**。同名但契约不同的程序(如 PyPI 上另一个 `bili-cli`)报 `incompatible` 并写明原因,不会被当成可用。`sources.deps` 显示每个 CLI 的 `installation`(missing / detected / incompatible)、版本、适配验证程度;`sources.status` 的每个平台带 `chain`:每条后端的顺序、状态、被跳过的原因。
|
|
411
|
-
|
|
412
|
-
| 内置规格 | 命令 | 已验证版本(2026-10-06) | 验证程度 |
|
|
413
|
-
|---|---|---|---|
|
|
414
|
-
| `bili`(bilibili) | `bili` | 0.6.2 | **live**:真实只读搜索并解析 |
|
|
415
|
-
| `gh`(github / github-issues / github-code 的回退) | `gh` | 2.101.0 | **live**(仓库搜索);issues / code 按帮助页字段,未运行 |
|
|
416
|
-
| `omnireach`(`wechat` 与多源 `omnireach`) | `omnireach` | 0.19.0-alpha | **live**(`--on wechat`);多源命令未运行 |
|
|
417
|
-
| `yt-dlp`(youtube) | `yt-dlp` | 2026.08.19 | contract-only:帮助与版本,未运行搜索 |
|
|
418
|
-
| `twitter` | `twitter` | 0.8.5 | contract-only:需要你的 Token,未运行搜索 |
|
|
419
|
-
| `xhs`(xiaohongshu) | `xhs` | 0.6.4 | contract-only:帮助 + 已装包源码里的输出形状;搜索需要你的登录 |
|
|
420
|
-
| `zhihu` | `zhihu`(PyPI `pyzhihu-cli`) | 0.2.4 | contract-only(同上) |
|
|
421
|
-
| `rdt`(reddit) | `rdt` | 0.4.2 | contract-only(同上) |
|
|
422
|
-
| 独立 `opencli`(按站点) | `opencli` | 1.8.8 | contract-only:只用 `opencli list -f json` 里有只读 `search` 命令的站点,输出用 `-f json` |
|
|
423
|
-
| `wx-search-cli`(wechat 第二后端) | `wx-search-cli` | 0.1.0 | contract-only:帮助与版本已在真实安装上探测;输出形状按上游 README / 源码,未运行搜索 |
|
|
424
|
-
| `tanso` | `tanso` | 2.0.2 | docs-only:按上游 README,未安装;只选 `bocha_web` 与 `zhihu_search` |
|
|
68
|
+
读页用 `read.fetch`(长页传 `offset` 续读),不知道用哪个来源就问 `search.recommend`。更多用法见 [搜索用法与证据包](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/search-and-evidence.md)。
|
|
425
69
|
|
|
426
|
-
|
|
70
|
+
## 工具与动作概览
|
|
427
71
|
|
|
428
|
-
|
|
72
|
+
常驻工具只有两个:`web_index({group?, action?, query?})` 渐进披露目录,`web_call({action, args})` 执行动作并返回统一信封。使用指引在随包的 skill `dsh-web-search-pro` 里。
|
|
429
73
|
|
|
430
|
-
|
|
|
431
|
-
|---|---|
|
|
432
|
-
| xiaohongshu | `opencli`(独立)→ `xhs` → `browser-opencli`(dsh-browser) |
|
|
433
|
-
| reddit | `rdt` → `opencli` → `browser-opencli` |
|
|
434
|
-
| zhihu | `zhihu` → `browser-search`(dsh-browser 搜索页) |
|
|
435
|
-
| twitter | `twitter` → `opencli` → `browser-opencli` |
|
|
436
|
-
| bilibili / youtube | `bili` / `yt-dlp` |
|
|
437
|
-
| github、github-issues、github-code | `rest`(现有 REST)→ `gh` |
|
|
438
|
-
| wechat(公众号,新) | `omnireach` → `wx-search-cli` |
|
|
439
|
-
| omnireach(多源,不自动选入) | `omnireach` |
|
|
440
|
-
| instagram / facebook | `browser-opencli`;weibo、douban、tieba、douyin、kuaishou:`browser-search` |
|
|
441
|
-
|
|
442
|
-
链上缺失、不兼容或未登录的后端被跳过并记下原因;只有整条链都不可用时平台才不可用,错误会列出每条后端缺什么、该安装或运行什么。M0 冷却、并发合并、输出整形、证据模式与显式 `platform` 的语义不变。
|
|
443
|
-
|
|
444
|
-
**启用一个 CLI**:按 `sources.deps` 给出的命令安装(`sources.install` 需要你的审批),再**自己**运行它的登录命令(`xhs login`、`rdt login`、`zhihu login`、`gh auth login`;twitter 用环境变量 `TWITTER_AUTH_TOKEN` / `TWITTER_CT0`)。想优先用它,就改链序:
|
|
445
|
-
|
|
446
|
-
web-search-pro:
|
|
447
|
-
platformBackends:
|
|
448
|
-
xiaohongshu: [xhs, opencli, browser-opencli]
|
|
449
|
-
cliAdapters: # 自定义只读适配(同样的校验),注册为平台 custom-cli:mytool
|
|
450
|
-
mytool:
|
|
451
|
-
bins: [mytool]
|
|
452
|
-
packageNote: 'mytool (npm i -g mytool)'
|
|
453
|
-
platforms: [mytool]
|
|
454
|
-
probe: { versionArgs: ['--version'], helpArgs: ['search', '--help'], mustContain: ['--json'] }
|
|
455
|
-
allowedSubcommands: [search]
|
|
456
|
-
search:
|
|
457
|
-
argv: [search, '{query}', '--limit', '{count}', '--json']
|
|
458
|
-
maxCount: 10
|
|
459
|
-
output: { format: json, itemsPath: data, fields: { url: url, title: title, snippet: summary } }
|
|
460
|
-
env: {}
|
|
461
|
-
needsLogin: false
|
|
462
|
-
timeoutMs: 30000
|
|
463
|
-
maxOutputBytes: 1048576
|
|
464
|
-
|
|
465
|
-
无效的 `cliAdapters` 条目被忽略,原因出现在 `sources.status` 的 `notes`;设置面板里两项都有 JSON 编辑器,校验与服务端是同一批函数。
|
|
466
|
-
|
|
467
|
-
**English** Platforms no longer depend almost entirely on dsh-browser's bundled OpenCLI. A declarative adapter layer (`src/cli/`) describes each standalone CLI: `bins` (the package name may differ from the command, see `packageNote`), a contract `probe` (version floor, subcommands / flags that must appear in `--help`), a `search.argv` ARRAY with `{query}` / `{count}` placeholders (never a shell string), the output format and field mapping, `env.required` / `passthrough` NAMES only, timeout and output cap. The runner decodes UTF-8 and maps failures to `CLI_NOT_FOUND`, `CLI_CONTRACT_MISMATCH`, `CLI_NOT_LOGGED_IN` (hint: run `<cli> login` yourself), `ENGINE_EMPTY`, `CLI_FAILED`; environment values never appear in errors. **Read-only guarantee** for built-in and user specs alike: every command word must be in the spec's `allowedSubcommands` and none may match the global deny-list (login, logout, post, comment, delete*, like, follow, favorite, upvote, save, subscribe, publish, ask, article, pin, setup, init, ...); flags that read browser cookies or carry credentials are rejected. The plugin never runs a login and never logs in for you; xhs / rdt / zhihu are not run without a saved login (without one they read your browser cookies themselves). Children get a minimal environment. Probes are local (version + help, cached 60 s), never search or log in; a same-named program with another contract is `incompatible`. The table above lists each built-in spec with the verified version and how far it was checked (live / contract-only / docs-only). Each platform has an ordered backend chain (table above, configurable with `platformBackends`); skipped backends show their reason in `sources.status` (`providers[].chain`) and in the error when the whole chain is down. Your own adapters go in `cliAdapters` (validated by the same rules; registered as `custom-cli:<id>`).
|
|
468
|
-
|
|
469
|
-
## 外部依赖(按需)
|
|
470
|
-
|
|
471
|
-
多数后端需要系统额外安装的工具;插件提供 `sources.deps` / `sources.install` 动作检测与安装:
|
|
472
|
-
|
|
473
|
-
| 依赖 | 用途 | 安装 |
|
|
74
|
+
| 组 | 动作 | 用途 |
|
|
474
75
|
|---|---|---|
|
|
475
|
-
|
|
|
476
|
-
|
|
|
477
|
-
|
|
|
478
|
-
|
|
|
479
|
-
|
|
|
480
|
-
|
|
|
481
|
-
|
|
|
482
|
-
| playwright / patchright | 渲染/截图后端 | 由 dsh-browser 内置;默认 Playwright,兼容场景可显式切 Patchright;缺 Chromium 时调用 `runtime.install`(`browser_call`) |
|
|
483
|
-
|
|
484
|
-
> B站后端使用 `public-clis/bilibili-cli` 的 `bili` 命令;上述提交对应上游
|
|
485
|
-
> `v0.6.2`。不要安装 PyPI 上同名的 `bili-cli 0.1.1`,它是另一个项目且不提供
|
|
486
|
-
> `bili search` 契约。`sources.deps` 会同时检查版本和 `--json` 搜索能力,避免只因
|
|
487
|
-
> PATH 中存在一个同名命令就误报可用。
|
|
488
|
-
|
|
489
|
-
> Windows 上这些 CLI 必须能被 `where` 解析(插件按 PATH 查找):安装后若
|
|
490
|
-
> `sources.deps` 仍报缺失,把可执行文件所在目录加入 PATH,或直接把
|
|
491
|
-
> `bili.exe`/`yt-dlp.exe` 放进一个已在 PATH 的目录。用 `uv` 安装时可先重定向工具目录,
|
|
492
|
-
> 避免默认写入系统盘:`UV_TOOL_DIR`、`UV_TOOL_BIN_DIR`、`UV_PYTHON_INSTALL_DIR`。
|
|
493
|
-
|
|
494
|
-
## 平台与引擎
|
|
495
|
-
|
|
496
|
-
`seam`(ctx.web/DeepSeek 原生)· `exa` · `bocha`(博查,中文强项,需 Key)· `ddg` · `bing` · `jina` · `github`(REST 搜索 API,免 CLI;可选 `$GITHUB_TOKEN`/`githubToken` 提升限额并解锁代码搜索)· `bilibili` · `v2ex` · `youtube`。默认顺序 `ddg, bing, exa, seam, jina`(免费优先),失败自动回退;失败后短时冷却,`sources.status` 可查看原因;`multi` 并行融合。
|
|
497
|
-
|
|
498
|
-
Exa 优先使用原生 API 客户端:`search.run` 可传 `exaType`、域名包含/排除、发布时间范围和 category。若没有 API Key、但启用了 CLI 后端且 `mcporter` 里配置了 Exa MCP(`https://mcp.exa.ai/mcp`,匿名、有限额),搜索会自动通过 `mcporter` 完成——**这是匿名路线(2026-10-04 实测:无 Key 的 `exa.web_search_exa` 调用成功,结果可被插件解析),证据模式的英文任务默认先用它**;该路线只支持 query + 结果数,高级筛选和 `read.contents` 仍要求 `EXA_API_KEY`。不同选项、结果数、引擎顺序和单/多引擎模式使用不同缓存指纹。
|
|
499
|
-
|
|
500
|
-
### 博查(Bocha)与搜索 provider 注册器 / Bocha and the provider registry
|
|
76
|
+
| `search` | `run` `recommend` | 多引擎 / 平台搜索与证据包;按任务推荐至多 3 个来源 |
|
|
77
|
+
| `read` | `fetch` `contents` `snapshot` | 可读化抓取与续读;Exa 批量正文;浏览器快照 |
|
|
78
|
+
| `history` | `list` `replay` `expand` `export` `delete` | 查询历史的列出、回放、展开证据块、导出、删除 |
|
|
79
|
+
| `sources` | `status` `deps` `install` | 后端探测与诊断;外部依赖检测与安装 |
|
|
80
|
+
| `rules` | `list` `upsert` `remove` `import` `export` | 按站点提取规则 |
|
|
81
|
+
| `cache` | `clear` `stats` | 缓存清理与统计 |
|
|
82
|
+
| `config` | `inspect` `set_order` `set_confidence` `upsert_cli` `remove_cli` `reset` `export` | agent 覆盖层:运行时调整搜索顺序、置信度、自定义 CLI |
|
|
501
83
|
|
|
502
|
-
|
|
84
|
+
完整参数、审批规则、输出预算与旧工具映射见 [工具与动作](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/tools-and-actions.md)。
|
|
503
85
|
|
|
504
|
-
|
|
505
|
-
- **费用**:按请求计费(余额或套餐),插件只记录请求数(用量账本里的 `bocha-search`,token 不适用、金额未知,`sources.status` 显示当日请求数)。**账号没有搜索余额 / 套餐时服务返回 HTTP 403 “You do not have enough money or package quota”**:插件把它归为不可重试的 `ENGINE_QUOTA`(与 401 的 `ENGINE_AUTH` 一样不进冷却),先到博查控制台确认搜索额度。429 按 `Retry-After` 冷却。
|
|
506
|
-
- **原生过滤**(证据模式):硬性 `site` / `exclude_site` → `include` / `exclude`,硬性 `time_window` → `freshness` 的 `起始..今天` 日期区间;其余约束(`exclude_term` 等)仍在本地校验,`verification.native / local` 如实报告。
|
|
507
|
-
- **验证状态**:2026-10-04 用已开通额度的搜索 Key 实测 4 次请求(`api.bocha.cn` 3 次、`api.bochaai.com` 1 次):成功响应的结构(`code` 为数字 200、`data.webPages.value[]`、`summary` 与约 100 字的 `snippet`)、`include` / `exclude`(含子域名)、`freshness` 日期区间都按预期生效,两个域名都接受该 Key。此前(2026-10-02,Jev Key)的 403 只说明当时账号没有搜索额度。脱敏样本:`test/fixtures/bocha-web-search.json`、`bocha-filters.json`。**博查是付费来源**:只在你配置了它的 Key 时才进入中文任务的第一轮;可选地用 `sources.budget.bocha` 限制请求数(例:账号有 1000 次请求,写 `total: 1000, daily: 50`)。
|
|
86
|
+
## 常用配置
|
|
508
87
|
|
|
509
|
-
|
|
88
|
+
写在 `$DSH_HOME/settings.yaml` 的 `web-search-pro:` 段,或用设置卡片;几乎都是热更新。
|
|
510
89
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
**English** Bocha (`bocha` / `builtin:bocha`) is a Chinese-strong key-based search source: `POST /v1/web-search`, Bearer key from `bochaApiKey` or `$BOCHA_SEARCH_API_KEY` (env name configurable via `bochaApiKeyEnv`), falling back to `$BOCHA_JEV_API_KEY` (same account). Billed per request; the plugin counts requests in the usage ledger (`bocha-search`, tokens n/a, price unknown). HTTP 401/403 are non-retryable (no cooldown; "no balance or package" is reported as quota), 429 cools down for `Retry-After`. Hard site / exclude_site / time_window are pushed down (`include` / `exclude` / `freshness` date range); everything else is verified locally and reported. The success path was verified live on 2026-10-04 (4 requests: success shape, include / exclude / freshness range, both hosts accept the key); the default host is `https://api.bocha.cn`. Search sources are registry entries (descriptor + adapter, namespaced ids with legacy aliases, duplicates throw, `register` returns an unregister function); evidence-mode source planning is described in the next section.
|
|
514
|
-
|
|
515
|
-
### 来源策略 / Source policy
|
|
516
|
-
|
|
517
|
-
**中文** 插件主要依赖**免费 / 匿名额度**的来源;付费来源是预留接口,**不会因为存在 Key 之外的任何原因成为默认**。每个来源有成本层级(`search.recommend`、`sources.status` 与目录的 `costTier` 都显示):
|
|
518
|
-
|
|
519
|
-
| 层级 | 含义 | 来源 |
|
|
90
|
+
| 键 | 默认值 | 说明 |
|
|
520
91
|
|---|---|---|
|
|
521
|
-
| `
|
|
522
|
-
| `
|
|
523
|
-
| `
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
92
|
+
| `engines` | `[ddg, bing, exa, seam, jina]` | 通用搜索的回退顺序 |
|
|
93
|
+
| `sources.priority` / `sources.disabled` | 空 | 提前 / 完全不用的来源 id(如 `disabled: [deepseek]`) |
|
|
94
|
+
| `searchMaxResults` | `8` | 普通搜索的默认结果数 |
|
|
95
|
+
| `evidence.jevMode` | `off` | 博查 Jev 评分:`off` \| `shadow` \| `control` \| `hybrid`(付费,默认不调用) |
|
|
96
|
+
| `evidence.sourcePolicy` | `default` | `anonymous-only` = 自动规划绝不用需要 Key / 账号 / 登录的来源 |
|
|
97
|
+
| `deepseekFetch` | `false` | `true` = `read.fetch` 可用 DeepSeek Fetch API(按请求计费) |
|
|
98
|
+
| `fetchDefaultChars` | `20000` | `read.fetch` 默认返回字符数 |
|
|
99
|
+
| `toolSurface` | `indexed` | `flat` = 每个动作一个工具(仅对照与调试);**需重启** |
|
|
100
|
+
| `registerProvider` | `false` | 让宿主内置 `web_search` / `web_fetch` 经过本插件;**需重启** |
|
|
528
101
|
|
|
529
|
-
|
|
530
|
-
2. 你的偏好 `sources.priority: [id, …]`:列出的、已就绪且适合任务 profile 和语言的来源按此顺序排最前;`sources.disabled: [id, …]` 的来源完全不用(自动规划和 `engines` 配置列表都不用)。
|
|
531
|
-
3. 你**配置了 Key** 的来源按任务语言 / profile 提前(最多 2 个,其余留给第二轮):这是“排名跟着用户的需要走”——配了的可以排在前面,付费的也一样。
|
|
532
|
-
4. 什么都没配置时用匿名 / 免费默认:**英文先 Exa 的无 Key 路线**(需要 `mcporter` 且其中配置了 Exa MCP),再 `ddg`;**中文** `ddg` + `bing`(Exa 擅长英文,退到第二轮);`academic` / `experience` / `docs_code` 的垂直来源不变。
|
|
102
|
+
需要重启插件的只有 `toolSurface`、`registerProvider`、`providerId`、`timeoutMs`、`memoryCacheEntries`、`dbPath`、`verbose`、`playwright.snapshotDir`。全部键与默认值见 [配置参考](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/config.md)。
|
|
533
103
|
|
|
534
|
-
|
|
104
|
+
## 文档索引
|
|
535
105
|
|
|
536
|
-
|
|
106
|
+
详细文档(中文)在 [docs/](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/README.md):
|
|
537
107
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
**中文** 无需 Key 的官方开放接口(引擎 id 同名,也可写 `builtin:<id>`):`wikipedia`(MediaWiki 搜索,按任务语言选 zh / en 版,事实与背景)· `hackernews`(Algolia,英文社区经验)· `stackexchange`(Stack Overflow,匿名每日配额 300 次 / IP,遵守 `backoff`)· `openalex`(学术;匿名额度较小,可选免费 `$OPENALEX_API_KEY` 提升约十倍,`openalexMailto` 写进 User-Agent)· `semanticscholar`(学术;匿名共享池常 429,可选 `$SEMANTIC_SCHOLAR_API_KEY`)· `anysearch`(匿名按 IP 限额,可选 `$ANYSEARCH_API_KEY`;匿名超额时服务的 402 响应会夹带自动生成的账号凭据,适配器**丢弃**它们,只报 `quota_exhausted` 并暂停一小时)· `searxng`(仅当设置 `searxngUrl` 指向你自己的实例且开启 JSON 格式时可用;该地址是你的配置,允许内网地址,不内置公共实例)。它们都走 SSRF 防护的 HTTP 通道(`allowProxyFakeIp` 同样适用),使用礼貌的 User-Agent,请求数记入用量账本(token 不适用);硬 `time_window` 下推为各自的日期过滤。它们是垂直 / 补充来源,S1 不会把它们提到网页搜索之前:`academic` 为 arxiv / openalex / pubmed(semanticscholar、ddg 留给第二轮),`experience` 为 ddg / bing 加按任务语言排序的社区来源(英文任务 hackernews / stackexchange,中文 v2ex),Wikipedia 与 Stack Overflow 分别是 `news_fact` / `general` 与 `docs_code` 的第二轮补充。`semanticscholar` 的成功响应未能实测(两次匿名请求都是 429),`searxng` 从未对真实实例运行,`sources.status` 标为 `[not verified live]`。
|
|
543
|
-
|
|
544
|
-
**来源目录**:`catalog/sources.v1.json`(随包发布,纯数据,不执行任何东西)列出已知来源——API、CLI(bili、yt-dlp、twitter、OpenCLI 各站命令及“没有 search 命令”的反例、wx-search-cli、OmniReach)、MCP 与需要 Browser 的平台——每条带认证、安装 / 配置说明、许可证、成本、`sourceFamily`、核验状态与反例。`search.recommend`(可带 `task` / `profile` / `query` / `language` / `platform`)按任务至多给出 3 个来源:已就绪的在前,其次是缺什么、怎么装;目录里没有适配器的条目永远不可执行。常驻提示只加了一句“不要遍历全部来源”。设置:`searxngUrl`、`openalexMailto`(settings.yaml,设置面板暂无字段)。
|
|
545
|
-
|
|
546
|
-
**English** Keyless official APIs: `wikipedia` (zh/en edition from the task language), `hackernews` (Algolia), `stackexchange` (anonymous quota 300/day/IP, `backoff` honoured), `openalex` (small keyless budget; optional free key; contact address in the User-Agent), `semanticscholar` (shared pool, often 429; optional key), `anysearch` (anonymous per-IP quota; a 402 may carry auto-generated credentials which the adapter discards, reporting only `quota_exhausted` and pausing for an hour) and `searxng` (only with your own `searxngUrl`, JSON format enabled; private addresses are allowed because the URL is your configuration). All use the SSRF-safe HTTP path (`allowProxyFakeIp` applies), a polite User-Agent and count requests in the usage ledger. They are vertical / supplementary: S1 never promotes them ahead of web search (academic: arxiv/openalex/pubmed; experience: language-ordered community sources; Wikipedia and Stack Overflow are second-round supplements). `catalog/sources.v1.json` is pure data about known sources (never executed); `search.recommend` returns at most 3 sources for a task, ready ones first, then what is missing and how to set it up; catalog-only entries are never executable.
|
|
547
|
-
|
|
548
|
-
### 付费 / 需 Key 的来源(预留接口)/ Keyed sources (reserved interfaces)
|
|
549
|
-
|
|
550
|
-
**中文** 七个需要 Key 的搜索 API 已有“描述符 + 适配器”,**配置 Key 之前不会执行**:没有 Key 时 `sources.status` 显示 `credential: missing`,`search.recommend` 把它们列为“缺什么 + 怎么配置”的建议(永远 `executable: false`);配置后进入可执行候选。**全部未对真实服务调用过**(没有 Key),描述符标 `verification.live=false`、`sources.status` 显示 `[not verified live]`,响应样例(`test/fixtures/*-search.json`)都在文件头声明“由文档构造,非真实抓取”。Key 解析顺序:settings 的 `keyedSources.<id>.apiKey` → 凭据引用 → 环境变量;变量名可用 `keyedSources.<id>.apiKeyEnv` 改,`baseUrl` 可改端点。
|
|
551
|
-
|
|
552
|
-
| 来源(id) | 语言 | 默认环境变量 | 官方文档 / 参考 | 原生过滤 | 说明与未采用 / 存疑字段 |
|
|
553
|
-
|---|---|---|---|---|---|
|
|
554
|
-
| Tavily(`tavily`) | en | `TAVILY_API_KEY` | docs.tavily.com 搜索 API 参考;MIT `crayonlu/dsh-web-search-tavily` | `include_domains`(≤300)/ `exclude_domains`(≤150)/ `start_date` | 不请求也不读 `answer`;432 / 433 为套餐上限(quota);未用 `country` / `language` |
|
|
555
|
-
| Brave(`brave`,family `brave`) | en | `BRAVE_API_KEY` | api-dashboard.search.brave.com 文档与 API 参考、限速指南 | 查询里的 `site:` / `-site:` / `-term`;`freshness` 日期区间 | 查询按较小的文档值截断(400 字符 / 50 词,API 参考写 600 / 75);错误体格式文档未给出;限速头 `X-RateLimit-*` 的第二个值为 0 = 月额度用尽 |
|
|
556
|
-
| Linkup(`linkup`) | en | `LINKUP_API_KEY` | docs.linkup.so `/search` 参考、错误页、限速页 | `includeDomains`(≤100)/ `excludeDomains` / `fromDate` | 只用 `outputType: searchResults`(排序后的 URL,从不用 `sourcedAnswer`);429 同时表示额度不足和并发过高,按信息判断;402(x402 付款)一律不付款 |
|
|
557
|
-
| Serper(`serper`,family `google`) | en | `SERPER_API_KEY` | **无公开 API 参考**:取自 MIT 参考代码(LangChain `GoogleSerperAPIWrapper`、`searchsuite`) | 查询里的 Google 运算符 | 只读 `organic`;`gl` / `hl` / `tbs` 未发送(取值未经确认);与其他 Google 系来源不算独立印证 |
|
|
558
|
-
| 秘塔(`metaso`) | zh | `METASO_API_KEY` | 官方页面是脚本渲染读不到;取自两个 MIT 参考(`TZHR-invest/dsh-plugins`、`HundunOnline/mcp-metaso`) | 无(全部本地核验) | 仅 `webpage` scope;`size` 取 ≤20(两个参考写 100 与 20);reader 留待后续;不请求综合摘要 |
|
|
559
|
-
| 智谱(`zhipu`) | zh | `ZHIPU_API_KEY` | docs.bigmodel.cn 搜索指南、API 参考、错误码页 | `search_domain_filter`(单个域名)/ `search_recency_filter`(仅整日 / 周 / 月 / 年窗口算原生) | 只读 `search_result`(不是 GLM 生成回答);查询 ≤70 字符;按文档的业务错误码映射(1113 / 1308–1310 quota,1302 / 1701 限速) |
|
|
560
|
-
| 百度千帆(`baidu-qianfan`,family `baidu`) | zh | `QIANFAN_API_KEY`,其次 `BAIDU_API_KEY` | ai.baidu.com AppBuilder「百度搜索」页;Qianfan v2 约定;MIT `searchsuite` | `search_filter.match.site`(≤20)/ `block_websites` / `range.page_time` | **官方页请求头自相矛盾**(表里同时列 `Authorization` 与 `X-Appbuilder-Authorization`,curl 只用后者):两个头都发送同一个 Bearer 值,待真实调用确认;只读 `references`;查询按参考 SDK 截到 72 单位(汉字算 2) |
|
|
561
|
-
|
|
562
|
-
错误映射与博查一致:401 / 403 / 402 不可重试、不进冷却,信息含余额 / 额度 / credit 的归 `ENGINE_QUOTA`;429 按 `Retry-After`(Brave 用限速头)冷却;空结果 `ENGINE_EMPTY`;请求不跟随重定向(Key 不会被转发到别的域);报错和日志不含 Key。请求数记入用量账本(provider 为来源 id,token 不适用、金额未知)。S1:就绪且有 Key 的英文来源在英文任务里、中文来源在中文任务里提前,优先级低于 Exa(10)与博查(10)(秘塔 20、智谱 30、千帆 40;Tavily 20、Brave 30、Linkup 40、Serper 60);一次至多提前 2 个,同一 `sourceFamily` 只算一个,其余留给第二轮,所以第一轮仍至多 3 个来源且留一个免费引擎。
|
|
563
|
-
|
|
564
|
-
**English** Seven keyed search APIs ship as descriptor + adapter pairs that are inactive until a key is configured: without one `sources.status` shows `credential: missing` and `search.recommend` lists them as setup steps (never executable). None was ever called live (no keys), every descriptor says `verification.live=false`, and the sample responses under `test/fixtures` state they are constructed from the docs, not captured. Key order: `keyedSources.<id>.apiKey` -> credentials ref -> environment (name via `keyedSources.<id>.apiKeyEnv`, endpoint via `baseUrl`). Defaults: `TAVILY_API_KEY`, `BRAVE_API_KEY`, `LINKUP_API_KEY`, `SERPER_API_KEY`, `METASO_API_KEY`, `ZHIPU_API_KEY`, `QIANFAN_API_KEY` (then `BAIDU_API_KEY`). Only ranked results are used (Tavily `answer`, Linkup `sourcedAnswer`, Zhipu `search_intent`, Baidu chat answers and Serper answer boxes are ignored). Serper has no public API reference (contract from MIT reference code) and is `sourceFamily: google`, so it is not independent corroboration of other Google-based sources. Metaso's docs page is script-rendered (contract from two agreeing MIT references; webpage scope only). Baidu Qianfan's page contradicts itself on the auth header, so both documented headers carry the same bearer value until a live call settles it. Errors follow Bocha: 401 / 403 / 402 are non-retryable with no cooldown (credit / quota wording is `ENGINE_QUOTA`), 429 cools down for `Retry-After`, redirects are refused, errors never contain the key. Ready keyed sources are promoted for their language below Exa / Bocha (at most two per task, one per index family) so round 1 stays at three providers with a free engine left.
|
|
565
|
-
|
|
566
|
-
## 开发
|
|
567
|
-
|
|
568
|
-
```bash
|
|
569
|
-
pnpm install
|
|
570
|
-
pnpm test
|
|
571
|
-
pnpm build # tsc src → lib
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
源码在 `src/`;`lib/` 为发布产物(已提交)。
|
|
108
|
+
- 使用:[安装与首次使用](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/install.md) · [搜索用法与证据包](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/search-and-evidence.md) · [工具与动作](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/tools-and-actions.md) · [历史管理](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/history.md)
|
|
109
|
+
- 配置:[配置参考](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/config.md) · [设置面板](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/settings-panel.md) · [来源与引擎](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/sources.md) · [内置 web 工具路由](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/web-provider.md) · [Jev 评分](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/evidence-scoring.md)
|
|
110
|
+
- 平台与 CLI:[平台登录态](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/platform-login.md) · [CLI 适配与自定义 CLI](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/cli-adapters.md) · [外部依赖](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/dependencies.md) · [自定义平台](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/custom-platforms.md) · [浏览器脚本与自动化分层](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/browser-scripts.md)
|
|
111
|
+
- 版本与开发:[兼容范围](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/compatibility.md) · [CHANGELOG](./CHANGELOG.md) · [开发](https://github.com/anweat/dsh-web-search-pro/blob/master/docs/guide/development.md) · [设计与调研记录](https://github.com/anweat/dsh-web-search-pro/tree/master/docs/dev) · [验证记录](https://github.com/anweat/dsh-web-search-pro/tree/master/docs/validation)
|
|
575
112
|
|
|
576
113
|
## License
|
|
577
114
|
|
|
578
|
-
MIT
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
## 中文社区平台登录态
|
|
582
|
-
|
|
583
|
-
zhihu / weibo / douban / tieba / douyin / kuaishou 的免登录公开接口都被风控,
|
|
584
|
-
所以走 **Playwright 驱动登录态浏览器**(借鉴 MediaCrawler 思路、MIT 独立实现,未用其签名算法):
|
|
585
|
-
|
|
586
|
-
1. 登录一次保存登录态:`node scripts/save-login.mjs all login-state.json`
|
|
587
|
-
2. 在 dsh-browser 配置中声明按域名隔离的 `authProfiles`
|
|
588
|
-
3. 在 `browserBindings` 把平台绑定到 profile;站点改版时用 `platformRules` 或 dsh-browser `rulePacks`
|
|
589
|
-
|
|
590
|
-
详见 [LOGIN.md](./LOGIN.md)。
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
## 历史管理
|
|
594
|
-
|
|
595
|
-
`history.list` 支持 kind/query/engine/platform 过滤(`kind=all` 等同省略 kind),`history.replay` 与 `history.export` 回放与导出 JSON。search/platform 回放保存的来源;fetch/snapshot 回放当次持久化的正文、HTML/截图路径。旧数据库会自动迁移 pages 表;历史上无法关联 queryId 的旧页面按 URL 做兼容回放。
|
|
596
|
-
|
|
597
|
-
## 自定义平台
|
|
598
|
-
|
|
599
|
-
在 settings.yaml 里定义任意站点(URL 模板 + 结果选择器),
|
|
600
|
-
`search.run` 传 `platform` 就能直接搜它——不需要改代码:
|
|
601
|
-
|
|
602
|
-
web-search-pro:
|
|
603
|
-
customPlatforms:
|
|
604
|
-
mybili:
|
|
605
|
-
name: '我的B站'
|
|
606
|
-
url: 'https://search.bilibili.com/all?keyword={query}'
|
|
607
|
-
item: '.bili-video-card'
|
|
608
|
-
title: '.bili-video-card__info--tit'
|
|
609
|
-
link: 'a'
|
|
610
|
-
# 需要登录时优先通过 browserBindings 绑定命名 authProfile。
|
|
611
|
-
myforum:
|
|
612
|
-
name: '某论坛'
|
|
613
|
-
url: 'https://forum.example.com/search?q={query}'
|
|
614
|
-
item: '.thread'
|
|
615
|
-
title: '.thread-title a'
|
|
616
|
-
link: '.thread-title a'
|
|
617
|
-
browserBindings:
|
|
618
|
-
myforum:
|
|
619
|
-
authProfile: forum
|
|
620
|
-
|
|
621
|
-
旧版 `customPlatforms.*.cookie` 仍兼容,但会让 Cookie 明文进入配置;新配置应使用 dsh-browser 的命名 AuthProfile,状态文件不要提交到仓库。
|
|
115
|
+
MIT。路由控制面借鉴 Agent-Reach 的后端探测、顺序选择和失败冷却思路,核心逻辑为本项目原生 TypeScript 实现。
|