20 Apr, 2026

1 commit

  •  变更清单
    
     修复(6 处漂移用例,全部更新到最新实现)
    - `tests/test_eval_metrics.py` — 整体重写为新的 4 级 label + 级联公式断言,放弃旧的 `RELEVANCE_EXACT/HIGH/LOW/IRRELEVANT` 和硬编码 ERR 值。
    - `tests/test_embedding_service_priority.py` — 补齐 `_TextDispatchTask(user_id=...)` 新必填位。
    - `tests/test_embedding_pipeline.py` — cache-hit 路径的 `np.allclose` 改用 `np.asarray(..., dtype=float32)` 避开 object-dtype。
    - `tests/test_es_query_builder_text_recall_languages.py` — keywords 次 combined_fields 的期望值对齐现行值(`MSM 60% / boost 0.8`)并重命名。
    - `tests/test_product_enrich_partial_mode.py`
      - `test_create_prompt_supports_taxonomy_analysis_kind`:去掉错误假设(fr 不属于任何 taxonomy schema),明确 `(None, None, None)` sentinel 的契约。
      - `test_build_index_content_fields_non_apparel_taxonomy_returns_en_only`:fake 模拟真实 schema 行为(unsupported lang 返回空列表),删除"zh 未被调用"的过时断言。
    
     清理历史过渡物(per 开发原则:不保留内部双轨)
    - 删除 `tests/test_keywords_query.py`(已被 `query/keyword_extractor.py` 生产实现取代的早期原型)。
    - `tests/test_facet_api.py` / `tests/test_cnclip_service.py` 移动到 `tests/manual/`,更新 `tests/manual/README.md` 说明分工。
    - 重写 `tests/conftest.py`:仅保留 `sys.path` 注入,删除全库无人引用的 `sample_search_config / mock_es_client / test_searcher / temp_config_file` 等 fixture。
    - 删除 `tests/test_suggestions.py` 中 13 处残留 `@pytest.mark.unit` 装饰器(模块级 `pytestmark` 已覆盖)。
    
     新建一致性基础设施
    - `pytest.ini`:权威配置源。`testpaths = tests`、`norecursedirs = tests/manual`、`--strict-markers`、登记所有子系统 marker + `regression` marker。
    - `tests/ci/test_service_api_contracts.py` + 30 个 `tests/test_*.py` 批量贴上 `pytestmark = [pytest.mark.<subsystem>, pytest.mark.regression]`(AST 安全插入,避开多行 import)。
    - `scripts/run_regression_tests.sh` 新建,支持 `SUBSYSTEM=<name>` 选子集。
    - `scripts/run_ci_tests.sh` 扩容:由原先的 `tests/ci -q` 改为 `contract` marker + `search ∧ regression` 双阶段。
    
     文档统一(删除历史双轨)
    - 重写 `docs/测试Pipeline说明.md`:删除 `tests/unit/` / `tests/integration/` / `scripts/start_test_environment.sh` 等早已不存在的引用,给出目录约定、marker 表、回归锚点矩阵、覆盖缺口清单、联调脚本用法。
    - 删除 `docs/测试回归钩子梳理-2026-04-20.md`(内容已合并进上面一份权威文档,按"一处真相"原则下掉)。
    - `docs/DEVELOPER_GUIDE.md §8.2 测试` 改写,指向 pipeline 权威文档。
    - `CLAUDE.md` 的 `Testing` 与 `Testing Infrastructure` 两节同步更新。
    
     最终状态
    
    | 指标 | 结果 |
    |------|------|
    | 全量 `pytest tests/` | **241 passed** |
    | `./scripts/run_ci_tests.sh` | 45 passed |
    | `./scripts/run_regression_tests.sh` | 233 passed |
    | 子系统子集(示例) | search=45 / rerank=35 / embedding=23 / intent=25 / translation=33 / indexer=17 / suggestion=13 / query=6 / eval=8 / contract=34 |
    | 未清零的已知缺口 | 见新版 `测试Pipeline说明.md §4`(function_score / facet / image search / config loader / document_transformer 等 6 条) |
    
    Pipeline 文档里 §4 的覆盖缺口我没有强行补测用例——那属于"新增覆盖",不是这次清理的范畴;只要后续谁补,把对应 marker 贴上去、从清单里划掉即可。
    tangwang
     

14 Apr, 2026

1 commit


20 Mar, 2026

1 commit

  • 问题描述
    ----------
    使用 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
     

19 Mar, 2026

2 commits

  • 推理”,不再是先按原始输入条数切块。也就是说,如果 100 条请求分句后变成
    150 个 segments,batch_size=64 时会按 64 + 64 + 22
    三批推理,推理完再按原始分句计划合并并还原成 100 条返回。这个改动在
    local_seq2seq.py (line 241) 和 local_ctranslate2.py (line 391)。
    
    日志这边也补上了两层你要的关键信息:
    
    分句摘要日志:Translation segmentation
    summary,会打印输入条数、非空条数、发生分句的输入数、总 segments
    数、当前 batch_size、每条输入分成多少段的统计,见 local_seq2seq.py (line
    216) 和 local_ctranslate2.py (line 366)。
    每个预测批次日志:Translation inference
    batch,会打印第几批、总批数、该批 segment
    数、长度统计、首条预览。CTranslate2 另外还会打印 Translation model batch
    detail,补充 token 长度和 max_decoding_length,见 local_ctranslate2.py
    (line 294)。
    我也补了测试,覆盖了“分句后再
    batching”和“日志中有分句摘要与每批推理日志”,在
    test_translation_local_backends.py (line 358)。
    tangwang
     
  • 改动:
    
    新增分句与预算工具:translation/text_splitter.py
    接入 HF 本地后端:translation/backends/local_seq2seq.py (line 157)
    接入 CT2 本地后端:translation/backends/local_ctranslate2.py (line 301)
    补了测试:tests/test_translation_local_backends.py
    我先把代码里实际限制梳理了一遍,关键配置在 config/config.yaml (line
    133):
    
    nllb-200-distilled-600m: max_input_length=256,max_new_tokens=64,并且是
    ct2_decoding_length_mode=source +
    extra=8。现在按这个配置计算出的保守输入预算是 56 token。
    opus-mt-zh-en:
    max_input_length=256,max_new_tokens=256。现在保守输入预算是 248 token。
    opus-mt-en-zh: 同上,也是 248 token。
    这版分句策略是:
    
    先按强边界切:。!?!?;;…、换行、英文句号
    不够再按弱边界切:,,、::()()[]【】/|
    再不够才按空白切
    最后才做 token 预算下的硬切
    超长时会“分句翻译后再回拼”,中文目标语言默认无空格回拼,英文等默认按空格回拼,尽量别切太碎
    验证:
    
    python3 -m compileall translation
    tests/test_translation_local_backends.py 已通过
    tangwang
     

18 Mar, 2026

1 commit


17 Mar, 2026

1 commit