# CN-CLIP 服务（clip-as-service）说明

> 本文是本仓库 CN-CLIP（图片向量）专项运行手册。目标是让新同事在新机器上独立完成环境准备、部署、验收与排障。

## 1. 作用与边界

- `cnclip` 是独立 gRPC 服务（默认 `grpc://127.0.0.1:51000`），底层为 **Chinese-CLIP**：**图像与文本在同一向量空间**（图文可互检）。
- 图片 embedding 服务（默认 `6008`）在 `image_backend: clip_as_service` 时通过 gRPC 调用它完成：
  - `POST /embed/image`：图片 URL / 本地路径 → 图向量；
  - `POST /embed/clip_text`：**自然语言短语** → 文本塔向量（与上图向量同空间，用于 `image_embedding` 检索、以文搜图等）。
- **语义检索用的文本向量**（`title_embedding`、query 语义召回）仍由 **TEI（8080）+ `POST /embed/text`（6005）** 负责，与 CN-CLIP 不是同一模型、不是同一向量空间；请勿混淆。

## 2. 代码与脚本入口

- 环境初始化：`scripts/setup_cnclip_venv.sh`
- 启动：`scripts/start_cnclip_service.sh`
- 停止：`scripts/stop_cnclip_service.sh`
- 统一编排：`scripts/service_ctl.sh`

## 3. 前置条件

### 3.1 必需软件

- Python 3.12（由仓库脚本创建虚拟环境）
- 可访问模型下载源（Hugging Face / 镜像源）

### 3.2 GPU 模式额外要求

- NVIDIA 驱动可用（`nvidia-smi` 可执行）
- `cnclip` 设备设为 `cuda`

检查命令：

```bash
nvidia-smi
```

## 4. 环境安装（可复现）

在仓库根目录执行：

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

脚本会创建 `.venv-cnclip`，并安装 `clip-as-service` 服务端所需依赖。  
`start_cnclip_service.sh` 只使用这个专用环境，不会污染主 `.venv`。

## 5. 启动与停止

### 5.1 GPU 模式（推荐）

```bash
./scripts/start_cnclip_service.sh --device cuda
```

### 5.2 CPU 模式（显式）

```bash
./scripts/start_cnclip_service.sh --device cpu
```

### 5.3 模型配置与覆盖

- 仓库默认模型取自 `embeddings/config.py` 的 `CLIP_AS_SERVICE_MODEL_NAME`。
- `scripts/start_cnclip_service.sh` 会自动读取这个配置，因此修改默认模型时不需要再去脚本里找硬编码。
- 覆盖优先级：`--model-name` > `CNCLIP_MODEL_NAME` > `CLIP_AS_SERVICE_MODEL_NAME` / `embeddings/config.py`。

查看当前默认模型：

```bash
python3 -c "from embeddings.config import CONFIG; print(CONFIG.CLIP_AS_SERVICE_MODEL_NAME)"
```

临时覆盖模型：

```bash
./scripts/start_cnclip_service.sh --model-name CN-CLIP/ViT-L-14   # 768 维，需与索引 dims 一致
CNCLIP_MODEL_NAME=CN-CLIP/ViT-H-14 ./scripts/service_ctl.sh start cnclip   # 默认 1024 维（与 mappings 中 image_embedding 一致）
```

### 5.4 停止服务

```bash
./scripts/stop_cnclip_service.sh
```

## 6. service_ctl 的行为与约定

### 6.1 通过统一编排启动

```bash
./scripts/service_ctl.sh start cnclip
# 或一次启动可选能力：./scripts/service_ctl.sh start embedding embedding-image tei cnclip
```

### 6.2 设备选择优先级

- 显式传入 `CNCLIP_DEVICE` 时，以该值为准：

```bash
CNCLIP_DEVICE=cuda ./scripts/service_ctl.sh start cnclip
CNCLIP_DEVICE=cpu  ./scripts/service_ctl.sh start cnclip
```

- 不传 `CNCLIP_DEVICE` 时，默认 `cuda`。

### 6.3 运行中模式校验（重要）

`service_ctl` 会在“已运行”场景下检查当前 `cnclip` 的 device 与期望是否一致。  
若不一致会直接报错，防止“服务已启动但模式错了”的静默问题。

例如：当前运行是 CPU，但你期望 CUDA，会提示先 stop 再按 CUDA 重启。

## 7. 验收步骤（必须做）

### 7.1 基础状态

```bash
./scripts/service_ctl.sh status cnclip
```

### 7.2 核对运行配置

```bash
cat third-party/clip-as-service/server/torch-flow-temp.yml
```

应看到：

- GPU 模式：`device: 'cuda'`
- CPU 模式：`device: 'cpu'`
- 模型名：`name: '<当前实际模型名>'`

### 7.2.1 日志与 PID 文件

- 日志：`logs/cnclip.log`
- PID：`logs/cnclip.pid`

### 7.3 发送一次编码请求（触发模型加载）

**gRPC 直连（文本或图片 URL 混传时由 client 自动区分）：**

```bash
PYTHONPATH="third-party/clip-as-service/client:${PYTHONPATH}" \
NO_VERSION_CHECK=1 \
.venv-cnclip/bin/python - <<'PY'
from clip_client import Client
c = Client('grpc://127.0.0.1:51000')
r = c.encode(['测试'])
print('shape:', r.shape)
PY
```

预期 `shape` 为 `(1, 1024)`。

**HTTP（推荐业务侧）：图片走 6008 `/embed/image`，纯文本走 `/embed/clip_text`（勿把 `http://` 图 URL 发到 `clip_text`）：**

```bash
curl -sS -X POST "http://127.0.0.1:6008/embed/clip_text?normalize=true&priority=1" \
  -H "Content-Type: application/json" \
  -d '["纯棉T恤", "芭比娃娃"]'
```

### 7.4 GPU 验证

```bash
nvidia-smi
```

GPU 模式下应出现 `clip_server` 相关 `python` 进程及显存占用。

### 7.5 与 embedding 服务联调

```bash
./scripts/start_embedding_image_service.sh

curl -sS -X POST "http://127.0.0.1:6008/embed/image" \
  -H "Content-Type: application/json" \
  -d '["https://oss.essa.cn/98532128-cf8e-456c-9e30-6f2a5ea0c19f.jpg"]'
```

返回应为向量数组（非空）。

## 8. 相关文档

- 开发总览：`docs/QUICKSTART.md`
- TEI 专项：`docs/TEI_SERVICE说明文档.md`
- 体系规范：`docs/DEVELOPER_GUIDE.md`
- embedding 模块：`embeddings/README.md`
