dsh-plugin-browserskill
Tencent/BrowserSkillBrowserSkill 的 DeepSeek Harness 浏览器自动化桥接插件,通过原生浏览器工具控制可见的 Chrome 和 Edge Agent Window,支持可访问性与 VOM 页面观察、截图、隔离的多会话控制和 Web UI 实时观察浮层。
聚合网络搜索插件:一次查询并发分发到 11 个引擎(Exa、Parallel、DuckDuckGo、SearXNG、Brave、Tavily、Serper、Mojeek、DeepSeek、Anthropic、OpenAI),URL 去重后合并结果。设置面板卡片可开关后端、将 API Key 存入凭证库,并调整并发与超时。
Aggregated web search provider for DeepSeek Harness — one plugin id
unifiedthat fans out to eleven backends concurrently, merges + URL-dedups results, and stays usable even when some backends go down.
A pure-Cordis drop-in: registers ONE provider at ctx.web so the dsh-web selection rule never fires WEB_PROVIDER_AMBIGUOUS. Zero-config search out of the box (Exa + Parallel + DuckDuckGo + SearXNG are keyless); API-key backends (DeepSeek / Anthropic / OpenAI / Brave / Tavily / Serper / Mojeek) auto-activate once their key is supplied via the credentials service or env.
DSH(DeepSeek Harness)原生插件:向 ctx.web 注册唯一的聚合搜索 provider unified,内置十一后端并发 fan-out,合并 + URL 去重后返回——即使部分后端宕机仍可用。
纯 Cordis 直插:只注册一个 provider,所以 dsh-web 的选择规则永远不会触发 WEB_PROVIDER_AMBIGUOUS。零配置即可搜索——Exa + Parallel + DuckDuckGo + SearXNG 四者皆无 key、开箱即用;DeepSeek / Anthropic / OpenAI / Brave / Tavily / Serper / Mojeek 在提供 API key 后自动激活。
dsh plugin --profile web add @240xu/dsh-websearch
# 重启 dsh web 后,在 设置 → Web Search 选择 unified
dsh plugin --profile web add @240xu/dsh-plugin-hub(会带入本插件)dsh plugin --profile web remove @240xu/dsh-websearch| id | key | endpoint | how it returns | notes |
|---|---|---|---|---|
exa | none | https://mcp.exa.ai/mcp | streamable-http MCP, tool web_search_exa { query, numResults }, text blocks "Title:/URL:/Published:/Highlights:" | opencode 同源,无 key |
parallel | none | https://search.parallel.ai/mcp | streamable-http MCP, tool web_search { objective, search_queries[], session_id?, model_name? } returns JSON-string { search_id, results: [{ url, title, publish_date, excerpts[] }] } | opencode 同源,无 key |
ddg | none | https://html.duckduckgo.com/html/ | HTML scrape, decode uddg= redirect param for real URL + result__snippet | 兜底,零依赖 |
searxng | none/optional | https://searx.be/search (default) | REST JSON /search?q=, public instances unlimited | 元搜索,隐私友好 |
brave | BRAVE_API_KEY | https://api.search.brave.com/res/v1/web/search | REST JSON, independent index, 2000/mo free | 高质量,无 Google 偏见 |
tavily | TAVILY_API_KEY | https://api.tavily.com/search | REST JSON, AI-focused, answer + results[], deep search | AI 专用,含答案摘要 |
serper | SERPER_API_KEY | https://google.serper.dev/search | REST JSON, scrapes Google organic[], 2500/mo free | 极速,结构化 |
mojeek | MOJEEK_API_KEY | https://api.mojeek.com/v1/search | REST JSON, independent index, 1000/day free | 无追踪,英文为主 |
deepseek | DEEPSEEK_API_KEY | https://api.deepseek.com/anthropic/v1/messages | Anthropic Messages API + native web_search_20250305 server tool, model deepseek-v4-flash | 与 dav web-search-deepseek 同机制 |
anthropic | ANTHROPIC_API_KEY | https://api.anthropic.com/v1/messages | Anthropic Messages + web_search_20250305, model claude-sonnet-4-6 | Claude Code 同机制 |
openai | OPENAI_API_KEY | https://api.openai.com/v1/responses | Responses API native web_search tool, parse url_citation annotations | Codex 同机制 |
可用性说明:available() 只做本地配置检查(不联网探测可达性);密钥在每次搜索时按操作解析,缺失时该后端以明确错误码软失败(不阻塞其他后端)。全部缺 Key 时错误会指向设置页。
SearXNG 默认指向 https://searx.be;公共实例可用性随网络环境波动,失败时错误信息会提示换源。
在设置面板修改 searxngBaseURL 即可切换,常见候选:
| 实例 | 备注 |
|---|---|
| https://searx.be | 官方默认,部分地区不可达 |
| https://searx.tiekoetter.com | 稳定性较好 |
| https://priv.au | 隐私友好 |
| 自托管 | 最可靠,支持 API Key(searxngApiKeyEnv) |
cd ~/.dsh/profiles/web
# 在 package.json 的 dependencies 里加入:
# "@240xu/dsh-websearch": "file:/path/to/@240xu/dsh-websearch"
pnpm install
插件自带 dsh.bundle 元数据(package.json 声明 "dsh": {"bundle": {"patch": "./cordis.patch.yml"}}),把它加入 profile package.json 的 dsh.profile.bundles 列表后即自动注册,无需在 cordis.patch.yml 手写条目。
只需在 ~/.dsh/profiles/web/cordis.patch.yml 里把内置 web_search 工具指到 unified provider:
- id: web
name: '@deepseek-ai/dsh-web'
config:
searchProvider: unified
- id: web-search-deepseek
name: '@deepseek-ai/dsh-web-search-deepseek'
disabled: true
重启 dsh web 即生效。
v2.0.3 起,配置界面位于 Settings → 左侧栏「搜索 / Web Search」(顶级分区,与通用/模型/插件同级)。 每个 API Key 字段旁有 「获取 Key ↗」 直达对应平台控制台;保存即写入凭证库并立即生效。
插件向 DSH 设置面板注册 unified-search namespace(Settings → Unified Search),全部字段扁平化、开箱可编辑:
全局
| 字段 | 默认 | 说明 |
|---|---|---|
numResults | 8 | 每次搜索返回结果数(1-50) |
concurrency | 6 | 并发后端数(1-10) |
backendTimeoutMs | 30000 | 单后端超时毫秒 |
recency | any | 时间范围过滤:day / week / month / year(映射到各后端原生参数) |
language | 空 | 搜索语言,如 en、zh-CN(SearXNG/Brave/Serper 支持) |
safeSearch | true | 安全搜索开关(SearXNG/Brave 支持) |
dedupStrategy | url | url 仅按链接去重;url+title 额外合并同标题的转载镜像 |
rerank | false | 按查询词相关性重排序(确定性;并列时保持后端优先级) |
v2.1 结果策略:设置面板新增「结果策略」分区。时间/语言过滤按各后端能力自动映射——Brave freshness/country/search_lang、Tavily time_range、Serper tbs/hl/gl(Google 日期语法)、SearXNG time_range/language、DDG df;不支持的后端自动忽略对应维度。内部超时以真实原因失败(backend "" timed out after Nms),不再被静默降级为取消;Mojeek 的 API Key 改走 Authorization 头,不再出现在 URL 中。
结果健康遥测(默认开):每次搜索的 content 尾部附一行 [websearch backends] exa ✓5ms/3 · ddg ✗30000ms,
模型可据此自诊断并建议用户调整设置;设置面板「结果策略」可关闭(resultTelemetry)。
11 个后端开关:enableExa / enableParallel / enableDdg / enableSearxng(默认开);enableBrave / enableTavily / enableSerper / enableMojeek / enableDeepseek / enableAnthropic / enableOpenai(默认关)
API Key 授权(credential-ref,填环境变量名,实际 key 存于 credentials 服务或环境变量):
| 字段 | 默认引用 |
|---|---|
braveApiKeyEnv | BRAVE_API_KEY |
tavilyApiKeyEnv | TAVILY_API_KEY |
serperApiKeyEnv | SERPER_API_KEY |
mojeekApiKeyEnv | MOJEEK_API_KEY |
deepseekApiKeyEnv | DEEPSEEK_API_KEY |
anthropicApiKeyEnv | ANTHROPIC_API_KEY |
openaiApiKeyEnv | OPENAI_API_KEY |
searxngApiKeyEnv | SEARXNG_API_KEY(私有实例可选) |
Base URL / Model:每个 key-gated 后端都有对应 ${id}BaseURL;DeepSeek/Anthropic/OpenAI 另有 ${id}Model;Tavily 有 tavilySearchDepth(basic/advanced)。
环境变量兜底:未在 credentials 配置时回退读同名环境变量;DSH_UNIFIED_SEARCH_BACKENDS 可逗号分隔强制指定启用集合。
meta.volatile——0.2.0 SettingsForms 从插件 Config 自动生成表单,但 volatileForm 只保留 volatile 字段(此前 websearch 表单为空);现整棵 Config 成为 0.2.0 表单。裁决与考证见 dsh-plugin-hub/docs/reviews/cross-settings-forms.md。面向「查的多一点、查的全一点」的检索深度升级,全部 feature-gated、默认关:
multiQueryEnabled,默认关)vs / and / 比较 / 对比 / 和 分隔词,才触发;简单查询单扇出,行为与旧版完全一致(arXiv:2404.01037:盲目多查询会劣化)。deepCoverage,默认关)ceil(maxResults × 1.5),全局去重后仍按 maxResults 截断——牺牲带宽换覆盖面。search_depth: advanced(2 credits,多 snippet/URL);SearXNG 类目扩为 general,news,it(Search API categories 逗号列表)。inferExaCategory 从查询推断 data category(github / research paper / news,仅高置信注入)。GET /api/websearch/history 响应新增 backends 字段:每后端最近真实成败/延迟/熔断状态({id, ok, ms, at, failCount, cooled},无错误串无凭证),兜底冗余度可观测。ddg 专属超时上限 8s、searxng 5s(min(上限, backendTimeoutMs),全局默认仍 30s、配置更短则以配置为准)——不可达后端不再拖住整个扇出尾。ddg 由 5s 上调至 8s:实测可用的慢抓取需 ~7.7s(route6 审计),5s 会把「即将返回」误判为超时。实测见 docs/real-call-2.8.0.md。node >= 20.3(AbortSignal.any 实测需求,Node 18/20.0-20.2 首次搜索即 TypeError)。os.homedir()(此前 process.env.HOME 在 Windows 通常未设 → 相对路径 → 缓存静默失效)。breakerCooldownMs=0 显式关闭冷却(此前静默回退 60s)。dsh-websearch:open-settings(devkit 命令联动);收到时经 devkit toast 提示设置路径(宿主无分区跳转 API)。三大增量功能,全部经设置开关控制、默认为保守值,且不改变既有结果语义(v2.6.0 提示词原样保留):
sources 与首次搜索完全一致。content 追加一行 [websearch cache] cache hit, age Ns。$DSH_HOME/cache/websearch/results/,单条目一个 JSON 文件,tmp+rename 原子写,超出上限按最旧淘汰;任何 fs 错误都被吞掉,缓存坏了绝不影响搜索。cacheEnabled(默认 true)、cacheTtl(秒,默认 900)。id ⏸cooled Ns。recordBackendHealth 共用(健康条目新增 failCount / cooledUntil 字段),任意一次成功立即复位。breakerEnabled(默认 true)、breakerThreshold(默认 3)、breakerCooldownMs(默认 60000)。$DSH_HOME/cache/websearch/history.json,写前原子替换。GET /api/websearch/history → { ok, entries: [{ query, time, resultCount, backendsOk, backendsTotal }] }(只读,无凭证、无 URL)。POST /api/websearch/history/clear → { ok, cleared: true } 显式清空。historyEnabled(默认 true)。registeredSearchProvider({id:"unified"}) — the dsh-web seam's selection rule picks it unambiguously, and search() caps maxResults itself.Promise.allSettled fan-out: every enabled + available backend fires concurrently; a single backend failure is demoted to a soft null so the rest still contribute — only when ALL fail does the provider throw WEB_PROVIDER_ERROR.WEB_ABORTED when the caller's own AbortSignal fires.url keeps the historical URL-key merge with cross-backend field fill-in; url+title additionally collapses same-story syndicated mirrors (title Jaccard >= 0.9, CJK-aware tokenizer) while still enriching the kept entry.concurrency (default 6) limits simultaneous calls; backendTimeoutMs (default 30s) caps each backend.lib/util/mcp-client.js implements initialize → notifications/initialized → tools/call with Mcp-Session-Id header caching against Exa and Parallel — no dependency on the full MCP SDK.lib/
index.js # name/inject/Config/apply — registers settings + provider
provider.js # UnifiedSearchProvider — fan-out, abort demotion, dedup, truncate, concurrency, timeout
util/
abort.js # isAbortError / searchAborted / throwIfSearchAborted / maybeAbortError
mcp-client.js # streamable-http MCP client (initialize + tools/call + session cache)
log.js # recordBackendRequest / recordBackendOutcome → session event log
backends/
exa.js # Exa (web_search_exa) via mcp-client
parallel.js # Parallel (web_search) via mcp-client
ddg.js # DuckDuckGo HTML scrape (uddg redirect decode)
searxng.js # SearXNG REST JSON (keyless)
brave.js # Brave Search REST (key-gated)
tavily.js # Tavily REST (key-gated, AI answer + deep search)
serper.js # Serper.dev REST (key-gated, Google scrape)
mojeek.js # Mojeek REST (key-gated)
anthropic-like.js # shared: DeepSeek + Anthropic (Messages API + web_search_* server tool)
openai.js # OpenAI /responses + web_search tool (url_citation parsing)
index.js # unified registry + individual exports
tests/
parse.test.js # 28 unit tests for each backend's pure parse fn
provider.test.js # 5 fan-out tests (merge/dedup, abort demotion, all-fail, maxResults cap, concurrency)
node --test tests/
v2.7.0 起共 76+ tests pass(node --test "tests/*.test.js" "test/*.test.mjs")。
v2.7.0 缓存/熔断/历史三项的依据与刻意取舍(含查证过的反例):
search_depth: advanced 2 credits、max_results 0-20、include_answer、topic=news 时间加权)https://docs.tavily.com/api-reference/endpoint/search ;Exa Search API(type 分类检索、category 枚举、livecrawl;注意 2026 版 coding-agent 指南声明 neural 为 legacy 术语、且告诫勿臆造 category 值——本插件只注入 OpenAPI 枚举内的高置信值)https://exa.ai/docs/reference/search-api-guide-for-coding-agents 与 https://github.com/exa-labs/openapi-spec/blob/master/exa-openapi-spec.yaml ;SearXNG Search API(categories 逗号多类目、language)https://docs.searxng.org/dev/search_api.html ;Brave Search API(freshness、result_filter,本轮仅调研未接线)https://api-dashboard.search.brave.com/app/documentation 。多查询融合:RRF 出自 Cormack et al. 2009,RAG-Fusion 实现 https://github.com/Raudaschl/rag-fusion ;HyDE https://arxiv.org/abs/2212.10496 ;门控依据 arXiv:2404.01037(盲目多查询劣化)——因此 multiQuery 默认关、启发式从严、原查询全权重参与。
4c. per-backend 超时:不可达主机的 TCP 连接失败不受应用层超时保护,只能靠 abort 计时器兜底;ddg(HTML 抓取)与 SearXNG(公共实例无 SLA)是拖尾主力。上限取「P50 成功延迟的一个数量级」并留余量:SearXNG 5s(实测成功 ~1.3s),ddg 8s(route6 实测成功可达 ~7.7s)。
4d. 搜索历史隐私:Article 29 Working Party 意见书 WP148 https://ec.europa.eu/justice/article-29/documentation/opinion-recommendation/files/2008/wp148_en.pdf 认定查询日志(query + 时间 + 来源标识)属个人数据;EFF 六条自保建议 https://w2.eff.org/Privacy/search/searchtips.pdf 的第一条是"别在搜索词里放 PII"——说明 query 天然可能携带 PII(人名、账号、地址)。本插件的取舍:历史只存 {query, time, resultCount, backendsOk/Total}(无 IP、无 cookie、无结果 URL)、环形 50 条自动滚动即短保留、本机存储不出网、提供 historyEnabled 开关与 POST clear 端点。未做 query 脱敏:脱敏会破坏"查看最近搜了什么"的核心用途,且本机单用户场景下该数据本就在 DSH 会话日志里存在;若未来历史跨设备同步,脱敏/加密是上线前置条件。MIT © 2026 240xu
all enabled backends failed (0) - (计数自相矛盾、detail 空)并丢弃 mergedContent(Tavily
直答)。成功即空 → 正常返回 {sources:[]} + history 记录。Promise.resolve(fn()) 先执行 fn——同步抛出(如 baseURL
校验在建 timeout 前)逃出后 running 永不递减,泄漏 ≥ concurrency 后所有任务
永久排队且超时机制未武装(实测 HUNG exit=13)。改 Promise.resolve().then(fn)。createLimiter 导出供测试。89/89。搜索一下? 要求带“一”漏掉裸词)。