关于PaddleOCR-VL部署与使用说明
起因是要做一个关于票据OCR识别+LLM的综合应用,在部署PaddleOCR-VL时踩了不少坑,主要是因为GPU显存、计算等级、CUDA版本、选择的部署方案等多维度导致,特此记录。
分享基于NVIDIA RTX PRO 6000 Blackwell (sm_120) 部署 PaddleOCR-VL-1.6 的完整过程与接口调用方法,仅供参考。
参考资料
- PaddleOCR GitHub 地址
- PaddleOCR-VL Blackwell GPU 使用教程
- PaddleOCR-VL 使用教程
- PaddleOCR-VL 服务调用与 API 参数说明
- 官方 SM120 Compose 文件
- 官方 SM120 环境变量文件
根据显卡选择部署方案
部署方案说明
访问PaddleOCR项目的
deploy目录,该目录包含多种部署方案。这里采用Docker部署,因此关注paddleocr_vl_docker目录,其中存放了基于docker不同环境的部署方案

选择 paddleocr_vl_docker 下的部署方案时,先确认两件事:
- GPU 的 CUDA Compute Capability(计算能力),例如
8.6、9.0、12.0。 - 是否需要 HPS 的 Triton 动态批处理和高并发架构。
已验证当前显卡不支持HPS部署,因此不能选择HPS方式部署。应选择nvidia-gpu-sm120方式进行部署
需特别说明的一点是:nvidia-gpu-sm120/.env中提到的镜像标签,其中 sm_120 是 CUDA 编译目标,表示 Compute Capability 12.0:
text
sm_120 -> major = 12, minor = 0 -> Compute Capability 12.0
它不是 CUDA Toolkit 版本,也不是显存容量。例如 nvidia-smi 顶部显示的 CUDA Version: 13.2 表示当前 NVIDIA 驱动最高支持的 CUDA 运行时版本,不能用于判断应选择 sm_120 还是普通 GPU 镜像。
bash
$ nvidia-smi
Wed Aug 12 09:41:06 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.91.07 Driver Version: 595.91.07 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA RTX PRO 6000 Blac... Off | 00000000:16:00.0 Off | Off |
| 30% 30C P8 6W / 600W | 88712MiB / 97887MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
查看 Compute Capability
nvidia-smi默认不显示 Compute Capability,可通过以下2种方式查看。
1.运行时检查方式:在已部署的 vLLM 容器中执行:
bash
docker exec paddleocr-vlm-server python -c "import torch; print(torch.cuda.get_device_name(0)); print(torch.cuda.get_device_capability(0))"
当前机器预期输出如下,其中 (12, 0) 即 sm_120。
text
NVIDIA RTX PRO 6000 Blackwell Workstation Edition
(12, 0)
2.按照显卡型号在 NVIDIA 的 CUDA GPU Compute Capability 列表 中查询。

PaddleOCR-VL 部署选择
针对 PaddleOCR 当前仓库中的 PaddleOCR-VL Docker 部署目录。选择前还需确认官方所要求的 NVIDIA 驱动、Docker 和 CUDA 版本。
| GPU Compute Capability | 推荐目录或方式 | 说明 |
|---|---|---|
12.0,即 sm_120;例如 RTX PRO 6000 Blackwell、RTX 50 系列 |
deploy/paddleocr_vl_docker/accelerators/nvidia-gpu-sm120/ |
使用 SM120 专用镜像,标签必须包含 -sm120 |
8.x 或 9.x;例如 RTX 30/40、A10、A100、H100 |
deploy/paddleocr_vl_docker/accelerators/nvidia-gpu/ |
常规 NVIDIA GPU Compose;vLLM 要求 Compute Capability >= 8.0 |
7.x;例如 T4、V100 |
不建议使用默认 vLLM Compose | 官方提示 vLLM 容易超时或 OOM;选择 PaddlePaddle 本地推理或手工服务部署 |
| 无 NVIDIA GPU | CPU 手工部署 | 不使用 NVIDIA GPU Docker Compose |
需要 Triton 动态批处理和高并发,且 Compute Capability >= 8.0 且 < 10.0 |
deploy/paddleocr_vl_docker/hps/ |
HPS 架构;需要确认 HPS 基础镜像中的 PaddlePaddle 支持该显卡架构 |
本机的选择结论
本机显卡为 NVIDIA RTX PRO 6000 Blackwell Workstation Edition,运行时 Compute Capability 是 12.0,因此必须使用:
text
deploy/paddleocr_vl_docker/accelerators/nvidia-gpu-sm120/
bash
# ls -a accelerators/nvidia-gpu-sm120
. .. compose.yaml .env pipeline.Dockerfile vllm_config.yaml vlm.Dockerfile
对应的镜像标签为:
text
API_IMAGE_TAG_SUFFIX=latest-nvidia-gpu-sm120-offline
VLM_BACKEND=vllm
VLM_IMAGE_TAG_SUFFIX=latest-nvidia-gpu-sm120-offline
架构与端口
正确的 sm120 Compose 方案包含两个容器:
| 容器 | 职责 | 容器端口 | 宿主机端口 |
|---|---|---|---|
paddleocr-vlm-server |
vLLM VLM 推理服务 | 8080 |
默认不暴露 |
paddleocr-vl-api |
完整 PaddleOCR-VL 流水线,含版面检测、阅读顺序和 VLM 调用 | 8080 |
8080 |
数据流:
text
客户端 -> paddleocr-vl-api:8080 -> paddleocr-vlm-server:8080
paddleocr-vl-api 是完整 OCR 服务,使用 /layout-parsing。它不是 OpenAI API,因此访问 /v1/models 会返回 404。
vLLM 显存异常说明
部署过程中需要特别注意的是vLLM 显存异常,例如 vLLM 启动错误:
text
Free memory on device (26.34/94.97 GiB) on startup is less than desired GPU memory utilization (0.5, 47.49 GiB)
含义如下:
- GPU 总显存约
94.97 GiB。 - 当时可用显存约
26.34 GiB。 - 默认
gpu-memory-utilization: 0.5需要约47.49 GiB,因此 vLLM 拒绝启动。
基于HPS方案部署特别说明
访问HPS目录,其中
README.md有提及如何部署,按照文档部署问题不大,但无关于VLLM显存调整的说明,可参考以下步骤完成HPS部署方案的VLLM显存配置。
1.基于hps/compose.yaml同级路径,创建vllm_config.yaml,添加如下参数,需根据实际情况修改。
bash
gpu-memory-utilization: 0.12
2.编辑genai_server_entrypoint.sh,添加VLLM_CONFIG变量与--backend_config参数,用于指定vllm配置,手动分配显存分配。
bash
#!/usr/bin/env sh
set -eu
CONFIG="${PIPELINE_CONFIG:-/config/pipeline_config.yaml}"
VLLM_CONFIG="${VLLM_CONFIG:-/config/vllm_config.yaml}"
VLM_NAME=$(
grep -A5 'module_name: vl_recognition' "$CONFIG" \
| grep 'model_name:' \
| head -1 \
| awk '{print $2}'
)
if [ -z "$VLM_NAME" ]; then
echo "Failed to read VLM name from ${CONFIG}" >&2
exit 1
fi
exec paddleocr genai_server \
--model_name "$VLM_NAME" \
--host 0.0.0.0 \
--port 8080 \
--backend vllm \
--backend_config "$VLLM_CONFIG"
3.修改hps/compose.yaml,添加vllm_config.yaml的挂载
bash
paddleocr-vlm-server:
image: ccr-2vdh3abv-pub.cnc.bj.baidubce.com/paddlepaddle/paddleocr-genai-vllm-server:latest-nvidia-gpu
container_name: paddleocr-vlm-server
volumes:
- ./${HPS_SDK_DIR:-paddlex_hps_PaddleOCR-VL-1.6_sdk}/server/pipeline_config.yaml:/config/pipeline_config.yaml:ro
- ./genai_server_entrypoint.sh:/entrypoint.sh:ro
- ./vllm_config.yaml:/config/vllm_config.yaml:ro
部署
前置条件
- NVIDIA RTX Blackwell 架构 GPU,例如 RTX PRO 6000 Blackwell。
- NVIDIA 驱动支持 CUDA
12.9或更高版本。 - Docker
19.03或更高版本。 - Docker Compose
2.x。
检查驱动支持的 CUDA 版本:
bash
nvidia-smi
输出顶部的 CUDA Version 应为 12.9 或更高。
获取官方Compose 文件
创建目录并下载Compose、env文件
bash
mkdir -p /root/paddleocr-sm120
cd /root/paddleocr-sm120
curl -fsSL -o compose.yaml https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/main/deploy/paddleocr_vl_docker/accelerators/nvidia-gpu-sm120/compose.yaml
curl -fsSL -o .env https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/main/deploy/paddleocr_vl_docker/accelerators/nvidia-gpu-sm120/.env
.env 必须使用 SM120 镜像标签:
dotenv
API_IMAGE_TAG_SUFFIX=latest-nvidia-gpu-sm120-offline
VLM_BACKEND=vllm
VLM_IMAGE_TAG_SUFFIX=latest-nvidia-gpu-sm120-offline
说明:
-offline镜像包含模型,首次拉取较大,但启动不依赖模型下载。- 有网络且希望首次启动时下载模型时,可去掉两个标签中的
-offline。 - 无论是否使用离线镜像,两个标签都必须保留
-sm120。
配置vLLM显存使用量
需要根据实际情况配置vLLM显存使用量,默认使用显存50%是不合理的,具体操作方式是:在Compose文件所在目录创建 vllm_config.yaml:
yaml
gpu-memory-utilization: 0.18
在 compose.yaml 的 paddleocr-vlm-server 服务中,加入 volumes 和 command。保留其原有的配置:
yaml
paddleocr-vlm-server:
image: ccr-2vdh3abv-pub.cnc.bj.baidubce.com/paddlepaddle/paddleocr-genai-${VLM_BACKEND}-server:${VLM_IMAGE_TAG_SUFFIX}
container_name: paddleocr-vlm-server
volumes:
- ./vllm_config.yaml:/home/paddleocr/vlm_server_config.yaml:ro
command:
- paddleocr
- genai_server
- --model_name
- PaddleOCR-VL-1.6-0.9B
- --host
- 0.0.0.0
- --port
- "8080"
- --backend
- vllm
- --backend_config
- /home/paddleocr/vlm_server_config.yaml
启动服务
bash
cd /root/paddleocr-sm120
docker compose up -d
docker compose ps
bash
# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
paddleocr-vl-api ccr-2vdh3abv-pub.cnc.bj.baidubce.com/paddlepaddle/paddleocr-vl:latest-nvidia-gpu-sm120-offline "/bin/bash -c 'paddl..." paddleocr-vl-api 17 hours ago Up 17 hours (healthy) 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp
paddleocr-vlm-server ccr-2vdh3abv-pub.cnc.bj.baidubce.com/paddlepaddle/paddleocr-genai-vllm-server:latest-nvidia-gpu-sm120-offline "paddleocr genai_ser..." paddleocr-vlm-server 17 hours ago Up 17 hours (healthy) 8080/tcp
检查日志:
bash
docker compose logs -f paddleocr-vlm-server
docker compose logs -f paddleocr-vl-api
成功后,paddleocr-vl-api 会出现:
text
Uvicorn running on http://0.0.0.0:8080
服务验证
paddleocr-vl-api的健康检查接口如下,应返回 HTTP 200。
bash
curl -i http://<server-ip>:8080/health
验证底层 vLLM 容器:
bash
docker exec paddleocr-vlm-server curl -i http://127.0.0.1:8080/health
OCR API使用
Docs信息
访问http://IP:8080/docs查看接口文档信息 
| 接口 | 作用 | 何时调用 | ||
|---|---|---|---|---|
GET /health |
存活检测。 | 确认 API 容器进程正在运行。 Docker 健康检查、负载均衡探针、部署验证。 | ||
POST /layout-parsing |
核心 OCR 接口。 | 对图片/PDF 做版面检测、阅读顺序分析、文字/表格/公式识别,并返回 Markdown | 与结构化结果。 | 每次提交新的票据、图片或 PDF 时调用。 |
POST /restructure-pages |
对已完成的多页解析结果做跨页重组,例如合并跨页表格、重建多级标题、拼接多页 Markdown。 | 多页 PDF 的第二步。单张票据通常不需要。 |
API调用
这里调用/layout-parsing接口,该接口是完整的 PaddleOCR-VL 流水线,不采用 OpenAI 的 messages 请求格式。
file 可以是服务端能够访问的文件 URL,或图片、PDF内容的Base64字符串。
bash
curl -sS -X POST "http://<server-ip>:8080/layout-parsing" \
-H "Content-Type: application/json" \
-d '{
"file": "https://paddle-model-ecology.bj.bcebos.com/paddlex/imgs/demo_image/paddleocr_vl_demo.png",
"fileType": 1
}'
fileType 的取值:
| 值 | 含义 |
|---|---|
1 |
图片或 TIFF |
0 |
Python调用
python
import base64
from pathlib import Path
import requests
file_path = Path("./demo.jpg")
# 对本地图像进行Base64编码
with open(file_path, "rb") as file:
image_bytes = file.read()
image_data = base64.b64encode(image_bytes).decode("ascii")
payload = {
"file": image_data,
"fileType": 1,
"visualize": False,
"returnMarkdownImages": False
}
response = requests.post(
"http://IP:8080/layout-parsing",
json=payload,
timeout=600,
)
response.raise_for_status()
page = response.json()
Path("result.md").write_text(str(page), encoding="utf-8")
响应结构
成功响应的顶层字段:
json
{
"logId": "...",
"errorCode": 0,
"errorMsg": "Success",
"result": {
"layoutParsingResults": [],
"dataInfo": {}
}
}
常用结果字段:
| 字段 | 说明 |
|---|---|
markdown.text |
可直接保存为 .md 的文本结果 |
prunedResult |
结构化识别结果 |
outputImages |
可视化/中间图片,默认可能为 Base64 |
inputImage |
输入图片,默认可能为 Base64 |
markdown.images |
Markdown 引用图片的 Base64 映射 |
exports.docx.content |
请求 DOCX 导出时的 Base64 文件内容 |
visualize: false 和 returnMarkdownImages: false 可以减少图片 Base64 数据。部分镜像版本仍可能返回图片字段,因此客户端应优先读取 markdown.text 和 prunedResult。
常用请求参数
仅发送实际需要的参数。没有特殊需求时,保留默认值通常更稳定。
| 参数 | 类型 | 作用 | 使用建议 |
|---|---|---|---|
file |
string | 必填,输入 URL 或 Base64 | 必填 |
fileType |
integer | 0 PDF,1 图片/TIFF |
Base64 输入时建议显式提供 |
visualize |
boolean | 返回可视化和中间图片 | 日常调用设为 false |
returnMarkdownImages |
boolean | 返回 Markdown 图片数据 | 无需图片时设为 false |
useDocOrientationClassify |
boolean | 文档方向分类 | 扫描件方向不确定时启用 |
useDocUnwarping |
boolean | 文档去弯曲/矫正 | 拍照、变形文档时启用 |
useLayoutDetection |
boolean | 版面检测与阅读顺序 | 多栏、表格、公式文档保持 true |
useChartRecognition |
boolean | 图表解析 | 需要识别图表时启用 |
useSealRecognition |
boolean | 印章识别 | 需要印章内容时启用 |
useOcrForImageBlock |
boolean | 对图片块内部文字 OCR | 图文混排且图片内有文字时启用 |
layoutThreshold |
number/object | 版面检测阈值 | 仅在漏检或误检时调优 |
layoutNms |
boolean | 版面框 NMS | 一般使用默认值 |
layoutUnclipRatio |
number/array/object | 版面框扩展比例 | 一般使用默认值 |
layoutShapeMode |
string | 版面区域几何类型 | 可选 rect、quad、poly、auto |
temperature |
number | VLM 采样温度 | 文档解析建议 0 |
topP |
number | VLM top-p 采样参数 | 一般使用默认值 |
repetitionPenalty |
number | 重复惩罚 | 输出重复时调整 |
minPixels |
integer | VLM 图片最小像素数 | 一般使用默认值 |
maxPixels |
integer | VLM 图片最大像素数 | 超大图片显存紧张时调低 |
maxNewTokens |
integer | 最大输出 token 数 | 长文档输出被截断时调高 |
prettifyMarkdown |
boolean | 美化 Markdown | 默认可保持 true |
showFormulaNumber |
boolean | 在 Markdown 中保留公式编号 | 按需开启 |
restructurePages |
boolean | 多页 PDF 结果重组 | 多页 PDF 建议开启 |
mergeTables |
boolean | 跨页表格合并 | 仅 restructurePages: true 时生效 |
relevelTitles |
boolean | 重建多级标题 | 仅 restructurePages: true 时生效 |
outputFormats |
array | 附加导出格式 | 当前可使用 ["docx"] |
推荐图片请求
json
{
"file": "https://example.com/document.png",
"fileType": 1,
"visualize": false,
"returnMarkdownImages": false,
"useLayoutDetection": true,
"temperature": 3
}
推荐 PDF 请求
json
{
"file": "https://example.com/document.pdf",
"fileType": 0,
"visualize": false,
"returnMarkdownImages": false,
"restructurePages": true,
"mergeTables": true,
"relevelTitles": true,
"temperature": 3
}
OpenAI兼容的底层vLLM接口
完整OCR服务没有官方的 /v1/chat/completions 接口。若只需要访问底层 VLM,可在 paddleocr-vlm-server 中增加端口映射:
yaml
ports:
- "127.0.0.1:8118:8080"
验证:
bash
curl http://127.0.0.1:8118/v1/models
CURL调用
bash
curl http://IP:8118/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "PaddleOCR-VL-1.6-0.9B",
"messages": [
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://paddle-model-ecology.bj.bcebos.com/paddlex/imgs/demo_image/paddleocr_vl_demo.png"
}
},
{
"type": "text",
"text": "OCR:"
}
]
}
],
"temperature": 0.0,
"max_tokens": 16000
}'
此接口可供 OpenAI SDK 使用,但它只执行 VLM 推理,不包含完整的版面检测、裁切、阅读顺序和结果整合。复杂页面、表格和多栏文档应优先调用 /layout-parsing。
Python调用
具体查阅 PaddleOCR-VL-1.6 on Hugging Face
bash
import base64
import mimetypes
from pathlib import Path
from openai import OpenAI
# 将本地图片转换为 OpenAI 多模态接口支持的 Data URL
def local_image_to_data_url(image_path: str) -> str:
path = Path(image_path)
if not path.is_file():
raise FileNotFoundError(f"图片文件不存在: {image_path}")
# 根据图片扩展名推断 MIME 类型,例如 image/png、image/jpeg
mime_type, _ = mimetypes.guess_type(str(path))
if not mime_type or not mime_type.startswith("image/"):
raise ValueError(f"不支持的图片格式: {path.suffix}")
# 读取图片并编码为 Base64
image_base64 = base64.b64encode(path.read_bytes()).decode("utf-8")
# 拼接为 data URL,发送给 OpenAI 兼容接口
return f"data:{mime_type};base64,{image_base64}"
# 当前 vLLM 服务地址。
# 如果通过 Docker 映射为宿主机 8118:8080,应使用:
VLLM_BASE_URL = "http://127.0.0.1:8118/v1"
# 从 /v1/models 返回结果中确认实际模型名。
# SM120 官方镜像通常使用这个模型名。
MODEL_NAME = "PaddleOCR-VL-1.6-0.9B"
IMAGE_PATH = "Screenshot 2025-11-13 100012.png"
# 初始化 OpenAI 兼容客户端
client = OpenAI(
api_key="EMPTY", # 本地 vLLM 通常不校验 API Key
base_url=VLLM_BASE_URL,
timeout=3600,
)
# 根据任务选择提示词
TASKS = {
"ocr": "OCR:",
"table": "Table Recognition:",
"formula": "Formula Recognition:",
"chart": "Chart Recognition:",
}
task_type = "ocr"
data_url = local_image_to_data_url(IMAGE_PATH)
# 构造多模态消息:
# 一部分是图片,一部分是任务提示词
messages = [
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": data_url,
},
},
{
"type": "text",
"text": TASKS[task_type],
},
],
}
]
# stream=True 表示流式返回,需要逐块读取结果
stream = client.chat.completions.create(
model=MODEL_NAME,
messages=messages,
temperature=0,
stream=True,
)
# 输出模型返回的文本
print("OCR 结果:")
for chunk in stream:
if not chunk.choices:
continue
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
print()