

一、扩展评估标注集
二、在大标注集上寻参
三、（暂时不做，克制，业务发展初期不要做！做通用性强维护成本低的！）coarse rank使用LTR（各个因子做多个非线性映射+FM拟合 pairwise，参考ranknet）



0、得到all_keywords.txt.top1w.shuf.top1k.clothing_filtered（done）

方法1（目前这么做的）：
用awk，读取not_clothing.txt作为set，对all_keywords.txt.top1w.shuf.top1k每一行，如果该行在set中，则过滤，得到过滤后的文件，生成文件：all_keywords.txt.top1w.shuf.top1k.clothing_filtered

方法2：
scripts/evaluation/queries/all_keywords.txt.top1w.shuf.top1k
这个文件还混杂了一些非“服饰鞋帽”类搜索词，请先做一遍清理。
用llm做剔出，每次输入50条，提示词是：
Please filter out the queries from the following list that do not belong to the clothing, shoes, and accessories category. Output the original list of queries, one query per line, without any additional content.

然后将返回的，从原始query剔出。
生成文件：all_keywords.txt.top1w.shuf.top1k.clothing_filtered



一、扩展评估标注集

参考当前的评估框架
@scripts/evaluation/README.md @scripts/evaluation/eval_framework/framework.py 
@start_eval.sh.sh
当前，是基于54个评测样本（queries.txt），建立了自动化评估的系统，便于发现策略在这个评估集上的效果。

我需要扩大评估样本，使用all_keywords.txt.top1w.shuf.top1k.clothing_filtered（771条）为query集合，走标注流程，从而新建一个标注集。
那么以后eval-web服务，现在的Batch Evaluation按钮，应该支持多个评估集合，左侧的History，也有对应多个评估集合的评估结果，请你考虑如何支持、如何设计。请进行统一的设计，不要补丁式的支持。

统一设计方案（2026-04-17）

先校正一下现状口径：

- `scripts/evaluation/queries/queries.txt` 当前仓库里是 53 条非空 query，不是 54 条。
- `scripts/evaluation/queries/all_keywords.txt.top1w.shuf.top1k.clothing_filtered` 当前是 771 条。

当前实现的问题，不只是 UI 没有下拉框，而是“评估集”这个概念在系统里还不是一等公民：

- 配置层只有一个全局 `search_evaluation.queries_file`
- Web UI 左侧 Queries/History 默认只服务这一份 query 文件
- `batch_runs` / `build_runs` 历史记录没有 `dataset_id`
- 产物目录是全局平铺的 `batch_reports/`、`query_builds/`
- `start_eval.sh` / `start_eval_web.sh` / `tune_fusion.py` 都是通过 `queries_file` 隐式指定评估集
- `--reset-artifacts` 现在会清空整套 SQLite + query_builds，多评估集后这个语义会变得危险

所以这里要做的，不是“给 batch API 多传一个文件路径”，而是把“评估集”抽成贯穿配置、存储、API、UI、产物、调参脚本的一层统一模型。

设计目标

1. 一个 eval-web 服务同时支持多个评估集。
2. Batch Evaluation、History、调参任务都必须明确绑定某个评估集。
3. 历史结果必须可追溯到“当时到底用了哪一批 query”，不能因为 query 文件后续变更而失真。
4. 相同 `(tenant_id, query, spu_id)` 的标签尽量复用，不因为 query 同时出现在两个评估集里就重复标注。
5. 扩展到第三个、第四个评估集时，不需要再改表结构思路或前端交互模型。

核心抽象：区分“评估集”与“标签缓存”

- 评估集（Evaluation Dataset）：一组有稳定 `dataset_id` 的 query 集合，用来驱动 build、batch、history、调参。
- 标签缓存（Label Cache）：对 `(tenant_id, query_text, spu_id)` 的相关性判断结果。

这两者不要混为一谈。

建议保留现有 `relevance_labels` / `rerank_scores` 的“按 query 共享缓存”设计，不按 dataset 拆表，原因：

- 同一个 query 如果同时属于 `core_queries` 和 `clothing_top771`，其 `(query, spu_id)` 标签语义本质相同，应该复用。
- 这样新增大评估集时，只需要补齐新 query 的标签，不会对已有 query 重复做 LLM 标注。
- 真正需要 dataset 维度的是：运行历史、构建历史、覆盖率统计、产物归档、UI 选择上下文。

配置设计

把当前单一 `queries_file` 升级为“评估集注册表”。建议在 `config.yaml` 中变成：

```yaml
search_evaluation:
  artifact_root: artifacts/search_evaluation
  default_dataset_id: core_queries
  datasets:
    - dataset_id: core_queries
      display_name: Core Queries
      description: Legacy baseline query set from queries.txt
      query_file: scripts/evaluation/queries/queries.txt
      tenant_id: "163"
      language: en
      enabled: true
    - dataset_id: clothing_top771
      display_name: Clothing Filtered 771
      description: 771 filtered clothing/shoes/accessories queries
      query_file: scripts/evaluation/queries/all_keywords.txt.top1w.shuf.top1k.clothing_filtered
      tenant_id: "163"
      language: en
      enabled: true

  # 保留这些作为全局默认值；dataset 没显式覆盖时继承
  batch_top_k: 100
  audit_top_k: 100
  build_search_depth: 1000
  build_rerank_depth: 10000
```

建议点：

- `dataset_id` 是稳定主键，前后端、SQLite、历史记录、调参脚本都只认它，不认文件路径。
- `query_file` 只是这个 dataset 当前版本的来源，不是外部协议的一部分。
- 继续保留全局默认参数；以后如果某个 dataset 需要特殊 top_k / language，再支持局部覆盖。
- 为兼容老脚本，可暂时保留 `queries_file`，但只作为 fallback，在 loader 里自动转换成一个隐式 dataset；新代码不再直接依赖它。

产物目录设计

当前所有 batch 报告都平铺在 `artifacts/search_evaluation/batch_reports/` 下，后面 dataset 一多会很乱。建议改成“共享缓存 + dataset 独立产物目录”：

```text
artifacts/search_evaluation/
  search_eval.sqlite3                  # 共享标签缓存/共享 rerank 缓存/运行索引
  datasets/
    core_queries/
      batch_reports/
        <batch_id>/
          report.json
          report.md
          config_snapshot.json
          dataset_snapshot.json
          queries.txt
      query_builds/
        <run_id>.json
      audits/
        ...
    clothing_top771/
      batch_reports/
        <batch_id>/
          ...
      query_builds/
        <run_id>.json
      audits/
        ...
```

重点是每次 batch/build 都要固化 dataset snapshot：

- `dataset_id`
- `display_name`
- `query_file`
- `query_count`
- `query_sha1`
- 当次实际 queries 副本 `queries.txt`

这样即使以后 `all_keywords...clothing_filtered` 文件被重新清洗、条数变化，历史 batch 仍然可复现“当时到底评了哪些 query”。

SQLite / 存储层设计

共享缓存表可以继续保留：

- `relevance_labels(tenant_id, query_text, spu_id, ...)`
- `rerank_scores(tenant_id, query_text, spu_id, ...)`
- `query_profiles(tenant_id, query_text, prompt_version, ...)`

需要升级的是运行历史表：

1. `build_runs` 增加
   - `dataset_id`
   - `dataset_display_name`
   - `dataset_query_file`
   - `dataset_query_count`
   - `dataset_query_sha1`

2. `batch_runs` 增加
   - `dataset_id`
   - `dataset_display_name`
   - `dataset_query_file`
   - `dataset_query_count`
   - `dataset_query_sha1`

3. `list_batch_runs()` / `get_batch_run()` / `insert_batch_run()` 全部变成 dataset-aware

4. 覆盖率统计接口按 dataset 聚合，而不是简单按全库 query 聚合

   - 当前 `list_query_label_stats(tenant_id)` 是“全量 query_text 分组”
   - 以后应该是“给定 dataset_id 后，只统计该 dataset queries 的覆盖情况”

这里建议不要额外把 query 全量写进 SQLite 做注册表主数据，query 主数据仍从 config + query_file 解析即可；SQLite 只负责记录 run 时的 snapshot 元数据。

API 设计

建议把 Web API 升级成以 dataset 为主轴，而不是默认只服务一个 `query_file`：

1. `GET /api/datasets`

返回所有可用评估集：

- `dataset_id`
- `display_name`
- `description`
- `query_count`
- `query_file`
- `tenant_id`
- `language`
- `coverage_summary`

2. `GET /api/datasets/{dataset_id}/queries`

返回该 dataset 的 query 列表，以及 dataset 元信息。

3. `POST /api/search-eval`

请求体增加可选 `dataset_id`。

- 单 query 评估本身仍然可以支持任意 query 文本
- 但当页面处于某个 dataset 上下文时，返回里也带上该 dataset 信息，便于 UI 一致展示

4. `POST /api/batch-eval`

请求体优先使用 `dataset_id`，不再默认依赖服务启动时绑定的唯一 `query_file`。

建议请求模型变成：

```json
{
  "dataset_id": "clothing_top771",
  "top_k": 100,
  "auto_annotate": false,
  "language": "en",
  "force_refresh_labels": false
}
```

`queries` 字段可保留为高级/调试能力，但 UI 主路径和调参脚本主路径都应该走 `dataset_id`。

5. `GET /api/history?dataset_id=clothing_top771&limit=20`

History 默认按当前 dataset 过滤；如有需要再支持 `all=true` 看全量。

6. `GET /api/history/{batch_id}/report`

返回报告时补充 dataset 元信息，前端 report modal 里能看到这是哪个 dataset 的报告。

前端 / eval-web 交互设计

现在左侧栏写死了：

- Queries 来自 `queries.txt`
- History 没有 dataset 维度

建议改成三层结构：

1. 左上增加 Dataset Selector

- 下拉框或 tabs，显示 `Core Queries (53)`、`Clothing Filtered 771 (771)`
- 当前选中的 dataset 决定左侧 query 列表和默认 history 过滤

2. Queries 区域绑定当前 dataset

- 标题显示 dataset 名称 + query 数
- 副标题显示 query 文件路径
- 点击 query 触发单 query 评估

3. History 区域绑定当前 dataset

- 默认只显示当前 dataset 的 batch history
- 每个 item 显示 `dataset badge + batch_id + created_at + query_count + primary metrics`
- 可选再加一个 “All Datasets” 开关，但默认视角一定要是“当前 dataset”

4. 主区 Batch Evaluation 按钮绑定当前 dataset

- 点击时执行当前 dataset 的 batch，而不是对服务启动时唯一 query_file 执行
- 按钮文案建议带上 dataset 名，例如：`Batch Evaluate: Clothing Filtered 771`

5. 页面顶端增加当前 dataset 概览卡片

- `dataset_id`
- query 数
- 已有标签 query 数 / 覆盖率
- 最近一次 batch 时间

这样进入页面时，用户始终知道自己正在看哪个评估集，不会把 53 条基线集和 771 条大集合的结果混在一起。

CLI / 启动脚本设计

需要把 `--dataset-id` 提升为第一入口参数：

- `build_annotation_set.py build --dataset-id clothing_top771`
- `build_annotation_set.py batch --dataset-id clothing_top771`
- `build_annotation_set.py audit --dataset-id clothing_top771`
- `serve_eval_web.py serve --dataset-id core_queries`

说明：

- `serve` 的 `--dataset-id` 只决定页面初始选中哪个 dataset，不应该再把整个服务绑定死到一个 query 文件。
- `--queries-file` 可以保留一段时间做兼容，但内部先解析 registry；如果能映射到某个 dataset，就统一转成 `dataset_id` 处理。

`start_eval.sh` / `start_eval_web.sh` 也要同步升级：

- 读取 `REPO_EVAL_DATASET_ID`
- 保留 `REPO_EVAL_QUERIES` 兼容模式，但新用法优先 `REPO_EVAL_DATASET_ID`

额外要修正的一点：

- 当前 `--reset-artifacts` 会删整个 SQLite 和整个 `query_builds/`
- 多 dataset 后这个行为太危险
- 应拆成更明确的选项，例如：
  - `--reset-dataset-build-artifacts`
  - `--purge-shared-label-cache`（显式危险操作，默认不要碰）

调参框架联动设计

`tune_fusion.py`、`start_coarse_fusion_tuning_long.sh`、`resume_coarse_fusion_tuning_long.sh` 也必须带 dataset 维度，否则之后同一套 coarse rank 参数可能分别在 53 条集和 771 条集上跑出完全不同的结论，但 leaderboard 会混在一起。

建议：

- `tune_fusion.py` 增加 `--dataset-id`
- `summary.json` / `leaderboard.csv` / `trials.jsonl` 记录 `dataset_id`
- 调参时调用 eval-web batch API，也传 `dataset_id`
- `seed-report` 如果来自历史 batch 报告，也校验 `dataset_id` 一致

迁移方案

建议采用兼容迁移，而不是硬切：

1. 先在配置中注册两个 dataset

- `core_queries` -> `scripts/evaluation/queries/queries.txt`
- `clothing_top771` -> `scripts/evaluation/queries/all_keywords.txt.top1w.shuf.top1k.clothing_filtered`

2. 旧历史记录回填 dataset 元信息

- 如果历史记录没有 `dataset_id`，且 query 列表 hash 与 `queries.txt` 一致，则回填为 `core_queries`
- 无法确认的旧记录，标记为 `legacy_unknown`

3. UI 默认只展示 registry 中 `enabled=true` 的 dataset

4. 保留一段时间旧 CLI 参数，但 README、新脚本、新前端只文档化 dataset 模式

实施顺序

建议按下面顺序做，避免半途出现“后端支持了但前端看不出来”或者“前端能选但历史存不准”：

1. 配置层：引入 dataset registry 与解析器
2. 公共帮助层：统一的 dataset resolve / snapshot / artifact path helper
3. SQLite：`batch_runs` / `build_runs` 增加 dataset 元字段
4. Framework：`build` / `batch` / `audit` 全面改为 dataset-aware
5. Web API：新增 `/api/datasets`，History 支持 dataset filter
6. eval-web 前端：selector + dataset-scoped queries/history/batch
7. 调参脚本：`--dataset-id` 全链路打通
8. README / issue / 运维脚本更新

这套设计的关键点

- “评估集”是显式主键，不再靠文件路径暗示
- “标签缓存”继续按 `(tenant_id, query, spu_id)` 共享复用
- “历史报告”按 dataset 严格隔离并带 snapshot
- “UI 交互”始终围绕当前 dataset 上下文展开
- “调参结果”必须标记 dataset，防止不同集合上的指标被误比

结论

这件事的统一做法，不是给现有单评估集逻辑加几个 if/else，而是把 eval framework 从“单 query 文件模式”升级为“多 dataset registry 模式”。

如果按这套方案落地，后面新增第三个评估集时，应该只需要：

1. 在 `config.yaml` 注册一个新 dataset
2. 跑对应 build
3. 在 UI 中选择它做 batch / 看 history
4. 在调参脚本里指定 `--dataset-id`

而不需要再次改数据模型和交互模型。



二、在大标注集上寻参

我以前经过过一轮调参，是基于54个评测样本（queries.txt），过程中发现的最优的参数是这一组：
0.641241 {'es_bias': '7.214', 'es_exponent': '0.2025', 'text_bias': '4.0', 'text_exponent': '1.584', 'text_translation_weight': '1.4441', 'knn_text_weight': '0.1', 'knn_image_weight': '5.6232', 'knn_tie_breaker':
    '0.021', 'knn_bias': '0.0019', 'knn_exponent': '11.8477', 'knn_text_bias': '2.3125', 'knn_text_exponent': '1.1547', 'knn_image_bias': '0.9641', 'knn_image_exponent': '5.8671'}

这一组参数分布比较极端，text_bias太大（文本项得分事0~1的，加上4被稀释的很大），图片的exponent太大，不过在这个数据集上面确实是最好的，我觉得有过拟合的可能，因此要扩大数据集，先扩展标注集，然后使用扩展的标注集，继续进行寻参。

因此新建了一个标注集合，标注任务也已经完成：Clothing Filtered 771。请启动寻参任务，把任务跑起来，以后等程序跑完了应该能拿到寻参结果，下次你可以结合程序执行的结果进行结论分析。

至于调参方式，请参考以前的一轮调参：
我当时的调参需求：

请对coarse_rank fusion公式进行调参：
  目前的baseline是这一组，Primary_Metric_Score: 0.637642：
  coarse_rank:
  ...
    fusion:
      es_bias: 10.0
      es_exponent: 0.05
      text_bias: 0.1
      text_exponent: 0.35
      text_translation_weight: 1.0
      knn_text_weight: 1.0
      knn_image_weight: 2.0
      knn_tie_breaker: 0.3
      knn_bias: 0.2
      knn_exponent: 5.6
      knn_text_bias: 0.2
      knn_text_exponent: 0.0
      knn_image_bias: 0.2
      knn_image_exponent: 0.0
  评估指标在/data/saas-search/artifacts/search_evaluation/batch_reports/batch_20260415T150754Z_00b6a8aa3d.md
  请以这个为基准，发散思维，进行宽一点的范围调参。因为每次重启、评测都需要几分钟，请写一个调参的框架，基于框架、设定好多组参数、写好脚本，每轮收集效果自动的调整参数分布（因为笛卡尔积、多种参数调参成本太高，因此考虑贝叶斯调参等方法通
  过多轮迭代通过脚本自动收敛）
  每次调整参数后需要重启backend （有时候重启backend后eval-web好像也挂了，如果有可以追查原因并修复）
  ./restart.sh backend
  注意：请你调试脚本、进行一轮分析，最终要沉淀一套调参脚本，我下次可以重新跑（还是针对这组参数调参），能自动迭代（调整参数分布）、收集每组参数的指标、调优到最优的参数组合。



你当时给出的调参脚本（ “种子实验 + 随机探索 + 基于历史最优分布的自适应采样”反复迭代。搜索范围已经放宽，新增了多组偏离 baseline 很远的 seed，也保留了 baseline 附近抖动，适合长时间慢慢收敛。）：

  长时间自动调参脚本已经配好，核心文件是：

  - 调参主程序：scripts/evaluation/tune_fusion.py
  - 宽范围搜索空间：scripts/evaluation/tuning/coarse_rank_fusion_space.yaml
  - 后台长跑启动脚本：scripts/evaluation/start_coarse_fusion_tuning_long.sh
  - 续跑脚本：scripts/evaluation/resume_coarse_fusion_tuning_long.sh
  - 使用说明：scripts/evaluation/tuning/README.md

  直接启动一轮：

  ./scripts/evaluation/start_coarse_fusion_tuning_long.sh

  如果想跑更久一点：

  MAX_EVALS=48 BATCH_SIZE=3 CANDIDATE_POOL_SIZE=512 RUN_NAME=coarse_fusion_long_001 \
  ./scripts/evaluation/start_coarse_fusion_tuning_long.sh

  看日志：

  tail -f artifacts/search_evaluation/tuning_launches/<run_name>.log

  看结果目录：

  ls artifacts/search_evaluation/tuning_runs/<run_name>/

  续跑：

  ./scripts/evaluation/resume_coarse_fusion_tuning_long.sh <run_name>

  每轮会自动做这些事：

  - 生成一批候选 coarse_rank.fusion 参数
  - 写入配置并重启 backend
  - 检查 eval-web，必要时自愈拉起
  - 跑评测并收集 Primary_Metric_Score
  - 更新 trials.jsonl、leaderboard.csv、summary.md
  - 基于历史结果调整下一轮采样分布

上面只是历史的寻参任务的参考。现在请你使用新的数据集来进行寻参。
要注意的是这个数据集比较大，因此每一轮行参任务耗时会比较长，进行大范围的精细的搜索不太可取。考虑仔细分析上次寻参的结果，在已有成果上，进行精细搜索，如果已有成果还不太够，可以在小数据集上进行粗搜，然后再到大数据集上寻参。