01 Apr, 2026

14 commits

  • tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     
  • 现在的行为(按你的路径)
    用途	路径(相对仓库根 PROJECT_ROOT)
    评估主日志(CLI + framework 的 INFO)	logs/eval.log
    LLM 全量 prompt / 原始响应	logs/verbose/eval_verbose.log
    实现要点:
    
    constants.py:EVAL_LOG_DIR、EVAL_VERBOSE_LOG_DIR、EVAL_LOG_FILE、EVAL_VERBOSE_LOG_FILE。
    logging_setup.py:setup_eval_logging() 给名为 search_eval 的 logger 挂
    文件 + stderr,只初始化一次;build_annotation_set.py / serve_eval_web.py
    走的 eval_framework.cli.main() 开头会先调用。
    cli.py:原来的 print 改为 search_eval.cli 的 logging.info;启动时写一条
    CLI start command=... log_file=... 到 logs/eval.log。
    framework.py:rebuild 相关 print 改为 search_eval.framework 的
    logging.info。
    clients.py:verbose 改为写入
    logs/verbose/eval_verbose.log;首次需要时调用 setup_eval_logging(),并用
    search_eval.info 提示 verbose 文件路径(不再用 print)。
    tangwang
     
  • tangwang
     
  • scripts/evaluation/eval_framework/constants.py:500 → 200
    Rebuild 里 rank <= recall_n 的 rerank_score: 1.0 仍按该 K 生效。
    2. LLM 批次上下限
    最少批次:DEFAULT_REBUILD_MIN_LLM_BATCHES 20 → 10
    最多批次:仍为 40(未改)
    3. 提前结束条件(_annotate_rebuild_batches)
    在已跑满 min_batches 之后,对每个批次:
    
    本批无 Exact(exact_n == 0),且满足其一即视为 bad batch:
    irrelevant_ratio >= 0.94
    或 (irrelevant + Low Relevant) / n >= 0.96(弱相关用 RELEVANCE_LOW)
    连续 2 个 bad batch 则 early stop(原先是连续 3 次、irrelevant >
    0.92)。
    
    批次日志里增加了 low_ratio、irrelevant_plus_low_ratio;rebuild
    元数据里增加了 rebuild_irrel_low_combined_stop_ratio。
    
    4. CLI
    --search-recall-top-k 说明改为默认 200
    --rebuild-min-batches 说明改为默认 10
    --rebuild-irrelevant-stop-ratio / --rebuild-irrelevant-stop-streak
    说明与新逻辑一致
    新增 --rebuild-irrel-low-combined-stop-ratio(默认 0.96)
    tangwang
     
  • `indexer/product_enrich.py`,不是再补一层判断。
    
    根因有两个:缓存 key 按内容复用,但缓存值里还带着旧商品的
    `id/title_input`;同时内部分析结果在历史上混用了 `tags` 和
    `enriched_tags`。这样一旦命中旧缓存,`build_index_content_fields()`
    会因为 `id` 对不上把结果丢掉,最后对外就变成全空。
    
    现在的处理是:
    - 内部分析结果统一用 `tags` 作为 LLM/缓存层字段。
    - 对外只在 `build_index_content_fields()` 封装时映射成
      `enriched_tags`,`enriched_attributes` 里也统一产出
    `name="enriched_tags"`。
    - 读取缓存时会先做归一化:把旧缓存里的 `enriched_tags` 兼容成内部
      `tags`,并把命中的缓存结果重绑到当前请求商品的 `id/title_input`。
    - 写缓存时也统一写成归一化后的内部结构,并且空内容不再写入缓存。
    tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     

31 Mar, 2026

13 commits


30 Mar, 2026

11 commits

  • tangwang
     
  • tangwang
     
  • must里面的两个combined_fields查询,boost分别设置为2和0.6,和其他查询条件一起,都放到should里面,设置minimum_should_match==1
    2.
    如果keywords_query跟combined_fields主查询的query一样,那么不需要再添加了
    tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     
  • 一、tags字段改支持多语言:
    spu表tags字段,跟title走一样的翻译逻辑,填入原始语言、zh、en。
    
    检查以下字段,都跟title一样走翻译逻辑
    title
    keywords
    tags
    brief
    description
    vendor
    category_path
    category_name_text
    
    二、/indexer/enrich-content接口的修改
    1.
    请求参数,把language去掉,因为我返回的内容直接对应索引结构,不用你做处理了,因此不需要指定语言,降低耦合。
    2. 返回 enriched_attributes enriched_tags
       qanchors三个字段,按原始内容填入。
    3. enriched_tags是本次新增的,注意区别于tags字段。tags字段来源于mysql
       spu表,enriched_tags是本接口返回的。
    
    三、specifications的value,需要翻译,也是需要填中英文:
    {
      "specifications": [
        {
          "sku_id": "sku-red-s",
          "name": "color",
          "value_keyword": "красный",
          "value_text": {
            "zh": "红色",
            "en": "red"
          }
        }
      ]
    }
    tangwang
     
  • tangwang
     
  • tangwang
     
  • tangwang
     
  • …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 原逻辑返回。
    tangwang
     

27 Mar, 2026

2 commits