210 lines
8.8 KiB
Markdown
210 lines
8.8 KiB
Markdown
# LLM 推理服务
|
||
|
||
为 [minor Agent](../README.md) 提供本地化推理后端,包含 **6 个独立服务**,均跑在 GPU 服务器上,通过 OpenAI 兼容 / 自定义 HTTP API 暴露。可整体部署,也可按需启用。
|
||
|
||
## 服务总览
|
||
|
||
| 服务 | 端口 | 模型 | 启动脚本 | Python 服务端 | 关键端点 |
|
||
|------|:----:|------|----------|---------------|----------|
|
||
| **LLM** | 8900 | Qwen3.6-35B-A3B-FP8 | `start_llm.sh` | vLLM 内置 | `/v1/chat/completions` |
|
||
| **ASR** | 8901 | Qwen3-ASR-1.7B | `start_asr.sh` | vLLM 内置 | `/v1/chat/completions` |
|
||
| **TTS** | 8902 | VoxCPM 1.5 / 2 | `start_streaming_tts.sh` | `streaming_tts_server.py` | `/stream_tts` |
|
||
| **RAG** | 8903 | Embedding-0.6B + Reranker-4B | `start_rag_server.sh` | `rag_server.py` | `/embed` `/rerank` |
|
||
| **Image Gen** | 8904 | Z-Image-Turbo | `start_image_gen.sh` | `image_gen_server.py` | `/generate` |
|
||
|
||
> 所有脚本 / Python 文件均通过 `os.path.dirname(__file__)` 与 `${SCRIPT_DIR}` 动态定位模型路径,**目录整体迁移无需改代码**。
|
||
|
||
---
|
||
|
||
## 一、部署流程
|
||
|
||
### 1. 上传到服务器
|
||
|
||
```bash
|
||
scp -P <SSH_PORT> -r Agent/llm_server/* <user>@<your-gpu-server>:~/llm_server/
|
||
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -p <SSH_PORT> <user>@<your-gpu-server>
|
||
```
|
||
|
||
### 2. 服务器环境
|
||
|
||
```bash
|
||
cd ~/llm_server
|
||
chmod +x *.sh
|
||
|
||
# HuggingFace 镜像加速(国内必需)
|
||
export HF_ENDPOINT=https://hf-mirror.com
|
||
export HF_HUB_ENABLE_XET=0
|
||
echo 'export HF_ENDPOINT=https://hf-mirror.com' >> ~/.bashrc
|
||
echo 'export HF_HUB_ENABLE_XET=0' >> ~/.bashrc
|
||
|
||
python3 -m venv myenv && source myenv/bin/activate
|
||
|
||
# 一次性安装全部依赖
|
||
pip install vllm modelscope transformers fastapi uvicorn torch numpy torchaudio soundfile \
|
||
bitsandbytes huggingface_hub voxcpm diffusers
|
||
sudo apt update && sudo apt install -y ffmpeg libavcodec-dev libavformat-dev libavutil-dev \
|
||
libavdevice-dev libavfilter-dev libswscale-dev libswresample-dev
|
||
```
|
||
|
||
### 3. 下载模型
|
||
|
||
所有模型统一存放 `~/llm_server/models/`:
|
||
|
||
| 模型 | 命令 |
|
||
|------|------|
|
||
| Qwen3.6-35B-A3B-FP8 (LLM) | `modelscope download --model Qwen/Qwen3.6-35B-A3B-FP8 --local_dir ./models/Qwen/Qwen3___6-35B-A3B-FP8` |
|
||
| Qwen3-ASR-1.7B | `modelscope download --model Qwen/Qwen3-ASR-1.7B --local_dir ./models/Qwen/Qwen3-ASR-1.7B` |
|
||
| Qwen3-Embedding-0.6B | `modelscope download --model Qwen/Qwen3-Embedding-0.6B --local_dir ./models/Qwen/Qwen3-Embedding-0___6B` |
|
||
| Qwen3-Reranker-4B | `modelscope download --model Qwen/Qwen3-Reranker-4B --local_dir ./models/Qwen/Qwen3-Reranker-4B` |
|
||
| VoxCPM 1.5 (推荐) | `modelscope download --model OpenBMB/VoxCPM1.5 --local_dir ./models/VoxCPM1__5` |
|
||
| VoxCPM 2 (音质更佳) | `modelscope download --model OpenBMB/VoxCPM2 --local_dir ./models/VoxCPM2` |
|
||
| Z-Image-Turbo | `modelscope download --model Tongyi-MAI/Z-Image-Turbo --local_dir ./models/Tongyi-MAI/Z-Image-Turbo` |
|
||
|
||
完成后目录结构:
|
||
|
||
```
|
||
~/llm_server/models/
|
||
├── Qwen/
|
||
│ ├── Qwen3___6-35B-A3B-FP8/ # LLM
|
||
│ ├── Qwen3-ASR-1.7B/ # ASR
|
||
│ ├── Qwen3-Embedding-0___6B/ # Embedding
|
||
│ └── Qwen3-Reranker-4B/ # Reranker
|
||
├── VoxCPM1__5/ # TTS
|
||
└── Tongyi-MAI/Z-Image-Turbo/ # Image Gen
|
||
```
|
||
|
||
---
|
||
|
||
## 二、启动与测试
|
||
|
||
每个服务独立启动,互不依赖。所有 `start_*.sh` 支持环境变量覆盖:
|
||
|
||
| 环境变量 | 作用 | 适用脚本 |
|
||
|----------|------|----------|
|
||
| `HOST_PORT` | 服务端口 | 全部 |
|
||
| `LOCAL_MODEL_DIR` / `MODEL_PATH` / `VOXCPM_MODEL_PATH` | 模型目录 | 全部 |
|
||
| `CUDA_VISIBLE_DEVICES` | GPU 设备号 | 全部 |
|
||
| `SPECIFIC_GPU` | 单 GPU 指定(写 `CUDA_VISIBLE_DEVICES`) | image_gen / tts |
|
||
| `SERVER_API_KEY` | API Key 鉴权(空=不校验,任意值放行) | 全部 |
|
||
| `SERVER_MODELNAME` | 服务模型名(请求 body.model 须匹配;空=不校验) | 全部 |
|
||
|
||
> **鉴权与模型名校验**:`SERVER_API_KEY` 为空时服务不校验 API Key(任意值放行);
|
||
> `SERVER_MODELNAME` 为空时服务不校验请求中的 `model` 字段。
|
||
> 部署时如需启用,示例:
|
||
> ```bash
|
||
> SERVER_API_KEY=sk-xxx SERVER_MODELNAME=Qwen3-ASR-1.7B bash start_asr.sh
|
||
> ```
|
||
|
||
### LLM · 8900
|
||
|
||
```bash
|
||
bash start_llm.sh
|
||
# 启用鉴权与自定义模型名
|
||
# SERVER_API_KEY=sk-xxx SERVER_MODELNAME=Qwen/Qwen3.6-35B-A3B-FP8 bash start_llm.sh
|
||
# 测试
|
||
python3 test_llm.py --url http://127.0.0.1:8900 # 非流式
|
||
python3 test_llm.py --url http://127.0.0.1:8900 --stream # 流式
|
||
```
|
||
|
||
> vLLM 加载 Qwen3.6-35B-A3B-FP8,OpenAI 兼容 API,使用 `chat_template.jinja` 格式化对话。
|
||
> `--served-model-name` 由 `SERVER_MODELNAME` 控制(默认 `Qwen/Qwen3.6-35B-A3B-FP8`);
|
||
> `SERVER_API_KEY` 非空时传给 vLLM `--api-key`,请求需携带 `Authorization: Bearer <key>`。
|
||
|
||
### ASR · 8901
|
||
|
||
```bash
|
||
bash start_asr.sh
|
||
# 启用鉴权与自定义模型名
|
||
# SERVER_API_KEY=sk-xxx SERVER_MODELNAME=Qwen3-ASR-1.7B bash start_asr.sh
|
||
# 测试
|
||
python3 test_asr.py --audio /path/to/audio.wav --url http://127.0.0.1:8901/v1/chat/completions
|
||
```
|
||
|
||
> vLLM 加载 Qwen3-ASR-1.7B,`--served-model-name` 由 `SERVER_MODELNAME` 控制(默认 `Qwen3-ASR-1.7B`)。
|
||
|
||
### TTS · 8902(流式)
|
||
|
||
```bash
|
||
bash start_streaming_tts.sh
|
||
# 启用鉴权与自定义模型名
|
||
# SERVER_API_KEY=sk-xxx SERVER_MODELNAME=VoxCPM1.5 bash start_streaming_tts.sh
|
||
# 测试(流式返回 wav 分块)
|
||
curl -X POST http://127.0.0.1:8902/stream_tts \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"model": "VoxCPM1.5", "text": "你好,这是一个流式语音合成测试。", "request_id": "test-1"}'
|
||
```
|
||
|
||
> VoxCPM 流式合成,边生成边返回 wav chunk,配合 `reference_audio.wav` 控制音色。
|
||
> 配置了 `SERVER_MODELNAME` 时请求 body 须带 `model` 字段;配置了 `SERVER_API_KEY` 时须带 `Authorization: Bearer <key>` 头。
|
||
|
||
### RAG · 8903(Embedding + Reranker)
|
||
|
||
```bash
|
||
bash start_rag_server.sh
|
||
# 启用鉴权与自定义模型名
|
||
# SERVER_API_KEY=sk-xxx SERVER_MODELNAME=Qwen3-Embedding-0.6B bash start_rag_server.sh
|
||
python3 test_rag.py
|
||
```
|
||
|
||
### Image Gen · 8904
|
||
|
||
```bash
|
||
LOCAL_MODEL_DIR=./models/Tongyi-MAI/Z-Image-Turbo SPECIFIC_GPU=3 bash start_image_gen.sh
|
||
# 启用鉴权与自定义模型名
|
||
# SERVER_API_KEY=sk-xxx SERVER_MODELNAME=Z-Image-Turbo bash start_image_gen.sh
|
||
# 测试
|
||
curl -X POST http://127.0.0.1:8904/generate \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"model": "Z-Image-Turbo", "prompt": "一只橘猫坐在窗台上,阳光洒落", "width": 1024, "height": 1024, "return_format": "base64"}' \
|
||
| python3 -c "import sys,json; d=json.load(sys.stdin); print(f'成功:{d.get(\"success\")} 尺寸:{d.get(\"width\")}x{d.get(\"height\")} 耗时:{d.get(\"elapsed_seconds\",\"?\")}s')"
|
||
```
|
||
|
||
> Z-Image-Turbo 基于 Diffusers,**9 步推理**生成 1024×1024 图片,`return_format=base64` 直接返回内联数据。
|
||
|
||
---
|
||
|
||
## 三、本地端口映射(开发调试)
|
||
|
||
将远程端口映射到本地,用 `http://localhost:8900` 直连远程服务:
|
||
|
||
```bash
|
||
# 一次性映射全部端口
|
||
ssh -fN -o ServerAliveInterval=60 -o ServerAliveCountMax=3 \
|
||
-L 8900:0.0.0.0:8900 -L 8901:0.0.0.0:8901 -L 8902:0.0.0.0:8902 \
|
||
-L 8903:0.0.0.0:8903 -L 8904:0.0.0.0:8904 \
|
||
<user>@<your-gpu-server> -p <SSH_PORT>
|
||
```
|
||
|
||
验证映射(HTTP 200 即通):
|
||
|
||
```bash
|
||
for p in 8900 8901 8902 8903 8904; do
|
||
curl -o /dev/null -s -w "$p: %{http_code}\n" http://localhost:$p/health
|
||
done
|
||
```
|
||
|
||
---
|
||
|
||
## 四、停止服务
|
||
|
||
```bash
|
||
for p in 8900 8901 8902 8903 8904; do kill $(lsof -t -i:$p); done
|
||
```
|
||
|
||
---
|
||
|
||
## 五、接入 Agent
|
||
|
||
四个本地服务(ASR / TTS / RAG / ImageGen)在 Agent 的 `env_config.json` **`models` 列表中注册**(设置 → 环境变量 → LLM 模型列表,或直接编辑 `src/agent/config/env_config.json`),`base_url` 指向本地映射端口,`api_key` 留空(对应服务端未启用鉴权)。Agent 运行时按 `model` 字段匹配取得服务地址:
|
||
|
||
| Agent 能力 | 模型列表条目 | 字段示例 |
|
||
|-----------|-------------|----------|
|
||
| 语音识别 | `name: "ASR 语音识别"` | `model: "Qwen3-ASR-1.7B"`, `base_url: "http://localhost:8901"`, `api_key: ""` |
|
||
| 语音合成 | `name: "TTS 语音合成"` | `model: "VoxCPM1.5"`, `base_url: "http://localhost:8902"`, `api_key: ""` |
|
||
| 知识库检索 | `name: "RAG 检索"` | `model: "Qwen3-Embedding-0.6B"`, `base_url: "http://localhost:8903"`, `api_key: ""` |
|
||
| 图像生成 | `name: "图像生成"` | `model: "Z-Image-Turbo"`, `base_url: "http://localhost:8904"`, `api_key: ""` |
|
||
|
||
> 💡 若服务端部署时设置了 `SERVER_API_KEY` / `SERVER_MODELNAME`,请把模型条目中的 `api_key` 与 `model` 改成与之一致,Agent 发出的请求会携带 `Authorization: Bearer <api_key>` 与 `model` 字段。
|
||
>
|
||
> 💡 不必自建本服务,Agent 也支持任何 **OpenAI 兼容 API**(云端 Qwen / DeepSeek / OpenAI 等),填入 `base_url` + `api_key` 即可。
|