# 搜索API对接指南-07-微服务接口(Embedding-Reranker-Translation) 本篇覆盖向量服务(Embedding)、重排服务(Reranker)、翻译服务(Translation)以及 Indexer 服务内的内容理解字段生成(原文第 7 章)。 ## 7. 微服务接口(向量、重排、翻译) 以下三个微服务独立部署,**外部系统可直接调用**。它们被搜索后端(6002)和索引服务(6004)内部使用,也可供其他业务系统直接对接。 | 服务 | 默认端口 | Base URL | 说明 | |------|----------|----------|------| | 向量服务(文本) | 6005 | `http://localhost:6005` | 文本向量化,用于 query/doc 语义检索 | | 向量服务(图片 / 多模态 CN-CLIP) | 6008 | `http://localhost:6008` | 图片向量 `/embed/image`;同空间文本向量 `/embed/clip_text`(以文搜图等) | | 翻译服务 | 6006 | `http://localhost:6006` | 多语言翻译(云端与本地模型统一入口) | | 重排服务 | 6007 | `http://localhost:6007` | 对检索结果进行二次排序 | 生产环境请将 `localhost` 替换为实际服务地址。 服务管理入口与完整启停规则见:`docs/Usage-Guide.md` -> `服务管理总览`。 ### 7.1 向量服务(Embedding) - **Base URL**: - 文本:`http://localhost:6005`(可通过 `EMBEDDING_TEXT_SERVICE_URL` 覆盖) - 图片:`http://localhost:6008`(可通过 `EMBEDDING_IMAGE_SERVICE_URL` 覆盖) - **启动**: - 文本:`./scripts/start_embedding_text_service.sh` - 图片:`./scripts/start_embedding_image_service.sh` - **依赖**: - 文本向量后端默认走 TEI(`http://127.0.0.1:8080`) - 图片向量依赖 `cnclip`(`grpc://127.0.0.1:51000`) - TEI 默认使用 GPU(`TEI_DEVICE=cuda`);当配置为 GPU 且不可用时会启动失败(不会自动降级到 CPU) - cnclip 默认使用 `cuda`;若配置为 `cuda` 但 GPU 不可用会启动失败(不会自动降级到 `cpu`) - 当前单机部署建议保持单实例,通过**文本/图片拆分 + 独立限流**隔离压力 补充说明: - 文本和图片现在已经拆成**不同进程 / 不同端口**,避免图片下载与编码波动影响文本向量化。 - 服务端对 text / image 有**独立 admission control**: - `TEXT_MAX_INFLIGHT` - `IMAGE_MAX_INFLIGHT` - 当超过处理能力时,服务会直接返回过载错误,而不是无限排队。 - 文本与图片服务均支持 `priority` query 参数(图片不做队列插队,仅 admission 规则与文本一致): - `priority=0`(默认):适合离线索引,仍分别受 `TEXT_MAX_INFLIGHT` / `IMAGE_MAX_INFLIGHT` admission control 约束。 - `priority>0`(建议在线请求用 `1`):不会因 admission control 被拒绝,但仍会占用对应 text/image 的 inflight。 - 文本服务端会优先处理高优先级文本请求;图片端不实现插队,顺序按请求到达处理即可。 - `GET /health` 会返回各自的 `limits`、`stats`、`cache_enabled` 等状态;`GET /ready` 用于就绪探针。 #### 7.1.1 `POST /embed/text` — 文本向量化 将文本列表转为 1024 维向量,用于语义搜索、文档索引等。 **请求体**(JSON 数组): ```json ["文本1", "文本2", "文本3"] ``` **响应**(JSON 数组,与输入一一对应): ```json [[0.01, -0.02, ...], [0.03, 0.01, ...], ...] ``` **完整 curl 示例**: ```bash curl -X POST "http://localhost:6005/embed/text?normalize=true&priority=1" \ -H "Content-Type: application/json" \ -d '["芭比娃娃 儿童玩具", "纯棉T恤 短袖"]' ``` 说明: - 在线 query / 实时请求:建议显式传 `priority=1` - 离线索引 / 批量回填:保持默认 `priority=0` 即可 #### 7.1.2 `POST /embed/image` — 图片向量化 将图片 URL 或路径转为向量,用于以图搜图。 前置条件:`cnclip` 服务已启动(默认端口 `51000`)。若未启动,图片 embedding 服务启动会失败或请求返回错误。 **请求体**(JSON 数组): ```json ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] ``` **响应**(JSON 数组,与输入一一对应): ```json [[0.01, -0.02, ...], [0.03, 0.01, ...], ...] ``` **完整 curl 示例**: ```bash curl -X POST "http://localhost:6008/embed/image?normalize=true&priority=1" \ -H "Content-Type: application/json" \ -d '["https://oss.essa.cn/98532128-cf8e-456c-9e30-6f2a5ea0c19f.jpg"]' ``` 在线以图搜图等实时场景可传 `priority=1`;离线索引回填保持默认 `priority=0`。 #### 7.1.3 `POST /embed/clip_text` — CN-CLIP 文本多模态向量(与图片同空间) 将**自然语言短语**编码为向量,与 `POST /embed/image` 输出的图向量**处于同一向量空间**(Chinese-CLIP 文本塔 / 图塔),用于 **以文搜图**、与 ES `image_embedding` 对齐的 KNN 等。默认配置为 **ViT-H-14**,向量长度 **1024**(与 `mappings/search_products.json` 中 `image_embedding.vector.dims` 一致);若改为 ViT-L-14 则为 768 维,须同步索引映射与全量重索引。 与 **7.1.1** 的 `POST /embed/text`(TEI/BGE,语义检索)**不是同一模型、不是同一空间**,请勿混用。 **请求体**(JSON 数组,每项为字符串;**不要**传入 `http://` / `https://` 图片 URL,图片请用 `/embed/image`): ```json ["纯棉短袖T恤", "芭比娃娃连衣裙"] ``` **响应**(JSON 数组,与输入一一对应): ```json [[0.01, -0.02, ...], [0.03, 0.01, ...], ...] ``` **curl 示例**: ```bash curl -X POST "http://localhost:6008/embed/clip_text?normalize=true&priority=1" \ -H "Content-Type: application/json" \ -d '["纯棉短袖", "street tee"]' ``` 说明:与 `/embed/image` 共用图片侧限流与 `IMAGE_MAX_INFLIGHT`;Redis 缓存键 namespace 为 `clip_text`,与 TEI 文本缓存区分。 #### 7.1.4 `GET /health` — 健康检查 ```bash curl "http://localhost:6005/health" curl "http://localhost:6008/health" ``` 返回中会包含: - `service_kind`:`text` / `image` / `all` - `cache_enabled`:text/image/clip_text Redis 缓存是否可用 - `limits`:当前 inflight limit、active、rejected_total 等 - `stats`:request_total、cache_hits、cache_misses、avg_latency_ms 等 #### 7.1.5 `GET /ready` — 就绪检查 ```bash curl "http://localhost:6005/ready" curl "http://localhost:6008/ready" ``` #### 7.1.6 缓存与限流说明 - 文本与图片都会先查 Redis 向量缓存。 - Redis 中 value 仍是 **BF16 bytes**,读取后恢复成 `float32` 返回。 - cache key 已区分 `normalize=true/false`,避免不同归一化策略命中同一条缓存。 - 当服务端发现请求是 **full-cache-hit** 时,会直接返回,不占用模型并发槽位。 - 当服务端发现超过 `TEXT_MAX_INFLIGHT` / `IMAGE_MAX_INFLIGHT` 时,会直接拒绝,而不是无限排队。 - 其中 `POST /embed/text` 的 `priority=0` 会按上面的 inflight 规则直接拒绝;`priority>0` 不会被 admission 拒绝,但仍计入 inflight,并在服务端排队时优先于 `priority=0` 请求。 - `POST /embed/image` 的 `priority=0` 受 `IMAGE_MAX_INFLIGHT` 约束;`priority>0` 不会被 admission 拒绝,但仍计入 inflight(无插队)。 - `POST /embed/clip_text` 与 `/embed/image` 共用同一后端与 `IMAGE_MAX_INFLIGHT`(计入图片侧并发)。 #### 7.1.7 TEI 统一调优建议(主服务) 使用单套主服务即可同时兼顾: - 在线 query 向量化(低延迟,常见 `batch=1~4`) - 索引构建向量化(高吞吐,常见 `batch=15~20`) 统一启动(主链路): ```bash ./scripts/start_tei_service.sh ./scripts/service_ctl.sh restart embedding ``` 默认端口: - TEI: `http://127.0.0.1:8080` - 文本向量服务(`/embed/text`): `http://127.0.0.1:6005` - 图片向量服务(`/embed/image`、`/embed/clip_text`): `http://127.0.0.1:6008` 当前主 TEI 启动默认值(已按 T4/短文本场景调优): - `TEI_MAX_BATCH_TOKENS=4096` - `TEI_MAX_CLIENT_BATCH_SIZE=24` - `TEI_DTYPE=float16` ### 7.2 重排服务(Reranker) - **Base URL**: `http://localhost:6007`(可通过 `RERANKER_SERVICE_URL` 覆盖) - **启动**: `./scripts/start_reranker.sh` 说明:默认后端为 `qwen3_vllm`(`Qwen/Qwen3-Reranker-0.6B`),需要可用 GPU 显存。 补充:若切换到 `jina_reranker_v3`,在当前 `Tesla T4` 上建议使用: - `dtype: float16` - `batch_size: 64` - `max_doc_length: 160` - `max_query_length: 64` - `sort_by_doc_length: true` 原因:`jina_reranker_v3` 的 `auto` 在当前机器上会落到 `bfloat16`,性能明显差于 `float16`;而它的 listwise 架构在 T4 上对上下文长度更敏感,过大的 batch 会显著拉长延迟。 补充:`docs` 的请求大小与模型推理 `batch size` 解耦。即使一次传入 1000 条文档,服务端也会按 `services.rerank.backends.qwen3_vllm.infer_batch_size` 自动拆分。 #### 7.2.1 `POST /rerank` — 结果重排 根据 query 与 doc 的相关性对文档列表重新打分排序。 **请求体**: ```json { "query": "玩具 芭比", "docs": [ "12PCS 6 Types of Dolls with Bottles", "纯棉T恤 短袖 夏季" ], "normalize": true } ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `query` | string | Y | 搜索查询 | | `docs` | array[string] | Y | 待重排的文档列表(单次最多由服务端配置限制) | | `normalize` | boolean | N | 是否对分数做 sigmoid 归一化,默认 true | **响应**: ```json { "scores": [0.92, 0.15], "meta": { "service_elapsed_ms": 45.2, "input_docs": 2, "unique_docs": 2 } } ``` **完整 curl 示例**: ```bash curl -X POST "http://localhost:6007/rerank" \ -H "Content-Type: application/json" \ -d '{ "query": "玩具 芭比", "docs": ["12PCS 6 Types of Dolls with Bottles", "纯棉T恤 短袖"], "top_n":386, "normalize": true }' ``` #### 7.2.2 `GET /health` — 健康检查 ```bash curl "http://localhost:6007/health" ``` ### 7.3 翻译服务(Translation) - **Base URL**: `http://localhost:6006`(以 `config/config.yaml -> services.translation.service_url` 为准) - **启动**: `./scripts/start_translator.sh` #### 7.3.1 `POST /translate` — 文本翻译 支持 translator service 内所有已启用 capability,适用于商品名称、描述、query 等电商场景。当前可配置能力包括 `qwen-mt`、`llm`、`deepl` 以及本地模型 `nllb-200-distilled-600m`、`opus-mt-zh-en`、`opus-mt-en-zh`。 **请求体**(支持单条字符串或字符串列表): ```json { "text": "商品名称", "target_lang": "en", "source_lang": "zh", "model": "qwen-mt", "scene": "sku_name" } ``` 也支持批量列表形式: ```json { "text": ["商品名称1", "商品名称2"], "target_lang": "en", "source_lang": "zh", "model": "qwen-mt", "scene": "sku_name" } ``` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `text` | string \| string[] | Y | 待翻译文本,既支持单条字符串,也支持字符串列表(批量翻译) | | `target_lang` | string | Y | 目标语言:`zh`、`en`、`ru` 等 | | `source_lang` | string | N | 源语言。云端模型可不传;`nllb-200-distilled-600m` 建议显式传入 | | `model` | string | N | 已启用 capability 名称,如 `qwen-mt`、`llm`、`deepl`、`nllb-200-distilled-600m`、`opus-mt-zh-en`、`opus-mt-en-zh` | | `scene` | string | N | 翻译场景参数,与 `model` 配套使用;当前标准值为 `sku_name`、`ecommerce_search_query`、`general` | 说明: - 外部接口不接受 `prompt`;LLM prompt 由服务端按 `scene` 自动生成。 - 传入未定义的 `scene` 或未启用的 `model` 会返回 `400`。 **SKU 名称场景选型建议**: - 批量 SKU 名称翻译,优先考虑本地大吞吐方案时,可使用 `"model": "nllb-200-distilled-600m"`(该模型"scene":参数无效)。 - 如果目标是更高质量,且可以接受更慢速度与额外 LLM API 费用,可使用 `"model": "llm"` + `"scene": "sku_name"`。 - 如果是en-zh互译、期待更高的速度,可以考虑`opus-mt-zh-en` / `opus-mt-en-zh`。(质量未详细评测,一些文章说比blib-200-600m更好,但是我看了些case感觉要差不少) **实时翻译选型建议**: - 在线 query 翻译如果只是 `en/zh` 互译,优先使用 `opus-mt-zh-en` 或 `opus-mt-en-zh`。 - 如果涉及其他语言,或对质量要求高于本地轻量模型,优先考虑 `deepl`。 - `nllb-200-distilled-600m` 不建议作为在线 query 翻译默认方案;我们在 `Tesla T4` 上测到 `batch_size=1` 时,根据query长短,耗时大概在70-150ms之间。 **Batch Size / 调用方式建议**: - 本接口支持 `text: string[]`;离线或批量索引翻译时,应尽量合并请求,让底层 backend 发挥批处理能力。 - `nllb-200-distilled-600m` 在当前 `Tesla T4` 压测中,推荐配置是 `batch_size=16`、`max_new_tokens=64`、`attn_implementation=sdpa`;继续升到 `batch_size=32` 虽可能提高吞吐,但 tail latency 会明显变差。 - 在线 query 场景可直接把“单条请求”理解为 `batch_size=1`;更关注 request latency,而不是离线吞吐。 - `opus-mt-zh-en` / `opus-mt-en-zh` 当前生产配置也是 `batch_size=16`,适合作为中英互译的低延迟本地默认值;若走在线单条调用,同样按 `batch_size=1` 理解即可。 - `llm` 按单条请求即可。 **响应**: ```json { "text": "商品名称", "target_lang": "en", "source_lang": "zh", "translated_text": "Product name", "status": "success", "model": "qwen-mt", "scene": "sku_name" } ``` 当请求为列表形式时,`text` 与 `translated_text` 均为等长数组: ```json { "text": ["商品名称1", "商品名称2"], "target_lang": "en", "source_lang": "zh", "translated_text": ["Product name 1", "Product name 2"], "status": "success", "model": "qwen-mt", "scene": "sku_name" } ``` > **失败语义(批量)**:当 `text` 为列表时,如果其中某条翻译失败,对应位置返回 `null`(即 `translated_text[i] = null`),并保持数组长度与顺序不变;接口整体仍返回 `status="success"`,用于避免“部分失败”导致整批请求失败。 > **实现提示(可忽略)**:服务端会尽可能使用底层 backend 的批量能力(若支持),否则自动拆分逐条翻译;无论采用哪种方式,上述批量契约保持一致。 **完整 curl 示例**: 中文 → 英文: ```bash curl -X POST "http://localhost:6006/translate" \ -H "Content-Type: application/json" \ -d '{ "text": "商品名称", "target_lang": "en", "source_lang": "zh" }' ``` 俄文 → 英文: ```bash curl -X POST "http://localhost:6006/translate" \ -H "Content-Type: application/json" \ -d '{ "text": "Название товара", "target_lang": "en", "source_lang": "ru" }' ``` 使用 DeepL 模型: ```bash curl -X POST "http://localhost:6006/translate" \ -H "Content-Type: application/json" \ -d '{ "text": "商品名称", "target_lang": "en", "source_lang": "zh", "model": "deepl" }' ``` 使用本地 OPUS 模型(中文 → 英文): ```bash curl -X POST "http://localhost:6006/translate" \ -H "Content-Type: application/json" \ -d '{ "text": "蓝牙耳机", "target_lang": "en", "source_lang": "zh", "model": "opus-mt-zh-en", "scene": "sku_name" }' ``` 使用本地 NLLB 做 SKU 名称批量翻译: ```bash curl -X POST "http://localhost:6006/translate" \ -H "Content-Type: application/json" \ -d '{ "text": ["商品名称1", "商品名称2", "商品名称3"], "target_lang": "en", "source_lang": "zh", "model": "nllb-200-distilled-600m", "scene": "sku_name" }' ``` 使用 LLM 做高质量 SKU 名称翻译: ```bash curl -X POST "http://localhost:6006/translate" \ -H "Content-Type: application/json" \ -d '{ "text": "男士偏光飞行员太阳镜", "target_lang": "en", "source_lang": "zh", "model": "llm", "scene": "sku_name" }' ``` #### 7.3.2 `GET /health` — 健康检查 ```bash curl "http://localhost:6006/health" ``` 典型响应: ```json { "status": "healthy", "service": "translation", "default_model": "llm", "default_scene": "general", "available_models": ["qwen-mt", "llm", "opus-mt-zh-en"], "enabled_capabilities": ["qwen-mt", "llm", "opus-mt-zh-en"], "loaded_models": ["llm"] } ``` ### 7.4 内容理解字段生成(Indexer 服务内) 内容理解字段生成接口部署在 **Indexer 服务**(默认端口 6004)内,与「翻译、向量化」等独立端口微服务并列,供采用**微服务组合**方式的 indexer 调用。 - **Base URL**: Indexer 服务地址,如 `http://localhost:6004` - **路径**: `POST /indexer/enrich-content` - **说明**: 根据商品标题批量生成 `qanchors`、`enriched_attributes`、`enriched_tags`、`enriched_taxonomy_attributes`,用于拼装 ES 文档。支持通过 `enrichment_scopes` 选择执行 `generic` / `category_taxonomy`,并通过 `category_taxonomy_profile` 选择对应大类的 taxonomy prompt/profile;默认执行 `generic + category_taxonomy(apparel)`。当前支持的 taxonomy profile 包括 `apparel`、`3c`、`bags`、`pet_supplies`、`electronics`、`outdoor`、`home_appliances`、`home_living`、`wigs`、`beauty`、`accessories`、`toys`、`shoes`、`sports`、`others`。其中 `apparel` 的 taxonomy 输出为 `zh` + `en`,其余 profile 的 taxonomy 输出仅返回 `en`。内部使用大模型(需配置 `DASHSCOPE_API_KEY`),支持多语言与 Redis 缓存;单次最多 50 条,建议批量调用以提升效率。 请求/响应格式、示例及错误码见 [-05-索引接口(Indexer)](./搜索API对接指南-05-索引接口(Indexer).md#58-内容理解字段生成接口)。 ---