戰(zhàn):從單卡到多卡的多模態(tài)服務(wù)化方案)
最近在業(yè)務(wù)里遇到一個(gè)很典型的需求讓系統(tǒng)能夠“看懂圖片”。輸入是產(chǎn)品截圖、工單圖片、紙質(zhì)單據(jù)照片模型不僅要理解圖里的內(nèi)容還要給出描述或者回答。純文本大模型肯定做不到最終我們選了 Qwen2-VL 配合 vLLM 來(lái)做服務(wù)化部署。這套方案把單卡和多卡場(chǎng)景都跑通了后面還寫(xiě)了一套爬蟲(chóng)腳本去批量調(diào)用模型接口算是把部署、壓測(cè)、穩(wěn)定性驗(yàn)證的完整流程走了一遍。這篇文章會(huì)把整個(gè)過(guò)程拆開(kāi)來(lái)講包括環(huán)境選型、啟動(dòng)參數(shù)、多卡并行原理、多模態(tài)請(qǐng)求體構(gòu)造以及各種能預(yù)見(jiàn)的坑。適合剛接觸視覺(jué)語(yǔ)言模型部署、想在本地或內(nèi)網(wǎng)環(huán)境快速拉起服務(wù)并接業(yè)務(wù)的人。1. 部署前的整體規(guī)劃為什么是 Qwen2-VL 配合 vLLM1.1 多模態(tài)模型落地的現(xiàn)實(shí)約束實(shí)際業(yè)務(wù)里的圖片大多屬于內(nèi)部數(shù)據(jù)比如工單截圖、產(chǎn)品頁(yè)、掃描件不可能全部發(fā)到外部接口去處理私有化部署幾乎是唯一選擇。多模態(tài)開(kāi)源模型里Qwen2-VL 的視覺(jué)理解能力比較均衡能處理單圖、多圖和視頻輸入在中文場(chǎng)景下的 OCR、版面理解也表現(xiàn)不錯(cuò)。它有幾個(gè)不同的規(guī)格2B 可以放在小顯存環(huán)境里驗(yàn)證鏈路7B 適合作為日常服務(wù)72B 交給算力更充足的機(jī)器。而且 vLLM 原生支持這類(lèi)模型架構(gòu)直接把權(quán)重路徑傳給 vLLM 就能運(yùn)行不需要額外改模型代碼。真正決定部署方案的往往是顯存所以硬件約束必須先說(shuō)清楚。1.2 vLLM 為什么更適合服務(wù)化部署如果直接用推理框架逐條處理請(qǐng)求每來(lái)一個(gè)用戶(hù)都要重新加載權(quán)重、獨(dú)占顯存做前向計(jì)算GPU 利用率非常低。vLLM 做對(duì)了三件事第一把 KV cache 分成固定大小的塊按需分配解決了顯存碎片化的問(wèn)題第二實(shí)現(xiàn)了連續(xù)批處理當(dāng)一個(gè)請(qǐng)求在生成 token 的間隙其他請(qǐng)求可以插進(jìn)來(lái)一起計(jì)算吞吐量提升非常明顯第三自帶一個(gè)兼容常用大模型 API 格式的 HTTP 服務(wù)層業(yè)務(wù)側(cè)直接用 HTTP JSON 就能調(diào)用不用自己拼底層協(xié)議。這三點(diǎn)的組合讓 vLLM 成為目前私有化部署推理服務(wù)的主流方案。尤其是長(zhǎng)上下文和并發(fā)請(qǐng)求同時(shí)存在時(shí)連續(xù)批處理帶來(lái)的收益非??捎^(guān)。1.3 整體鏈路與資源估算整條鏈路是模型權(quán)重文件 → vLLM 進(jìn)程加載模型對(duì)外暴露 HTTP 端口→ 業(yè)務(wù)腳本批量請(qǐng)求。資源評(píng)估方面一個(gè)粗略但好用的經(jīng)驗(yàn)是半精度下每 10 億參數(shù)大約占 2GB 顯存。7B 模型權(quán)重約 14GB一張 24GB 的卡能放下但留給 KV cache 的余量取決于上下文長(zhǎng)度72B 模型權(quán)重約 144GB單卡無(wú)論如何都放不下必須上多卡。除了權(quán)重還要考慮 KV cache、圖片變換出來(lái)的視覺(jué) token、激活值緩存。部署前先按這個(gè)量級(jí)估算一下能少踩很多彎路。2. 環(huán)境準(zhǔn)備與模型權(quán)重獲取先把“原料”備好2.1 硬件、驅(qū)動(dòng)與基礎(chǔ)軟件版本基線(xiàn)顯存是最核心的約束其次是顯存帶寬。想要流暢跑 7B 級(jí)別模型建議至少一張 24GB 顯存的卡跑 72B 則要準(zhǔn)備 4 張 80GB 以上的卡。系統(tǒng)層面顯卡驅(qū)動(dòng)需要支持當(dāng)前使用的 CUDA 版本一般建議 CUDA 12.1 以上。Python 版本建議 3.10 或 3.11太老的版本容易在安裝 vLLM 時(shí)遇到編譯問(wèn)題。建議先把 PyTorch 裝好確認(rèn)torch.cuda.is_available()返回正常再安裝 vLLM。這個(gè)順序很重要先裝 vLLM 再裝 PyTorch很容易碰到版本不匹配然后報(bào)一堆算子相關(guān)的錯(cuò)誤。vLLM 更新很快不同版本的參數(shù)和算子支持有差異建議用相對(duì)新的穩(wěn)定版本不要盲目追最新。2.2 權(quán)重獲取與目錄結(jié)構(gòu)確認(rèn)Qwen2-VL 的權(quán)重可以從開(kāi)源模型托管站點(diǎn)下載選擇本地網(wǎng)絡(luò)訪(fǎng)問(wèn)順暢的渠道就好。下載完成之后權(quán)重目錄里必須包含 config.json、tokenizer.json、tokenizer_config.json以及一個(gè)或多個(gè) safetensors 文件。啟動(dòng)前一定要檢查目錄完整性重點(diǎn)看 config.json 里的 model_type 和 vision_config 是否正常。如果是從某些渠道拉下來(lái)的文件可能在傳輸中出現(xiàn)截?cái)鄦?dòng)時(shí)才報(bào)錯(cuò)到那時(shí)再排查就比較被動(dòng)了。穩(wěn)妥做法是下載后先做一次哈希校驗(yàn)或者直接用模型倉(cāng)庫(kù)自帶的校驗(yàn)記錄比對(duì)。vLLM 加載模型時(shí)會(huì)先讀取 config.json如果格式不對(duì)或者缺少視覺(jué)相關(guān)配置進(jìn)程會(huì)在加載階段直接退出。2.3 啟動(dòng)前的三項(xiàng)快速檢查第一確認(rèn) GPU 能被系統(tǒng)識(shí)別執(zhí)行nvidia-smi能看到卡的數(shù)量和顯存大小。第二確認(rèn) PyTorch 能看到 GPU在 Python 里執(zhí)行python -c import torch; print(torch.cuda.is_available(), torch.cuda.device_count())如果輸出 False 或者設(shè)備數(shù)為 0說(shuō)明驅(qū)動(dòng)或 CUDA 環(huán)境有問(wèn)題。第三確認(rèn)端口沒(méi)有被占用vLLM 默認(rèn)監(jiān)聽(tīng) 8000 端口如果機(jī)器上已經(jīng)有服務(wù)占用要么關(guān)掉舊進(jìn)程要么啟動(dòng)時(shí)改端口。這三項(xiàng)檢查一分鐘就能完成但能避免后面啟動(dòng)失敗時(shí)在錯(cuò)誤方向上折騰。3. 單卡部署實(shí)操?gòu)膯?dòng)命令到第一個(gè)接口響應(yīng)3.1 新版命令與最小啟動(dòng)參數(shù)vLLM 新版直接提供了一個(gè)vllm serve命令早期版本需要寫(xiě)更長(zhǎng)的一串啟動(dòng)腳本。先看一個(gè)最小啟動(dòng)示例vllm serve /data/models/Qwen2-VL-7B-Instruct \ --served-model-name qwen2vl \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --dtype auto這里幾個(gè)關(guān)鍵參數(shù)要說(shuō)明一下。--served-model-name是給接口層使用的模型別名調(diào)用 API 時(shí) model 字段必須寫(xiě)這個(gè)值不設(shè)置的話(huà)默認(rèn)用真實(shí)模型名實(shí)際使用中很容易因?yàn)槊植灰恢聦?dǎo)致接口 404。--max-model-len控制模型能接受的最大上下文長(zhǎng)度包括文本和視覺(jué) token這個(gè)值直接影響 KV cache 預(yù)留大小通常設(shè)為 8192 或 16384 夠用如果業(yè)務(wù)里沒(méi)有超長(zhǎng)文檔沒(méi)必要拉太高。--gpu-memory-utilization表示 vLLM 最多可以占用多少比例的顯存默認(rèn) 0.9一般建議 0.85 到 0.9太低會(huì)浪費(fèi)顯存太高則可能把顯存撐爆。--dtype auto讓系統(tǒng)根據(jù)硬件自動(dòng)選擇半精度格式。3.2 驗(yàn)證服務(wù)模型列表與首輪多模態(tài)請(qǐng)求啟動(dòng)之后日志里會(huì)看到 vLLM 打印模型加載和端口監(jiān)聽(tīng)信息。先在另一個(gè)終端驗(yàn)證服務(wù)是否正常curl http://localhost:8000/v1/models返回結(jié)果里能看到模型列表。接著構(gòu)造第一個(gè)多模態(tài)請(qǐng)求。圖片有兩種傳法傳公網(wǎng)可訪(fǎng)問(wèn)的圖片 URL或者傳 base64 編碼的 Data URI。本地文件建議用 base64避免服務(wù)端還需要出網(wǎng)拉圖。示例代碼如下import requests import base64 image base64.b64encode(open(test.jpg, rb).read()).decode() payload { model: qwen2vl, messages: [ { role: user, content: [ {type: image_url, image_url: {url: fdata:image/jpeg;base64,{image}}}, {type: text, text: 描述這張圖片的內(nèi)容} ] } ] } resp requests.post(http://localhost:8000/v1/chat/completions, jsonpayload, timeout120) result resp.json() print(result[choices][0][message][content])多模態(tài)請(qǐng)求的 content 必須是數(shù)組不能是一個(gè)普通字符串。數(shù)組里每個(gè)元素包含 type 字段可以是 image_url也可以是 text。很多第一次接觸的人把 content 直接寫(xiě)成字符串vLLM 會(huì)返回 400 錯(cuò)誤。3.3 單卡場(chǎng)景下的參數(shù)調(diào)節(jié)經(jīng)驗(yàn)單卡部署并不只是一個(gè)啟動(dòng)命令的事情參數(shù)之間是相互制約的。顯存總量固定時(shí)max-model-len 越大KV cache 占用越多留給并發(fā) batch 的空間就越少。如果一張卡既想處理長(zhǎng)文檔又想同時(shí)服務(wù)多個(gè)請(qǐng)求很可能出現(xiàn)啟動(dòng)成功后一旦并發(fā)上來(lái)就 OOM。實(shí)際項(xiàng)目里建議先根據(jù)業(yè)務(wù)最長(zhǎng)輸入設(shè)置 max-model-len再觀(guān)察顯存占用最后調(diào)整 gpu-memory-utilization。另外--max-num-seqs參數(shù)可以控制單個(gè) batch 內(nèi)最多并行處理的序列數(shù)默認(rèn)值在某些版本下可能偏大。并發(fā)不高時(shí)反而應(yīng)該調(diào)小一點(diǎn)避免多個(gè)請(qǐng)求擠在同一 batch 里拖慢單個(gè)請(qǐng)求的響應(yīng)速度。4. 多卡部署張量并行原理、啟動(dòng)參數(shù)與顯存分配4.1 什么時(shí)候必須從單卡切到多卡兩種情況需要多卡一是模型權(quán)重本身超出單卡顯存比如 72B 半精度需要 144GB 以上二是單卡吞吐跑不滿(mǎn)業(yè)務(wù)峰值需要通過(guò)并行推理提升并發(fā)能力。如果只是顯存放不下用 CPU offload 或量化也可以緩解但性能損失明顯。多卡部署能讓多個(gè) GPU 同時(shí)承擔(dān)推理計(jì)算吞吐往往能翻幾倍。但也要清楚多卡不是萬(wàn)能的卡間通信會(huì)成為新的瓶頸尤其當(dāng)模型較小而卡數(shù)較多時(shí)通信開(kāi)銷(xiāo)可能超過(guò)并行收益。4.2 張量并行與流水線(xiàn)并行怎么理解vLLM 支持兩種并行方式。張量并行是把模型權(quán)重切分到多張卡上每一層都分布在所有卡里前向計(jì)算時(shí)卡與卡之間需要同步交換數(shù)據(jù)適合單機(jī)多卡。流水線(xiàn)并行則是按層切分模型的第 1 到第 N 層放在卡 A第 N1 到第 M 層放在卡 B數(shù)據(jù)一層一層往下傳適合跨機(jī)場(chǎng)景但存在流水線(xiàn)氣泡利用率可能不如張量并行。對(duì)大多數(shù)單機(jī)多卡場(chǎng)景來(lái)說(shuō)優(yōu)先使用張量并行。實(shí)際啟動(dòng)時(shí)通過(guò)--tensor-parallel-size指定使用多少?gòu)埧ū热?2 表示兩塊卡組成一個(gè)并行組??〝?shù)不是隨意定的要能被模型內(nèi)部結(jié)構(gòu)整除常見(jiàn)選擇是 2、4、8。4.3 多卡啟動(dòng)命令與通信環(huán)境變量多卡啟動(dòng)和單卡很類(lèi)似核心差異是指定并行參數(shù)。以?xún)蓮埧槔齰llm serve /data/models/Qwen2-VL-7B-Instruct \ --served-model-name qwen2vl \ --tensor-parallel-size 2 \ --pipeline-parallel-size 1 \ --max-model-len 12288 \ --gpu-memory-utilization 0.9啟動(dòng)完成后日志中能看到張量并行相關(guān)信息。多卡之間通信依賴(lài) NCCL 庫(kù)如果通信初始化失敗配置環(huán)境變量往往能解決export NCCL_DEBUGINFO export NCCL_P2P_DISABLE1NCCL_DEBUG 會(huì)打印通信細(xì)節(jié)方便定位是哪一步卡住。NCCL_P2P_DISABLE 主要用在某些不支持 GPU 間直接內(nèi)存訪(fǎng)問(wèn)的環(huán)境強(qiáng)制走顯存拷備路徑犧牲一點(diǎn)速度換取穩(wěn)定。還有一種情況是只有普通以太網(wǎng)而沒(méi)有專(zhuān)用高速網(wǎng)絡(luò)此時(shí)可能需要設(shè)置NCCL_IB_DISABLE1關(guān)閉遠(yuǎn)程直接內(nèi)存訪(fǎng)問(wèn)否則 NCCL 會(huì)在初始化時(shí)等待超時(shí)。4.4 多卡場(chǎng)景的顯存分配與并發(fā)上限多卡部署時(shí)gpu-memory-utilization 對(duì)每張卡分別生效建議保持默認(rèn) 0.9如果某張卡的顯存被其他進(jìn)程占用可以單獨(dú)調(diào)低。vLLM 會(huì)在啟動(dòng)時(shí)根據(jù)模型權(quán)重、已配置的 max-model-len 和 gpu-memory-utilization 自動(dòng)計(jì)算 KV cache 大小日志里能看到分配結(jié)果。多卡下的并發(fā)控制同樣靠 max-num-seqs但這個(gè)參數(shù)是全局的作用于整個(gè)并行組而不是單卡疊加。還要注意多卡的總 KV cache 是邏輯上共享的不要簡(jiǎn)單用“單卡顯存 × 卡數(shù)”去算可用 KV cache實(shí)際值要看 vLLM 日志打印。如果發(fā)現(xiàn)一張卡顯存使用特別高而另一張很低多半是通信或并行配置出現(xiàn)了問(wèn)題。5. 爬蟲(chóng)請(qǐng)求批量調(diào)用多模態(tài)接口的適配層實(shí)現(xiàn)5.1 請(qǐng)求體結(jié)構(gòu)從文本到多模態(tài)vLLM 暴露的接口兼容常見(jiàn)的聊天補(bǔ)全協(xié)議核心是 POST /v1/chat/completions。純文本請(qǐng)求的 content 可以是一個(gè)數(shù)組也可以是一個(gè)普通字符串但多模態(tài)請(qǐng)求必須使用數(shù)組形式。一個(gè)圖片請(qǐng)求體包含兩個(gè)關(guān)鍵部分model 字段必須與啟動(dòng)時(shí)設(shè)置的 served-model-name 一致content 數(shù)組里的每個(gè)對(duì)象要么是 image_url 類(lèi)型要么是 text 類(lèi)型。image_url 支持傳入公網(wǎng) URL 或 base64 Data URI。公網(wǎng) URL 的好處是請(qǐng)求體體積小缺點(diǎn)是 vLLM 進(jìn)程需要能訪(fǎng)問(wèn)外網(wǎng)而且圖片網(wǎng)站可能有限流導(dǎo)致拉圖超時(shí)。base64 方式最穩(wěn)定缺點(diǎn)是圖片越大請(qǐng)求體越大服務(wù)端解析也會(huì)更慢。如果要傳多張圖就在 content 數(shù)組里多放幾個(gè) image_url模型會(huì)按順序理解。視頻可以拆幀成多個(gè) image_url 傳入。調(diào) max_tokens 時(shí)要注意多模態(tài)輸入的視覺(jué) token 已經(jīng)占用了上下文長(zhǎng)度給生成的文本留下合理空間即可。5.2 批量抓取與并發(fā)請(qǐng)求的工程化所謂“爬蟲(chóng)請(qǐng)求”本質(zhì)就是寫(xiě)一個(gè) HTTP 客戶(hù)端去批量調(diào)用模型接口。最簡(jiǎn)單的場(chǎng)景是預(yù)先準(zhǔn)備一批圖片 URL逐個(gè)請(qǐng)求模型。如果圖片列表來(lái)自網(wǎng)頁(yè)可以先寫(xiě)一個(gè)爬蟲(chóng)提取圖片鏈接再用同樣的會(huì)話(huà)去請(qǐng)求模型。批量調(diào)用不能用一個(gè) for 循環(huán)串行跑那樣吞吐太低應(yīng)該用線(xiàn)程池并發(fā)。示例思路from concurrent.futures import ThreadPoolExecutor, as_completed def call_one_image(model_name, image_path, prompt): # 讀取圖片轉(zhuǎn) base64 # 構(gòu)造 payload # 請(qǐng)求 vLLM 接口 # 返回內(nèi)容或異常 pass with ThreadPoolExecutor(max_workers8) as pool: futures [pool.submit(call_one_image, qwen2vl, path, prompt) for path in image_paths] for future in as_completed(futures): result future.result() # 收集結(jié)果記錄失敗項(xiàng)并發(fā)數(shù)不要盲目拉高。vLLM 本身有 batch 能力并發(fā)請(qǐng)求越多單 batch 越大吞吐會(huì)上升但單請(qǐng)求延遲也會(huì)增加。如果業(yè)務(wù)要求的是穩(wěn)定單請(qǐng)求延遲并發(fā)數(shù)要保守一點(diǎn)如果追求整體吞吐可以逐步提高并發(fā)觀(guān)察顯存占用和響應(yīng)時(shí)間的變化。一個(gè)穩(wěn)妥做法是客戶(hù)端做限速比如固定每秒只發(fā)起 N 個(gè)請(qǐng)求避免把模型服務(wù)打垮。5.3 流式輸出的解析與半結(jié)構(gòu)化結(jié)果對(duì)話(huà)場(chǎng)景中用戶(hù)往往希望模型邊生成邊輸出。請(qǐng)求體里加一個(gè)stream: true服務(wù)端會(huì)返回 SSE 格式的流式數(shù)據(jù)。使用 requests 請(qǐng)求時(shí)設(shè)置streamTrue然后逐行解析import json resp requests.post(http://localhost:8000/v1/chat/completions, jsonpayload, streamTrue, timeout120) for line in resp.iter_lines(): if not line: continue text line.decode(utf-8) if not text.startswith(data: ): continue data text[6:] if data [DONE]: break try: obj json.loads(data) delta obj[choices][0][delta].get(content) if delta: print(delta, end, flushTrue) except json.JSONDecodeError: continue流式響應(yīng)每次返回的是一個(gè)片段通過(guò) delta.content 累積起來(lái)才是完整文本。邊界是固定的data: [DONE]收到它就表示生成結(jié)束。流式接口的超時(shí)設(shè)置要比普通接口更寬松因?yàn)榈谝粋€(gè) token 可能需要較長(zhǎng)的思考時(shí)間客戶(hù)端如果按普通接口的 30 秒超時(shí)極容易誤判。5.4 一個(gè)可直接套用的批量調(diào)用腳本綜合上面幾點(diǎn)可以整理成一個(gè)可復(fù)用的適配層。這個(gè)腳本適合批量處理一批本地圖片或圖片 URL輸出每個(gè)圖片對(duì)應(yīng)的模型回復(fù)同時(shí)帶超時(shí)和重試邏輯。核心注意點(diǎn)有三個(gè)圖片讀取和 base64 編碼盡量在子線(xiàn)程中做好避免阻塞請(qǐng)求線(xiàn)程失敗請(qǐng)求要記錄原因不要直接丟棄服務(wù)端一旦返回非 200 狀態(tài)碼要打印響應(yīng)體因?yàn)?vLLM 的錯(cuò)誤信息基本都在 body 里。另外建議把結(jié)果寫(xiě)成 JSON 文件方便后續(xù)核對(duì)而不是只打印到終端。6. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄6.1 顯存 OOM這是遇到最多的問(wèn)題。模型加載階段就 OOM說(shuō)明單卡顯存放不下模型要么換小規(guī)格模型要么開(kāi)啟量化要么增加卡數(shù)。運(yùn)行時(shí) OOM 則多半是并發(fā)太高或上下文太長(zhǎng)處理方式是把 max-model-len 調(diào)低、把 gpu-memory-utilization 從 0.9 降到 0.85同時(shí)調(diào)大 max-num-seqs 的下限而不是盲目并發(fā)。如果是量化方案AWQ/GPTQ 這類(lèi)格式可以把 7B 模型權(quán)重壓到 8GB 左右但多模態(tài)部分對(duì)量化的支持因版本而異視覺(jué)編碼器在極端量化下可能有輕微精度損失需要實(shí)測(cè)。6.2 接口返回 400/404 的常見(jiàn)原因400 錯(cuò)誤絕大多數(shù)是請(qǐng)求體格式問(wèn)題尤其是 content 傳成了字符串而不是數(shù)組或者 image_url 缺少 url 字段。404 錯(cuò)誤通常是 model 字段和 served-model-name 不一致啟動(dòng)時(shí)設(shè)置的別名是 qwen2vl請(qǐng)求里寫(xiě)的卻是別的名字自然找不到模型。422 錯(cuò)誤屬于字段校驗(yàn)失敗比如 temperature 超出范圍、messages 里缺少 role。排查這類(lèi)問(wèn)題最快的方式是打印 vLLM 的響應(yīng) body錯(cuò)誤信息會(huì)寫(xiě)得非常明確。6.3 多卡通信失敗與 NCCL 配置多卡部署時(shí)如果日志卡在 NCCL 初始化或者報(bào)出無(wú)法連接 peer 的信息先確認(rèn)各卡是否都空閑再檢查驅(qū)動(dòng)和 NCCL 版本。部分環(huán)境不支持 GPU 間的直接內(nèi)存訪(fǎng)問(wèn)設(shè)置NCCL_P2P_DISABLE1可以繞過(guò)去。如果是多機(jī)部署或者網(wǎng)絡(luò)環(huán)境普通按要求設(shè)置好NCCL_IB_DISABLE也有幫助。多卡通信問(wèn)題最常見(jiàn)的根源是網(wǎng)絡(luò)權(quán)限而不是模型本身。6.4 上下文長(zhǎng)度與視覺(jué) token 估算圖片進(jìn)入模型后會(huì)先被切分成多個(gè) patch再轉(zhuǎn)換成視覺(jué) token。Qwen2-VL 采用動(dòng)態(tài)分辨率策略同一張圖在不同尺寸下產(chǎn)生的 token 數(shù)不同。一張 1000 像素級(jí)別的普通圖片通常會(huì)產(chǎn)生數(shù)百到上千個(gè)視覺(jué) token具體取決于圖像內(nèi)容和分辨率。這導(dǎo)致一個(gè)常見(jiàn)的誤判文本很短請(qǐng)求看起來(lái)很小但圖片 token 已經(jīng)占了很大上下文。如果 max-model-len 設(shè)置得不夠服務(wù)端會(huì)直接報(bào)錯(cuò)。排查時(shí)可以看 vLLM 日志中每個(gè)請(qǐng)求的 prompt_tokens 字段它會(huì)明確告訴你這個(gè)請(qǐng)求消耗了多少上下文長(zhǎng)度。6.5 性能排查與日志分析vLLM 提供了 metrics 接口返回 Prometheus 格式的指標(biāo)可以看到吞吐、延遲、當(dāng)前排隊(duì)請(qǐng)求數(shù)。GPU 利用率不高時(shí)先看是不是卡在 CPU 的數(shù)據(jù)預(yù)處理環(huán)節(jié)比如圖片解碼、base64 轉(zhuǎn)碼、token 化在大規(guī)模并發(fā)下會(huì)占用不少 CPU。如果 GPU 利用率高但延遲依然很長(zhǎng)多半是 max-model-len 設(shè)置過(guò)大導(dǎo)致 KV cache 碎片化或者并發(fā)數(shù)過(guò)高導(dǎo)致 batch 內(nèi)序列過(guò)多。日志方面啟動(dòng)時(shí)重點(diǎn)看模型加載階段有沒(méi)有 warning運(yùn)行中重點(diǎn)看錯(cuò)誤棧和 OOM 信息。最后說(shuō)幾個(gè)實(shí)際操作中積累下來(lái)的體會(huì)。第一先跑通單卡再考慮多卡。很多問(wèn)題其實(shí)出在請(qǐng)求體格式上多卡并行只會(huì)讓排查更困難。第二max-model-len 不要貪大能覆蓋業(yè)務(wù)最長(zhǎng)輸入就夠了剩下的顯存留給 KV cache 去換吞吐。第三客戶(hù)端必須做超時(shí)和重試vLLM 處理長(zhǎng) prompt 或大圖時(shí)首 token 延遲會(huì)比較長(zhǎng)客戶(hù)端默認(rèn) 30 秒超時(shí)很容易誤殺請(qǐng)求。第四多模態(tài)請(qǐng)求的 content 一定用數(shù)組這是我在實(shí)際對(duì)接中見(jiàn)過(guò)最多的一種低級(jí)錯(cuò)誤。暫時(shí)就寫(xiě)到這里這套鏈路目前運(yùn)行穩(wěn)定后續(xù)如果再壓一下視頻輸入場(chǎng)景有結(jié)論了再回來(lái)補(bǔ)充。