Blame view

docs/相关性检索优化说明.md 34 KB
bcada818   tangwang   last
1
  # 相关性检索优化说明(当前实现)
7bc756c5   tangwang   优化 ES 查询构建
2
  
bcada818   tangwang   last
3
  ## 1. 文档目标
7bc756c5   tangwang   优化 ES 查询构建
4
  
0536222c   tangwang   query parser优化
5
  本文描述当前代码中的文本检索策略,重点覆盖:
7bc756c5   tangwang   优化 ES 查询构建
6
  
bcada818   tangwang   last
7
8
  - 多语言检索路由(`detector` / `translator` / `indexed` 的关系)
  - 统一文本召回表达式(无布尔 AST 分支)
0536222c   tangwang   query parser优化
9
  - 解析层与检索表达式层的职责边界
c90f80ed   tangwang   相关性优化
10
  - 重排融合打分与调试字段
bcada818   tangwang   last
11
  - 典型场景下实际生成的 ES 查询结构
7bc756c5   tangwang   优化 ES 查询构建
12
  
bcada818   tangwang   last
13
  > 说明:向量召回(KNN)是另一维度,本篇仅简要提及,不展开。
7bc756c5   tangwang   优化 ES 查询构建
14
  
bcada818   tangwang   last
15
  ## 2. 核心流程
7bc756c5   tangwang   优化 ES 查询构建
16
  
bcada818   tangwang   last
17
  查询链路(文本相关):
7bc756c5   tangwang   优化 ES 查询构建
18
  
bcada818   tangwang   last
19
  1. `QueryParser.parse()`  
35da3813   tangwang   中英混写query的优化逻辑,不适...
20
     负责产出解析事实:`query_normalized`、`rewritten_query`、`detected_language`、`translations`、`query_vector`、`query_tokens`
0536222c   tangwang   query parser优化
21
  2. `Searcher.search()`  
35da3813   tangwang   中英混写query的优化逻辑,不适...
22
     负责读取租户 `index_languages`,并将其传给 `QueryParser` 作为 `target_languages`(控制翻译目标语种);`ESQueryBuilder` 仅根据 `detected_language` 与各条译文构建子句字段,不再接收 `index_languages`
bcada818   tangwang   last
23
  2. `ESQueryBuilder._build_advanced_text_query()`  
0536222c   tangwang   query parser优化
24
     基于 `rewritten_query + detected_language + translations + index_languages` 构建 `base_query` 与 `base_query_trans_*`;并按语言动态拼接 `title/brief/description/vendor/category_*` 的 `.{lang}` 字段,叠加 shared 字段(`tags`、`option*_values`)。
bcada818   tangwang   last
25
26
  3. `build_query()`  
     统一走文本策略,不再有布尔 AST 枝路。
7bc756c5   tangwang   优化 ES 查询构建
27
  
bcada818   tangwang   last
28
  ## 3. 能力矩阵(Detector / Translator / Indexed)
7bc756c5   tangwang   优化 ES 查询构建
29
  
bcada818   tangwang   last
30
  三类能力的职责边界:
7bc756c5   tangwang   优化 ES 查询构建
31
  
bcada818   tangwang   last
32
33
34
  - **Detector**:识别 query 源语言(`detected_language`
  - **Indexed**:租户可检索语言集合(`tenant_config.*.index_languages`
  - **Translator**:源语言到目标语言的可翻译能力及实时成功率
7bc756c5   tangwang   优化 ES 查询构建
35
  
bcada818   tangwang   last
36
  ### 3.1 决策规则
7bc756c5   tangwang   优化 ES 查询构建
37
  
bcada818   tangwang   last
38
39
40
41
  1.`detected_language in index_languages`
     源语言字段做主召回;其他语言走翻译补召回(低权重)。
  2.`detected_language not in index_languages`
     翻译到 `index_languages` 是主路径;源语言字段仅作弱召回。
0536222c   tangwang   query parser优化
42
43
  3. 若翻译部分失败或全部失败:  
     当前实现不会再额外生成“原文打到其他语种字段”的兜底子句;系统保留 `base_query` 并继续执行,可观测性由 `translations` / warning / 命名子句分数提供。
7bc756c5   tangwang   优化 ES 查询构建
44
  
e874eb50   tangwang   docs
45
  ### 3.2 翻译与向量:并发提交与共享超时
7bc756c5   tangwang   优化 ES 查询构建
46
  
0536222c   tangwang   query parser优化
47
  `QueryParser.parse()` 内对翻译与向量采用线程池提交 + **一次** `concurrent.futures.wait`
7bc756c5   tangwang   优化 ES 查询构建
48
  
0536222c   tangwang   query parser优化
49
50
  - **翻译**:对调用方传入的 `target_languages` 中、除 `detected_language` 外的每个目标语种各提交一个 `translator.translate` 任务(多目标时并发执行)。
  - **查询向量**:若开启 `enable_text_embedding`,再提交一个 `text_encoder.encode` 任务。
e874eb50   tangwang   docs
51
  - 上述任务进入**同一** future 集合;例如租户索引为 `[zh, en]` 且检测语种**不在**索引内时,常为 **2 路翻译 + 1 路向量,共 3 个任务并发**,共用超时。
7bc756c5   tangwang   优化 ES 查询构建
52
  
0536222c   tangwang   query parser优化
53
  **等待预算(毫秒)**`detected_language` 是否属于调用方传入的 `target_languages` 决定(`query_config`):
e874eb50   tangwang   docs
54
55
56
57
58
  
  - **在索引内**`translation_embedding_wait_budget_ms_source_in_index`(默认较短,如 80ms)— 主召回已能打在源语种字段,翻译/向量稍慢可容忍。
  - **不在索引内**`translation_embedding_wait_budget_ms_source_not_in_index`(默认较长,如 200ms)— 翻译对可检索文本更关键,给足时间。
  
  超时未完成的任务会被丢弃并记 warning,解析继续(可能无部分译文或无数向量)。
7bc756c5   tangwang   优化 ES 查询构建
59
  
bcada818   tangwang   last
60
  ## 4. 统一文本召回表达式
ea118f2b   tangwang   build_query:根据 qu...
61
  
bcada818   tangwang   last
62
  每个语言子句的基础形态:
ea118f2b   tangwang   build_query:根据 qu...
63
  
bcada818   tangwang   last
64
65
66
  ```json
  {
    "multi_match": {
0536222c   tangwang   query parser优化
67
      "_name": "base_query|base_query_trans_xx",
bcada818   tangwang   last
68
69
70
71
72
73
74
75
      "query": "<text>",
      "fields": ["title.xx^3.0", "brief.xx^1.5", "...", "tags", "option1_values^0.5", "..."],
      "minimum_should_match": "75%",
      "tie_breaker": 0.9,
      "boost": "<按策略决定,可省略>"
    }
  }
  ```
ea118f2b   tangwang   build_query:根据 qu...
76
  
bcada818   tangwang   last
77
  最终按 `bool.should` 组合,`minimum_should_match: 1`
ea118f2b   tangwang   build_query:根据 qu...
78
  
bcada818   tangwang   last
79
  ## 5. 关键配置项(文本策略)
7bc756c5   tangwang   优化 ES 查询构建
80
  
e874eb50   tangwang   docs
81
82
83
84
85
  `query_config` 下与解析等待相关的项:
  
  - `translation_embedding_wait_budget_ms_source_in_index`
  - `translation_embedding_wait_budget_ms_source_not_in_index`
  
bcada818   tangwang   last
86
  位于 `config/config.yaml -> query_config.text_query_strategy`
7bc756c5   tangwang   优化 ES 查询构建
87
  
bcada818   tangwang   last
88
89
  - `base_minimum_should_match`
  - `translation_minimum_should_match`
0536222c   tangwang   query parser优化
90
  - `translation_boost`(所有 `base_query_trans_*` 共用)
bcada818   tangwang   last
91
  - `tie_breaker_base_query`
7bc756c5   tangwang   优化 ES 查询构建
92
  
c90f80ed   tangwang   相关性优化
93
94
  说明:
  
0536222c   tangwang   query parser优化
95
  - `phrase_query` / `keywords_query` 已从当前实现中移除,文本相关性只由 `base_query`、`base_query_trans_*` 两类子句组成。
c90f80ed   tangwang   相关性优化
96
  
bcada818   tangwang   last
97
  ## 6. 典型场景与实际 DSL
7bc756c5   tangwang   优化 ES 查询构建
98
  
bcada818   tangwang   last
99
  以下示例来自当前 `ESQueryBuilder` 生成结果(已按当前代码验证)。
7bc756c5   tangwang   优化 ES 查询构建
100
  
bcada818   tangwang   last
101
  ### 场景 A:源语种已在索引语言中,且翻译成功
7bc756c5   tangwang   优化 ES 查询构建
102
  
bcada818   tangwang   last
103
104
  - `detected_language=de`
  - `index_languages=[de,en]`
0536222c   tangwang   query parser优化
105
106
  - `rewritten_query="herren schuhe"`
  - `translations={en:"men shoes"}`
7bc756c5   tangwang   优化 ES 查询构建
107
  
bcada818   tangwang   last
108
  策略结果:
7bc756c5   tangwang   优化 ES 查询构建
109
  
0536222c   tangwang   query parser优化
110
  - `base_query`:德语字段,**不写** `multi_match.boost`
bcada818   tangwang   last
111
  - `base_query_trans_en`:英语字段,`boost=translation_boost`(默认 0.4)
7bc756c5   tangwang   优化 ES 查询构建
112
  
bcada818   tangwang   last
113
  ### 场景 B:源语种不在索引语言中,部分翻译缺失
7bc756c5   tangwang   优化 ES 查询构建
114
  
bcada818   tangwang   last
115
116
117
  - `detected_language=de`
  - `index_languages=[en,zh]`
  - 只翻译出 `en`,`zh` 失败
7bc756c5   tangwang   优化 ES 查询构建
118
  
bcada818   tangwang   last
119
  策略结果:
7bc756c5   tangwang   优化 ES 查询构建
120
  
0536222c   tangwang   query parser优化
121
122
123
  - `base_query`(德语字段):**不写** `multi_match.boost`(默认 1.0)
  - `base_query_trans_en`(英文字段):`boost=translation_boost`(如 0.4)
  - 不会生成额外中文兜底子句
7bc756c5   tangwang   优化 ES 查询构建
124
  
bcada818   tangwang   last
125
  ### 场景 C:源语种不在索引语言中,翻译全部失败
7bc756c5   tangwang   优化 ES 查询构建
126
  
bcada818   tangwang   last
127
128
  - `detected_language=de`
  - `index_languages=[en,zh]`
0536222c   tangwang   query parser优化
129
  - `translations={}`
7bc756c5   tangwang   优化 ES 查询构建
130
  
bcada818   tangwang   last
131
  策略结果:
7bc756c5   tangwang   优化 ES 查询构建
132
  
0536222c   tangwang   query parser优化
133
134
  - `base_query`(德语字段,**无** `boost` 字段)
  - 不会生成 `base_query_trans_*`
7bc756c5   tangwang   优化 ES 查询构建
135
  
0536222c   tangwang   query parser优化
136
  这意味着当前实现优先保证职责清晰与可解释性,而不是继续在 Builder 内部隐式制造“跨语种原文兜底”。
7bc756c5   tangwang   优化 ES 查询构建
137
  
0536222c   tangwang   query parser优化
138
  ## 7. QueryParser 与 Searcher / ESBuilder 的职责分工
7bc756c5   tangwang   优化 ES 查询构建
139
  
0536222c   tangwang   query parser优化
140
141
142
143
144
145
146
  - `QueryParser` 负责“解析事实”:
    - `query_normalized`
    - `rewritten_query`
    - `detected_language`
    - `translations`
    - `query_vector`
    - `query_tokens`
0536222c   tangwang   query parser优化
147
148
149
  - `Searcher` 负责“租户语境”:
    - `index_languages`
    - 将其传给 parser 作为 `target_languages`
bcada818   tangwang   last
150
151
152
  - `ESQueryBuilder` 负责“表达式展开”:
    - 动态字段组装
    - 子句权重分配
0536222c   tangwang   query parser优化
153
154
    - `base_query` / `base_query_trans_*` 子句拼接
    - 跳过“与 base_query 文本和语言完全相同”的重复翻译子句
7bc756c5   tangwang   优化 ES 查询构建
155
  
0536222c   tangwang   query parser优化
156
  这种分层让 parser 不再返回 ES 专用的“语言计划字段”,职责边界更清晰。
7bc756c5   tangwang   优化 ES 查询构建
157
  
9df421ed   tangwang   基于eval框架开始调参
158
  ## 8. 融合打分(ES + Text + KNN + Model)
c90f80ed   tangwang   相关性优化
159
160
161
162
163
  
  当前融合逻辑位于 `search/rerank_client.py`
  
  ### 8.1 文本相关性大分
  
0536222c   tangwang   query parser优化
164
  文本大分由两部分组成:
c90f80ed   tangwang   相关性优化
165
166
167
  
  - `base_query`
  - `base_query_trans_*`
c90f80ed   tangwang   相关性优化
168
169
170
171
172
  
  聚合方式:
  
  1. `source_score = base_query`
  2. `translation_score = max(base_query_trans_*)`
0536222c   tangwang   query parser优化
173
  3. 加权:
c90f80ed   tangwang   相关性优化
174
175
     - `weighted_source = source_score`
     - `weighted_translation = 0.8 * translation_score`
0536222c   tangwang   query parser优化
176
177
178
  4. 合成:
     - `primary = max(weighted_source, weighted_translation)`
     - `support = weighted_source + weighted_translation - primary`
c90f80ed   tangwang   相关性优化
179
180
181
182
     - `text_score = primary + 0.25 * support`
  
  如果以上子分都缺失,则回退到 ES `_score` 作为 `text_score`,避免纯文本召回被误打成 0。
  
9df421ed   tangwang   基于eval框架开始调参
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  ### 8.2 向量相关性大分
  
  向量不是两路分别进入最终公式,而是**先融合成一个统一的 `knn_score`**
  
  当前实现位于 `search/rerank_client.py` 的 `_collect_knn_score_components()`
  
  1. `text_knn_score = matched_queries["knn_query"]`
  2. `image_knn_score = matched_queries["image_knn_query"]`
  3. 分别乘权重:
     - `weighted_text_knn_score = knn_text_weight * text_knn_score`
     - `weighted_image_knn_score = knn_image_weight * image_knn_score`
  4. 再做一层 dismax 融合:
     - `primary_knn_score = max(weighted_text_knn_score, weighted_image_knn_score)`
     - `support_knn_score = 另一侧较弱信号`
     - `knn_score = primary_knn_score + knn_tie_breaker * support_knn_score`
  
  当前默认配置在 [config.yaml](/data/saas-search/config/config.yaml) 中是:
  
  - `knn_text_weight = 1.0`
  - `knn_image_weight = 1.0`
  - `knn_tie_breaker = 0.1`
  
  也就是说:
  
  - 现在确实是“文本 KNN + 图片 KNN 先融合成一项 `knn_score`
  -**图片权重目前并没有略高于文本权重**
  - 当前两路权重是相等的,只是通过 dismax 机制保留“主路 + 辅助路”
  
  如果业务上希望 image 语义更主导,可以把 `knn_image_weight` 调成略高于 `knn_text_weight`,例如 `1.1 ~ 1.3` 这一类小幅领先值,再观察 query 分布与 bad case。
  
  ### 8.3 各阶段融合公式
c90f80ed   tangwang   相关性优化
214
215
  
  ```python
9df421ed   tangwang   基于eval框架开始调参
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  coarse_score = (
      (es_score + es_bias) ** es_exponent
      * (text_score + text_bias) ** text_exponent
      * (knn_score + knn_bias) ** knn_exponent
  )
  
  fine_stage_score = (
      (es_score + es_bias) ** es_exponent
      * (fine_score + fine_bias) ** fine_exponent
      * (text_score + text_bias) ** text_exponent
      * (knn_score + knn_bias) ** knn_exponent
      * style_boost
  )
  
  final_score = (
      (es_score + es_bias) ** es_exponent
      * (rerank_score + rerank_bias) ** rerank_exponent
      * (fine_score + fine_bias) ** fine_exponent   # 仅当 fine rank 打开且有分数时参与
      * (text_score + text_bias) ** text_exponent
      * (knn_score + knn_bias) ** knn_exponent
      * style_boost
c90f80ed   tangwang   相关性优化
237
238
239
  )
  ```
  
9df421ed   tangwang   基于eval框架开始调参
240
241
242
243
244
245
246
  当前默认配置下:
  
  - `coarse`: `es_exponent=0.05`, `text_exponent=0.35`, `knn_exponent=0.2`
  - `fine/final`: `es_exponent=0.05`, `text_exponent=0.25`, `knn_exponent=0.2`
  - `final`: 额外有 `rerank_exponent=1.15`
  
  设计意图可以概括成:
c90f80ed   tangwang   相关性优化
247
  
9df421ed   tangwang   基于eval框架开始调参
248
249
250
251
252
  - `es_score` 不再只做 debug,而是作为全阶段都保留的弱先验
  - `text_score` 是稳定主干信号
  - `knn_score` 是统一的语义信号入口
  - `fine_score` / `rerank_score` 是越往后越贵、越强的模型因子
  - `style_boost` 只在命中已选 SKU 时乘上去
c90f80ed   tangwang   相关性优化
253
  
9df421ed   tangwang   基于eval框架开始调参
254
  ### 8.4 调试字段
c90f80ed   tangwang   相关性优化
255
256
257
258
  
  开启 `debug=true` 后,`debug_info.per_result` 会暴露:
  
  - `es_score`
9df421ed   tangwang   基于eval框架开始调参
259
  - `es_factor`
c90f80ed   tangwang   相关性优化
260
  - `rerank_score`
465f90e1   tangwang   添加LTR数据收集
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  - `retrieval_plan`(query 级)
  - `ltr_summary`(query 级)
  - `ltr_features`(doc/stage 级稳定特征块)
  
  ### 8.5 面向调参与 LTR 的新日志视角
  
  为了让 bad case 分析和后续 LTR 更直接,当前调试信息建议按下面三层来读:
  
  1. **Query 级:`retrieval_plan`**
     看这次请求到底走了哪套 KNN 计划:
     - `text_knn.k / num_candidates`
     - `image_knn.k / num_candidates`
     - 是否命中长查询分支
  
  2. **Top-N 汇总:`ltr_summary`**
     看最终第一页/前 20 个结果中,信号分布是否异常:
     - `translation_match_docs`
     - `text_knn_docs`
     - `image_knn_docs`
     - `text_fallback_to_es_docs`
     - 各类 score 的均值
  
  3. **Doc 级漏斗:`per_result[*].ranking_funnel.*.ltr_features`**
     用稳定特征字段来判断“为什么这个 doc 上去了/没上去”:
     - 文本主召回还是翻译召回在主导
     - text KNN / image KNN 哪一路在抬分
     - rerank 是否足够强,能否纠正上游 lexical 噪声
  
  推荐的诊断顺序:
  
  - 先看 `missing_relevant`,确认是不是**召回缺失**
  - 若召回到了但没进最终页,先看 `coarse_rank` 是否提前裁掉。
  - 若进了 rerank 窗口但排序差,再比较 `rerank_score` 与 `text_score/knn_score` 谁在压制谁。
  - 对多语言 query,优先确认 `source_score` 是否为 0;如果是,说明当前结果主要吃翻译召回。
c90f80ed   tangwang   相关性优化
295
296
297
  - `text_score`
  - `text_source_score`
  - `text_translation_score`
c90f80ed   tangwang   相关性优化
298
299
300
301
302
303
304
305
  - `text_primary_score`
  - `text_support_score`
  - `knn_score`
  - `fused_score`
  - `matched_queries`
  
  `debug_info.query_analysis` 还会暴露:
  
0536222c   tangwang   query parser优化
306
307
308
  - `translations`
  - `detected_language`
  - `rewritten_query`
c90f80ed   tangwang   相关性优化
309
310
311
312
  
  这些字段用于检索效果评估与 bad case 归因。
  
  ## 9. 兼容与注意事项
7bc756c5   tangwang   优化 ES 查询构建
313
  
bcada818   tangwang   last
314
315
  1. 当前文本主链路已移除布尔 AST 分支。  
  2. 文档中的旧描述(如 `operator: AND` 固定开启)不再适用,当前实现未强制设置该参数。  
0536222c   tangwang   query parser优化
316
  3. `HanLP` 为必需依赖;当前 parser 不再提供轻量 fallback。  
bcada818   tangwang   last
317
318
319
320
  4. 若后续扩展到更多语种,请确保:
     - mapping 中存在对应 `.<lang>` 字段
     - `index_languages` 配置在支持列表内
     - 翻译 provider 对目标语种可用
7bc756c5   tangwang   优化 ES 查询构建
321
  
c90f80ed   tangwang   相关性优化
322
323
324
325
326
327
328
329
330
331
332
  ## 10. 评估与复现
  
  建议使用项目根目录虚拟环境:
  
  ```bash
  cd /data/saas-search
  source ./activate.sh
  python -m pytest -q tests/test_rerank_client.py tests/test_es_query_builder.py tests/test_search_rerank_window.py tests/test_query_parser_mixed_language.py
  ./scripts/service_ctl.sh restart backend
  sleep 3
  ./scripts/service_ctl.sh status backend
a345b01f   tangwang   eval framework
333
  ./scripts/evaluation/start_eval.sh.sh batch
c90f80ed   tangwang   相关性优化
334
335
  ```
  
3ac1f8d1   tangwang   评估标准优化
336
  评估产物在 `artifacts/search_evaluation/`(如 `search_eval.sqlite3`、`batch_reports/` 下的 JSON/Markdown)。流程与参数说明见 [scripts/evaluation/README.md](../scripts/evaluation/README.md)
c90f80ed   tangwang   相关性优化
337
338
  
  ## 11. 建议测试清单
7bc756c5   tangwang   优化 ES 查询构建
339
  
bcada818   tangwang   last
340
  建议在 `tests/` 增加文本策略用例:
7bc756c5   tangwang   优化 ES 查询构建
341
  
bcada818   tangwang   last
342
  1. 源语种在索引语言,翻译命中缓存  
0536222c   tangwang   query parser优化
343
344
345
  2. 源语种不在索引语言,翻译部分失败(验证仅保留 `base_query` + 成功翻译子句)  
  3. 源语种不在索引语言,翻译全部失败(验证无 `base_query_trans_*` 时仍可正常执行)  
  4.`zh/en` 语种字段动态拼接(如 `de/fr/es`
fb973d19   tangwang   configs
346
347
  
  
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
348
349
350
351
352
353
  # 搜索pipeline
  **整体图**
  这个 pipeline 现在可以理解成一条“先广召回,再逐层收窄、逐层加贵信号”的漏斗:
  
  1. Query 解析
  2. ES 召回
9df421ed   tangwang   基于eval框架开始调参
354
  3. 粗排:ES 原始总分 + 文本大分 + 统一 KNN 大分
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
355
  4. 款式 SKU 选择 + title suffix
9df421ed   tangwang   基于eval框架开始调参
356
357
  5. 精排:轻量 reranker + ES/text/KNN 融合
  6. 最终 rerank:重 reranker + fine score + ES/text/KNN 融合
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  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 原逻辑返回。
  
  这点非常重要,因为它决定了“贵模型只服务头部结果”。
  
  **Step 1:Query 解析阶段**
  [searcher.py:432](/data/saas-search/search/searcher.py#L432)[searcher.py:469](/data/saas-search/search/searcher.py#L469)
  `query_parser.parse()` 做几件事:
  
  - 规范化 query
  - 检测语言
  - 可能做 rewrite
  - 生成文本向量
  - 如果有图搜,还会带图片向量
  - 生成翻译结果
  - 识别 style intent
  
  这一步的结果存在 `parsed_query` 里,后面 ES 查询、style SKU 选择、fine/final rerank 全都依赖它。
  
  **Step 2:ES Query 构建**
  ES DSL 在 [searcher.py:471](/data/saas-search/search/searcher.py#L471) 开始,通过 [es_query_builder.py:181](/data/saas-search/search/es_query_builder.py#L181)`build_query()` 生成。
  
  这里的核心结构是:
  - 文本召回 clause
  - 文本向量 KNN clause
  - 图片向量 KNN clause
  - 它们一起放进 `bool.should`
  - 过滤条件放进 `filter`
  - facet 的多选条件走 `post_filter`
  
  KNN 部分在 [es_query_builder.py:250](/data/saas-search/search/es_query_builder.py#L250) 之后:
  - 文本向量 clause 名字固定叫 `knn_query`
  - 图片向量 clause 名字固定叫 `image_knn_query`
  
  而文本召回那边,后续 fusion 代码约定会去读:
  - 原始 query 的 named query:`base_query`
  - 翻译 query 的 named query:`base_query_trans_*`
  
  也就是说,后面的粗排/精排/最终 rerank,并不是重新理解 ES score,而是从 `matched_queries` 里把这些命名子信号拆出来自己重算。
  
  **Step 3:ES 召回**
  [searcher.py:579](/data/saas-search/search/searcher.py#L579)[searcher.py:627](/data/saas-search/search/searcher.py#L627)
  
  这里有个很关键的工程优化:
  如果在 rerank window 内,第一次 ES 拉取时会把 `_source` 关掉,只取排序必需信号,见 [searcher.py:517](/data/saas-search/search/searcher.py#L517)[searcher.py:523](/data/saas-search/search/searcher.py#L523)
  
  原因是:
  - 粗排先只需要 `_score` 和 `matched_queries`
  - 不需要一上来把 700 条完整商品详情都拉回来
  - 等粗排收窄后,再补 fine/final rerank 需要的字段
  
  这是现在这条 pipeline 很核心的性能设计点。
  
  **Step 4:粗排**
  粗排入口在 [searcher.py:638](/data/saas-search/search/searcher.py#L638),真正的打分在 [rerank_client.py:348](/data/saas-search/search/rerank_client.py#L348)`coarse_resort_hits()`
  
9df421ed   tangwang   基于eval框架开始调参
432
433
  粗排现在看三类信号:
  - `es_score`
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
  - `text_score`
  - `knn_score`
  
  它们先都从统一 helper `_build_hit_signal_bundle()` 里拿,见 [rerank_client.py:246](/data/saas-search/search/rerank_client.py#L246)
  
  文本分怎么来,见 [rerank_client.py:200](/data/saas-search/search/rerank_client.py#L200)
  - `source_score = matched_queries["base_query"]`
  - `translation_score = max(base_query_trans_*)`
  - `weighted_translation = 0.8 * translation_score`
  - `primary_text = max(source, weighted_translation)`
  - `support_text = 另一路`
  - `text_score = primary_text + 0.25 * support_text`
  
  这就是一个 text dismax 思路:
  原 query 是主路,翻译 query 是辅助路,但不是简单相加。
  
  向量分怎么来,见 [rerank_client.py:156](/data/saas-search/search/rerank_client.py#L156)
  - `text_knn_score`
  - `image_knn_score`
  - 分别乘自己的 weight
  - 取强的一路做主路
  - 弱的一路按 `knn_tie_breaker` 做辅助
9df421ed   tangwang   基于eval框架开始调参
456
  - 产出一个统一的 `knn_score`
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
457
  
9df421ed   tangwang   基于eval框架开始调参
458
459
460
461
462
  然后粗排融合公式在 [rerank_client.py:346](/data/saas-search/search/rerank_client.py#L346)
  - `coarse_score = es_factor * text_factor * knn_factor`
  - `es_factor = (es_score + es_bias)^es_exponent`
  - `text_factor = (text_score + text_bias)^text_exponent`
  - `knn_factor = (knn_score + knn_bias)^knn_exponent`
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
  
  配置定义在 [schema.py:124](/data/saas-search/config/schema.py#L124)[config.yaml:231](/data/saas-search/config/config.yaml#L231)
  
  算完后:
  - 写入 `hit["_coarse_score"]`
  -`_coarse_score` 排序
  - 留前 240,见 [searcher.py:645](/data/saas-search/search/searcher.py#L645)
  
  **Step 5:粗排后补字段 + SKU 选择**
  粗排完以后,`searcher` 会按 doc template 反推 fine/final rerank 需要哪些 `_source` 字段,然后只补这些字段,见 [searcher.py:669](/data/saas-search/search/searcher.py#L669)
  
  之后才做 style SKU 选择,见 [searcher.py:696](/data/saas-search/search/searcher.py#L696)
  
  为什么放这里?
  因为现在 fine rank 也是 reranker,它也要吃 title suffix。
  而 suffix 是 SKU 选择之后写到 hit 上的 `_style_rerank_suffix`
  真正把 suffix 拼进 doc 文本的地方在 [rerank_client.py:65](/data/saas-search/search/rerank_client.py#L65)[rerank_client.py:74](/data/saas-search/search/rerank_client.py#L74)
  
  所以顺序必须是:
  - 先粗排
  - 再选 SKU
  - 再用带 suffix 的 title 去跑 fine/final rerank
  
  **Step 6:精排**
  入口在 [searcher.py:711](/data/saas-search/search/searcher.py#L711),实现是 [rerank_client.py:603](/data/saas-search/search/rerank_client.py#L603)`run_lightweight_rerank()`
  
  它会做三件事:
  
  1.`build_docs_from_hits()` 把每条商品变成 reranker 输入文本
  2.`service_profile="fine"` 调轻量服务
  3. 不再只按 `fine_score` 排,而是按融合后的 `_fine_fused_score`
  
  精排融合公式现在是:
9df421ed   tangwang   基于eval框架开始调参
496
  - `fine_stage_score = es_factor * fine_factor * text_factor * knn_factor * style_boost`
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
497
498
  
  具体公共计算在 [rerank_client.py:286](/data/saas-search/search/rerank_client.py#L286)`_compute_multiplicative_fusion()`
9df421ed   tangwang   基于eval框架开始调参
499
  - `es_factor = (es_score + es_bias)^es_exponent`
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  - `fine_factor = (fine_score + fine_bias)^fine_exponent`
  - `text_factor = (text_score + text_bias)^text_exponent`
  - `knn_factor = (knn_score + knn_bias)^knn_exponent`
  - 如果命中了 selected SKU,再乘 style boost
  
  写回 hit 的字段见 [rerank_client.py:655](/data/saas-search/search/rerank_client.py#L655)
  - `_fine_score`
  - `_fine_fused_score`
  - `_text_score`
  - `_knn_score`
  
  排序逻辑在 [rerank_client.py:683](/data/saas-search/search/rerank_client.py#L683)
  `_fine_fused_score` 降序排,然后留前 80,见 [searcher.py:727](/data/saas-search/search/searcher.py#L727)
  
  这就是你这次特别关心的点:现在 fine rank 已经不是“模型裸分排序”,而是“模型分 + ES 文本/KNN 信号融合后排序”。
  
  **Step 7:最终 rerank**
  入口在 [searcher.py:767](/data/saas-search/search/searcher.py#L767),实现是 [rerank_client.py:538](/data/saas-search/search/rerank_client.py#L538)`run_rerank()`
  
  它和 fine rank 很像,但多了一个更重的模型分 `rerank_score`
  最终公式是:
  
9df421ed   tangwang   基于eval框架开始调参
522
  - `final_score = es_factor * rerank_factor * fine_factor * text_factor * knn_factor * style_boost`
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
523
524
  
  也就是:
9df421ed   tangwang   基于eval框架开始调参
525
  - ES 原始总分也会继续保留到最终阶段
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  - fine rank 产生的 `fine_score` 不会丢
  - 到最终 rerank 时,它会继续作为一个乘法项参与最终融合
  
  这个逻辑在 [rerank_client.py:468](/data/saas-search/search/rerank_client.py#L468)[rerank_client.py:476](/data/saas-search/search/rerank_client.py#L476)
  
  算完后写入:
  - `_rerank_score`
  - `_fused_score`
  
  然后按 `_fused_score` 排序,见 [rerank_client.py:531](/data/saas-search/search/rerank_client.py#L531)
  
  这里你可以把它理解成:
  - fine rank 负责“轻量快速筛一遍,把 240 缩成 80”
  - 最终 rerank 负责“用更贵模型做最终拍板”
  - 但最终拍板时,不会忽略 fine rank 结果,而是把 fine score 当成一个先验信号保留进去
  
  **Step 8:分页与字段补全**
  多阶段排序只在头部窗口内完成。
  真正返回给用户前,在 [searcher.py:828](/data/saas-search/search/searcher.py#L828) 之后还会做两件事:
  
  - 先按 `from_:from_+size` 对最终 80 条切片
  - 再按用户原始 `_source` 需求补回页面真正要显示的字段,见 [searcher.py:859](/data/saas-search/search/searcher.py#L859)
  
  所以这条链路是“三次不同目的的数据访问”:
  
  - 第一次 ES:只要排序信号
  - 第二次按 id 回填:只要 fine/final rerank 需要字段
  - 第三次按页面 ids 回填:只要最终页面显示字段
  
  这也是为什么它性能上比“一次全量拉 700 条完整文档”更合理。
  
  **Step 9:结果格式化与 debug funnel**
  最后在 [searcher.py:906](/data/saas-search/search/searcher.py#L906) 进入结果处理。
  这里会把每个商品的阶段信息组装成 `ranking_funnel`,见 [searcher.py:1068](/data/saas-search/search/searcher.py#L1068)
  
  - `es_recall`
  - `coarse_rank`
  - `fine_rank`
  - `rerank`
  - `final_page`
  
  其中:
9df421ed   tangwang   基于eval框架开始调参
568
  - coarse stage 保留 es/text/translation/knn 的拆分信号
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
569
570
  - fine/rerank stage 现在都保留 `fusion_inputs`、`fusion_factors`、`fusion_summary`
  - `fusion_summary` 来自真实计算过程本身,见 [rerank_client.py:265](/data/saas-search/search/rerank_client.py#L265)
9df421ed   tangwang   基于eval框架开始调参
571
  -`fine_rank` 关闭时,`rerank.rank_change` 会继承 `coarse_rank` 作为上游阶段,不会错误地全部显示为 0
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
  
  这点很重要,因为现在“实际排序逻辑”和“debug 展示逻辑”是同源的,不是两套各写一份。
  
  **一句话总结这条 pipeline**
  这条 pipeline 的本质是:
  
  - ES 负责便宜的大范围召回
  - 粗排负责只靠 ES 内置信号先做一次结构化筛选
  - style SKU 选择负责把商品文本改造成更适合 reranker 理解的输入
  - fine rank 负责用轻模型把候选进一步压缩
  - final rerank 负责用重模型做最终判定
  - 每一层都尽量复用前一层信号,而不是推翻重来
  
  如果你愿意,我下一步可以继续按“一个具体 query 的真实流转样例”来讲,比如假设用户搜 `black dress`,我把它从 `parsed_query`、ES named queries、coarse/fine/final 的每个分数怎么出来,完整手推一遍。
  
9df421ed   tangwang   基于eval框架开始调参
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
  ## 12. 值得优先探索的相关性实验方向
  
  下面这些方向按我对当前 rank 体系的判断,优先级大致是“先做低风险高收益,再做结构性升级”。
  
  ### 12.1 Query 分桶,而不是所有 query 共用一套融合参数
  
  当前问题:
  
  - 所有 query 基本共用同一套 exponent / bias
  - 但“强词法 query”、“泛类目 query”、“风格词 query”、“图搜触发 query”、“中英混输 query”的最优信号配比通常不同
  
  建议实验:
  
  - 先做轻量 query 分桶:
    - 精准实体词
    - 泛类目词
    - 风格/属性词
    - 中英混输
    - 带强图片语义的 query
  - 每个桶单独调:
    - `text_translation_weight`
    - `knn_text_weight / knn_image_weight`
    - `es_exponent / text_exponent / knn_exponent`
  
  为什么值得先做:
  
  - 不改主架构
  - 容易上线灰度
  - 往往比“全局调一个 exponent”稳定得多
  
  ### 12.2 把 image KNN 设成略高于 text KNN,但只在合适 query 上生效
  
  当前问题:
  
  - 现在 `knn_text_weight = 1.0`,`knn_image_weight = 1.0`
  - 对鞋、服饰款式、图案、轮廓类 query,image embedding 往往比 text embedding 更接近用户真实意图
  - 但不是所有 query 都适合直接全局抬高 image 权重
  
  建议实验:
  
  - 离线先试:
    - `knn_image_weight = 1.1 / 1.2 / 1.3`
    - `knn_text_weight = 1.0`
  - 再进一步试 query gating:
    - 若 query 命中款式词、形状词、鞋包词、图案词,则抬高 image weight
    - 若 query 是明确品类词或强属性词,则维持中性
  
  为什么我不建议一上来全局大幅抬高:
  
  - 会把一些“文本很明确,但图像泛相似”的结果抬上来
  - 容易让高视觉相似、低语义准确的商品误冲前排
  
  ### 12.3 不只融合“分数”,还要融合“排名证据”
  
  当前问题:
  
  - 现在所有阶段都高度依赖 score 级别的乘法融合
  - 不同信号源的 score 标度未必天然可比
  - reranker 分数、ES score、named query score、KNN score 的数值空间差异很大
  
  建议实验:
  
  - 增加 rank-based 特征:
    - `es_rank`
    - `text_rank`
    - `knn_rank`
    - `rerank_rank`
  - 试两类简单方法:
    - RRF(Reciprocal Rank Fusion)
    - score-rank 混合:先做 rank 融合,再乘少量 score 因子
  
  为什么值得做:
  
  - 对异常 score 分布更稳
  - 对模型偶发极端分更鲁棒
  - 很适合拿来做基线对照
  
  ### 12.4 将 `base_query` 和 `translation_query` 从“单点 max”升级为“更完整的 lexical 证据”
  
  当前问题:
  
  - 文本大分现在只抓:
    - `base_query`
    - `max(base_query_trans_*)`
  - 这很干净,但可能过于压缩文本证据
  - phrase 命中、best_fields 命中、多语言字段命中、字段质量差异,没有更细粒度地进入后续 rank
  
  建议实验:
  
  - 把 lexical 证据拆得更细:
    - exact / phrase
    - best_fields
    - title 命中
    - category 命中
    - brand/vendor 命中
  - 后续不一定都入主公式,但可以先做 debug / feature log
  
  这样做的收益:
  
  - 更容易解释“为什么这条词法上明明更准却没排上来”
  - 为后续 learning-to-rank 或规则门控准备特征
  
  ### 12.5 增加“类目先验”和“商品类型约束”
  
  当前问题:
  
  - 现在体系更偏“文本/向量相似度驱动”
  - 对“牛仔裤 vs 连裤袜”这种 bad case,问题常常不只是分数融合,而是**商品类型约束太弱**
  
  建议实验:
  
  - query 侧先做轻量商品类型识别:
    - 裙子
    - 裤子
    - 上衣
    -
  - doc 侧取:
    - category_path
    - taxonomy leaf
    - 类目 embedding / one-hot
  - 然后试:
    - 作为 hard filter 候选约束
    - 作为 coarse/final 的 boost 因子
    - 作为 rerank 输入字段增强
  
  这是我认为对明显 bad case 最有价值的一类结构性修复。
  
  ### 12.6 把“负证据”纳入体系,而不只是累加正证据
  
  当前问题:
  
  - 当前乘法体系主要是在积累正向因子
  - 但很多错误结果不是“正向不够强”,而是“存在明显负证据”
  - 例如 query 是“半身裙”,doc 却强命中“上衣”“打底衫”“连裤袜”
  
  建议实验:
  
  - 抽取轻量负词特征:
    - 商品类型冲突词
    - 性别/人群冲突词
    - 长度/版型冲突词
  - 方式可以先很简单:
    - penalty factor
    - blacklist term penalty
    - query-doc type mismatch penalty
  
  这是当前体系里非常缺的一块。
  
  ### 12.7 把 KNN 从“单一总分”升级为“多语义子通道”
  
  当前问题:
  
  - 现在 KNN 最终会被压成一个 `knn_score`
  - 这对工程简单很好,但损失了“这条向量信号到底为什么相似”的信息
  
  建议实验:
  
  - 分通道记录和使用:
    - text semantic similarity
    - image appearance similarity
    - category-aware similarity
    - style-aware similarity
  - 即使最终仍合成一个总分,也建议先保留分通道特征
  
  这样未来才能回答:
  
  - 这条结果是“外观像”
  - 还是“描述语义像”
  - 还是“类目像但款式不对”
  
  ### 12.8 从纯手工公式,逐步过渡到轻量 LTR
  
  当前问题:
  
  - 目前公式已经比较清晰,但本质还是手工 feature engineering + 手工 exponent
  - 一旦信号变多,靠手调很难长期维护
  
  建议实验:
  
  - 先不引入复杂在线模型
  - 先做离线 LTR baseline:
    - LambdaMART / XGBoost ranker
    - 输入现成特征:
      - es_score
      - text_score
      - text_source_score
      - translation_score
      - text_knn_score
      - image_knn_score
      - coarse_rank
      - rerank_score
      - category match
      - style intent match
  
  为什么这一步值得准备:
  
  - 你们现在的 debug 字段已经很接近 feature log 了
  - 其实已经具备往 LTR 过渡的土壤
  
  ### 12.9 先把评估体系补齐,再谈大改
  
  当前问题:
  
  - 很多相关性讨论容易停留在个例
  - 但融合改动经常存在 query 分布层面的 tradeoff
  
  建议实验配套:
  
  - 建立 query slice 指标:
    - 鞋靴
    - 裙装
    - 裤装
    - 中英混输
    - 图像语义强 query
    - 属性词强 query
  - 每次实验至少看:
    - overall
    - top 1
    - top 3
    - slice breakdown
    - bad case 回归集
  
  ### 12.10 我对当前体系的几个核心判断
  
  1. 当前体系最大的优点不是公式本身,而是已经把信号拆成了可解释的层级,这非常适合继续做实验。
  2. 当前体系最大的短板不是“knn exponent 还不够准”,而是缺少 query 分桶、类目先验和负证据。
  3. 只调融合公式还能继续拿到一部分收益,但中期最值得投入的是:
     - query-aware 参数
     - 类型/类目约束
     - score + rank 混合融合
     - 为 LTR 做特征沉淀
  
c3425429   tangwang   在以下文件中完成精排/融合清理工作...
819
  
fb973d19   tangwang   configs
820
821
822
823
824
825
826
827
828
829
830
831
  
  ## reranker方面:
  BAAI/bge-reranker-v2-m3的一个严重badcase:
  q=黑色中长半身裙
  
  Rerank score: 0.0785
  title.zh: 2026款韩版高腰显瘦雪尼尔包臀裙灯芯绒开叉中长款咖啡色半身裙女
  title.en: 2026 Korean-style High-waisted Slimming Corduroy Skirt with Slit, Mid-Length Coffee-colored Skirt for Women
  
  Rerank score: 0.9643
  title.en: Black Half-high Collar Base Shirt Women's Autumn and Winter fleece-lined Contrast Color Pure Desire Design Sense Horn Sleeve Ruffled Inner Top
  title.zh: 黑色高领半高领女士秋冬内搭加绒拼色纯欲设计荷叶边袖内衬上衣
ef5baa86   tangwang   混杂语言处理
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
  
  
  
  qwen3-0.6b的严重badcase:
  q=牛仔裤
  
  Rerank score: 0.0002
  title.en: Wrangler Womens Cowboy Cut Slim Fit Jean Bleach
  title.zh: Wrangler 女士牛仔裤 牛仔剪裁 紧身版型 漂白色
  
  Rerank score: 0.0168
  title.en: Fleece Lined Tights Sheer Women - Fake Translucent Warm Pantyhose Leggings Sheer Thick Tights for Winter
  title.zh: 加绒透肤女士连裤袜 - 仿透视保暖长筒袜 冬季厚款透肤连裤袜
  
  Rerank score: 0.1366
  title.en: Dockers Men's Classic Fit Workday Khaki Smart 360 FLEX Pants (Standard and Big & Tall)
  title.zh: Dockers 男士经典版型工作日卡其色智能360度弹力裤(标准码与加大码)
  
  Rerank score: 0.0981
  title.en: Lazy One Pajama Shorts for Men, Men's Pajama Bottoms, Sleepwear
  title.zh: 懒人男士睡裤,男式家居裤,睡眠服饰
ccbdf870   tangwang   enriched_attribut...
853
854
855
856
857
858
859
860
861
862
863
864
  
  
  
  
  q=修身牛仔裤
  
  这些好结果得分很低:
  
  rerank_score:0.0564
      "en": "Judy Blue Women's High Waist Button Fly Skinny Jeans 82319",
      "zh": "Judy Blue 女士高腰纽扣开叉修身牛仔裤 82319"
  
ccbdf870   tangwang   enriched_attribut...
865
866
867
868
  rerank_score:0.0790
      "en": "2025 New Fashion European and American Women's Jeans High-Waisted Slim Straight Denim Pants Popular Floor-Length Pants",
      "zh": "2025新款欧美风女式高腰显瘦直筒牛仔裤 时尚及地长裤"
  
ccbdf870   tangwang   enriched_attribut...
869
870
871
872
  rerank_score:0.0822
      "en": "roswear Women's Trendy Stretchy Flare Jeans Mid Rise Bootcut Curvy Denim Pants",
      "zh": "Roswear 女士时尚弹力喇叭牛仔裤 中腰高腰修身直筒牛仔裤"
  
ccbdf870   tangwang   enriched_attribut...
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
  rerank_score:0.0956
      "en": "POSHGLAM Women's Maternity Jeans Over Belly 29'' Skinny Denim Jeggings Comfy Stretch Clearance Pregnancy Pants",
      "zh": "POSHGLAM 女士孕产期高腰显瘦牛仔紧身裤 29英寸 紧身弹力孕妇裤 休闲舒适 清仓特价"
  
  (带有 Slim Stretch Jeans,但是打分只有0.0135,极低)
  rerank_score:0.0135
      "en": "European and American Export Temu American Retro Sexy Bell-Bottomed Pants Slim Slim Stretch Jeans Women's Pants",
      "zh": "欧美出口 蒂姆美国复古性感喇叭裤 修身弹力女裤"
  
  
  这几个结果比较差,但是得分很高:
  
  rerank_score:0.4692
      "en": "American Vintage Low Waist Non-Elastic Washed Straight-Leg Jeans Women's Autumn New Street Wide Leg Denim Women's Pants",
      "zh": "美式复古低腰无弹洗水直筒阔腿牛仔裤 女士秋季新款阔腿牛仔裤"
  
  
  rerank_score:0.4784
      "en": "Europe and the United States cross-border foreign trade 2025 spring and summer new Amazon independent station washed waist adjustable Denim pants",
      "zh": "欧美跨境外贸2025春夏新款亚马逊独立站洗水腰 adjustable 牛仔裤"
  
  
  rerank_score:0.5849
      "zh": "新款女士修身仿旧牛仔短裤 – 休闲性感磨边水洗牛仔短裤,时尚舒",
      "en": "New Women's Slim-fit Vintage Washed Denim Shorts – Casual Sexy Frayed Hem, Fashionable & Comfortable"