15 Apr, 2026
1 commit
-
背景与问题 - 现有粗排/重排依赖 `knn_query` 和 `image_knn_query` 分数,但这两路分数来自 ANN 召回,并非所有进入 rerank_window (160) 的文档都同时命中文本和图片向量召回,导致部分文档得分为 0,影响融合公式的稳定性。 - 简单扩大 ANN 的 k 无法保证 lexical 召回带来的文档也包含两路向量分;二次查询或拉回向量本地计算均有额外开销且实现复杂。 解决方案 采用 ES rescore 机制,在第一次搜索的 `window_size` 内对每个文档执行精确的向量 script_score,并将分数以 named query 形式附加到 `matched_queries` 中,供后续 coarse/rerank 优先使用。 **设计决策**: - **只补分,不改排序**:rescore 使用 `score_mode: total` 且 `rescore_query_weight: 0.0`,原始 `_score` 保持不变,避免干扰现有排序逻辑,风险最小。 - **精确分数命名**:`exact_text_knn_query` 和 `exact_image_knn_query`,便于客户端识别和回退。 - **可配置**:通过 `exact_knn_rescore_enabled` 开关和 `exact_knn_rescore_window` 控制窗口大小,默认 160。 技术实现细节 1. 配置扩展 (`config/config.yaml`, `config/loader.py`) ```yaml exact_knn_rescore_enabled: true exact_knn_rescore_window: 160 ``` 新增配置项并注入到 `RerankConfig`。 2. Searcher 构建 rescore 查询 (`search/searcher.py`) - 在 `_build_es_search_request` 中,当 `enable_rerank=True` 且配置开启时,构造 rescore 对象: - `window_size` = `exact_knn_rescore_window` - `query` 为一个 `bool` 查询,内嵌两个 `script_score` 子查询,分别计算文本和图片向量的点积相似度: ```painless // exact_text_knn_query (dotProduct(params.query_vector, 'title_embedding') + 1.0) / 2.0 // exact_image_knn_query (dotProduct(params.image_query_vector, 'image_embedding.vector') + 1.0) / 2.0 ``` - 每个 `script_score` 都设置 `_name` 为对应的 named query。 - 注意:当前实现的脚本分数**尚未乘以 knn_text_boost / knn_image_boost**,保持与原始 ANN 分数尺度对齐的后续待办。 3. RerankClient 优先读取 exact 分数 (`search/rerank_client.py`) - 在 `_extract_coarse_signals` 中,从文档的 `matched_queries` 里读取 `exact_text_knn_query` 和 `exact_image_knn_query` 分数。 - 若存在且值有效,则用作 `text_knn_score` / `image_knn_score`,并标记 `text_knn_source='exact_text_knn_query'`。 - 若不存在,则回退到原有的 `knn_query` / `image_knn_query` (ANN 分数)。 - 同时保留原始 ANN 分数到 `approx_text_knn_score` / `approx_image_knn_score` 供调试对比。 4. 调试信息增强 - `debug_info.per_result[*].ranking_funnel.coarse_rank.signals` 中输出 exact 分数、回退分数及来源标记,便于线上观察覆盖率和数值分布。 验证结果 - 通过单元测试 `tests/test_rerank_client.py` 和 `tests/test_search_rerank_window.py`,验证 exact 优先级、配置解析及 ES 请求体结构。 - 线上真实查询采样(6 个 query,top160)显示: - **exact 覆盖率达到 100%**(文本和图片均有分),解决了原 ANN 部分缺失的问题。 - 但 exact 分数与原始 ANN 分数存在量级差异(ANN/exact 中位数比值约 4.1 倍),原因是 exact 脚本未乘 boost 因子。 - 当前排名影响:粗排 top10 重叠度最低降至 1/10,最大排名漂移超过 100。 后续计划 1. 对齐 exact 分与 ANN 分的尺度:在 script_score 中乘以 `knn_text_boost` / `knn_image_boost`,并对长查询额外乘 1.4。 2. 重新评估 top10 重叠度和漂移,若收敛则可将 coarse 融合公式整体迁移至 ES rescore 阶段。 3. 当前版本保持“只补分不改排序”的安全策略,已解决核心的分数缺失问题。 涉及文件 - `config/config.yaml` - `config/loader.py` - `search/searcher.py` - `search/rerank_client.py` - `tests/test_rerank_client.py` - `tests/test_search_rerank_window.py`
01 Apr, 2026
1 commit
30 Mar, 2026
1 commit
-
…nt.py)、[search/searcher.py](/data/saas-search/search/searcher.py)、[frontend/static/js/app.js](/data/saas-search/frontend/static/js/app.js) 以及 [tests/test_rerank_client.py](/data/saas-search/tests/test_rerank_client.py)。 主要修复内容如下: - 精排现依据融合阶段得分进行排序,而非仅依据原始的 `fine_score`。 - 最终重排不再依赖独立的 `fine_scores` 数组(该数组在精排排序后可能产生同步偏差),而是直接读取命中结果附带的 `_fine_score`。 - 精排与最终重排现均通过同一计算路径生成融合调试信息,该路径同时也决定实际排序结果,从而保证记录逻辑与生产逻辑保持一致。 - 调试信息载荷更加清晰:精排和最终重排阶段都会暴露融合输入/因子以及规范的 `fusion_summary`,前端界面现在会渲染该摘要信息。 主要问题:阶段逻辑重复且存在并行的数据通道:一个通道用于计算排序,另一个通道用于组装调试字段,还有第三个通道用于传递辅助数组。这造成了潜在的差异风险。本次重构通过将阶段得分作为唯一事实来源,并让调试/前端直接消费其输出而非事后重构,降低了该风险。 验证结果: - `./.venv/bin/python -m pytest -q tests/test_rerank_client.py tests/test_search_rerank_window.py` - `./.venv/bin/python -m py_compile search/rerank_client.py search/searcher.py` 结果:`22 passed`。 当前的主流程: 1. Query 解析 2. ES 召回 3. 粗排:只用 ES 内部文本/KNN 信号 4. 款式 SKU 选择 + title suffix 5. 精排:轻量 reranker + 文本/KNN 融合 6. 最终 rerank:重 reranker + fine score + 文本/KNN 融合 7. 分页、补全字段、格式化返回 主控代码在 [searcher.py](/data/saas-search/search/searcher.py),打分与 rerank 细节在 [rerank_client.py](/data/saas-search/search/rerank_client.py),配置定义在 [schema.py](/data/saas-search/config/schema.py) 和 [config.yaml](/data/saas-search/config/config.yaml)。 **先看入口怎么决定走哪条路** 在 [searcher.py:348](/data/saas-search/search/searcher.py#L348) 开始,`search()` 先读租户语言、开关、窗口大小。 关键判断在 [searcher.py:364](/data/saas-search/search/searcher.py#L364) 到 [searcher.py:372](/data/saas-search/search/searcher.py#L372): - `rerank_window` 现在是 80,见 [config.yaml:256](/data/saas-search/config/config.yaml#L256) - `coarse_rank.input_window` 是 700,`output_window` 是 240,见 [config.yaml:231](/data/saas-search/config/config.yaml#L231) - `fine_rank.input_window` 是 240,`output_window` 是 80,见 [config.yaml:245](/data/saas-search/config/config.yaml#L245) 所以如果请求满足 `from_ + size <= rerank_window`,就进入完整漏斗: - ES 实际取前 `700` - 粗排后留 `240` - 精排后留 `80` - 最终 rerank 也只处理这 `80` - 最后再做分页切片 如果请求页超出 80,就不走后面的多阶段漏斗,直接按 ES 原逻辑返回。
27 Mar, 2026
2 commits
-
重排融合:之前有knn的配置bias和exponential。现在,文本和图片的embedding相似需要融合,融合方式是dis_max,因此需要配置: 1)各自的权重和tie_breaker 2)整个向量方面的权重(bias和exponential)
25 Mar, 2026
1 commit
24 Mar, 2026
2 commits
-
The backend now exposes a structured debug_info that is much closer to the real ranking pipeline: query_analysis now includes index_languages, query_tokens, query-vector summary, translation/enrichment plan, and translation debug. query_build now explains the ES recall plan: base-language clause, translated clauses, filters vs post-filters, KNN settings, function-score config, and related inputs. es_request distinguishes the logical DSL from the actual body sent to ES, including rerank prefetch _source. es_response now includes the initial ES ranking window stats used for score interpretation. rerank now includes execution state, templates, rendered rerank query text, window/top_n, service/meta, and the fusion formula. pagination now shows rerank-window fetch vs requested page plus page-fill details. For each result in debug_info.per_result, ranking debug is now much richer: initial rank and final rank raw ES score es_score_normalized = raw score / initial ES window max es_score_norm = min-max normalization over the initial ES window explicit normalization notes explaining that fusion does not directly consume an ES-normalized score rerank input details: doc template, title suffix, template field values, doc preview/length fusion breakdown: rerank_factor, text_factor, knn_factor, constants, raw inputs, final fused score text subcomponents: source/translation/weighted/primary/support/fallback evidence via matched_queries richer style-intent SKU debug, including selected SKU summary and intent texts
22 Mar, 2026
1 commit
18 Mar, 2026
2 commits
-
核心改动在 rerank_client.py (line 99):fuse_scores_and_resort 现在按 rerank * knn * text 的平滑乘法公式计算,优先从 hit["matched_queries"] 里取 base_query 和 knn_query,并把 _text_score / _knn_score 一并写回调试字段。为了让 KNN 也有名字,我给 top-level knn 加了 name: "knn_query",见 es_query_builder.py (line 273)。搜索执行时会在 rerank 窗口内打开 include_named_queries_score,并在显式排序时加上 track_scores,见 searcher.py (line 400) 和 es_client.py (line 224)。