anweat/dsh-web-search-pro ↗★ 75
dsh-web-search-pro
提供多引擎路由的增强网页搜索功能 适合需要高精度、多源聚合搜索及网页内容深度提取任务的用户。
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:anweat/dsh-web-search-pro说明文档
阅读完整 README ↗快速使用与适用情形
安装并重启后,直接在 DSH 会话里要求模型调用工具即可:
请先用 web_call 的 sources.status 检查后端,然后用 search.run 搜索
"DeepSeek Harness community feedback",指定 exa、fresh=true、返回 8 条来源。
开箱默认值(不改任何设置):默认关闭 Jev(evidence.jevMode: off,证据评分用本地规则,不调用任何模型,也不需要任何模型 Key);只有配置了博查 Key(bochaApiKey / BOCHA_SEARCH_API_KEY 或凭据引用)才会使用博查,没配置时博查不在引擎列表里,也不会被自动规划选中。默认引擎是 seam、exa、ddg、bing、jina(Exa 可走无 Key 的 MCP 回退,ddg / bing 免 Key)。Defaults out of the box: Jev is off by default (evidence.jevMode: off; evidence scoring uses the local rules and no model is called, no model key is needed); Bocha is used only when you configure a key (bochaApiKey / BOCHA_SEARCH_API_KEY / a credentials ref) — without one it is not in the engine list and never planned.
模型通常不需要先翻目录:web_index() 根目录直接给出搜索与读取两个最常用调用,随包的 skill dsh-web-search-pro 带有证据模式的工作示例;没有 skill 服务时根目录附一段精简指南。
| 情形 | 推荐入口 | 说明 |
|---|---|---|
| 日常网页搜索 | search.run | 默认按配置顺序回退;需要强制刷新时传 fresh=true |
| 语义研究、社区观点 | search.run + engines=exa | 有 API Key 时走原生 Exa API;只有 Exa MCP 连接时自动经 mcporter 回退 |
| 已知 URL 的批量正文 | read.contents | 直接调用 Exa /contents,必须配置 EXA_API_KEY |
| GitHub/B站/Reddit 等平台 | search.run + platform=… | Reddit 等 OpenCLI 平台需要 Chrome 扩展在线;中文受限站点使用 AuthProfile |
| 登录后页面或私有论坛 | browserBindings + AuthProfile | Cookie 保存在本地 storageState,按域名授权,默认只读 |
| 页面改版、懒加载 | platformRules 或 RulePack | 优先改选择器;需要等待/点击/滚动时再使用有界 RulePack |
| 模型生成多步页面操作 | browser_call → automation.run_recipe | 只读步骤直接运行;页面交互按 dsh-browser 的 automationMode 决定拒绝/审批/直通 |
| 外部模型生成油猴脚本 | script.validate → script.run_userscript | 强制 @match、@grant none、禁用 @require;仅 unrestricted 跳过审批 |
| 有限泛爬取 | crawl.crawl | 匿名、默认同源;调用参数不能突破浏览器插件的页数/深度预算 |
| OpenCLI 站点适配器或浏览器桥 | opencli.status → opencli.catalog → opencli.run | 先发现精确 adapter;仅 unrestricted 跳过通用 argv 审批 |
平台来源
每个平台(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 适配。sources.status 在同一张列表里按维度报告它们的就绪状态(浏览器安装、已绑定登录、设置开关);目录条目用 provider 指向同名 id。
search.run platform=zhihu query=…只搜这个平台,历史类型仍为platform;engines=zhihu在经典搜索里同样可用。url(RSS)、authProfile、rulePack随platform传,browserBindings补全后两者。- 证据模式:
platform可与task/profile/needs/constraints同用——该平台成为 S1 / S2 的显式来源,之后门限、读取前几页(有 Browser 时自动升级)、评分、证据包照常。 - 平台不可用(缺浏览器 / 登录 / CLI / Token、被设置关闭、冷却中)时返回
CAPABILITY_UNAVAILABLE,写明缺什么,并附目录里的安装说明;不会悄悄改搜网页引擎。传allowFallback=true才改搜该 profile 的网页引擎,证据包的notes会写明。平台已运行但没有结果时返回空列表和平台自己的提示(多半是缺登录),不冷却。 - 自动规划:平台从不被自动选入第一轮;只有任务带硬
site约束且命中某平台域名(如zhihu.com)并且该平台就绪时,它才排第一,并保留一个网页引擎做回退(同一上游家族只取一个,其余留给第二轮)。平台没就绪就沿用网页计划并在notes说明。search.recommend对experience类任务可以推荐已就绪的平台(仍至多 3 个)。 - 自定义平台随设置热更新:新增、修改、删除都会同步注册表;与已有 provider 同名的键不会覆盖内置来源,
sources.status的notes会列出被拒绝的键。
先运行 sources.status 判断后端是否 ready(要不要用哪个来源,直接问 search.recommend)。指定单一引擎时失败会原样返回;不指定时才会按 engines 顺序自动回退。所有引擎都返回空结果或不可用(没有运行时错误)时,search.run 返回空结果和说明,不再报错。
两种用法
同一套证据管线有两个入口。模型是否走哪一个,取决于它看到哪些工具;第二种能在模型“顺手用内置 web_search”时仍然让证据管线生效。
(a)直接调用 web_call search.run。 模型加载 skill dsh-web-search-pro(或按常驻提示)调用 search.run,传 task / profile 得到证据包(见下节)。常驻提示和 skill 描述都写明:做网页研究时优先用它,而不是 web_search + web_fetch,因为它只返回过滤后的证据,占用的上下文少得多;读页用 read.fetch(长页用 offset 续读)。
(b)让宿主内置的 web_search / web_fetch 经过本插件。 宿主自带这两个工具,模型常常直接用它们(实测 DeepSeek 在自然任务里就是这样,绕过了 web_index)。把本插件注册并选中为 ctx.web 的 provider 后:
web_search的返回内容 = 本插件的证据包(与search.run同一渲染:resultId、缺口Gaps、“覆盖是启发式的”提示,末尾一行说明可用web_call history.expand/read.fetch展开),sources仍是来源列表;truncated只在确有来源被截掉时为真。查询即任务(task = query,needs = [query],profile 按规则推断,推断不出为general,预算为默认值)。- 证据运行有自己的期限(
provider.deadlineMs,默认 25 秒;到点返回PARTIAL的已有结果)。超时、管线报错都退回今天的普通来源列表,不会让内置工具失败;只有调用方取消会原样抛出。为避免递归,这条路径不会再调用ctx.web引擎(seam)。 web_fetch沿用同一套抓取管线,上限是fetchDefaultChars的两倍;被截断时truncated为真,并在正文末尾追加一行[Continue with web_call read.fetch url=… offset=N]。provider.evidence: off让web_search回到只返回来源列表(与旧行为完全一致)。
怎么选中(宿主的规则,不是本插件的)。 @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。
-
settings.yaml的web-search-pro:段打开注册(启动时生效,改完重启):web-search-pro: registerProvider: true providerId: web-search-pro # 默认值;与下面的 id 必须一致 provider: evidence: auto # auto(默认)| off deadlineMs: 25000 -
在 profile 的
cordis.patch.yml($DSH_HOME/profiles/ /cordis.patch.yml)里把web条目指向它:- id: web name: '@deepseek-ai/dsh-web' config: searchProvider: web-search-pro fetchProvider: web-search-pro # 只想换搜索、保留宿主抓取时,写宿主抓取 provider 的 id(宿主源码里内置的是 `http`,以所用宿主版本为准),不要省略或者只在启动环境里设
DSH_WEB_SEARCH_PROVIDER=web-search-pro(需要时再加DSH_WEB_FETCH_PROVIDER)。配置里的值与环境变量同时存在时以配置为准。不要只注册而不写fetchProvider:本插件会同时注册搜索与抓取两个 provider,若宿主的web条目没有固定抓取 provider,宿主自带的http与本插件的会同时可用,web_fetch会报WEB_PROVIDER_AMBIGUOUS。 -
重启后用
web_call sources.status检查:ctx.web route一行会写明是否已注册、web_search/web_fetch当前是否选中本插件、宿主固定的是哪个 id(未选中时还给出上面的配置提示)。宿主不暴露 provider 状态查询接口,所以本插件读的是web运行时已合并好的 id(读不到时只看环境变量,并在状态行里注明)。
和其他“联网调研”skill 并存。 用户自己安装的 skill(例如 agent-reach)也可能在描述里声称负责联网调研;本插件的 skill 描述只陈述自己做什么(中英文触发词:联网搜索 / 查证 / 调研 / 读取网页,web research / look up / verify / read a page),不声称对其他 skill 的优先权,也无法阻止模型选别的 skill。(b)正是为这种情形准备的:不管模型加载了哪个 skill,只要它最终调用内置 web_search / web_fetch,走的就是本插件的证据管线。
证据包模式(search.run 传 task 或 profile)
传 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 时行为和输出与以前完全相同。
相关度门(S4):需求与候选标题/摘要语言不同(如中文需求对英文结果)时,门限判断改用跨语言对齐(需求、query、目标与实体/必含词里的拉丁词一并参与匹配),同语言保持原有的词法规则;若门限之后保留的候选少于 3 个而更多候选存在,则按融合排序补回最靠前的、未违反硬约束的候选,标为“低相关”(sources[].lowConfidence / evidence[].lowConfidence,渲染为 (low relevance),stats.lowConfidence 计数,并在 notes 说明),不会因为相关度启发式而返回空包。
有界第二轮(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 给出实际轮数与查询数。
块评分默认用本地词法规则。可选的博查 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 的只有一句话目标、需求文字和页面块文本。
Jev 提示词(rubric):可配置、有版本 / Judge prompts: configurable and versioned
中文 发给 Jev 的问题措辞、评分等级和长度上限是带版本的 rubric(内置 score.support,另有 gate.relevance、gate.constraint 备用)。默认值与离线评测(bench/rubrics/*.v1.json)逐字相同。要调整,在设置文件 evidence.rubrics 里按 rubric id 写覆盖项,无需改代码:
evidence:
jevMode: hybrid
rubrics:
score.support:
version: v2 # 必填;内容有改动就必须换新版本号(不能是 v1)
instructions: | # 只能用 {task} {need} {candidate};必须含 {need} 和 {candidate}(≤2000 字符)
文本块本身是否直接陈述了需求所问的答案,而不只是提到该主题?
需求:{need}
文本块:{candidate}
criteria: [无关, 只提到主题, 部分回答, 直接回答且含证据] # 2–10 级,从低到高;不是 4 级时分数按比例换算到 0..3
maxStateChars: 200 # 20–2000,任务描述上限
maxCandidateChars: 1200 # 100–8000,每个文本块上限
- 校验与回退:含未知变量、缺必需变量、等级数不在 2–10、长度越界、未换版本等,整条覆盖被忽略,改用内置版本;原因出现在
sources.status的evidence.diagnostics和证据包的notes(仅启用 Jev 时)。 - 版本规则:改了措辞、等级或长度就换新
version(如 v2、v3)。每次 Jev 评分都记录id@version#内容哈希:证据包stats.jev.rubric、shadow 日志(evidence_runs.pack_json的shadow.rubric)、证据行(evidence_blocks.rubric);离线评测的缓存键同样包含 id、版本和全文,所以改提示词不会复用旧分数。 - 查看:
sources.status的evidence.rubrics列出每个 rubric 当前生效的版本、是否被覆盖。 - 恢复默认:删除对应的
evidence.rubrics.条目即可(设置页暂不提供该项的编辑界面,只读设置文件)。 - 先离线对照再上线:
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文件头注释)。 - 不让线上模型改提示词:rubric 只能由人通过设置文件修改;插件和任何线上模型都不会自动改写或上线新版本。
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. 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. 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.
评分模型 provider 与用量上限 / Judge providers and usage caps
中文 S6 的模型评分分成「协议 × provider」两层,换模型只改配置:
- 协议:
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.supportrubric;rerank以需求文本作 query。 - 内置 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(未验证,从未对真实服务调用过)。 - 自定义 provider(
jevMode/judge.mode决定怎么用;judge.mode是jevMode的中性写法,两者都设时前者优先,且control不再需要scorer: jev):
evidence:
judge:
mode: hybrid # off | shadow | control | hybrid
provider: my-reranker # 默认 bocha-jev
providers:
my-reranker:
protocol: rerank
baseUrl: http://127.0.0.1:8080/v1 # 非本机必须 https;路径可用 path 覆盖(默认 /rerank)
model: bge-reranker-v2-m3
keyRef: MY_RERANK_KEY # 可选:凭据引用或环境变量名,永远不写密钥本身
limits: { maxDocumentsPerRequest: 50, blockChars: 1000 }
calibration: # rerank 必填,见下
version: v1
points: [[0.1, 0], [0.4, 1], [0.7, 2], [0.9, 3]]
bocha-jev: # 与预设同名 = 覆盖预设的个别字段
limits: { maxQuestionsPerRequest: 16 }
budget:
perSearchInputTokens: 60000 # 默认
dailyInputTokens: 1000000 # 默认
timezone: Asia/Shanghai # 日界线时区,默认系统时区
providers: { laya-local: { dailyInputTokens: 200000 } } # 按 provider 覆盖,取更严格者
- 校准是硬要求:reranker 的相关度分数不是等级,不同模型/语言差异很大。
calibration.points是[原始分, 等级0..3]的单调分段线性映射(原始分严格递增、等级不降;区间外取端点),版本号和内容哈希随结果记录;没有校准的 rerank provider 不会被使用(规则评分继续,并在notes说明)。不同 provider 的分数从不混用,离线缓存也按 provider+模型分开(rerank 缓存的是原始分,换校准无需重新请求)。systemone也可选填calibration(Laya 建议)。先用shadow观察,再考虑hybrid/control。 - 用量账本与上限:每次模型调用前按保守估算预留 token、调用后按服务返回的
usage结算(服务不返回则按估算记账并标estimated;价格未声明时金额为空,不是 0;price可选声明)。预留是原子的(SQLiteusage_ledger表,跨搜索、跨进程,重启不清零,未结算的预留继续占用额度)。超过单次搜索或当日上限时跳过模型阶段、回退规则评分,notes出现 “model budget exceeded”。sources.status的evidence.provider/evidence.usage显示当前 provider 是否可用、今日用量与上限。注意默认单次上限 60000 输入 token 小于maxJevQuestions: 64全量发送的估算量,较大的 hybrid 搜索可能提前停在上限处。 - 离线评测:
bench/src/eval-pack.ts --provider [--providers-file providers.json]、bench/src/run-judges.ts --provider;文件格式同evidence.judge.providers。
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 .
覆盖判定(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/READ