# Translation Module

`translation/` 是当前项目翻译能力的主目录。  
如果要开发、部署、联调、压测翻译服务，优先看这份文档。

对应服务：
- translator service：`http://127.0.0.1:6006`
- 业务侧统一通过 [`translation/client.py`](/data/saas-search/translation/client.py) 调用
- 服务内统一通过 [`translation/service.py`](/data/saas-search/translation/service.py) 路由到具体翻译实现

相关脚本与报告：
- 启动脚本：[`scripts/start_translator.sh`](/data/saas-search/scripts/start_translator.sh)
- 虚拟环境：[`scripts/setup_translator_venv.sh`](/data/saas-search/scripts/setup_translator_venv.sh)
- 模型下载：[`scripts/translation/download_translation_models.py`](/data/saas-search/scripts/translation/download_translation_models.py)
- 本地模型压测：[`benchmarks/translation/benchmark_translation_local_models.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models.py)
- 聚焦压测脚本：[`benchmarks/translation/benchmark_translation_local_models_focus.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models_focus.py)
- 基线性能报告：[`perf_reports/20260318/translation_local_models/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models/README.md)
- CT2 扩展报告：[`perf_reports/20260318/translation_local_models_ct2/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2/README.md)
- CT2 聚焦调优报告：[`perf_reports/20260318/translation_local_models_ct2_focus/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2_focus/README.md)
- NLLB T4 商品标题专项报告：[`perf_reports/20260318/nllb_t4_product_names_ct2/README.md`](/data/saas-search/perf_reports/20260318/nllb_t4_product_names_ct2/README.md)

## 1. 设计目标

翻译模块采用：

- 一个 translator service
- 多个 capability backend
- 一个统一外部接口：`model + scene`

这套设计的目标是：
- 翻译能力可以独立扩展、独立启停
- scene、语言码、prompt 模板、模型方向约束等翻译域知识集中在 `translation/`
- 配置尽量集中在 [`config/config.yaml`](/data/saas-search/config/config.yaml) 的 `services.translation`
- 配置错误应尽早报错，不做静默兼容和隐式回退

## 2. 目录结构

核心文件：

- [`translation/client.py`](/data/saas-search/translation/client.py)
  业务侧 HTTP client，供 query/indexer 等模块调用
- [`translation/service.py`](/data/saas-search/translation/service.py)
  translator service 内部的统一编排层
- [`translation/settings.py`](/data/saas-search/translation/settings.py)
  翻译配置的规范化与校验辅助函数
- [`translation/scenes.py`](/data/saas-search/translation/scenes.py)
  scene 规范和值校验
- [`translation/languages.py`](/data/saas-search/translation/languages.py)
  语言码映射、Marian 方向映射等静态知识
- [`translation/prompts.py`](/data/saas-search/translation/prompts.py)
  LLM 翻译 prompt 模板
- [`translation/protocols.py`](/data/saas-search/translation/protocols.py)
  输入输出协议类型

后端实现：

- [`translation/backends/qwen_mt.py`](/data/saas-search/translation/backends/qwen_mt.py)
  Qwen-MT 云端翻译
- [`translation/backends/llm.py`](/data/saas-search/translation/backends/llm.py)
  通用 LLM 翻译
- [`translation/backends/deepl.py`](/data/saas-search/translation/backends/deepl.py)
  DeepL 翻译
- [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py)
  本地 CTranslate2 翻译模型，包括 NLLB 和 Marian/OPUS MT

## 3. 配置约定

翻译的部署配置统一放在：

- [`config/config.yaml`](/data/saas-search/config/config.yaml) -> `services.translation`

示例：

```yaml
services:
  translation:
    service_url: "http://127.0.0.1:6006"
    default_model: "llm"
    default_scene: "general"
    timeout_sec: 10.0
    cache:
      ttl_seconds: 62208000
      sliding_expiration: true
    capabilities:
      qwen-mt:
        enabled: true
        backend: "qwen_mt"
        model: "qwen-mt-flash"
        base_url: "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
        timeout_sec: 10.0
        use_cache: true
      llm:
        enabled: true
        backend: "llm"
        model: "qwen-flash"
        base_url: "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
        timeout_sec: 30.0
        use_cache: true
      deepl:
        enabled: false
        backend: "deepl"
        api_url: "https://api.deepl.com/v2/translate"
        timeout_sec: 10.0
        use_cache: true
      nllb-200-distilled-600m:
        enabled: true
        backend: "local_nllb"
        model_id: "facebook/nllb-200-distilled-600M"
        model_dir: "./models/translation/facebook/nllb-200-distilled-600M"
        ct2_model_dir: "./models/translation/facebook/nllb-200-distilled-600M/ctranslate2-float16"
        ct2_compute_type: "float16"
        ct2_conversion_quantization: "float16"
        ct2_auto_convert: true
        ct2_inter_threads: 4
        ct2_max_queued_batches: 32
        ct2_batch_type: "examples"
        ct2_decoding_length_mode: "source"
        ct2_decoding_length_extra: 8
        ct2_decoding_length_min: 32
        device: "cuda"
        torch_dtype: "float16"
        batch_size: 16
        max_input_length: 256
        max_new_tokens: 64
        num_beams: 1
        use_cache: true
      opus-mt-zh-en:
        enabled: true
        backend: "local_marian"
        model_id: "Helsinki-NLP/opus-mt-zh-en"
        model_dir: "./models/translation/Helsinki-NLP/opus-mt-zh-en"
        ct2_model_dir: "./models/translation/Helsinki-NLP/opus-mt-zh-en/ctranslate2-float16"
        ct2_compute_type: "float16"
        ct2_conversion_quantization: "float16"
        ct2_auto_convert: true
        device: "cuda"
        torch_dtype: "float16"
        batch_size: 16
        max_input_length: 256
        max_new_tokens: 256
        num_beams: 1
        use_cache: true
      opus-mt-en-zh:
        enabled: true
        backend: "local_marian"
        model_id: "Helsinki-NLP/opus-mt-en-zh"
        model_dir: "./models/translation/Helsinki-NLP/opus-mt-en-zh"
        ct2_model_dir: "./models/translation/Helsinki-NLP/opus-mt-en-zh/ctranslate2-float16"
        ct2_compute_type: "float16"
        ct2_conversion_quantization: "float16"
        ct2_auto_convert: true
        device: "cuda"
        torch_dtype: "float16"
        batch_size: 16
        max_input_length: 256
        max_new_tokens: 256
        num_beams: 1
        use_cache: true
```

配置边界：

- `config.yaml` 只放部署和运行参数
  例如 `service_url`、`default_model`、`default_scene`、`enabled`、`base_url`、`api_url`、`model_dir`、`device`
- translation 目录内部放翻译静态知识
  例如 scene 规则、语言码映射、prompt 模板、Marian 方向约束

说明：
- `service_url`、`default_model`、`default_scene` 只从 YAML 读取
- 不再通过环境变量静默覆盖翻译行为配置
- 密钥仍通过环境变量提供
- `local_nllb` / `local_marian` 当前由 CTranslate2 运行；首次启动时若 `ct2_model_dir` 不存在，会从 `model_dir` 自动转换

## 4. 环境变量

当前翻译模块主要依赖：

```bash
# Qwen / LLM
DASHSCOPE_API_KEY=sk-xxx

# DeepL
DEEPL_AUTH_KEY=xxx
```

服务启动端口仍可以由启动脚本环境控制：

```bash
TRANSLATION_HOST=0.0.0.0
TRANSLATION_PORT=6006
```

## 5. Scene 规则

当前只支持 3 个标准 scene：

- `general`
- `sku_name`
- `ecommerce_search_query`

定义位置：
- [`translation/scenes.py`](/data/saas-search/translation/scenes.py)

约定：
- `scene` 是公共接口字段
- 不再接受旧的 `context`
- 不再对外暴露 `prompt`
- LLM prompt 在服务内根据 `scene` 自动生成

## 6. 对外 HTTP 接口

服务入口在：
- [`api/translator_app.py`](/data/saas-search/api/translator_app.py)

默认地址：
- `http://localhost:6006`

提供接口：
- `POST /translate`
- `GET /health`

### 6.1 POST /translate

请求体：

```json
{
  "text": "商品名称",
  "target_lang": "en",
  "source_lang": "zh",
  "model": "opus-mt-zh-en",
  "scene": "sku_name"
}
```

字段说明：

- `text`
  支持 `string` 或 `string[]`
- `target_lang`
  目标语言
- `source_lang`
  源语言
- `model`
  已配置的 capability 名称
- `scene`
  翻译场景

响应体：

```json
{
  "text": "商品名称",
  "target_lang": "en",
  "source_lang": "zh",
  "translated_text": "Product name",
  "status": "success",
  "model": "opus-mt-zh-en",
  "scene": "sku_name"
}
```

批量时：
- 返回列表和输入等长
- 单条失败返回 `null`

### 6.2 GET /health

返回示例：

```json
{
 "status": "healthy",
  "service": "translation",
  "default_model": "llm",
  "default_scene": "general",
  "available_models": ["qwen-mt", "llm", "nllb-200-distilled-600m", "opus-mt-zh-en", "opus-mt-en-zh"],
  "enabled_capabilities": ["qwen-mt", "llm", "nllb-200-distilled-600m", "opus-mt-zh-en", "opus-mt-en-zh"],
  "loaded_models": ["qwen-mt", "llm", "nllb-200-distilled-600m", "opus-mt-zh-en", "opus-mt-en-zh"]
}
```

说明：
- translator service 进程启动时会一次性初始化全部已启用 capability
- 因此本地模型加载失败、依赖缺失、配置错误会在启动阶段直接暴露，而不是拖到首个在线请求

## 7. 代码调用方式

业务侧统一这样调用：

```python
from translation.client import create_translation_client

translator = create_translation_client()
result = translator.translate(
    text="商品名称",
    source_lang="zh",
    target_lang="en",
    model="opus-mt-zh-en",
    scene="sku_name",
)
```

批量调用：

```python
results = translator.translate(
    text=["商品1", "商品2"],
    source_lang="zh",
    target_lang="en",
    model="opus-mt-zh-en",
    scene="sku_name",
)
```

接口 shape 约定：
- `translate(text="...")` 返回 `Optional[str]`
- `translate(text=[...])` 返回 `List[Optional[str]]`
- 批量模式始终保持“等长、同序返回”；某条失败时对应位置为 `None`
- backend/client 可通过 `supports_batch` 暴露是否支持原生批量；服务端会在必要时自动逐条拆分并保持返回 shape 不变

## 8. 具体实现说明

### 8.1 Qwen-MT

实现文件：
- [`translation/backends/qwen_mt.py`](/data/saas-search/translation/backends/qwen_mt.py)

特点：
- 云端机翻
- 支持 Redis 翻译缓存
- 适合质量优先、非超高并发场景

注意：
- 当前默认 `qwen-mt-flash` 限速较低
- 大量重复请求应依赖缓存

### 8.2 LLM Translation

实现文件：
- [`translation/backends/llm.py`](/data/saas-search/translation/backends/llm.py)

特点：
- 通用大模型翻译
- 根据 `scene` 生成内部 prompt
- 更灵活，但成本和稳定性取决于上游模型
- 支持 Redis 翻译缓存

### 8.3 DeepL

实现文件：
- [`translation/backends/deepl.py`](/data/saas-search/translation/backends/deepl.py)

特点：
- 商业翻译 API
- scene 会映射到内部上下文
- 当前默认关闭
- 支持 Redis 翻译缓存

### 8.4 `facebook/nllb-200-distilled-600M`

实现文件：
- [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py)

模型信息：
- Hugging Face 名称：`facebook/nllb-200-distilled-600M`
- 简介：多语种翻译：覆盖约 200 种语言。作为NLLB-200系列的蒸馏版本，该模型通过知识蒸馏技术将原130亿参数模型压缩至600M，同时保持了80%以上的翻译质量。
- 本地目录：`models/translation/facebook/nllb-200-distilled-600M`
- 当前磁盘占用：约 `2.4G`
- 支持 Redis 翻译缓存
- 模型类型：多语种 Seq2Seq 机器翻译模型
- 来源：Meta NLLB（No Language Left Behind）系列的 600M 蒸馏版
- 结构特点：
  - Transformer encoder-decoder 架构
  - 12 层 encoder + 12 层 decoder
  - `d_model=1024`
  - 通过 `source_lang + forced_bos_token_id` 控制翻译方向
  - 语言标识采用 `language_script` 形式，例如 `eng_Latn`、`zho_Hans`
  - 改良 encoder-decoder（含嵌入层缩放 `scale_embedding`、相对位置等）

核心配置如下：

| 配置项 | 参数值 | 备注 |
| --- | --- | --- |
| 隐藏层维度（`d_model`） | 1024 |  |
| 编码器 / 解码器层数 | 12 / 12 |  |
| 注意力头数 | 16 |  |
| FFN 维度 | 4096 |  |
| 词表大小 | 256,206 | 多语统一词表 |
| 最大序列长度 | 1024 tokens | 满足长文本翻译 |

`config.json` 片段（示意）：

```json
{
  "d_model": 1024,
  "encoder_layers": 12,
  "decoder_layers": 12,
  "attention_dropout": 0.1,
  "use_cache": true,
  "torch_dtype": "float32",
  "max_length": 200
}
```

模型定位：
- 优势是多语覆盖面广，一个模型可以支撑很多语言方向
- 劣势是相较于 Marian 这种双语专用模型，推理更重、延迟更高
- 更适合做**索引翻译**（离线 / 批量），不建议作为在线 query 翻译的默认方案

显存占用情况：
- 600M 模型半精度（float16）权重约 `~1.25G`；推理还会叠加 CUDA context、allocator reserve、激活张量、batch、输入/生成长度等开销
- 当前这台 `Tesla T4` 上，优化后的实际运行峰值大约在 `2.8-3.0 GiB`

当前实现特点：
- backend 类型：`local_nllb`
- 运行时：`CTranslate2 Translator`
- 支持多语
- 调用时必须显式传 `source_lang`
- 语言码映射定义在 [`translation/languages.py`](/data/saas-search/translation/languages.py)
- 当前 T4 推荐配置：`device=cuda`、`ct2_compute_type=float16`、`ct2_inter_threads=4`、`ct2_max_queued_batches=32`、`ct2_batch_type=examples`、`ct2_decoding_length_mode=source(+8,min=32)`、`batch_size=16`、`max_new_tokens=64`

当前实现已经利用的优化：
- 已做批量分块：`translate()` 会按 capability 的 `batch_size` 分批进入模型
- 已切换到 CTranslate2 推理引擎：不再依赖 PyTorch `generate()`
- 已设置方向控制：NLLB 通过 target prefix 指定目标语言
- 已启用半精度：当前配置为 `float16`
- 已关闭高开销搜索：默认 `num_beams=1`，更接近线上低延迟设置

和你给出的批处理示例对照：
- 核心思路已经覆盖，现有实现与 `tokenizer(batch) -> model.generate(...) -> batch_decode(...)` 一致
- 差异在于服务端额外做了语言校验、统一 chunking、输入长度约束和单条/批量 shape 保持
- “预计算 attention mask” 目前没有单独缓存层；现状是每个 batch 在 tokenizer 阶段实时生成 `attention_mask`，这也是 HF 常规推理路径

优化空间（按场景）：
- **线上 query**：优先补测 `batch_size=1` 的真实延迟与 tail latency，而不是继续拉大 batch。
- **离线批量**：可再尝试更激进的 batching / 长度分桶 / 独立批处理队列（吞吐更高，但会增加在线尾延迟风险）。
- **进一步降显存 / 提速**：可在当前 CT2 方案上继续评估 `int8_float16`。

### 8.5 `opus-mt-zh-en`

实现文件：
- [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py)

模型信息：
- Hugging Face 名称：`Helsinki-NLP/opus-mt-zh-en`
- 本地目录：`models/translation/Helsinki-NLP/opus-mt-zh-en`
- 当前磁盘占用：约 `1.2G`
- 模型类型：Marian / OPUS MT 专用双语翻译模型
- 方向约束：只支持 `zh -> en`

结构特点：
- encoder-decoder Seq2Seq
- 聚焦特定语言对
- 模型更小、加载更轻、吞吐更高
- 支持 Redis 翻译缓存

### 8.6 `opus-mt-en-zh`

实现文件：
- [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py)

模型信息：
- Hugging Face 名称：`Helsinki-NLP/opus-mt-en-zh`
- 本地目录：`models/translation/Helsinki-NLP/opus-mt-en-zh`
- 当前磁盘占用：约 `1.5G`
- 模型类型：Marian / OPUS MT 专用双语翻译模型
- 方向约束：只支持 `en -> zh`

结构特点：
- encoder-decoder Seq2Seq
- 双语定向模型
- 更适合中英双向拆分部署
- 支持 Redis 翻译缓存

### 8.7 翻译缓存

- 所有 translation capability 都使用统一的 Redis 缓存层
- 每个 capability 通过各自的 `use_cache` 控制是否启用缓存
- 缓存 key 格式固定为 `trans:{model}:{target_lang}:{source_text[:4]}{sha256}`

## 9. 本地模型安装与部署

### 9.1 准备环境

```bash
cd /data/saas-search
./scripts/setup_translator_venv.sh
```

### 9.2 下载模型

下载全部本地模型：

```bash
./.venv-translator/bin/python scripts/translation/download_translation_models.py --all-local
```

下载完成后，默认目录应存在：

```bash
models/translation/facebook/nllb-200-distilled-600M
models/translation/Helsinki-NLP/opus-mt-zh-en
models/translation/Helsinki-NLP/opus-mt-en-zh
```

### 9.3 打开能力

编辑 [`config/config.yaml`](/data/saas-search/config/config.yaml)，把对应模型的 `enabled` 改成 `true`。

### 9.4 启动服务

```bash
./scripts/start_translator.sh
```

建议：
- 本地模型服务使用单 worker
- 避免多 worker 重复加载模型
- GPU 机器上优先使用 `cuda + float16`
- CPU 只建议用于功能验证或离线低频任务
- 对 NLLB，T4 上优先采用 `batch_size=16 + max_new_tokens=64 + ct2_compute_type=float16 + ct2_inter_threads=4 + ct2_max_queued_batches=32 + ct2_batch_type=examples + ct2_decoding_length_mode=source(+8,min=32)`

### 9.5 验证

健康检查：

```bash
curl http://127.0.0.1:6006/health
```

翻译测试：

```bash
curl -X POST http://127.0.0.1:6006/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "text": "男士偏光飞行员太阳镜",
    "source_lang": "zh",
    "target_lang": "en",
    "model": "opus-mt-zh-en",
    "scene": "sku_name"
  }'
```

## 10. 性能测试与复现

说明：
- 本节现有数值是 `2026-03-18` 的 Hugging Face / PyTorch 基线结果。
- 切换到 CTranslate2 后需要重新跑一轮基准，尤其关注 `nllb-200-distilled-600m` 的单条延迟、并发 tail latency 和 `opus-mt-*` 的 batch throughput。

性能脚本：
- [`benchmarks/translation/benchmark_translation_local_models.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models.py)
- [`benchmarks/translation/benchmark_translation_local_models_focus.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models_focus.py)

数据集：
- [`products_analyzed.csv`](/data/saas-search/products_analyzed.csv)

最新报告：
- 摘要：[`perf_reports/20260318/translation_local_models/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models/README.md)
- 完整 Markdown：[`perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md`](/data/saas-search/perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md)
- 完整 JSON：[`perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.json`](/data/saas-search/perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.json)
- CT2 扩展总结：[`perf_reports/20260318/translation_local_models_ct2/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2/README.md)
- CT2 聚焦调优总结：[`perf_reports/20260318/translation_local_models_ct2_focus/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2_focus/README.md)
- NLLB T4 商品标题专项调优：[`perf_reports/20260318/nllb_t4_product_names_ct2/README.md`](/data/saas-search/perf_reports/20260318/nllb_t4_product_names_ct2/README.md)

### 10.1 先看哪组数据

这里把 3 类结果分开看，不再混在一张表里：

- `batch_sweep`
  固定 `concurrency=1`，只比较不同 `batch_size` 的单流批处理性能
- `concurrency_sweep`
  固定 `batch_size=1`，看“单条请求”在不同并发下的延迟和吞吐
- `batch x concurrency matrix`
  同时看 `batch_size` 和 `concurrency` 的交互效应；本轮限制为 `batch_size * concurrency <= 128`

建议：

- 看线上 query 翻译延迟：优先看 `concurrency_sweep`
- 看离线批量翻译吞吐：优先看 `batch_sweep`
- 看单 worker 服务容量边界：再看 `batch x concurrency matrix`

### 10.2 本轮补测参数

测试时间：`2026-03-18`

环境：
- GPU：`Tesla T4 16GB`
- Python env：`.venv-translator`
- Torch / Transformers：`2.10.0+cu128 / 5.3.0`

统一参数：
- cache：关闭（`--disable-cache`），避免缓存命中干扰性能结果
- `batch_sweep`：每档 `256` items
- `concurrency_sweep`：固定 `batch_size=1`，每档 `32` requests
- `batch x concurrency matrix`：每档 `32` requests，且只保留 `batch_size * concurrency <= 128`
- 预热：`1` batch

复现命令：

```bash
cd /data/saas-search
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py
```

本轮扩展压测复现命令：

```bash
cd /data/saas-search
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py \
  --suite extended \
  --disable-cache \
  --serial-items-per-case 256 \
  --concurrency-requests-per-case 32 \
  --concurrency-batch-size 1 \
  --output-dir perf_reports/20260318/translation_local_models
```

单模型扩展压测示例：

```bash
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py \
  --single \
  --suite extended \
  --model opus-mt-zh-en \
  --source-lang zh \
  --target-lang en \
  --column title_cn \
  --scene sku_name \
  --disable-cache \
  --batch-size-list 1,4,8,16,32,64 \
  --concurrency-list 1,2,4,8,16,64 \
  --serial-items-per-case 256 \
  --concurrency-requests-per-case 32 \
  --concurrency-batch-size 1
```

单条请求延迟复现：

```bash
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py \
  --single \
  --suite extended \
  --model nllb-200-distilled-600m \
  --source-lang zh \
  --target-lang en \
  --column title_cn \
  --scene sku_name \
  --disable-cache \
  --batch-size-list 1 \
  --concurrency-list 1,2,4,8,16,64 \
  --serial-items-per-case 256 \
  --concurrency-requests-per-case 32 \
  --concurrency-batch-size 1
```

### 10.3 单流 batch 结果

这组只看 `concurrency=1`，不要把这里的 `request p95` 当作线上并发请求的 p95。

`nllb-200-distilled-600m zh -> en`

| Batch | Items/s | Avg item ms | Req p95 ms |
|---:|---:|---:|---:|
| 1 | 2.91 | 343.488 | 616.27 |
| 4 | 8.44 | 118.545 | 722.95 |
| 8 | 14.85 | 67.335 | 728.47 |
| 16 | 27.28 | 36.662 | 769.18 |
| 32 | 38.6 | 25.908 | 1369.88 |
| 64 | 58.3 | 17.152 | 1659.9 |

`nllb-200-distilled-600m en -> zh`

| Batch | Items/s | Avg item ms | Req p95 ms |
|---:|---:|---:|---:|
| 1 | 1.91 | 524.917 | 866.33 |
| 4 | 4.94 | 202.473 | 1599.74 |
| 8 | 8.25 | 121.188 | 1632.29 |
| 16 | 13.52 | 73.956 | 1649.65 |
| 32 | 21.27 | 47.017 | 1827.16 |
| 64 | 32.64 | 30.641 | 2031.25 |

`opus-mt-zh-en zh -> en`

| Batch | Items/s | Avg item ms | Req p95 ms |
|---:|---:|---:|---:|
| 1 | 6.15 | 162.536 | 274.74 |
| 4 | 15.34 | 65.192 | 356.0 |
| 8 | 25.51 | 39.202 | 379.84 |
| 16 | 41.44 | 24.129 | 797.93 |
| 32 | 54.36 | 18.397 | 1693.31 |
| 64 | 70.15 | 14.255 | 2161.59 |

`opus-mt-en-zh en -> zh`

| Batch | Items/s | Avg item ms | Req p95 ms |
|---:|---:|---:|---:|
| 1 | 4.53 | 220.598 | 411.57 |
| 4 | 10.12 | 98.844 | 761.49 |
| 8 | 14.63 | 68.361 | 1930.85 |
| 16 | 24.33 | 41.1 | 2098.54 |
| 32 | 33.91 | 29.487 | 2152.28 |
| 64 | 42.47 | 23.547 | 2371.85 |

批处理结论：

- 纯吞吐看，4 个方向的最佳 raw throughput 都出现在 `batch_size=64`
- 如果还要兼顾单个 batch 的尾延迟，`batch_size=16` 往往更均衡
- `opus-mt-zh-en` 是本轮 bulk 场景最快模型，`nllb en->zh` 最慢

### 10.4 单条请求并发结果

这组固定 `batch_size=1`，可以直接理解成“单条请求在不同并发下的表现”。

`nllb-200-distilled-600m zh -> en`

| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---:|---:|---:|---:|---:|
| 1 | 4.17 | 239.99 | 226.34 | 373.27 |
| 2 | 4.1 | 477.99 | 459.36 | 703.96 |
| 4 | 4.1 | 910.74 | 884.71 | 1227.01 |
| 8 | 4.04 | 1697.73 | 1818.48 | 2383.8 |
| 16 | 4.07 | 2801.91 | 3473.63 | 4145.92 |
| 64 | 4.04 | 3714.49 | 3610.08 | 7337.3 |

`nllb-200-distilled-600m en -> zh`

| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---:|---:|---:|---:|---:|
| 1 | 2.16 | 463.18 | 439.54 | 670.78 |
| 2 | 2.15 | 920.48 | 908.27 | 1213.3 |
| 4 | 2.16 | 1759.87 | 1771.58 | 2158.04 |
| 8 | 2.15 | 3284.44 | 3658.45 | 3971.01 |
| 16 | 2.14 | 5669.15 | 7117.7 | 7522.48 |
| 64 | 2.14 | 7631.14 | 7510.97 | 14139.03 |

`opus-mt-zh-en zh -> en`

| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---:|---:|---:|---:|---:|
| 1 | 9.21 | 108.53 | 91.7 | 179.12 |
| 2 | 8.92 | 219.19 | 212.29 | 305.34 |
| 4 | 9.09 | 411.76 | 420.08 | 583.97 |
| 8 | 8.85 | 784.14 | 835.73 | 1043.06 |
| 16 | 9.01 | 1278.4 | 1483.34 | 1994.56 |
| 64 | 8.82 | 1687.08 | 1563.48 | 3381.58 |

`opus-mt-en-zh en -> zh`

| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---:|---:|---:|---:|---:|
| 1 | 3.6 | 277.73 | 145.85 | 1180.37 |
| 2 | 3.55 | 559.38 | 346.71 | 1916.96 |
| 4 | 3.53 | 997.71 | 721.04 | 2944.17 |
| 8 | 3.51 | 1644.28 | 1590.93 | 3632.99 |
| 16 | 3.5 | 2600.18 | 2586.34 | 5554.04 |
| 64 | 3.52 | 3366.52 | 2780.0 | 7950.41 |

并发结论：

- 当前本地 seq2seq backend 内部是单模型锁，单 worker 下提高客户端并发基本不会提升吞吐，主要会把等待时间堆到请求延迟上
- 线上 query 翻译如果追求稳定延迟，应优先控制在低并发；`8+` 并发后，4 个方向的 p95 都明显恶化
- 在线场景里，`opus-mt-zh-en` 延迟最稳；`nllb en->zh` 最慢，且并发放大后尾延迟最明显

### 10.5 batch x concurrency 怎么看

完整矩阵见：
- [`perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md`](/data/saas-search/perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md)

这张表主要回答两个问题：

- 如果已经知道自己要跑离线批处理，`batch_size` 拉大后，在不同并发下吞吐会不会继续涨
- 如果要拿单 worker 服务扛请求，在哪个 `batch_size x concurrency` 组合下开始明显排队

本轮矩阵的共同特征：

- 吞吐主要由 `batch_size` 决定，`concurrency` 不是主要增益来源
- 在 `batch_size` 固定时，`concurrency` 从 `1` 升到 `2/4/8/...`，`items/s` 变化很小，但 `avg req ms / p95` 会持续抬升
- 因此当前实现更像“单 worker + 内部串行 GPU 推理服务”，不是一个靠客户端并发放大吞吐的服务

NLLB 性能优化经验：

- 起作用的优化点 1：`float16 + cuda`
  - 当前本地 NLLB 由 `CTranslate2` 在 `cuda:0` 以 `float16` 运行
  - 优化后在 T4 上的峰值显存约 `2.8-3.0 GiB`
- 起作用的优化点 2：`batch_size=16`
  - 相比 `batch_size=8`，吞吐提升明显
  - 继续提升到 `32` 虽然还能增吞吐，但 batch p95 和 batch max 会恶化很多
- 起作用的优化点 3：`ct2_inter_threads=4 + ct2_max_queued_batches=32`
  - 对 `batch=1` 高并发商品标题场景收益最直接
  - 相比默认 CT2 配置，`zh->en` 和 `en->zh` 的在线吞吐都能稳定提升
- 起作用的优化点 4：动态解码上限
  - 推荐 `ct2_decoding_length_mode=source`
  - 推荐 `ct2_decoding_length_extra=8`
  - 推荐 `ct2_decoding_length_min=32`
  - 这样可以保留 `max_new_tokens=64` 的安全上限，同时让短标题不再为长标题上限付费
- 起作用的优化点 5：`ct2_batch_type=examples`
  - 在当前数据和 T4 上，比 `tokens` 更稳
  - 更适合作为线上默认
- 不建议直接作为默认的实验：
  - `max_new_tokens=48`
  - 大 batch 和在线吞吐都会继续变好
  - 但商品标题 spot-check 已看到明显截断，尤其 `en->zh`
- 收益有限或不稳定的实验：
  - `ct2_batch_type=tokens`
  - `ct2_max_queued_batches` 从 `16` 再继续拉高，收益很小
  - `ct2_decoding_length_mode=source(+4,min=24)` 更快，但仍有少量长标题截断风险

为什么最终没有采用其它方案：

- 当前本地最优路径已经切到 `CTranslate2 + float16`
- 对这个 600M 级 encoder-decoder 模型，T4 上最有效的是把 CT2 的并行和解码策略调对
- 因此这轮没有继续引入更重的服务化栈
- 如果未来目标变成“继续压缩显存”或“进一步追求更低延迟”，再评估 `int8_float16` 或服务级微批处理队列会更合适

关键结论：

- 当前机器上，`opus-mt-zh-en` 是三个新增本地模型里最快的
- `opus-mt-en-zh` 大约是 `opus-mt-zh-en` 吞吐的一半
- `nllb-200-distilled-600M` 在 T4 上推荐 `cuda + CTranslate2 float16 + batch_size=16 + ct2_inter_threads=4 + ct2_max_queued_batches=32 + dynamic decoding`
- `nllb` 最终可用，但吞吐仍明显低于两个 Marian 模型，更适合多语覆盖或独立资源环境

最终推荐部署方案：

- 模型：`facebook/nllb-200-distilled-600M`
- 设备：`cuda`
- 精度：`float16`
- 推荐卡型：至少 `Tesla T4 16GB` 这一级别
- 推荐 batch：`16`
- 推荐 `max_input_length`：`256`
- 推荐 `max_new_tokens`：`64`
- 推荐 `num_beams`：`1`
- 推荐 CT2 并行：`ct2_inter_threads=4`
- 推荐 CT2 队列：`ct2_max_queued_batches=32`
- 推荐 CT2 batch 类型：`examples`
- 推荐动态解码：`ct2_decoding_length_mode=source`、`ct2_decoding_length_extra=8`、`ct2_decoding_length_min=32`
- 运行方式：单 worker，避免重复加载

更详细的性能说明见：
- [`perf_reports/20260318/translation_local_models/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models/README.md)
- [`perf_reports/20260318/nllb_t4_product_names_ct2/README.md`](/data/saas-search/perf_reports/20260318/nllb_t4_product_names_ct2/README.md)

## 11. 开发说明

如果要新增翻译 backend，最少需要做这些事：

1. 在 [`translation/backends/`](/data/saas-search/translation/backends) 下新增实现
2. 在 [`translation/service.py`](/data/saas-search/translation/service.py) 注册 backend 创建逻辑
3. 在 [`config/config.yaml`](/data/saas-search/config/config.yaml) 的 `services.translation.capabilities` 中新增 capability 配置
4. 如果有新的静态规则：
   - scene 规则放到 [`translation/scenes.py`](/data/saas-search/translation/scenes.py)
   - 语言映射放到 [`translation/languages.py`](/data/saas-search/translation/languages.py)
   - prompt 模板放到 [`translation/prompts.py`](/data/saas-search/translation/prompts.py)

原则：
- 不要再引入 translation provider 兼容层
- 不要把 scene / prompt / 语言方向规则重新散落到别的目录
- 不要在代码里写隐式默认和静默兼容

## 12. 常见建议

- 中英商品标题双向场景，优先考虑 `opus-mt-zh-en` 和 `opus-mt-en-zh`
- 多语种统一方案，可以考虑 `nllb-200-distilled-600M`
- 但 `nllb` 更适合独占资源环境
- 如果追求更高质量或更复杂语义处理，可使用 `qwen-mt` 或 `llm`
- 如果追求稳定商业 API，可考虑 `deepl`

## 13. 相关文档

- [`docs/翻译模块说明.md`](/data/saas-search/docs/翻译模块说明.md)（已收口到本 README，保留为跳转页）
- [`docs/QUICKSTART.md`](/data/saas-search/docs/QUICKSTART.md)
- [`docs/DEVELOPER_GUIDE.md`](/data/saas-search/docs/DEVELOPER_GUIDE.md)
- [`docs/搜索API对接指南-00-总览与快速开始.md`](/data/saas-search/docs/搜索API对接指南-00-总览与快速开始.md)（分册导航；微服务见 `-07`）
