01 Apr, 2026

5 commits

  • tangwang
     
  • tangwang
     
  • 都会合并成数组,和 qanchors / enriched_tags
    的处理方式保持一致,更符合你现在这套 ES mapping 的灌入方式。ES 的 text
    字段本身支持数组,所以像 value.zh: ["舒适", "无鞋带设计"]
    这种写法是可以正常入库的;nested
    只是外层对象数组,不影响内部语言字段存数组。
    tangwang
     
  • 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
     

31 Mar, 2026

2 commits


30 Mar, 2026

4 commits

  • must里面的两个combined_fields查询,boost分别设置为2和0.6,和其他查询条件一起,都放到should里面,设置minimum_should_match==1
    2.
    如果keywords_query跟combined_fields主查询的query一样,那么不需要再添加了
    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

7 commits


26 Mar, 2026

2 commits


25 Mar, 2026

5 commits


24 Mar, 2026

4 commits

  • 加了一个过滤/降权词典,query中有独立分词匹配到指定的触发词,将过滤带某些分词的商品(比如fitted/修身,过滤宽松、loose、relaxed、baggy,slouchy等商品)
    2. reranker的query使用翻译后的
    tangwang
     
  • 上面一版实现,性能上完全无法接受。因此进行了一轮策略简化
    
    style_sku_prepare_hits阶段耗时太长。请根据需求,思考优化的方法,给出性能优化的方案。
    1.
    _select_by_embedding,有缓存吗,option_value的值是有限的,之前已经算过的,就不用再算了。不仅仅是embedding相似的结果,整个option_value的匹配结果,是有包含、还是没包含,相似度多少,都不用重新计算。比如之前已经有一个sku的某个属性值叫做“卡其色”,已经算出来是否文本匹配了,那么不需要再去做文本匹配。如果已经算出来向量的相似度,那么不需要再去取向量以及计算相似度。
    2. 匹配可以适当的优化:
    匹配流程简化:
    1)找到第一个文本匹配的,如果有直接匹配成功。不需要考虑匹配多个的情况。
    2)如果全部都没有匹配,那么进行embedding筛选。
    
    匹配规则:
    option_name的匹配,直接看规范化后的option_name是不是意图维度的泛化词之一(比如颜色、color、colour),如果没有匹配的,现在应该是把所有维度都算上,这样匹配成本和比较成本太高了,去掉这些逻辑,这种情况不需要加后缀、不需要选择sku。
    ption_value的匹配。意图检测的时候,有匹配的query中的命中的词,这个词被包含在属性值中,那么就算匹配。属性值被包含在query(包括翻译文本)中,也算匹配。提高匹配的覆盖率。
    
    3.
    这一阶段得到sku选择的结果即可(选中的sku的id,也可以为空值表示没找到匹配成功的,这种情况不需要拼接title后缀给重排输入),但是不用着急做image_url的替换和sku的置顶。等最后填充的时候判断有选中sku的时候直接做替换和置顶即可。
    请你思考如何进行设计,提高性能的时候不带来复杂度的提升,可以适当的重构以降低修改后的代码行数。
    @search/sku_intent_selector.py @query/style_intent.py
    tangwang
     
  • tangwang
     
  • 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
    tangwang
     

23 Mar, 2026

5 commits


22 Mar, 2026

3 commits


21 Mar, 2026

1 commit


20 Mar, 2026

2 commits

  • 问题描述
    ----------
    使用 facebook/nllb-200-distilled-600M(CTranslate2 后端)时,若 API 传入 ISO 639-1
    或 FLORES 短标签(如 ca、da、nl、sv、no、tr 等),会触发
    「Unsupported NLLB source/target language」。模型与 tokenizer 实际支持这些语言;
    根因是 resolve_nllb_language_code 仅依赖 translation/languages.py 里十余条
    NLLB_LANGUAGE_CODES 映射,大量合法短码未注册,校验误报为不支持。
    
    修改内容
    ----------
    1. 新增 translation/nllb_flores_short_map.py
       - NLLB_FLORES_SHORT_TO_CODE:与 HF 模型卡 language 列表对齐的短标签 ->
         NLLB 强制 BOS/src_lang 形式(<ISO639-3>_<ISO15924>,如 cat_Latn)。
       - NLLB_TOKENIZER_LANGUAGE_CODES:从 tokenizer.json 提取的 202 个语言 token
         全集,供直接传入 deu_Latn 等形式时做规范化解析。
       - 额外约定:ISO 639-1「no」映射 nob_Latn(书面挪威语 Bokmål);nb/nn 分别
         对应 nob_Latn / nno_Latn;「ar」显式指向 arb_Arab(与 NLLB 一致)。
    
    2. 调整 translation/languages.py
       - build_nllb_language_catalog:合并顺序为 FLORES 全表 -> NLLB_LANGUAGE_CODES
        (保留少量显式覆盖,如 zh->zho_Hans)-> 调用方 overrides。
       - resolve_nllb_language_code:在目录与别名之后,增加基于
         NLLB_TOKENIZER_LANGUAGE_CODES 的大小写不敏感匹配(如 eng_latn -> eng_Latn),
         覆盖「已传完整 NLLB 码」的场景。
    
    3. tests/test_translation_local_backends.py
       - 新增 test_nllb_resolves_flores_short_tags_and_iso_no,覆盖用户关心的短码及
         deu_Latn 直通解析。
    
    方案说明
    ----------
    NLLB 接口语义以 Hugging Face NllbTokenizer 为准:语言标识为 FLORES-200 风格
    三字母语种码 + 下划线 + 四字母脚本子标签(ISO 15924)。业务侧常用 ISO 639-1
    (de、sv)或模型卡短列表(ca、nl),需在服务内统一映射到 tokenizer 特殊 token。
    本实现以模型卡 language 字段 + tokenizer 词表为单一事实来源生成静态表,
    避免运行时依赖额外库;同时保留原有 NLLB_LANGUAGE_CODES 作为薄覆盖层以兼容
    既有配置与测试。
    
    Refs: https://huggingface.co/facebook/nllb-200-distilled-600M
    Made-with: Cursor
    tangwang
     
  • tangwang