中文EN

qp-exa-dynamic

browser已测通过npmMIT

基于 Exa 的网页搜索提供方:默认开启 Exa Dynamic Highlights(并带上该设置必需的 Exa-Beta 请求头),开放全部 8 种 Exa 检索类型,提供 /exa 命令在运行时改高亮、检索类型与返回条数,并注册一个可自行指定条数的 exa_search 工具。

下载量 / Downloads
898
依赖体积 / node_modules
8.64 MB
测试时间 / Tested
2026-11-07
DSH 版本 / Version
0.2.2-rc.1

安装

dsh plugin --profile web add qp-exa-dynamic

沙箱测试结果

阶段结果
1 安装✓
2 列装✓
3 挂载✓
4 启动—

四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。

README

qp-exa-dynamic

[English](README.md) | 简体中文

![npm](https://www.npmjs.com/package/qp-exa-dynamic)

Exa 支撑的 WebSearchProvider,接入

DeepSeek Harness 的 ctx.web seam。

默认开启 Exa Dynamic Highlights,提供 /exa 命令在运行时改高亮、检索类型和返回条数,以及一个

exa_search 工具让你按需走 Exa——而不改变 profile 里其它搜索用哪个提供方。

dsh plugin --profile web add qp-exa-dynamic

为什么需要它

官方的 @deepseek-ai/dsh-web-search-exa 够不到 Dynamic Highlights,是两处互相独立的硬伤:

1. 请求体写死。 它发的是 contents.highlights.highlightsPerUrl,配置里没有任何字段能影响到

dynamic。

2. Dynamic Highlights 是 beta 能力,需要请求头。 每个设了 dynamic: true 的请求都必须同时带上

Exa-Beta: dynamic-highlights-2026-08-28。那个提供方只发 authorization、content-type、

accept、user-agent——没有 Exa-Beta。缺了它 Exa 直接回 HTTP 400(实测):

   {"error":"'highlights.dynamic' is in beta. Send the 'Exa-Beta: dynamic-highlights-2026-08-28' request header to use it.","tag":"INVALID_REQUEST"}

本插件两样都发,并且彻底去掉了 highlightsPerUrl——对着真实 API 实测,Exa 已经忽略这个参数,

传 1 和传 5 返回逐字节相同的结果。改用 maxCharacters,那才是关掉动态高亮时真正生效的旋钮。

实测数据

真实 /search 调用,同一个查询,8 条结果:

配置 · 高亮字符数

官方提供方的默认值(无可用旋钮) · 51,152

本插件 dynamicHighlights: false + highlightsMaxCharacters: 1500 · 10,973

本插件 dynamicHighlights: true(默认) · 12,716

Dynamic Highlights 不是一刀切截断:它把所有召回文档拼成一条输入、只做一次前向,在全局范围分配

共享预算——好内容多给,冗余的不给。

安装

dsh plugin --profile web add qp-exa-dynamic

包自带 dsh.bundle 清单,安装后 bundle patch 会自动插入 provider 行,不需要手写。

这一步是可选的。 exa_search 工具按需走 Exa,完全不需要它;只有想让 web_search 本身也走 Exa

时才需要。在 $DSH_HOME/profiles/web/cordis.patch.yml 里覆盖 web 那一行:

- id: web
  name: '@deepseek-ai/dsh-web'
  config:
    searchProvider: exa
    fetchProvider: http
patch 是整块替换目标行的 config、不做合并,所以 fetchProvider: http 必须一起重写,
否则抓取提供方会丢。

然后给密钥,两种方式。写进插件配置:

- id: qp-exa-dynamic
  name: qp-exa-dynamic
  config:
    apiKey: '你的 Exa 密钥'

或者走环境变量。apiKey 标了 role('secret'),不会出现在任何 describe() 响应里——但明文配置文件

终究是明文配置文件,能用环境变量就尽量用。

密钥按这个顺序读取: 配置里的字面 apiKey → harness 凭据库里的 apiKeyEnv(把
EXA_API_KEY 加进去,用 GUI 的凭据卡片或 $DSH_HOME/.credentials.yaml)→ 启动环境快照(继承环境、
调用目录的 .env、Harness 主目录的 .env)。
凭据库排在快照前面,是因为快照在某些 Windows 安装上按文档就会漏掉变量——本插件正是跑在这类部署上。
如果全局选中了 exa 却报 registered but unavailable,先查凭据库,再考虑退回明文配置里的字面
apiKey。

改环境变量后要重启 dsh web。cordis.patch.yml 本身是热加载的,所以配置改动不用重启。

配置字段

全部有安全默认值,通常只需要提供密钥。

字段 · 默认值 · 含义

providerId · exa · 注册 id。只在需要与另一个 Exa 提供方共存时改。

apiKey · 未设置 · 字面密钥;不设则回退到 apiKeyEnv。

apiKeyEnv · EXA_API_KEY · apiKey 未设时读取的环境变量名。

baseURL · https://api.exa.ai · Exa 端点;会追加 /search。

searchType · auto · 检索类型,见下。可用 /exa type 运行时改。

numResults · 8 · 来源上限。可用 /exa results 运行时改。

dynamicHighlights · true · 默认开;开启时自动带上必需的 Exa-Beta 头。可用 /exa 运行时改。

highlightsMaxCharacters · 未设置 · 每页高亮上限,仅在 dynamicHighlights 为 false 时生效。

dynamicHighlights 不会和 highlightsMaxCharacters 同时发——动态开启时共享预算由 Exa 自行分配,

官方文档也明确警告两者不要并用。

/exa 命令

敲在输入框里。它直接对界面执行,不产生模型消息。

命令 · 作用

/exa · 切换 Dynamic Highlights

/exa on / /exa off · 明确设置

/exa type · 列出可用的检索类型

/exa type deep · 设置检索类型

/exa results · 报告来源上限

/exa results 3 · 设置来源上限

/exa status · 一次报全:当前状态、可用的 type 列表、真实天花板

命令声明了 input.hint(on|off|status|type deep|results 3)——**这是 Web 输入框把参数交给处理函数的

前提**,否则整行会被当成普通聊天消息发给模型。方括号和引号都会被当作透明字符,所以那个提示模板依然可

以就地编辑。/exa status 顺带把检索类型列出来,想不起来有哪些 type 时不用再敲第二条;参数打错时会回

一行可以直接照抄的示例。

写入走 settings 服务,命名空间是 Loader 的配置项 id qp-exa-dynamic,因此落在 profile patch 的

用户层,跨重启保留。清掉那一节即回到插件配置的默认值。命令会写的每个字段都在 schema 里标了

.volatile() —— 写入路径和自动生成的配置卡片都要求这一点。

实测同一个 provider 实例、同一个查询:关掉动态高亮让同一次搜索从 12,716 字符变成 57,958 字符——

差 4.6 倍,下一次搜索即刻生效。

exa_search 工具

插件还在 web_search 旁边注册了第二个面向模型的工具:

exa_search(query: string, maxResults?: integer)   // maxResults 1-50

它存在的目的是让 Exa 按需可达,而不改变 profile 里其它搜索用谁。seam 对每个 profile 只选一个

提供方——不是每次调用选,而且 searchProvider 在服务构造时就固化了——所以一个钉死官方搜索的

profile,只能靠一个绕过该选择的工具才够得着 Exa。这个工具就是直接调插件自己的 provider,不走

ctx.web.search。

路由就这么简单:

工具 · 路径 · 走谁

web_search · ctx.web · profile 钉死的那个(不受影响)

exa_search · 直连 · Exa

用哪个工具,就是选哪个提供方。 不改任何全局设置、不用重启,Exa 就在一次调用之外,而 web_search

照旧用默认提供方。

直连还顺带让这个工具拥有自己的 request.maxResults。dsh-tool-web 每次走 seam 都传自己的

searchMaxResults,所以任何经 seam 的提供方都不可能超过那个上限,要抬它就得 fork agent preset。

这个工具拥有自己的上限,于是条数从部署级天花板变成了每次调用的模型参数:

想要 · 怎么做 · 需要 fork preset 吗

某次要 20 条 · exa_search(query, maxResults: 20)——直接用话问就行 · 不需要

让 /exa results 20 生效 · /exa 命令 · 需要

因为不再经过 seam,截断这份契约改由工具自己履行(capToolResult):Exa 如果对有界请求超量返回,

sources 会被裁掉并置 truncated。它 50 的上限是自己的——动态高亮实测每条约 1.6k 字符,50 条已经是

约 2 万 token 的上下文。

没有密钥时会明确报错(no Exa API key: …),而不是悄悄回落到部署的默认提供方替你查。

web_search 旁边会多一段提示告诉模型什么时候该用它(日常检索仍走 web_search)。如果组合里没有

tools 注册表,provider 照常挂载,只是没有这个工具。

检索类型

Exa 的 type 就是延迟/质量的旋钮。8 种全部对着真实 API 验证过;同一查询、8 条结果的实测耗时:

类型 · 实测 · 用途

keyword · 464 ms · 纯关键词,最快

neural · 737 ms · 语义检索

fast · 798 ms · 快,质量损失极小

instant · 856 ms · 实时场景(对话、语音)

auto · 1,914 ms · 默认

deep-lite · 3,116 ms · 轻量综合输出

deep · 5,282 ms · 多步推理

deep-reasoning · 18,278 ms · 最难的研究任务

官方提供方的 schema 只列了 auto、keyword、neural 三种——那套已经过时。本插件全部开放。

deep* 在这个 seam 下有折扣

用本插件自己的类实测,同一查询,动态高亮开启:

类型 · 耗时 · 返回来源数

fast · 718 ms · 8

auto · 215 ms · 8

deep · 6,891 ms · 3

deep-reasoning · 14,776 ms · 4

原始 API 对 deep 是返回 8 条的;其余几条没有非空白高亮,被整个丢弃了——seam 没有别的字段能当

snippet,编造一个会让 seam 说谎。而 deep* 真正值钱的是跨来源综合出的 output,WebSearchSource

里没有字段承载它。所以在这个 seam 下,实用区间是 keyword、neural、fast、instant、`auto

…(truncated)

以上内容来自 npm 上的 README,我们只做格式化,不改内容。

版本

npm 上共发布:2,最后更新 2026-10-13

链接

GitHub · Homepage · npm

同类插件