dsh-plugin-browserskill
Tencent/BrowserSkillBrowserSkill 的 DeepSeek Harness 浏览器自动化桥接插件,通过原生浏览器工具控制可见的 Chrome 和 Edge Agent Window,支持可访问性与 VOM 页面观察、截图、隔离的多会话控制和 Web UI 实时观察浮层。
增强型、可持久化的网页搜索:多引擎路由(DeepSeek/Exa/DDG/Bing/Jina + GitHub/B站/YouTube/V2EX/小红书/Twitter/Reddit/RSS)、SQLite+LRU 缓存、userscript 风格抽取、Playwright 渲染。
增强型、可持久化的扩展网页搜索插件 for DeepSeek Harness(DSH)。
一个 DSH bundle 插件,把多引擎网页搜索、平台搜索、持久化缓存、受控按站增强和 Playwright 渲染打包成模型可调用的能力。常驻上下文只有 web_index / web_call 两个工具(原 11 个 web_* 工具已并入 20 个动作,见工具面)。路由控制面借鉴 Agent-Reach 的后端探测、顺序选择和失败冷却思路,核心逻辑为本项目原生 TypeScript 实现。
插件发布通道 · DSH 基线 · 兼容承诺 0.1.11 及更早的维护版本 · dsh-v0.1.1-rc.2 · 旧基线;不再维护,不与新插件混装 0.1.15 · dsh-v0.1.7-rc.2 + Browser 0.1.15 · 精确锁定该宿主版本;仍停留在 DSH 0.1.x 宿主或 dsh-browser 0.1.x 的用户请使用这一版(后续不再更新) 0.1.17 · dsh-v0.1.7-rc.2 ~ dsh-v0.2.0-rc.2 + Browser 0.1.17 · 过渡版本,已不再演进;新功能不会回到这条线 0.2.0(正式版) · 只支持 `dsh-v0.2.0-rc.2` 这一条线(peer >=0.2.0-rc.2 =0.2.0-rc.2 <0.2.1-0 + 可选 @anweat/dsh-browser ^0.2.0) · 独立 CLI 适配层与平台后端链,无破坏性变更(见 CHANGELOG.md);774 项单元测试、94 项 bench 测试,真实 Host(DSH 0.2.0-rc.2)验收
支持范围:DSH >=0.2.0-rc.2 <0.2.1-0,可选配套浏览器插件 @anweat/dsh-browser ^0.2.0(即 0.2.x,不含 0.3)。
不再支持 DSH 0.1.x 宿主线,也不再支持旧的 dsh-browser 0.1.x 线;仍在这两条线上的用户请停留在 dsh-web-search-pro 0.1.15。
dsh plugin add 会因 peer 不满足而拒绝,或在组装期点名报 incompatible。本插件已去掉为旧宿主保留的兼容分支。observe / listTargets / sessionState)即视为旧线。检测到旧线时:read.snapshot、read.fetch mode=playwright、浏览器平台与 OpenCLI 平台返回 CAPABILITY_UNAVAILABLE:dsh-browser 0.1.x is not supported by web-search-pro 0.2+; upgrade to @anweat/dsh-browser ^0.2.0;read.fetch mode=auto 不再尝试浏览器渲染,静默跳过(全部后端失败时,错误说明里会写明原因);sources.status 显示 browser: legacy (unsupported);系统提示不加浏览器那一行;旧服务上的 automationMode 不参与写操作审批(按没有 Browser 处理,standard 下询问);peer 范围的写法与 @anweat/dsh-browser 0.2.0 完全一致:每个 @deepseek-ai/dsh-* peer 都是 >=0.2.0-rc.2 <0.2.1-0。
semver.satisfies(host, range, { includePrerelease: true });真正决定装机成败的是 npm/pnpm 的默认 semver:预发布版本只有在某个比较符自带同号(同 major.minor.patch)预发布时才被判为满足。>=0.2.0-rc.2 这个比较符自带 0.2.0 的预发布,是承重的,必须保留;写成 >=0.1.7-rc.2 <0.2.1-0 这类下界在别的版本线的范围会过宿主校验、却让 dsh plugin add 报 ERESOLVE。与 @anweat/dsh-browser 必须声明同一套策略,否则两者互为 peer 时会出现 ERESOLVE。<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 构建的插件)。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 已知行为自校验。# 默认安装(面向 dsh-v0.2.0-rc.2 宿主;浏览器插件可选,需要读取渲染页 / 浏览器平台时一起装):
dsh plugin --profile web add @anweat/dsh-browser@0.2.0 dsh-web-search-pro@0.2.1
# 不需要浏览器能力时,只装本插件即可:
dsh plugin --profile web add dsh-web-search-pro@0.2.1
# 或本地目录 / tarball:
dsh plugin --profile web add ../dsh-browser ./dsh-web-search-pro
# 重启(web profile 关闭了 HMR):
dsh --profile web两个插件都必须是 profile 的直接依赖:DSH 只激活直接依赖的 bundle layer,且标准 profile 可能设置 autoInstallPeers: false。不要只安装 Web Search Pro 后依赖 peer 自动补齐。pnpm 11 若拦截 Browser 的 OpenCLI 依赖安装脚本,会要求在 profile 的pnpm-workspace.yaml中明确决定allowBuilds: { '@jackwener/opencli': false }(或在确实需要安装期下载 adapter 时自行审核后设为true),再重试安装;隔离 profile 中禁用脚本后,已发布 Browser 的 OpenCLI 入口仍可运行。
本版只支持dsh-v0.2.0-rc.2与 Browser^0.2.0,宿主或浏览器插件仍在 0.1.x 线上的,请安装dsh-web-search-pro@0.1.15。若你的 harness 是本地源码 checkout,版本号可能有出入——用
dsh plugin --profile web add ./并在 profile 的pnpm-workspace.yaml
里对齐版本后重装即可。
先确认宿主是 dsh-v0.2.0-rc.2,浏览器插件(如果装了)是 @anweat/dsh-browser@0.2.0;两者都需要作为 profile 的直接依赖。
dsh plugin --profile web add @anweat/dsh-browser@0.2.0 dsh-web-search-pro@0.2.1不再支持旧线:宿主仍是 DSH 0.1.x,或浏览器插件仍是@anweat/dsh-browser0.1.x,请不要升级,停留在dsh-web-search-pro@0.1.15。只升级本插件、保留旧浏览器插件时,本插件照常工作,但所有浏览器相关能力都会返回上面的CAPABILITY_UNAVAILABLE,sources.status显示browser: legacy (unsupported)。
破坏性变更(工具面):旧的web_search_pro、web_fetch_pro等 11 个工具名不再注册,也没有兼容包装。旧会话里对它们的调用会失败;把调用改成对应动作即可,对照表见旧工具到新动作。模型在web_call里写旧工具名会得到新动作名和翻译后的参数,web_index()根目录也列出同一张对照。已存储的数据(历史、页面、规则、证据、账本)与ctx.webprovider 不受影响。其他不兼容变化与新功能见 CHANGELOG.md。
升级完成后需要完整停止并重新启动 Web profile;仅刷新网页不会重新扫描插件的 client.js。随后依次检查:
browser_call({action:"runtime.status"}):确认 OpenCLI、playwright | patchright 运行时、automationMode 与 usagePolicy 符合预期。web_call({action:"sources.status"}):确认搜索、CLI、Agent Reach 与浏览器后端是否 ready(浏览器应显示 ready,不是 legacy (unsupported))。插件 → 已安装 中两个 bundle 各自的详情页,确认配置表单都已加载;浏览器表单负责自由度、运行时、OpenCLI 与调用缓冲。automationMode和防止过度调用的usagePolicy都属于 dsh-browser,升级不会自动改写现有配置。生产 profile 建议保留standard;unrestricted只用于隔离的自动化测试 profile,并且仍受并发、突发、页数/深度和 429/503 退避保护。
若 Clash/TUN 使用 fake-IP DNS,原生 HTTP 后端可能看到 198.18.0.0/15、fdfe:dcba:9876::/64 或 2001:2::/48(报错信息会直接提示 resolved to proxy fake-IP …)。可在可视化面板的高级设置中启用 allowProxyFakeIp;默认关闭。该开关只信任这些代理网段的 DNS 解析结果,字面 fake-IP URL、localhost 和其他私网地址仍会被 SSRF 防护拒绝。
安装并重启后,直接在 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 时自动升级)、评分、证据包照常。CAPABILITY_UNAVAILABLE,写明缺什么,并附目录里的安装说明;不会悄悄改搜网页引擎。传 allowFallback=true 才改搜该 profile 的网页引擎,证据包的 notes 会写明。平台已运行但没有结果时返回空列表和平台自己的提示(多半是缺登录),不冷却。site 约束且命中某平台域名(如 zhihu.com)并且该平台就绪时,它才排第一,并保留一个网页引擎做回退(同一上游家族只取一个,其余留给第二轮)。平台没就绪就沿用网页计划并在 notes 说明。search.recommend 对 experience 类任务可以推荐已就绪的平台(仍至多 3 个)。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: 25000cordis.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(内置 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,每个文本块上限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 文件头注释)。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.
中文 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.support rubric;rerank 以需求文本作 query。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(未验证,从未对真实服务调用过)。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 覆盖,取更严格者calibration.points 是 [原始分, 等级0..3] 的单调分段线性映射(原始分严格递增、等级不降;区间外取端点),版本号和内容哈希随结果记录;没有校准的 rerank provider 不会被使用(规则评分继续,并在 notes 说明)。不同 provider 的分数从不混用,离线缓存也按 provider+模型分开(rerank 缓存的是原始分,换校准无需重新请求)。systemone 也可选填 calibration(Laya 建议)。先用 shadow 观察,再考虑 hybrid / control。usage 结算(服务不返回则按估算记账并标 estimated;价格未声明时金额为空,不是 0;price 可选声明)。预留是原子的(SQLite usage_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/README.md “覆盖判定评测”,数字见开发计划 M9:增益有限,所以默认保持关闭,建议先用 shadow 积累数据。
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).
常驻上下文只有两个工具,常驻文本(系统提示 + 工具名、描述、参数)约 800 字符(原 11 个工具约 9300 字符):
工具 · 作用 web_index({group?, action?, query?}) · 渐进披露目录:无参数列出能力组、两个最常用调用、旧名对照;group 列出该组动作;action 给出完整参数;query 按关键词检索(至多 8 条) web_call({action, args}) · 执行一个动作,返回统一信封 {ok, action, result \ · error{code,message,hint,schema?}, truncation?};参数校验失败(INVALID_ARGS)时附带该动作的精简 schema,一次即可改对;未知动作会给出相近动作名
使用指引放在随包提供的 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)。
组 · 动作 · 作用 search · run · 多引擎搜索 + RRF 融合 + 内存/SQLite 双层缓存 + 历史;传 task / profile 得证据包;传 platform 搜单个平台(GitHub/B站/YouTube/V2EX/小红书/Twitter/Reddit/IG/FB/RSS + 知乎/微博/豆瓣/贴吧/抖音/快手,登录态走 Playwright;RSS 用 url 传 feed、query 可选过滤;平台与网页引擎走同一条注册表执行路径,可与 task/profile 同用得到证据包,见“平台来源”) · recommend · 按任务推荐至多 3 个来源(已就绪优先,其余给出缺失条件),不发搜索请求 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 不升级,只复用同后端缓存 · contents · 原生 Exa /contents 批量正文抓取(1-100 URL,需 Exa Key) · snapshot · Playwright HTML + 文本落盘;screenshot=false 时不生成 PNG(需要 dsh-browser) history · list / replay / expand / export / delete · 持久历史:过滤列出 / 按 id 回放 / 读回证据摘录及前后块 / 导出 JSON / 删除一条查询 | sources | status | 无副作用后端