跳到主内容
快讯直播
AI智模界
教程

把腾讯 Hy4 部署成私有智能体 API

当线上智能体的调用量在短期内涨了十倍,公有 API 的限流、按 token 计费和上下文外发就会同时变成问题。把 Hy4 的权重拿回自己的 GPU 上,用 vLLM 起一个 OpenAI 兼容的服务,再配好工具调用和负载均衡,是团队常见的一条落地路径。

适用场景

适合已经用 Hy4 做过 PoC、准备把智能体正式接入内部系统的团队:调用量增长后,需要自己掌控并发上限、把工具调用链路留在内网、并且能按压测数据决定加几张卡。也适合需要给多个业务线统一提供模型出口、做统一鉴权和计费的平台方。

环境与前置条件

操作系统与驱动

  • Linux 服务器,主流发行版的近期 LTS 版本均可,需支持所选 CUDA 版本,具体对应关系以 NVIDIA 与 vLLM 官方文档当前版本为准。
  • 已安装可用的 GPU 驱动,nvidia-smi 能正常输出。
  • 内网部署时需提前打通 PyPI 源或准备离线 wheel 包,以及模型权重的下载通道。

运行时

  • Python 3.10 及以上,具体下限以 vLLM 官方安装说明为准。
  • 建议用独立虚拟环境或容器,避免和机器上已有的 torch 版本打架。
  • 需要 openai 客户端库用于联调,prometheus_client 相关能力由 vLLM 自带。

显存估算方法

不要凭感觉选卡,先算一笔账:

  • 权重显存 ≈ 参数量 × 每参数字节数。BF16 约 2 字节/参数,FP8 约 1 字节,INT4 约 0.5 字节。
  • AI 词典:KV Cache">KV Cache 与激活值另算,通常再留 20%~30% 余量。
  • 结论:单卡放不下就上张量并行,把 --tensor-parallel-size 设为卡数,且该数字应能整除模型的注意力头数。

磁盘与内存

  • 磁盘:权重体积 × 2 作为下限,留出下载临时文件和日志空间。
  • 内存:建议 ≥ 权重体积 × 1.5,加载权重时会有明显的 CPU 侧峰值。
  • 每张卡最好预留几十 GB 本地盘放编译缓存。

权重来源

Hy4 的具体权重名称、仓库地址、量化版本和最大上下文长度,以腾讯官方模型页面与官方文档当前版本为准。下面的命令用 $MODEL_DIR 变量占位,替换成实际路径即可。

分步骤部署

第 1 步:确认硬件与驱动状态

```bash

nvidia-smi

nvidia-smi -L

```

前者应输出 GPU 型号、驱动版本和显存总量;后者列出所有可见 GPU 的 UUID。两者都能正常输出版本信息,说明驱动层就绪。

同时确认磁盘:

```bash

df -h /data

free -g

```

第 2 步:创建虚拟环境并安装 vLLM

```bash

sudo mkdir -p /opt/hy4

sudo python3 -m venv /opt/hy4/venv

source /opt/hy4/venv/bin/activate

pip install -U pip

pip install vllm

```

安装过程会连带拉取 torch 与 CUDA runtime wheel。如果机器上已有特定 CUDA 版本,按 vLLM 官方安装文档选择匹配的安装方式,不要盲目装最新版。

验证:

```bash

python -c "import vllm, torch; print(vllm.__version__, torch.cuda.is_available())"

```

输出中第二项为 True 即成功。

第 3 步:准备模型权重目录

把权重放到一个固定路径,例如 /data/models/hy4。若从模型仓库拉取:

```bash

export MODEL_DIR=/data/models/hy4

mkdir -p $MODEL_DIR

仓库名与分支以官方模型页面为准

huggingface-cli download <官方仓库名> --local-dir $MODEL_DIR

```

如果是离线拷贝,直接 rsync 整个目录过去,然后核对完整性:

```bash

ls -lh $MODEL_DIR

cat $MODEL_DIR/config.json | head -40

```

目录中应能看到 config.json、tokenizer 相关文件,以及若干 *.safetensors 分片。记录下 config.json 里的 max_position_embeddings,它是后面设置 --max-model-len 的上限依据。

第 4 步:单机起服务

先用最小参数跑通,再逐步加参数:

```bash

source /opt/hy4/venv/bin/activate

python -m vllm.entrypoints.openai.api_server \

--model /data/models/hy4 \

--served-model-name hy4 \

--host 0.0.0.0 \

--port 8000 \

--dtype auto \

--max-model-len 8192 \

--gpu-memory-utilization 0.9 \

--max-num-seqs 64

```

几个参数的含义:

  • --served-model-name hy4:客户端请求里 model 字段填这个名字,改名不影响内部逻辑,但改了之后所有调用方要同步。
  • --max-model-len:单请求最大上下文,直接决定 KV Cache 占用,是最常需要调的一个值。
  • --gpu-memory-utilization:vLLM 可占用的显存比例,0.85~0.92 之间是常见区间,留一点给驱动和其他进程。
  • --max-num-seqs:同时进行的序列数上限,也就是批大小上限,直接影响吞吐和单请求延迟。

看到类似下面的输出即为启动成功:

```

INFO: Started server process [xxxx]

INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

INFO: Application startup complete.

```

模型放不下一张卡时,加上张量并行:

```bash

--tensor-parallel-size 4

```

四张卡同时参与一个请求的计算,显存叠加,但卡间通信会带来额外延迟。

第 5 步:开启工具调用(Function Calling)

Agent 能不能干活,取决于服务端有没有正确解析工具调用。vLLM 侧需要两个参数配合:

```bash

--enable-auto-tool-choice \

--tool-call-parser <parser>

```

<parser> 的可选值与模型对话模板强相关,具体填哪个,以 vLLM 官方文档中与所用模型对应的说明为准。填错的表现是:模型把工具调用写进 content 的文本里,而不是返回结构化的 tool_calls。

如果模型权重自带对话模板,一般不需要额外指定;如果模板缺失或不匹配,再用 --chat-template 指向自定义模板文件。

发一个带工具的请求试试:

```bash

curl -s http://127.0.0.1:8000/v1/chat/completions \

-H "Content-Type: application/json" \

-H "Authorization: Bearer empty" \

-d '{

"model": "hy4",

"messages": [

{"role": "user", "content": "帮我查一下杭州现在天气"}

],

"tools": [{

"type": "function",

"function": {

"name": "get_weather",

"description": "查询指定城市的实时天气",

"parameters": {

"type": "object",

"properties": {"city": {"type": "string", "description": "城市名"}},

"required": ["city"]

}

}

}],

"tool_choice": "auto"

}'

```

预期返回的 choices[0].message 中有一个非空的 tool_calls 数组,function.name 为 get_weather,arguments 是 JSON 字符串。这就是 Agent 侧需要解析的东西。

客户端拿到后执行真实函数,再把结果按 role: "tool" 回传:

```python

from openai import OpenAI

import json

client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="empty")

messages = [{"role": "user", "content": "帮我查一下杭州现在天气"}]

tools = [{...}] # 同上面的定义

resp = client.chat.completions.create(model="hy4", messages=messages, tools=tools)

msg = resp.choices[0].message

if msg.tool_calls:

call = msg.tool_calls[0]

args = json.loads(call.function.arguments)

result = get_weather(**args) # 你自己的函数

messages.append(msg)

messages.append({

"role": "tool",

"tool_call_id": call.id,

"content": json.dumps(result, ensure_ascii=False),

})

final = client.chat.completions.create(model="hy4", messages=messages, tools=tools)

print(final.choices[0].message.content)

```

注意 messages.append(msg) 这一步不能省,多数模型要求把带 tool_calls 的助手消息原样带回去,否则模板渲染会报错。

第 6 步:托管进程

用 systemd 保证崩溃自动拉起:

```ini

/etc/systemd/system/hy4-vllm.service

[Unit]

Description=Hy4 vLLM OpenAI Compatible API

After=network-online.target

[Service]

Type=simple

Environment="HF_HOME=/data/hf_cache"

ExecStart=/opt/hy4/venv/bin/python -m vllm.entrypoints.openai.api_server \

--model /data/models/hy4 \

--served-model-name hy4 \

--host 0.0.0.0 --port 8000 \

--max-model-len 32768 \

--gpu-memory-utilization 0.9 \

--max-num-seqs 128 \

--disable-log-requests \

--enable-auto-tool-choice \

--tool-call-parser <parser>

Restart=always

RestartSec=15

LimitNOFILE=65535

[Install]

WantedBy=multi-user.target

```

```bash

sudo systemctl daemon-reload

sudo systemctl enable --now hy4-vllm

sudo systemctl status hy4-vllm

```

--disable-log-requests 在压测时必须加,否则每条请求都打日志,磁盘和 CPU 都会被拖垮。

第 7 步:多副本与负载均衡

单机跑满后,横向加机器是最直接的办法。每台起一份服务,前面挂 Nginx:

```nginx

upstream hy4_backend {

least_conn;

server 10.0.0.11:8000 max_fails=3 fail_timeout=30s;

server 10.0.0.12:8000 max_fails=3 fail_timeout=30s;

keepalive 64;

}

server {

listen 80;

client_max_body_size 20m;

location /v1/ {

proxy_pass http://hy4_backend;

proxy_http_version 1.1;

proxy_set_header Connection "";

proxy_set_header Host $host;

proxy_buffering off; # 流式输出必须关

proxy_read_timeout 600s;

proxy_send_timeout 600s;

}

}

```

proxy_buffering off 不能漏,否则 SSE 流式响应会被 Nginx 攒着一起发,前端表现为“卡很久然后一次性出来”。

扩容判断不要看 GPU 利用率(推理场景下它经常是满的但仍在排队),要看排队长度。vLLM 通过 /metrics 暴露 Prometheus 指标,以 vllm:num_requests_waiting 持续升高作为扩容信号比较可靠。

第 8 步:压测找拐点

vLLM 自带压测工具,新版命令形如:

```bash

vllm bench serve \

--model hy4 \

--base-url http://127.0.0.1:8000 \

--dataset-name sharegpt \

--num-prompts 1000 \

--request-rate 10

```

旧版入口为 python -m vllm.entrypoints.benchmark_serving,具体子命令以 vLLM 官方文档当前版本为准。

压测要按阶梯来,不要一上来就打满:

1. --request-rate 1,记录基线 TTFT(首 token 延迟)与 TPOT(每 token 时间)。

2. 依次升到 5、10、20、40,每次记录吞吐(tokens/s)和 P99 延迟。

3. 找到 P99 延迟开始非线性上升的那个点,那就是单副本的容量拐点。

4. 用「目标峰值 QPS ÷ 单副本拐点」向上取整,得出副本数,再留 30% 冗余。

压测时同步观察:

```bash

watch -n 2 nvidia-smi

curl -s http://127.0.0.1:8000/metrics | grep -E "waiting|running|kv_cache"

```

KV Cache 使用率长期贴近 1.0、waiting 队列持续增长,说明该加副本或降 --max-model-len 了。

验证部署是否成功

1. 服务存活

```bash

curl -s http://127.0.0.1:8000/v1/models

```

返回 JSON 中 data[].id 应包含 hy4。

2. 普通对话

```bash

curl -s http://127.0.0.1:8000/v1/chat/completions \

-H "Content-Type: application/json" \

-d '{"model":"hy4","messages":[{"role":"user","content":"用一句话解释什么是向量数据库"}],"max_tokens":128}'

```

choices[0].message.content 应是一段通顺中文。

3. 流式输出

```bash

curl -N http://127.0.0.1:8000/v1/chat/completions \

-H "Content-Type: application/json" \

-d '{"model":"hy4","messages":[{"role":"user","content":"数到十"}],"stream":true}'

```

应逐行输出 data: {...},最后一行是 data: [DONE]。逐行出现而不是一次性涌出,才算流式正常。

4. 工具调用

用第 5 步的 curl,确认返回体里有非空 tool_calls。

5. 通过网关访问

```bash

curl -s http://<nginx地址>/v1/models

```

结果应与直连一致,说明负载均衡链路通了。

6. 冒烟压测

```bash

vllm bench serve --model hy4 --base-url http://127.0.0.1:8000 \

--dataset-name random --num-prompts 50 --request-rate 5

```

能完整跑完并输出吞吐统计,说明服务在并发下稳定。

常见报错与解决

报错 1:torch.OutOfMemoryError: CUDA out of memory

原因:权重 + KV Cache 超过可用显存,常见于 --max-model-len 或 --gpu-memory-utilization 设得过大。

解决:先降上下文长度和并发,再考虑加卡。

```bash

降低单请求上下文与并发批大小

--max-model-len 16384 --max-num-seqs 32 --gpu-memory-utilization 0.85

```

仍不够则改用张量并行:--tensor-parallel-size <卡数>。

报错 2:ValueError: The model's max sequence length (...) is longer than the maximum sequence length supported

原因:--max-model-len 超过了模型位置编码支持的上限,或超过了当前显存能承载的 KV Cache。

解决:先看 config.json 里的 max_position_embeddings,把参数调到不超过它,并按显存再往下压。

```bash

grep max_position_embeddings /data/models/hy4/config.json

```

报错 3:模型不返回 tool_calls,而是把 JSON 写在 content 里

原因:没开 --enable-auto-tool-choice,或 --tool-call-parser 与模型的对话模板不匹配。

解决:确认启动参数包含前者,并按 vLLM 官方文档中对应模型的说明换成正确的 parser 名称后重启服务。

```bash

sudo systemctl restart hy4-vllm

```

报错 4:OSError: ... No space left on device

原因:权重分片没下完,或下载缓存把磁盘写满。

解决:

```bash

df -h

du -sh /data/models/hy4

清理未完成的下载缓存后重新拉取

rm -rf /data/hf_cache/hub/models--*

```

报错 5:客户端报 Connection refused 或请求超时

原因:服务只监听了 127.0.0.1,或端口被占用,或防火墙未放行。

解决:

```bash

ss -lntp | grep 8000

sudo systemctl restart hy4-vllm

```

确认启动参数里有 --host 0.0.0.0,并在安全组 / firewalld 放行对应端口。

报错 6:OMP: Error #15: Initializing libiomp5.so, but found libiomp5.so already initialized

原因:环境里存在多份 OpenMP 运行时,常见于 conda 与系统库混用。

解决:优先用干净虚拟环境重装 vLLM;临时规避可设置环境变量。

```bash

export KMP_DUPLICATE_LIB_OK=TRUE

```

后续维护

进程与日志

  • 统一用 systemd 或容器编排托管,开启 Restart=always,避免夜间 OOM 后服务长时间不可用。
  • 生产环境务必加 --disable-log-requests,只保留启动日志和错误日志。
  • 日志按天切割,保留 7~14 天,用 logrotate 配置即可。

监控指标

接入 Prometheus 抓取 /metrics,重点盯四个:

  • vllm:num_requests_waiting:排队数,扩容主信号
  • vllm:gpu_cache_usage_perc:KV Cache 占用,长期高位说明该降上下文或加卡
  • 首 token 延迟 P99:直接决定 Agent 的体感
  • 单卡显存余量:低于 5% 时容易触发 OOM

配一条「排队数持续 5 分钟 > 阈值」的告警,比看 GPU 利用率有用得多。

备份

  • 权重目录做一次 sha256sum 校验并存档,之后每次从对象存储恢复都对比一次,避免静默损坏。
  • 只备份配置:systemd 单元、Nginx 配置、启动参数、对话模板文件,全部进 Git 仓库。权重不进 Git。
  • 保留一份离线权重副本,避免上游仓库变更导致无法复现。

升级

  • 先在灰度实例上升级 vLLM,用同一套压测脚本跑一遍,对比 TTFT 与吞吐,没有回退再滚动替换。
  • 生产环境固定 vLLM 版本号,不要用浮动的最新版,升级窗口内改配置要一起记录。
  • 模型权重换代时,--served-model-name 建议改用新名字(例如带日期后缀),让调用方显式切换,避免新旧行为混在一起排查困难。

容量规划

把首次压测得到的「每千 token 消耗的 GPU 秒数」记下来,之后每次调用量翻倍,直接按这个系数推算需要加几张卡,比事后救火从容得多。工具调用场景还要额外留余量:一次带工具的多轮对话往往要请求模型两到三次,token 消耗远高于单轮问答。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。