1. vLLM 部署后压测跑不通?先把 endpoint 与鉴权改到 TaoToken
vLLM 部署大模型之后,很多人第一反应是「服务起来了,OpenAI 兼容接口能返回,那就没问题了」。但真正上线前必须回答一个问题:这套部署在并发压力下到底能扛多少吞吐、延迟会不会抖、请求成功率是多少。这就是 vLLM 基准测试要解决的事。vLLM 官方仓库自带benchmark_serving.py,配合 ShareGPT 数据集可以跑出一组可复现的吞吐(throughput)、首 token 延迟(TTFT)、并发成功率数据。但官方脚本默认打的是本地127.0.0.1:8080,鉴权、endpoint、模型名全是本地写法,一旦你要把压测目标换成远端统一入口,脚本里的--host、--port、--backend、--model就得整体改一遍,否则不是 404 就是 401。
这篇就聚焦这个改造环节:把 vLLM 基准测试脚本的 endpoint 与鉴权配置改到 TaoToken,让压测流量走统一 API 入口,同时保留 vLLM 本地部署作为被测对象或对照对象。适合两类人:一是已经在 4 卡 4090 这类机器上跑起 vLLM、想验证部署效果的人;二是想把压测脚本标准化、以后换模型只改一个 Model ID 的人。核心检索词就是 vLLM 基准测试、吞吐延迟并发压测、benchmark_serving 改造。
我试过直接拿官方脚本打本地端口,第一次跑就遇到NOT FOUND,因为脚本默认请求的是/v1/completions老格式,而 vLLM 现在暴露的是/v1/chat/completions。后来又遇到Unprocessable Entity,根因是--backend没选对。这些坑下面都会给对照报错和修法。整篇按「问题场景 → 前置准备 → 可复制配置 → 验证请求 → 错排查 → CTA」推进,配置片段可以直接抄。
先说清楚被测对象和压测入口的关系。vLLM 本地部署负责推理,TaoToken 在这里承担的是统一 API 入口和鉴权层:脚本不再直连裸端口,而是带上 Base URL + API Key + Model ID 三件套去打。这样压测脚本和线上调用走同一套寻址逻辑,测出来的成功率才有参考意义。如果你还没拿到 Key,先看下一节。
2. 前置准备:TaoToken Key、模型 ID 与 vLLM 环境对齐
动手改脚本之前,先把三样东西备齐,否则后面配置片段里全是占位符,跑起来还是报错。
第一样是 API Key。打开 TaoToken 控制台,进入 API Keys 页面创建一个新 Key,复制出来先存到环境变量里,别硬编码进脚本。控制台地址是 https://taotoken.net/console ,创建 Key 的入口在 https://taotoken.net/api-keys 。建议命名成vllm-bench-<日期>,方便压测完直接吊销。
第二样是 Model ID。这是最容易踩坑的地方:vLLM 本地启动时用的--model是权重路径或模型名,而走统一入口时,Model ID 必须是入口侧登记的标识。你可以在模型对话页面确认当前可用模型,地址是 https://taotoken.net/models ,或者直接在对话里选一个模型看它回显的 ID。压测脚本里的--model要填这个 ID,不是本地路径。
第三样是 vLLM 环境本身。确认benchmark_serving.py能跑,需要:
pip install vllm pip install aiohttp tqdm numpy如果你之前遇到ASYNC-REQUEST-FUNCS这类导入失败,说明脚本依赖 vLLM 项目内的模块,最稳的做法是直接克隆整个仓库再进benchmarks/目录跑:
git clone https://github.com/vllm-project/vllm.git cd vllm/benchmarks数据集用 ShareGPT 清洗版,官方脚本认这个格式:
wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json环境变量这样设,后面配置片段直接引用:
export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export BENCH_MODEL_ID="你的ModelID"这里要提醒一句:Base URL 用https://taotoken.net/api,不要带任何多余路径,脚本内部会自己拼/v1/chat/completions。很多人 401 就是因为把 Base URL 写成了带/v1的完整地址,结果拼出/v1/v1/...。前置准备做完,下面进入脚本改造。
3. 可复制配置:benchmark_serving.py 的 endpoint 与鉴权改造
官方benchmark_serving.py的参数里,和寻址鉴权相关的是--backend、--base-url、--host、--port、--model、--tokenizer。改造的核心思路:把--host/--port换成--base-url,把--backend从vllm换成openai-chat,再补上 API Key。
先看改造前的原始命令,这是很多人第一次跑会写的:
python benchmark_serving.py --backend vllm \ --tokenizer /root/autodl-tmp/Qwen/Qwen2___5-Coder-32B-Instruct-GPTQ-Int8 \ --dataset /root/LLaMA-Factory/ShareGPT_V3_unfiltered_cleaned_split.json \ --num-prompts 500 \ --request-rate 1 \ --host 127.0.0.1 \ --port 8080这条命令有两个隐患:--backend vllm走的是 vLLM 私有协议,--host/--port是裸端口直连,没有鉴权层。改成走 TaoToken 之后,命令变成:
python benchmark_serving.py \ --backend openai-chat \ --base-url "$TAOTOKEN_BASE_URL" \ --api-key "$TAOTOKEN_API_KEY" \ --model "$BENCH_MODEL_ID" \ --tokenizer /root/autodl-tmp/Qwen/Qwen2___5-Coder-32B-Instruct-GPTQ-Int8 \ --dataset-path /root/LLaMA-Factory/ShareGPT_V3_unfiltered_cleaned_split.json \ --dataset-name sharegpt \ --num-prompts 500 \ --request-rate 1 \ --trust-remote-code注意几个改动点。--backend openai-chat是关键,它决定脚本请求/v1/chat/completions而不是老的/v1/completions。--base-url替代了--host/--port。--api-key是新增的鉴权参数,如果你的脚本版本没有这个参数,就用环境变量方式注入,见下面的 settings 片段。
有些版本的脚本参数名是--dataset而不是--dataset-path,--model也可能叫--served-model-name。为了不每次手改,建议把配置抽成一个 JSON 文件,脚本启动时读取。下面这个bench_config.json可以直接抄:
{ "backend": "openai-chat", "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "model": "你的ModelID", "tokenizer": "/root/autodl-tmp/Qwen/Qwen2___5-Coder-32B-Instruct-GPTQ-Int8", "dataset_path": "/root/LLaMA-Factory/ShareGPT_V3_unfiltered_cleaned_split.json", "dataset_name": "sharegpt", "num_prompts": 500, "request_rate": 1, "max_concurrency": 32, "trust_remote_code": true }如果你用的是 Cline MCP 或 Claude Code 这类工具做压测编排,配置三件套要写全:Base URL 填https://taotoken.net/api,Key 填控制台生成的 Key,Model ID 填登记标识。三者缺一,请求就会在鉴权或路由阶段失败。Cline 的 MCP 配置里对应字段是baseUrl、apiKey、model,Claude Code 的settings.json里对应env.ANTHROPIC_BASE_URL、env.ANTHROPIC_API_KEY、model。Codex 的auth.json则写base_url、api_key、model三个键。这些配置和压测脚本共用同一套寻址逻辑,改一处即可。
配置就绪后,先别急着跑 500 条,用--num-prompts 5做一次冒烟,确认能通再放量。
4. 验证请求:跑通 5 条冒烟再放量到 500 条
冒烟命令:
python benchmark_serving.py \ --backend openai-chat \ --base-url "$TAOTOKEN_BASE_URL" \ --api-key "$TAOTOKEN_API_KEY" \ --model "$BENCH_MODEL_ID" \ --tokenizer /root/autodl-tmp/Qwen/Qwen2___5-Coder-32B-Instruct-GPTQ-Int8 \ --dataset-path /root/LLaMA-Factory/ShareGPT_V3_unfiltered_cleaned_split.json \ --dataset-name sharegpt \ --num-prompts 5 \ --request-rate 1成功时终端会先打印加载数据集的进度,然后逐条输出请求结果,最后给一张汇总表,包含Successful requests、Benchmark duration、Total input tokens、Total generated tokens、Request throughput、Output token throughput、Mean TTFT、Mean TPOT。看到Successful requests: 5且没有 traceback,就说明 endpoint 和鉴权都对了。
冒烟通过后放量:
python benchmark_serving.py \ --backend openai-chat \ --base-url "$TAOTOKEN_BASE_URL" \ --api-key "$TAOTOKEN_API_KEY" \ --model "$BENCH_MODEL_ID" \ --tokenizer /root/autodl-tmp/Qwen/Qwen2___5-Coder-32B-Instruct-GPTQ-Int8 \ --dataset-path /root/LLaMA-Factory/ShareGPT_V3_unfiltered_cleaned_split.json \ --dataset-name sharegpt \ --num-prompts 500 \ --request-rate 1 \ --max-concurrency 32跑完你会拿到一组可对比的数据。参考同类硬件(4×4090 24G)上的实测区间,7B 级 chat 模型的输出吞吐能到 380 token/s 上下,首 token 延迟 70ms 左右;32B 量化模型吞吐降到 38 token/s 量级,TTFT 升到 160ms 以上;推理型大模型吞吐更低,约 28 token/s,TTFT 接近 176ms。这些数字不是让你照抄,而是给你一个判断基准:如果你的结果偏离一个数量级,多半是并发参数或 endpoint 配错了。
结果校验动作有三个。第一,看Successful requests占比,低于 90% 就要查失败原因。第二,看Mean TTFT是否稳定,抖动超过 3 倍说明并发压过头。第三,把--request-rate从 1 调到 2、4,观察成功率变化,找到拐点。之前有人把 request-rate 设成 1 秒 1 次,500 条只成功 82 条,成功率 16.4%;改成 2 秒 1 次升到 19.4%,4 秒 1 次到 21%。这说明瓶颈不在 endpoint,而在被测模型的算力。换成 7B 模型后,同样 1 秒 1 次,成功率直接到 74.8%。所以压测结论要结合模型规模一起看。
5. 常见报错排查:401、NOT FOUND、Unprocessable Entity 对照修
压测脚本改造过程中,报错基本集中在四类,下面逐个给对照和修法。
第一类:401 Unauthorized或local proxy failed。前者是 Key 没传或传错,检查--api-key是否读到了环境变量,echo $TAOTOKEN_API_KEY确认非空。后者常见于本地代理配置残留,脚本请求被转发到了不存在的本地端口。修法是清掉HTTP_PROXY、HTTPS_PROXY环境变量,或者显式unset后再跑。注意 Base URL 必须是https://taotoken.net/api,多一个/v1就会拼错路径。
第二类:NOT FOUND。这个报错来自 API 返回,根因是请求路径不对。官方脚本老版本默认打/v1/completions,而当前入口只认/v1/chat/completions。对照方法:用 curl 手动打一次,看哪个路径通。
curl -s "$TAOTOKEN_BASE_URL/v1/chat/completions" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"'"$BENCH_MODEL_ID"'","messages":[{"role":"user","content":"hi"}],"max_tokens":16}'如果这条通、脚本不通,就是--backend没设成openai-chat。改完再跑。
第三类:Unprocessable Entity。这个错误说明请求体格式和 endpoint 不匹配。最常见原因是--backend vllm配了 chat 路径,或者--backend openai-chat却发了 completions 格式的 body。修法就是把--backend统一成openai-chat,并确认脚本版本支持该 backend。如果脚本里没有这个选项,去 vLLM 仓库拉最新benchmarks/目录。
第四类:reading choices相关报错,通常是响应体解析失败。原因可能是返回了错误 JSON(比如鉴权失败但状态码是 200),也可能是流式响应被中途打断。先看完整响应体,再确认--model填的是 Model ID 而不是本地路径。路径填错时,入口侧找不到模型,返回结构就不是标准 chat 格式,解析自然失败。
排查顺序建议固定成:先 curl 验证 endpoint 和 Key,再冒烟 5 条,最后放量。这样能把问题定位在最小范围。另外,--tokenizer仍然指向本地权重目录,它只用于本地分词统计,不参与网络请求,所以本地路径写错会报文件不存在,和 endpoint 无关。
6. 压测跑通之后:把脚本固化成可复现流程
跑通一次不算完,压测的价值在于可复现。建议把命令固化成 shell 脚本,参数从bench_config.json读,每次换模型只改 Model ID。同时把结果输出到带时间戳的文件,方便横向对比:
python benchmark_serving.py \ --backend openai-chat \ --base-url "$TAOTOKEN_BASE_URL" \ --api-key "$TAOTOKEN_API_KEY" \ --model "$BENCH_MODEL_ID" \ --tokenizer "$TOKENIZER_PATH" \ --dataset-path "$DATASET_PATH" \ --dataset-name sharegpt \ --num-prompts 500 \ --request-rate 1 \ --max-concurrency 32 \ 2>&1 | tee "bench_$(date +%Y%m%d_%H%M).log"长期做编码类压测或 Agent 场景的,可以走 Coding Plan,把压测流量和日常调用分开计量,地址是 https://taotoken.net/coding-plan 。需要确认模型能力是否匹配压测目标,先去模型对话页面实测几条,地址是 https://taotoken.net/models 。接入文档在 https://taotoken.net/doc ,里面有各语言 SDK 的 Base URL 和鉴权写法,改脚本时对照着看能少走弯路。Key 管理和轮换在 https://taotoken.net/api-keys ,压测用的 Key 建议单独建、单独吊销。
最后留一个实用技巧:压测前先确认 vLLM 本地服务的--max-model-len和--gpu-memory-utilization,这两个参数直接决定并发上限。如果本地服务本身把显存吃满,压测成功率低就不是 endpoint 的问题,而是部署参数的问题。把本地部署调稳,再走统一入口压测,数据才有意义。