serving-llms-on-epyc
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseServing LLMs on AMD EPYC™ (vLLM + zentorch, CPU)
在AMD EPYC™上部署LLM(vLLM + zentorch,CPU版)
Bring up a single vLLM OpenAI endpoint on an AMD EPYC™ host with the zentorch CPU
backend, sized to the hardware. Container-first (Docker or Podman); conda/host
is the fallback. An installed AMD Instinct GPU does not disqualify the host:
select this skill when the endpoint itself should run on the EPYC CPU.
This is single-socket serving: one instance pinned to one socket and its memory
(vLLM scales poorly across sockets, so we do not span them). On a dual-socket host it
runs on a single socket; the multi-socket answer is multiple instances (one per
socket), which is out of scope for this single-instance recipe.
Hard rule for this skill: on any failure, report the cause + logs and STOP.
Do not retry, do not debug. (Debugging is a separate workflow.)
The agent does the serve flow itself -- pull, configure, launch, poll --
using the runtime reports. Never hand the user per-serve commands.
Like serving-llms-on-instinct, an accessible container runtime is a one-time
prerequisite: if finds none, report its one-time fix (make
docker accessible / install podman / provide a conda env) and stop. Do not
attempt or privilege escalation.
validate.pyvalidate.pysudo在配备zentorch CPU后端的AMD EPYC™主机上搭建单个vLLM OpenAI端点,根据硬件规格进行适配。优先采用容器化方案(Docker或Podman);conda/主机环境作为备选方案。即使主机已安装AMD Instinct GPU也可使用本流程:当端点需运行在EPYC CPU上时,选择本技能即可。
本流程为单插槽部署:将一个实例固定到单个插槽及其内存上(vLLM跨插槽扩展性较差,因此不跨插槽部署)。在双插槽主机上,本流程仅在单个插槽上运行;多插槽场景的解决方案是多实例部署(每个插槽一个实例),这不在本单实例流程的覆盖范围内。
本技能的硬性规则:任何失败发生时,上报原因及日志并终止流程。请勿重试,也不要进行调试。(调试属于单独的工作流。)
代理将自行完成部署流程——拉取镜像、配置参数、启动服务、轮询状态——借助报告的运行时信息。绝不要让用户执行单独的部署命令。与serving-llms-on-instinct流程类似,可访问的容器运行时是一项一次性前置条件:如果未检测到容器运行时,需上报对应的一次性修复方案(配置Docker可访问权限/安装Podman/提供conda环境)并终止流程。请勿尝试使用或提升权限。
validate.pyvalidate.pysudoData file
数据文件
Read directly. It holds the container image, mandatory CPU run
flags, supported precision, the model-support policy, the default model, and the
verified throughput-flag gotcha. Its and image tag are one
validated default stack; keep them aligned and do not hardcode either from memory.
data/epyc.jsonvllm_version直接读取文件。该文件包含容器镜像、强制CPU运行标志、支持的精度、模型支持策略、默认模型以及已验证的吞吐量标志注意事项。其中的和镜像标签是一组经过验证的默认技术栈;需保持两者一致,不要凭记忆硬编码任何一项。
data/epyc.jsonvllm_versionStep 1: Detect the CPU
步骤1:检测CPU
bash
python3 scripts/detect.py # add --host user@box for a remote hostReturns , ,
(Naples/Rome/Milan/Genoa/Bergamo/Siena/Turin/Venice or EPYC 4004/4005),
, , , , ,
, , .
cpu_modelis_amd_epycepyc_generationzen_archis_supported_epycavx512logical_coresphysical_coressocketsnuma_nodesmemory_gbThree hard gates -- stop if any fails:
- is
is_amd_epyc-> stop: this skill targets AMD EPYC. (Other x86 may work but is unsupported here.)false - is
is_supported_epyc-> stop: this recipe supports only the AMD EPYC 9000 series for now -- Genoa (9004), Turin (9005), and Venice (9006). Other EPYC (Bergamo, Siena, EPYC 4004/4005, pre-Zen4) may even expose AVX-512, but ISA compatibility alone does not make them supported targets for this skill; stop.false - is
avx512-> stop: the zentorch CPU path requires AVX-512, i.e. Zen4+ on the supported 9000-series parts above. Pre-Zen4 EPYC (Naples / Rome / Milan) is not supported -- say so and stop rather than launching into a load-time failure.false
Carry / through the later phases -- e.g. Venice packs up
to 256 cores/socket, which the thread-binding in Step 5 sizes from.
epyc_generationavx512bash
python3 scripts/detect.py # 若为远程主机,添加--host user@box参数返回、、(Naples/Rome/Milan/Genoa/Bergamo/Siena/Turin/Venice或EPYC 4004/4005)、、、、、、、、。
cpu_modelis_amd_epycepyc_generationzen_archis_supported_epycavx512logical_coresphysical_coressocketsnuma_nodesmemory_gb存在三个硬性检查项——若任意一项不通过则终止流程:
- 为
is_amd_epyc→ 终止:本技能针对AMD EPYC处理器。(其他x86处理器可能可用,但不在本流程的支持范围内。)false - 为
is_supported_epyc→ 终止:本流程目前仅支持AMD EPYC 9000系列——Genoa(9004)、Turin(9005)和Venice(9006)。其他EPYC处理器(Bergamo、Siena、EPYC 4004/4005、Zen4之前的型号)即使支持AVX-512指令集,也不具备本技能所需的ISA兼容性;请终止流程。false - 为
avx512→ 终止:zentorch CPU路径必须依赖AVX-512指令集,即上述支持的9000系列处理器中的Zen4+架构。Zen4之前的EPYC处理器(Naples/Rome/Milan)不被支持——请明确告知用户并终止流程,避免后续加载时失败。false
将/参数带入后续流程——例如Venice每个插槽最多支持256核,步骤5中的线程绑定将据此配置。
epyc_generationavx512Step 2: Validate the runtime and environment
步骤2:验证运行时与环境
bash
python3 scripts/validate.py --image <image from data/epyc.json> --generation <epyc_generation from detect>Returns , , (, , or null),
, , , , , and
. Pick the path:
readyrequires_confirmationruntimedockerpodmanruntime_detailconda_path_availablestackcompatibilityram_gberrors/warnings/advisories- is
runtimeordocker-> container path (Step 6), used verbatim.podman - null but
runtime-> conda/host path.conda_path_available: true - null and no conda ->
runtimeis false. Report the one-time onboardingready(make docker accessible / install podman / conda env) and stop.fix
Do not proceed if is .
readyfalseStack-compatibility gate. probes the selected runtime for its
exact // versions and the active vLLM platform, then sets
:
validate.pyvllmzentorchtorchcompatibility.status- -> the stack is the validated default (or a validated family on a Zen platform); continue.
proceed - -> a stock CPU platform is active, so zentorch acceleration is not on (error). Report
blockedand stop.compatibility.message - (
confirmation_required) -> Venice on a vLLM other than the pinned default. This recipe has not been validated on Venice with that version. Surfacerequires_confirmation: true, recommend the pinnedcompatibility.messageimage fromvllm_version, and stop for an explicit user go/no-go before launching. On the pinned default vLLM, Venice proceeds with no warning.data/epyc.json
The gate only runs once the image is local. If reports the image is
not pulled, pull it (or let Step 6 pull it) and re-run so the
gate probes the real stack rather than only the tag.
validate.pyvalidate.pybash
python3 scripts/validate.py --image <data/epyc.json中的镜像> --generation <detect步骤获取的epyc_generation>返回、、(、或null)、、、、、以及。根据结果选择路径:
readyrequires_confirmationruntimedockerpodmanruntime_detailconda_path_availablestackcompatibilityram_gberrors/warnings/advisories- 为
runtime或docker→ 采用容器化路径(步骤6),直接使用返回值。podman - 为null但
runtime→ 采用conda/主机路径。conda_path_available: true - 为null且无conda环境 →
runtime为false。上报一次性配置方案(配置Docker可访问权限/安装Podman/创建conda环境)并终止流程。ready
若为false,请勿继续执行。
ready技术栈兼容性检查。会探测所选运行时的//精确版本以及当前激活的vLLM平台,然后设置:
validate.pyvllmzentorchtorchcompatibility.status- → 技术栈为经过验证的默认版本(或Zen平台上的验证版本族);继续执行。
proceed - → 当前激活的是原生CPU平台,zentorch加速未启用(错误)。上报
blocked并终止流程。compatibility.message - (
confirmation_required)→ Venice处理器搭配非固定默认版本的vLLM。本流程未在该组合下完成验证。需展示requires_confirmation: true,推荐使用compatibility.message中固定的data/epyc.json镜像,并在启动前等待用户明确确认是否继续。若使用固定默认版本的vLLM,Venice处理器可直接继续执行,无需警告。vllm_version
该检查仅在镜像已拉取到本地后执行。如果报告镜像未拉取,需先拉取镜像(或让步骤6自动拉取),然后重新运行,确保检查基于真实技术栈而非仅镜像标签。
validate.pyvalidate.pyStep 3: Resolve and validate the model
步骤3:解析并验证模型
If the user named no model, use from
( -- ungated, tiny, fast first success). Otherwise use theirs.
default_modeldata/epyc.jsonQwen/Qwen3-0.6BCheck that vLLM actually supports the model (do not blanket-block multimodal).
Pass the vLLM version the model will actually run on: use from
when it was probed (the conda env may differ from the pin), else the
from .
stack.vllmvalidate.pyvllm_versiondata/epyc.jsonbash
python3 scripts/check_model.py --model-id <model> --revision <rev or main> --vllm-version <stack.vllm from validate, else vllm_version from data/epyc.json>- Exit 0 = vLLM serves it as a generation endpoint, or support is undeterminable (gated/offline) -- proceed; launch confirms.
- Exit 1 = stop: the architecture is not in vLLM's registry, it is a
/embedding/reranker (not a chat/completion endpoint), or it is a multimodal model with no usable chat template (
pooling). Report the printedlaunchable: falseand stop.message
The result also carries the client endpoint the model supports:
- -- a usable chat template is present (
primary_endpoint: "chat_completions"); serve and hand offchat_template.status: present./v1/chat/completions - -- no usable/auto-selectable template (
primary_endpoint: "completions"/absent/ambiguous); serve and hand offunknownwith a raw/v1/completions. Chat can still be enabled by passingprompt(or, for--chat-template <file>, choosing one ofambiguous); never invent one.chat_template.names - Carry ,
primary_endpoint, andsupported_endpointsthrough to verification (Step 7) and the handoff (Step 8).chat_template - A model is allowed; a vLLM-supported multimodal arch may still hit a GPU-only kernel on CPU, which surfaces at load (the no-retry rule then applies).
multimodal
Precision/dtype: native CPU dtypes are (default), , . Use
unless the user asks otherwise.
bf16fp16fp32bfloat16For gated models (Llama, Gemma) must be set and the license accepted on
HuggingFace; if not, stop and say so.
HF_TOKEN若用户未指定模型,使用中的(——无权限限制、体积小、可快速验证成功)。否则使用用户指定的模型。
data/epyc.jsondefault_modelQwen/Qwen3-0.6B检查vLLM是否支持该模型(不要全面禁用多模态模型)。传入模型实际运行的vLLM版本:若已通过探测到版本,使用;否则使用中的。
validate.pystack.vllmdata/epyc.jsonvllm_versionbash
python3 scripts/check_model.py --model-id <模型> --revision <版本或main> --vllm-version <validate步骤获取的stack.vllm,否则为data/epyc.json中的vllm_version>- 退出码0 = vLLM可将其作为生成端点部署,或支持性无法确定(有权限限制/离线)→ 继续执行;启动时会再次确认。
- 退出码1 = 终止:模型架构不在vLLM注册表中,属于/嵌入/重排序模型(非聊天/补全端点),或为无可用聊天模板的多模态模型(
pooling)。上报打印的launchable: false并终止流程。message
结果还包含模型支持的客户端端点:
- → 存在可用聊天模板(
primary_endpoint: "chat_completions");部署后提供chat_template.status: present端点。/v1/chat/completions - → 无可用/自动可选模板(
primary_endpoint: "completions"/absent/ambiguous);部署后提供unknown端点,需传入原始/v1/completions。可通过传入prompt(若为--chat-template <文件>,可选择ambiguous中的一个模板)启用聊天功能;请勿自行创建模板。chat_template.names - 将、
primary_endpoint和supported_endpoints带入后续验证(步骤7)与交付环节(步骤8)。chat_template - 允许使用模型;vLLM支持的多模态架构在CPU上运行时可能会调用GPU专属内核,该问题会在加载时暴露(此时需遵循不重试规则)。
multimodal
精度/dtype:原生CPU支持的dtype为(默认)、、。除非用户指定,否则默认使用。
bf16fp16fp32bfloat16对于有权限限制的模型(Llama、Gemma),必须设置并在HuggingFace上接受许可;若未满足条件,需终止流程并告知用户。
HF_TOKENStep 4: Check it fits host RAM
步骤4:检查模型是否适配主机内存
RAM is the ceiling on CPU (weights + KV cache both live in RAM). Run on ONE line:
bash
python3 scripts/estimate_memory.py --model-id <model> --revision <rev or main> --ram-gb <memory_gb from detect> --max-model-len <4096 or user value> --num-prompts <1 or desired concurrency>Exit 0 = fits, exit 1 = does not fit. If is false: do not launch.
Tell the user vs and the printed -- reduce
to and retry, or use a smaller
model. and are the two knobs that move KV.
Extra flag: overrides weights if a model has no HF metadata
(rare). KV cache is bf16-only on zentorch CPU (no fp8 KV).
fit.fitsrequired_gbram_gbfit.action--max-model-lenfit.suggested_max_model_len--max-model-len--num-prompts--weight-gb N内存是CPU部署的上限(模型权重与KV缓存均存储在内存中)。执行以下单行命令:
bash
python3 scripts/estimate_memory.py --model-id <模型> --revision <版本或main> --ram-gb <detect步骤获取的memory_gb> --max-model-len <4096或用户指定值> --num-prompts <1或期望并发数>退出码0 = 内存适配,退出码1 = 内存不足。若为false:请勿启动服务。告知用户与的对比情况,以及打印的建议——将调整为后重试,或使用更小的模型。和是调整KV缓存占用的两个关键参数。额外标志:可在模型无HF元数据时覆盖权重内存估算(罕见情况)。zentorch CPU上的KV缓存仅支持bf16格式(无fp8 KV缓存)。
fit.fitsrequired_gbram_gbfit.action--max-model-lenfit.suggested_max_model_len--max-model-len--num-prompts--weight-gb NStep 5: Size the CPU runtime from the hardware
步骤5:根据硬件配置CPU运行参数
bash
eval "$(python3 scripts/cpu_tune.py)" # or --format json to inspectA single instance runs on one socket, with its memory (vLLM scales poorly across
sockets). exports (the chosen socket's
physical cores) and (sized from that socket's local RAM,
not whole-system, so the KV pool stays on-socket). It does not set
(vLLM derives it) or (vLLM's own default).
cpu_tune.pyVLLM_CPU_OMP_THREADS_BINDVLLM_CPU_KVCACHE_SPACEOMP_NUM_THREADSVLLM_CPU_NUM_OF_RESERVED_CPUSocket choice on a dual-socket host (load-aware): it samples per-socket CPU busy%
(~0.5s) and prefers a free socket -- both free → socket 0; one free → that socket;
both busy (≥ , default 15%) → it s and proceeds on the
least-busy socket. forces a choice. Single-socket hosts use socket 0.
--busy-thresholdwarning--socket NFor the chosen socket it also emits the memory-bound pin:
() for the container path, and
(, falling back to
CPU-only, or empty-with-note if neither tool exists) for conda. Surface
to the user if set. On NPS2/NPS4 a socket spans multiple NUMA nodes; memory is
bound across them and flags that finer binding could add performance.
container_cpuset--cpuset-cpus=<cores> --cpuset-mems=<nodes>conda_launch_prefixnumactl --cpunodebind/--membindtasksetwarningnps_notebash
eval "$(python3 scripts/cpu_tune.py)" # 或使用--format json参数查看详细结果单个实例将运行在单个插槽及其内存上(vLLM跨插槽扩展性较差)。会导出(所选插槽的物理核心)和(根据该插槽本地内存而非整机内存配置,确保KV缓存始终位于插槽本地)。该脚本不会设置(vLLM会自动推导)或(使用vLLM默认值)。
cpu_tune.pyVLLM_CPU_OMP_THREADS_BINDVLLM_CPU_KVCACHE_SPACEOMP_NUM_THREADSVLLM_CPU_NUM_OF_RESERVED_CPU双插槽主机上的插槽选择(基于负载):脚本会采样每个插槽的CPU使用率(约0.5秒),优先选择空闲插槽——若两个插槽均空闲则选择插槽0;若仅一个空闲则选择该插槽;若两个插槽均繁忙(使用率≥,默认15%)→ 发出警告并选择使用率最低的插槽。参数可强制指定插槽。单插槽主机默认使用插槽0。
--busy-threshold--socket N对于所选插槽,脚本还会输出内存绑定参数:容器化路径使用(),conda路径使用(,若该工具不可用则回退到仅绑定CPU,若两者均不可用则为空并附带说明)。若脚本返回,需告知用户。在NPS2/NPS4架构中,一个插槽包含多个NUMA节点;内存会跨节点绑定,会提示更精细的节点绑定可提升性能。
container_cpuset--cpuset-cpus=<核心> --cpuset-mems=<节点>conda_launch_prefixnumactl --cpunodebind/--membindtasksetwarningnps_noteStep 6: Confirm the plan, then launch (container-first)
步骤6:确认部署方案,然后启动服务(优先容器化)
Before launching, present this summary and wait for the user to confirm -- do
not launch unprompted. This is the human gate before anything runs:
| Field | Value |
|---|---|
| Model / kind | |
| Path | container ( |
| Precision | |
| Fit | required |
| CPU sizing | socket |
| Hardware | EPYC |
| Port | |
If returned a (e.g. all sockets busy), include it here so the user sees it before confirming.
cpu_tune.pywarningProceed only on a clear "go". If the user declines or wants changes (model,
, port), stop and adjust -- do not launch.
--max-model-lenBuild the launch from . The CLI is .
Do not pass on vLLM >= 0.20 -- the zentorch plugin
auto-selects the CPU platform and rejects the flag. Only add it if
lists it (older vLLM).
data/epyc.jsonvllm serve <model>--device cpuvllm servevllm serve --helpContainer path ( from validate.py). The agent runs these itself,
including the pull. is the resolved runtime verbatim:
runtimeRTbash
RT="<runtime from validate.py: docker | podman>"
$RT rm -f vllm-epyc 2>/dev/null # clear any leftover container from a prior run (name collision otherwise)
$RT pull <image from data/epyc.json> # agent pulls; do not ask the user to
$RT run -d --name vllm-epyc \
<run_flags from data/epyc.json> # --ipc=host --network=host (NO --shm-size: it conflicts with --ipc=host on podman)
<hf_cache_mount> \
<container_cpuset from cpu_tune> # --cpuset-cpus=<cores> --cpuset-mems=<nodes>
--env VLLM_CPU_OMP_THREADS_BIND="$VLLM_CPU_OMP_THREADS_BIND" \
--env VLLM_CPU_KVCACHE_SPACE=$VLLM_CPU_KVCACHE_SPACE \
--env HF_TOKEN=${HF_TOKEN} \
<image from data/epyc.json> \
vllm serve <model> --dtype bfloat16 --port <port> --max-model-len <len>Conda/host path (no container runtime, true). -ing
cpu_tune already exported the env vars; prefix the launch with
from cpu_tune so memory is bound to the chosen socket (empty → unpinned, with a note):
conda_path_availableevalconda_launch_prefixbash
<conda_launch_prefix from cpu_tune> vllm serve <model> --dtype bfloat16 --port <port> --max-model-len <len> &启动前,需向用户展示以下汇总信息并等待用户确认——请勿未经确认直接启动。这是实际运行前的人工检查环节:
| 字段 | 值 |
|---|---|
| 模型/类型 | |
| 部署路径 | 容器( |
| 精度 | |
| 内存适配 | 所需 |
| CPU配置 | 插槽 |
| 硬件 | EPYC |
| 端口 | |
若返回(例如所有插槽均繁忙),需将该警告包含在汇总信息中,确保用户在确认前看到。
cpu_tune.pywarning仅在用户明确确认“继续”后再执行后续步骤。若用户拒绝或需要修改(模型、、端口),需终止流程并调整参数——请勿启动服务。
--max-model-len基于构建启动命令。CLI命令为。请勿在vLLM >= 0.20版本中传入——zentorch插件会自动选择CPU平台,会因“无法识别的参数:--device cpu”报错。仅当中列出该参数时(旧版vLLM)才添加。
data/epyc.jsonvllm serve <model>--device cpuvllm servevllm serve --help容器化路径(返回的)。代理将自行执行以下命令,包括拉取镜像。为解析后的运行时命令:
validate.pyruntimeRTbash
RT="<validate.py返回的runtime: docker | podman>"
$RT rm -f vllm-epyc 2>/dev/null # 清除之前运行残留的容器(避免名称冲突)
$RT pull <data/epyc.json中的镜像> # 代理自动拉取;请勿让用户执行
$RT run -d --name vllm-epyc \
<data/epyc.json中的run_flags> # --ipc=host --network=host(请勿添加--shm-size:在podman上会与--ipc=host冲突)
<hf_cache_mount> \
<cpu_tune返回的container_cpuset> # --cpuset-cpus=<核心> --cpuset-mems=<节点>
--env VLLM_CPU_OMP_THREADS_BIND="$VLLM_CPU_OMP_THREADS_BIND" \
--env VLLM_CPU_KVCACHE_SPACE=$VLLM_CPU_KVCACHE_SPACE \
--env HF_TOKEN=${HF_TOKEN} \
<data/epyc.json中的镜像> \
vllm serve <model> --dtype bfloat16 --port <port> --max-model-len <len>conda/主机路径(无容器运行时,为true)。执行cpu_tune脚本时已导出环境变量;启动命令前需添加cpu_tune返回的,确保内存绑定到所选插槽(若为空则不绑定,并附带说明):
conda_path_availableconda_launch_prefixbash
<cpu_tune返回的conda_launch_prefix> vllm serve <model> --dtype bfloat16 --port <port> --max-model-len <len> &e.g. numactl --cpunodebind=0 --membind=0 vllm serve ...
示例:numactl --cpunodebind=0 --membind=0 vllm serve ...
Optional throughput flags are **opt-in and must move together** (see Gotchas):
`TORCHINDUCTOR_FREEZING=1` + `VLLM_USE_AOT_COMPILE=0` (+ `ZENTORCH_WEIGHT_PREPACK=1`).
The base launch sets none of them.
可选吞吐量标志**需同时启用**(参见注意事项):`TORCHINDUCTOR_FREEZING=1` + `VLLM_USE_AOT_COMPILE=0`(+ `ZENTORCH_WEIGHT_PREPACK=1`)。基础启动命令不包含任何这些标志。Step 7: Poll until up and responsive
步骤7:轮询直到服务启动并响应
A 503 while loading is normal. Poll until the server answers, confirm
the served model is listed, then prove the selected endpoint works (from
in Step 3). CPU first-token compile can take a minute or two.
Track a flag so a timeout is a failure, not a fall-through.
/healthprimary_endpointhealthybash
undefined加载过程中返回503状态码属于正常情况。轮询端点直到服务器响应,确认已部署的模型已注册,然后验证所选端点是否可用(来自步骤3的)。CPU首次编译可能需要1-2分钟。需跟踪标志,超时则判定为失败,而非继续执行。
/healthprimary_endpointhealthybash
undefined1. container alive (conda: process alive) + /health, with a real timeout
1. 容器存活(conda环境:进程存活)+ /health端点响应,设置超时时间
healthy=""
for i in $(seq 1 120); do
$RT inspect -f '{{.State.Running}}' vllm-epyc 2>/dev/null | grep -q true || { echo "FAILED: container exited"; $RT logs --tail 50 vllm-epyc; break; }
curl -sf http://localhost:<port>/health >/dev/null 2>&1 && { healthy=1; echo "HEALTHY"; break; }
sleep 3
done
[ -n "$healthy" ] || { echo "FAILED: not healthy before timeout"; $RT logs --tail 50 vllm-epyc; }
healthy=""
for i in $(seq 1 120); do
$RT inspect -f '{{.State.Running}}' vllm-epyc 2>/dev/null | grep -q true || { echo "失败:容器已退出"; $RT logs --tail 50 vllm-epyc; break; }
curl -sf http://localhost:<port>/health >/dev/null 2>&1 && { healthy=1; echo "健康状态正常"; break; }
sleep 3
done
[ -n "$healthy" ] || { echo "失败:超时前未进入健康状态"; $RT logs --tail 50 vllm-epyc; }
2. the served model is registered
2. 验证已部署模型已注册
curl -sf --max-time 30 http://localhost:<port>/v1/models
Then exercise the endpoint the model actually supports. Use deterministic
sampling and a small output cap for the smoke check:
```bashcurl -sf --max-time 30 http://localhost:<port>/v1/models
然后测试模型实际支持的端点。使用确定性采样并设置较小的输出上限进行冒烟测试:
```bashprimary_endpoint == chat_completions
primary_endpoint == chat_completions
curl -sf --max-time 180 http://localhost:<port>/v1/chat/completions -H 'Content-Type: application/json'
-d '{"model":"<served-model>","messages":[{"role":"user","content":"hi"}],"max_tokens":16,"temperature":0}'
-d '{"model":"<served-model>","messages":[{"role":"user","content":"hi"}],"max_tokens":16,"temperature":0}'
curl -sf --max-time 180 http://localhost:<port>/v1/chat/completions -H 'Content-Type: application/json'
-d '{"model":"<已部署模型>","messages":[{"role":"user","content":"hi"}],"max_tokens":16,"temperature":0}'
-d '{"model":"<已部署模型>","messages":[{"role":"user","content":"hi"}],"max_tokens":16,"temperature":0}'
primary_endpoint == completions (no chat template)
primary_endpoint == completions (无聊天模板)
curl -sf --max-time 180 http://localhost:<port>/v1/completions -H 'Content-Type: application/json'
-d '{"model":"<served-model>","prompt":"Hello, world","max_tokens":16,"temperature":0}'
-d '{"model":"<served-model>","prompt":"Hello, world","max_tokens":16,"temperature":0}'
Confirm the response is JSON with a non-error `choices[0]` (chat: `message.content`;
completion: `text`). An HTTP 200 that carries an `error` payload is **not** success.
Resource sanity (your validation list): `$RT stats --no-stream vllm-epyc`.
**If the server never becomes healthy, `/v1/models` omits the model, or the
endpoint returns an error/empty `choices`: print the container/process logs,
state the failing phase, and STOP. Do not retry. Do not start a debugging loop.**curl -sf --max-time 180 http://localhost:<port>/v1/completions -H 'Content-Type: application/json'
-d '{"model":"<已部署模型>","prompt":"Hello, world","max_tokens":16,"temperature":0}'
-d '{"model":"<已部署模型>","prompt":"Hello, world","max_tokens":16,"temperature":0}'
确认响应为JSON格式且包含非错误的`choices[0]`(聊天模型:`message.content`;补全模型:`text`)。返回HTTP 200但包含`error`负载**不代表成功**。资源状态检查:`$RT stats --no-stream vllm-epyc`。
**若服务器始终无法进入健康状态、`/v1/models`未列出模型,或端点返回错误/空`choices`:打印容器/进程日志,说明失败阶段并终止流程。请勿重试,也不要进入调试循环。**Step 8: On success, hand over the endpoint
步骤8:部署成功后交付端点
Give the user everything needed to call the server. Print a connection table:
| Field | Value |
|---|---|
| Base URL | |
| Served model | |
| Endpoint | |
| Why | chat = a chat template is present; completions = no template (raw prompts) |
| Runtime / port | |
| Sizing | OMP threads, KV GB, |
| Stop | |
Then a ready-to-run example for the selected endpoint.
Chat model ():
primary_endpoint: chat_completionsbash
curl -s http://localhost:<port>/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"<served-model>","messages":[{"role":"user","content":"Hello"}],"max_tokens":128,"temperature":0.7}'Base/prompt model ():
primary_endpoint: completionsbash
curl -s http://localhost:<port>/v1/completions -H 'Content-Type: application/json' \
-d '{"model":"<served-model>","prompt":"Hello, world","max_tokens":128,"temperature":0.7}'OpenAI Python client (point at the local server; the SDK requires a
non-empty key, so any placeholder works when the server has no auth):
base_urlpython
from openai import OpenAI
client = OpenAI(base_url="http://localhost:<port>/v1", api_key="EMPTY")
model = client.models.list().data[0].id向用户提供调用服务器所需的全部信息。打印连接信息表:
| 字段 | 值 |
|---|---|
| 基础URL | |
| 已部署模型 | |
| 端点 | |
| 说明 | chat = 存在聊天模板;completions = 无模板(需传入原始prompt) |
| 运行时/端口 | |
| 配置信息 | OMP线程数、KV缓存大小、 |
| 停止命令 | |
然后提供针对所选端点的可直接运行示例。
聊天模型():
primary_endpoint: chat_completionsbash
curl -s http://localhost:<port>/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"<已部署模型>","messages":[{"role":"user","content":"Hello"}],"max_tokens":128,"temperature":0.7}'基础/补全模型():
primary_endpoint: completionsbash
curl -s http://localhost:<port>/v1/completions -H 'Content-Type: application/json' \
-d '{"model":"<已部署模型>","prompt":"Hello, world","max_tokens":128,"temperature":0.7}'OpenAI Python客户端(将指向本地服务器;SDK要求非空密钥,因此当服务器无认证时可使用任意占位符):
base_urlpython
from openai import OpenAI
client = OpenAI(base_url="http://localhost:<port>/v1", api_key="EMPTY")
model = client.models.list().data[0].idchat model:
聊天模型:
r = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Hello"}],
max_tokens=128, temperature=0.7,
)
print(r.choices[0].message.content)
r = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Hello"}],
max_tokens=128, temperature=0.7,
)
print(r.choices[0].message.content)
base/prompt model:
基础/补全模型:
r = client.completions.create(model=model, prompt="Hello, world", max_tokens=128)
print(r.choices[0].text)
Argument guidance to pass along (see [reference.md](reference.md) for the full list):
- `max_tokens` caps the **output**; `prompt_tokens + max_tokens` must be `<= --max-model-len`.
- `temperature` (0 = deterministic/greedy, higher = more random); tune `top_p` *or*
`temperature`, not both.
- `stream: true` streams tokens (SSE) instead of one blocking response.
- The model's `generation_config.json` can set sampling defaults; pass explicit
values to be sure.r = client.completions.create(model=model, prompt="Hello, world", max_tokens=128)
print(r.choices[0].text)
同时提供参数使用指南(完整列表参见[reference.md](reference.md)):
- `max_tokens`限制**输出**长度;`prompt_tokens + max_tokens`必须`<= --max-model-len`。
- `temperature`(0 = 确定性/贪婪采样,值越高越随机);请调整`top_p`或`temperature`中的一个,不要同时调整。
- `stream: true`会以SSE方式流式返回令牌,而非一次性返回完整响应。
- 模型的`generation_config.json`可设置采样默认值;建议传入明确参数以确保效果。Offline (single-instance batch)
离线(单实例批量处理)
For a one-shot offline run instead of a server, replace Step 6-8 with a single
(or an offline ) using the same sized env,
wait for completion, and report the metrics. Same no-retry / no-debug rule.
vllm bench throughputLLM.generate若需一次性离线运行而非搭建服务器,可将步骤6-8替换为单个命令(或离线),使用相同的环境配置,等待执行完成后上报指标。同样遵循不重试/不调试规则。
vllm bench throughputLLM.generateGotchas
注意事项
See reference.md for the full list. The load-bearing ones:
- was removed from
--device cpuin vLLM >= 0.20. The zentorch plugin auto-selects CPU. Passing it makesvllm serveerror with "unrecognized arguments: --device cpu".vllm serve - alone crashes engine-core init on vLLM 0.23 / zentorch 2.11 (
TORCHINDUCTOR_FREEZING=1). It only works withAssertionError: expected OutputCode, got functionset alongside it. Never set one without the other.VLLM_USE_AOT_COMPILE=0 - — use
/dev/shm, not--ipc=host. vLLM needs a large--shm-size(the 64MB container default is too small). The base recipe uses/dev/shm, which shares the host's large shared memory. Do not also pass--ipc=host: podman errors with "cannot set shmsize when running in the host IPC Namespace", and it is redundant on docker. If you instead isolate IPC (drop--shm-size), then add--ipc=host— one or the other, never both.--shm-size=16g - NUMA / socket: one instance is pinned to one socket plus its memory --
CPU bind + (container) /
--cpuset-mems(conda), with KV sized from that socket's local RAM. On a dual-socket hostnumactl --membindpicks a free socket by load andcpu_tune.pys if both are busy. NPS2/NPS4 (multi-node socket) gets anwarningthat finer per-node binding could add more.nps_note - Rootless podman + /
--cpuset-cpus: these are cgroup limits and may be ignored or rejected on rootless podman without cpuset cgroup delegation (cgroup v1, or v2 without the controller delegated). This is not fatal: CPU thread binding still applies via--cpuset-memsinside the container; only the container-level memory pin is lost (reduced NUMA locality). If the run errors specifically on the cpuset flags, drop them and proceed -- do not treat it as a launch failure.VLLM_CPU_OMP_THREADS_BIND - HF cache mount: the default mounts . If
~/.cache/huggingfacepoints elsewhere (common on shared hosts, e.g.HF_HOME), mount that path to/proj/.../vllminstead, or the model re-downloads inside the container./root/.cache/huggingface - Container name reuse: a leftover from a prior run makes
vllm-epycfail with "name already in use" -- Step 6 clears it first withrun.$RT rm -f vllm-epyc
完整列表参见reference.md。关键注意事项:
- 已被移除:在vLLM >= 0.20版本中,
--device cpu不再支持该参数。zentorch插件会自动选择CPU平台。传入该参数会导致vllm serve报错:“无法识别的参数:--device cpu”。vllm serve - 单独设置会导致引擎核心初始化崩溃:在vLLM 0.23 / zentorch 2.11版本中会触发
TORCHINDUCTOR_FREEZING=1错误。必须同时设置AssertionError: expected OutputCode, got function才能生效。请勿单独设置其中一个参数。VLLM_USE_AOT_COMPILE=0 - — 使用
/dev/shm,而非--ipc=host。vLLM需要较大的--shm-size空间(容器默认的64MB过小)。基础流程使用/dev/shm,共享主机的大内存空间。请勿同时传入--ipc=host:podman会报错“在主机IPC命名空间中无法设置shmsize”,且在Docker上该参数是冗余的。若需隔离IPC(移除--shm-size),则添加--ipc=host——二选一,请勿同时使用。--shm-size=16g - NUMA/插槽:单个实例固定到单个插槽及其内存——CPU绑定 + (容器)/
--cpuset-mems(conda),KV缓存根据该插槽本地内存配置。在双插槽主机上,numactl --membind会根据负载选择空闲插槽,若两个插槽均繁忙则发出警告。NPS2/NPS4(多节点插槽)会附带cpu_tune.py,提示更精细的节点绑定可提升性能。nps_note - 无根Podman + /
--cpuset-cpus:这些是cgroup限制,在无根Podman环境中若未配置cpuset cgroup委托(cgroup v1,或v2未委托控制器),可能会被忽略或拒绝。这不致命:容器内部的CPU线程绑定仍会通过--cpuset-mems生效;仅容器级内存绑定失效(NUMA局部性降低)。若运行时因cpuset标志报错,可移除这些标志后继续执行——请勿将其视为启动失败。VLLM_CPU_OMP_THREADS_BIND - HF缓存挂载:默认挂载。若
~/.cache/huggingface指向其他路径(共享主机上常见,例如HF_HOME),需将该路径挂载到容器内的/proj/.../vllm,否则模型会在容器内重新下载。/root/.cache/huggingface - 容器名称重复:之前运行残留的容器会导致
vllm-epyc命令报错“名称已被使用”——步骤6会先执行run清除残留容器。$RT rm -f vllm-epyc