jev-websearch-eval
Independent offline evaluation of the Bocha Jev decision model in a web-search evidence pipeline (vs. rule baseline and Laya): tasks, metadata-only snapshots, LLM-draft labels, judge outputs, reports
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 6, 2026
- Updated
- Oct 6, 2026
Introduction
jev-websearch-eval
English summary. Independent, offline experiments (October 2026) on whether the hosted Bocha Jev decision model (
bocha-jev-v1) is worth using inside a web-search evidence pipeline (candidate filter, evidence-block scorer, coverage judge), compared with a plain lexical rule baseline and the local Laya model. They were run while building thedsh-web-search-proplugin: 100 tasks (60 for tuning, 40 held out), about 2.2k search candidates, LLM-draft labels. What we saw: (1) for the fast title/snippet filter, rules are about as discriminative as Jev (AUC 0.91 vs 0.92) and Jev is better calibrated; (2) Jev's real value is scoring evidence blocks (nDCG@5 0.565 vs 0.378 for rules); (3) asking relevance and constraint satisfaction as separate questions is more robust than one mixed question; (4) a hybrid that sends only borderline / cross-language pairs to Jev gave the best need-hit rate at about half the requests of full Jev scoring; (5) a Jev coverage judge removes some false "covered" claims, but modestly; (6) zero-shot Laya multilingual was near random. Caveats: labels are unreviewed DeepSeek drafts, samples are small, harvests are single snapshots (2026-10-01 / 2026-10-02), one Jev model version, independent work not affiliated with Bocha, and this is not a general benchmark. Data are metadata only (no page text is redistributed). The headline r1 numbers can be recomputed withnpm run verify.
| Headline (r1, 60 tasks, DeepSeek-draft labels) | Rule | Jev | Laya |
|---|---|---|---|
| S4 filter AUC, relevance question (1368 candidates) | 0.909 | 0.915 | 0.555 |
| S4 filter calibration error (ECE, lower is better) | 0.220 | 0.050 | 0.504 |
| S4 test split: positive recall / candidates dropped (threshold chosen on calibration for recall ≥ 0.95) | 94.6% / 44.7% | 94.2% / 50.3% | 94.2% / 9.9% |
| S6 evidence-block scoring, nDCG@5 | 0.378 | 0.565 | 0.310 |
| Task-profile accuracy | 60.0% | n/a (all 422) | 26.7% |
| Held-out v2 (36-task Jev-comparable subset) | Need hit | Claimed-coverage precision | Jev requests (cold) |
|---|---|---|---|
| Rules + cross-lingual alignment | 79.8% | 71.8% | 0 |
| Hybrid: language-mismatch + borderline pairs | 83.0% | 76.0% | 74 |
| Full Jev scoring | 78.7% | 76.5% | 148 |
背景与问题
dsh-web-search-pro 是 DSH 宿主的搜索插件。0.2.0 的核心是一条证据管线:把多个来源的搜索结果去重、过滤、读取、分块、评分,在预算内选出带出处的证据摘录,并报告“哪些需求已覆盖、哪些还有缺口”,而不是把十条链接和全文丢给主模型。
管线里有几处需要“判断”:这个候选相关吗?它满足“只要 Node 22 以后的官方文档”这类语义约束吗?这个文本块是否直接回答了某个需求?这些摘录够回答吗?规则(词法重叠、正则、URL)能做一部分,但跨语言、同名实体、版本条件这类场景它会吃力。博查(Bocha)提供托管的决策模型 Jev(POST /v1/systemone,问题类型 noul 真值概率 / score 等级期望 / choice 选一个,输出是一次前向、不生成文字),开源的 Laya 提供了相同形状的本地版本。
本仓库回答的问题是:在这条管线里,Jev 在哪些环节比规则基线更好?好多少?花多少请求和延迟?本地的 Laya 能不能替代它? 它是一次独立评测,与博查无隶属关系,也不是通用基准。
管线与 Jev 的位置
flowchart LR
T[TaskSpec 与约束编译] --> R[S2 召回]
R --> N[S3 归一化/去重/硬过滤]
N --> S4{{S4 快速过滤<br/>标题+摘要}}
S4 --> F[S5 读取与分块]
F --> S6{{S6 证据评分<br/>需求×文本块}}
S6 --> K[S7 预算内选择]
K --> S8{{S8 覆盖判定}}
S8 -->|缺口且有预算| R
S8 -->|够了或达限| O[EvidencePack]
- S4 快速过滤(
noul):只看标题 + 摘要,判断相关 / 满足语义约束 / 是否导航页;只丢高置信无关项,保召回。 - S6 证据评分(
score,0–3 档):对每个(需求,文本块)对打分,决定哪些块进入证据包。 - S8 覆盖判定(
noul):对规则声称“已覆盖”的需求,问“这些摘录本身是否足以回答”,用来减少假覆盖。
规则基线始终可用;模型步骤失败或超额时回退规则。详见 docs/experiment-design.md。
实验一览
| 实验 | 问题 | 数据 | 判定器 | 结果 |
|---|---|---|---|---|
| E0 | Jev 的真实接口与文档一致吗?单一相关性问题会怎样? | 一个样例(5 题) | jev | docs/e0-api-contract.md |
| r1 | S4 过滤、逐条约束、导航页、S6 评分、profile 分类,谁更好? | v1(60 任务,1368 候选,237 金标块) | rule / laya / jev | results/r1-20261001/ |
| M2b | 完整证据包管线(S3–S8)比“前 8 条 + 全文”好多少? | v1 | rule(+ jev 评分对照) | pack-m2b、pack-m2b-v2 |
| M2c | 摘录质量(IDF 预排序、取窗) | v1 | rule / jev | pack-m2c |
| M3a | 跨语言对齐;混合评分(只把部分对交给 Jev) | v1 | rule / hybrid / jev | pack-m3a |
| M3b | 通用标识符造成的假覆盖 | v1 | rule / hybrid / jev | pack-m3b |
| gatefix | S4 门限的跨语言修复 | v1、v2 | rule / hybrid / jev | v1、v2 |
| v2 留出 | 冻结参数后在没见过的 40 个任务上还成立吗? | v2(40 任务,865 候选,271 金标块) | rule / hybrid / jev | pack-v2-heldout |
| M9 | 覆盖判定能不能减少“假覆盖”? | v1 + v2 | jev(cover.sufficient.v1) | pack-m9-v1、pack-m9-v2 |
| 0.2.0 | 发布后复算;独立验收与摘录修复 | v1 + v2 | rule(+ 历史 Jev 缓存) | pack-020-*、independent-*、fixed-* |
| Laya | 本地模型能替代 Jev 吗? | E0 样例 + r1 | laya | docs/laya.md |
每个运行目录的说明在 results/INDEX.md;全部结果表见 docs/results.md;时间线见 docs/timeline.md。
主要结论
以下全部基于未经人工复核的 LLM 初稿标注、小样本、单次采集、一个 Jev 版本;数字只用来排定开发顺序。
- r1:S4 这一步规则已接近 Jev。 relevance 题 AUC rule 0.909 / Jev 0.915 / Laya 0.555;在 calibration 上取“召回 ≥ 0.95”的阈值,test 上规则丢弃 45–51%、Jev 丢弃 50–51%,含金标准证据的候选保留 92.5–95%(n = 40,一个候选 = 2.5 个点)。Jev 的优势是校准(ECE 0.05 对规则 0.22)。逐条约束规则 AUC 0.919 / Jev 0.908;导航页都偏弱(0.73 / 0.62)。
- 把“相关性”和“约束满足”拆开问更稳。 E0 样例里,满足需求的替代方案(
PRAGMA busy_timeout)被混合问题压到 0.033;r1 里 mixed 语言任务上,拆开问的正例召回 93.9%,混问 75.8%(总体 94.2% 对 90.8%)。但 zh 上混问略好,所以是“更稳”,不是“总是更好”。 - Jev 的价值主要在 S6 证据块评分:nDCG@5 0.565 对规则 0.378、Laya 0.310;中文 0.689 对 0.462。
- 混合策略(hybrid)把请求花在边界上。 只把“需求语言 ≠ 块语言”的对交给 Jev 不值得;再加上规则评分为 1 的边界对,用约一半的 Jev 请求量,需求命中追平或超过全量 Jev(v2 留出:83.0% 对 78.7%,规则 79.8%);全量 Jev 的声称覆盖正确率最高(v2:76.5–78.7%)。
- v2 留出集没有看到明显过拟合:规则默认需求命中 74.3% / 声称覆盖正确率 71.8%,v1 为 76.8% / 67.6%;证据包约为“前 8 条 + 全文”基线的 1/10 token,而同样大小的简单截断只有 16.8% 的需求命中。
- 覆盖判定(M9):有真实但有限的增益。 v2 声称覆盖正确率 72.9% → 75.8%,但区分力中等(AUC 约 0.70–0.82),在“误降级 ≤5%”下阈值只有 0.05,只挡下 17–21% 的错误声称;默认保持关闭。
- 跨语言与假覆盖的教训。 中文需求对英文证据,词法规则会把直接答案评成 1 分;一个在每个样例块里都出现的标识符(
DatabaseSync)曾让 9 个无关块评 2 分并把需求显示为“已覆盖”;一个大块支持两个需求时,摘录丢了第二个需求的支持句却仍标已覆盖(独立验收发现,现有按块 ID 的指标看不出来)。见 docs/lessons.md。 - Laya(本地,multilingual,zero-shot)接近随机:S4 AUC 0.51–0.62,导航题反向(0.353),S6 nDCG@5 0.310;需要在自己的标注上校准,r1 之后暂停。见 docs/laya.md。
成本与延迟
r1(60 个任务,所有 S4 四套题 + S6 + S1,来自 report.md,可用 npm run verify 复核):
| 判定器 | 请求数 | 输入 token | 平均批量 | 延迟 p50 / p95 |
|---|---|---|---|---|
| Jev(托管) | 486 | 4,558,926 | 19.4 | 632 / 2,288 ms |
| Laya(本地 MPS) | 484 | 1,771,962 | 19.5 | 564 / 1,127 ms |
- Jev 的 token 按阶段差别很大:S4(标题 + 摘要)约 253 token / 问题,S6(文本块)约 1,774 token / 问题;共享 state 在每个问题里重复计费,
score问题按等级展开计费。 - 422
token_budget_exceeded:4 个请求(2 个 profile 分类批、2 个 S6 批)被拒绝,服务返回的counted_tokens略高于 32,768,共 84 个失败行;服务不截断。官方文档写的是“每个问题连同共享 state”的上限,而这些请求里单个问题远小于此,看起来实际按整个请求的展开量计——这是我们的推断,未向博查确认(见 docs/e0-api-contract.md)。集成时发送前必须按预算切批、裁剪 state 与块长度。 - 实际建议:S6 用 Jev 时只评每个需求的前 12 个块;混合策略的冷缓存请求约为全量 Jev 的一半(v2:74 对 148)。
- M9 覆盖判定:Jev 共 155 次请求、约 34 万输入 token,平均每任务约一次请求。
- DeepSeek 标注:v1 654,064 输入 / 657,189 输出 token,v2 466,581 / 408,143 token(
data/labels.*/ledger.summary.json)。不发布任何金额或账户余额。
如何复核数字
零依赖,Node ≥ 18:
npm run verify # node scripts/verify-metrics.mjs
npm run check # node scripts/check-publication.mjs(发布前检查:无路径、密钥、账户数据、页面正文)
verify 用 results/r1-20261001/results.jsonl.gz(逐条判定输出)和 data/labels.v1/(标注)重新计算 r1 的:gate 的 AUC(relevance / single 两套题,rule / laya / jev)、在 calibration 上取“召回 ≥ 0.95”的阈值、test 上的召回与丢弃比例与含金标准块候选的召回、S6 的 Spearman 与 nDCG@5,以及请求数、token、批量、延迟 p50/p95,并与 report.json 逐项比较,超出容差(默认 1e-6)退出码非 0。当前 84 / 84 项一致。
能复核什么,不能复核什么:r1 的全部数字可以复核。pack-* 系列的数字依赖页面文本块(评分、选择、金标匹配),而本仓库不含页面正文,所以只能阅读报告,不能重算;块哈希(data/candidates.* 里的 hash)让你在自己重新采集后检查页面是否漂移。
如何重跑
完整的 bench(采集、标注、判定器、报告)是公开的,在插件仓库 tag v0.2.0 的 bench/:https://github.com/anweat/dsh-web-search-pro/tree/v0.2.0/bench(它的 README 有全部参数说明)。
git clone https://github.com/anweat/dsh-web-search-pro && cd dsh-web-search-pro
git checkout v0.2.0 && pnpm install # Node 24
# 1. 采集(免费引擎,礼貌限速;搜索结果不可复现,结果会与本仓库的快照不同)
node --experimental-transform-types bench/src/harvest.ts --task-set v1 --fetch-top 4
node --experimental-transform-types bench/src/harvest.ts --task-set v2 --fetch-top 4
# 2. 标注初稿(付费,需要你自己的 DeepSeek key;设花费上限与余额下限守卫)
DEEPSEEK_API_KEY=... node --experimental-transform-types bench/src/label-llm.ts --task-set v1 --effort low --max-spend-cny <N> --min-balance-cny <M>
# 3. 判定器对照 r1(需要你自己的 Jev key;laya 需先启动本地 sidecar,见 docs/laya.md)
BOCHA_JEV_API_KEY=... node --experimental-transform-types bench/src/run-judges.ts --judges rule,laya,jev --split all --max-jev-requests 600 --run-id my-r1
node --experimental-transform-types bench/src/report.ts --run my-r1
# 4. 证据包离线评测(只读缓存;缺口才发请求,--allow-jev N 设上限)
BOCHA_JEV_API_KEY=... node --experimental-transform-types bench/src/eval-pack.ts --run-id my-pack --allow-jev 40
要点:
- 你需要自己的 Jev key,从环境变量
BOCHA_JEV_API_KEY读取,只进Authorization头,不会被打印或缓存。脚本有硬性的请求次数上限和按条目缓存(重跑不重复计费)。 - 当前的 pnpm 12 会把
pnpm run ... -- --flags里多余的--传给脚本,出现unknown argument: --;直接用上面的node --experimental-transform-types ...命令。 - 本仓库的题目模板在
rubrics/,任务集在tasks/,与 tagv0.2.0的bench/里的文件一致;只想验证接口契约可以跑BOCHA_JEV_API_KEY=... node scripts/e0-probe.mjs(约 2.5k 输入 token;--dry-run只打印请求)。 - 想在自己的数据上复现,请重新采集并标注;本仓库的缓存(
results/judge-outputs/)只含输出,不含输入,不能直接重放(见 docs/results.md §8)。 - 把新数字与本仓库对照时请记住:搜索结果会变、Jev 托管模型可能已更新。
数据说明与限制
这是什么
tasks/:两套任务集(v1 60 个用于调参,v2 40 个留出),JSONL。data/candidates.v1|v2/:元数据快照——搜索结果的 URL、标题、摘要(snippet)、引擎状态与耗时,以及每个被抓取页面的抓取时间、状态、正文长度和每个文本块的blockId / start / end / hash / length。不含任何页面正文、块文本、块标题和页面标题(scripts/strip-snapshots.mjs,脚本在npm run check里也被检验:没有text字段、没有超过 600 字符的字符串)。data/labels.v1|v2/:标注(候选相关度 0–3、逐约束 yes/no/unknown、导航页标记、每个需求的金标准证据块)与标注者元数据;ledger.summary.json只有请求数与 token 总数。note字段是标注模型写的一句话说明,规则是“≤ 80 字符才保留,否则删除”,实际最长 47 字符,没有删除任何一条。results/:各次运行的报告;r1 的逐条结果压缩为results.jsonl.gz;results/judge-outputs/是三个判定缓存的打包(输出,按哈希键)。- 发布前处理(
data/strip-summary.json):v1 删除页面文本约 292 万字符、块文本约 287 万字符;v2 约 258 万 / 255 万字符;搜索结果标题 / 摘要里的 2 个电子邮件地址(均在 v2)被替换为[email]。报告里没有页面摘录,没有删除任何报告章节。
限制(请务必读)
- 标注是 DeepSeek(
deepseek-flash,effort low)的未复核初稿,指标反映的是“与这份初稿的一致性”,不是与真值的一致性。标注输入与判定器同源,词法规则可能被高估。 - 小样本:60 + 40 个任务;r1 test 划分里含金标准块的候选只有 40 个;很多差异在 1–2 个需求之内。
- 单次采集:v1 采集于 2026-10-01,v2 于 2026-10-02;搜索结果不可复现;采集用的是免费引擎的前 10 条,每任务最多抓 4 页,搜索召回(搜索引擎能不能找到答案页)不在评测范围内。
- 一个 Jev 模型版本:
bocha-jev-v1(E0 时 API 报告的 checkpoint id 为bocha-jev-v03);托管模型以后可能更新。 - v1 的 test 划分在管线调参里也被用过,偏乐观;v2 只用于检验冻结参数。
- 独立评测,与博查无隶属关系;不是通用基准,不要外推到别的任务分布、语言、模型或提示词。
-cached报告里的 Jev 数字是历史缓存的重放,不是新鲜测量。- 详见 DATA-NOTICE.md(第三方内容、下架请求)。
许可与致谢
- 代码、文档、标注、任务集:MIT(见 LICENSE,版权人 anweat)。搜索结果里的 URL、标题和摘要归其来源所有,仅为研究目的简短引用;如需下架请在 GitHub 提 issue(DATA-NOTICE.md)。
- 引用请见 CITATION.cff。
- 感谢博查提供公开的 Jev 接口资料;感谢 Laya 上游(Apache-2.0)。本仓库不分发 Laya 权重;sidecar 脚本只是我们当时用的本地包装。