预制服务
共绩算力 MiniCPM-OCR 1.0 使用最佳实践
一、MiniCPM-OCR 是什么
MiniCPM-OCR 1.0 是 OpenBMB 的 1.3B OCR 专用模型,支持中英文文本、表格、公式、印章、图表和关键信息抽取(KIE)。当前镜像使用 vLLM 提供 OpenAI 兼容接口,并配置了配套的 MiniCPM-OCR-1.0-DSpark 草稿模型。
| 使用方式 | 客户端是否需要版面模型 | 输入与场景 | 输出 |
|---|---|---|---|
| WebUI | 否 | 浏览器快速体验、检查连接与图片识别 | 页面展示识别文本 |
| OpenAI API | 否 | 单张票据、卡证、已裁切文本/表格/公式区域、KIE | 文本或待校验的 JSON 文本 |
| minicpm-ocr CLI | 是 | 命令行处理 PDF、复杂排版整页图片 | Markdown 文件 |
| minicpm-ocr Python SDK | 是 | Python 业务系统、任务队列、RAG 入库流水线 | Markdown 与中间产物 |
二、从预制服务启动 MiniCPM-OCR
01. 进入弹性部署服务并新增任务
访问弹性部署服务页面,在左侧点击“新增部署任务”,进入任务创建页面。若页面跳转到登录,先完成账号登录。

图:进入“新增部署任务”页面,依次选择计算资源和预制服务。
02. 选择计算资源
在“选择 GPU 型号”区域筛选地域,优先选择 四川五区、新疆一区、浙江四区。这三个区域已默认完成 MiniCPM-OCR 镜像预热,可减少镜像拉取等待。
卡数从 1 卡开始,先以单节点验证识别效果。可根据实时可用资源选择四川五区 RTX 4090(24 GB)、新疆一区 RTX 5090(32 GB)或浙江四区 RTX 4090-48G(48 GB)。核对显存、CPU、内存、库存与价格后,点击对应资源卡片,再向下滚动到“服务配置”。

图:以四川五区 RTX 4090 单卡为例;资源规格、库存和价格以创建时控制台显示为准。镜像预热后仍需等待资源调度与模型初始化。
03. 选择 MiniCPM-OCR-1.0 预制服务
在“服务配置”中保持“预制服务”标签,找到并点击 MiniCPM-OCR-1.0 卡片,使用平台提供的预制镜像和默认配置。首次使用无需手工填写镜像地址或启动脚本。

图:选中 MiniCPM-OCR-1.0 后,卡片边框高亮。卡片说明中列出了两个服务端口:80 提供 WebUI,8000 提供 OpenAI 兼容 API。
在“节点配置”中先保持 1 个节点,核对计算资源与服务选择;阅读服务协议并确认同意后,点击“部署服务”。
04. 等待实例就绪
部署后返回任务列表或任务详情,观察节点状态与启动日志。镜像拉取、模型加载和运行时初始化完成后,在“常规 → 快捷访问”中获取端口访问地址:
- 80 端口:WebUI,用于浏览器上传图片和查看识别结果。
- 8000 端口:推理 API,OpenAI SDK 的基础地址为该端口访问地址加
/v1;minicpm-ocr CLI / SDK 使用该端口的服务根地址。

图:任务详情中的运行节点与快捷访问入口。已有示例服务可直接打开任务详情,继续下一节体验识别。
节点显示“运行中”后,仍需通过 8000 端口的 /v1/models 确认模型 minicpm-ocr 已加载,再完成一次实际图片识别。WebUI 能打开只说明页面服务可访问。具体连通性检查见第四节;新建任务的访问域名以自己的“快捷访问”为准。
三、快速体验:浏览器完成一次图片识别
打开本次服务80端口的 url。
- 点击“加载示例图”,或通过“选择图片”上传自己的图片。
- 选择识别类型;先从“文本识别”开始。
- 展开“接口设置”,确认 API 地址为下表中的地址、模型名为 minicpm-ocr。本次服务无鉴权,API Key 留空。
- 点击“开始识别”,在右侧读取并复制结果。
| 项目 | 本次服务地址 / 值 |
|---|---|
| WebUI | https://deployment-452-99uowetb-80.550w.link |
| 服务根地址 OCR_SERVER_URL | https://deployment-452-99uowetb-8000.550w.link |
| OpenAI 基础地址 OCR_BASE_URL | https://deployment-452-99uowetb-8000.550w.link/v1 |
| 模型名 OCR_MODEL | minicpm-ocr |

图 3:本次实际识别结果为 HELLO 123,页面报告约 0.9 秒、289 TOKENS。这是示例图片的一次页面观测,不代表复杂 PDF 的解析速度或模型准确率;页面 token 统计也不应直接当作输出 token 数。
四、前置准备与连通性验证
4.1 配置地址、密钥与模型名
# CLI / SDK 使用根地址,不带 /v1
export OCR_SERVER_URL="https://deployment-452-99uowetb-8000.550w.link"
# OpenAI SDK 使用包含 /v1 的基础地址
export OCR_BASE_URL="${OCR_SERVER_URL%/}/v1"
export OCR_API_KEY="EMPTY" # 当前服务无鉴权;开启后替换为真实密钥
export OCR_MODEL="minicpm-ocr"
服务重建或地址变化后,重新从“快捷访问”复制 8000 端口地址。不要将 80 端口地址、完整 /chat/completions 路径或客户端本机的 127.0.0.1 作为远程 SDK 根地址。
4.2 检查模型与上下文上限
curl -fsS --max-time 30 "${OCR_BASE_URL%/}/models" \
-H "Authorization: Bearer ${OCR_API_KEY}"
本次接口实际返回的关键字段如下(省略权限等无关字段):
{
"object": "list",
"data": [{
"id": "minicpm-ocr",
"root": "/models/OpenBMB/MiniCPM-OCR-1.0",
"max_model_len": 16384
}]
}
五、方式一:通过 OpenAI API 识别图片
5.1 安装与调用
python -m pip install openai
准备 PNG 或 JPEG 图片 sample.png,保存下列代码为 ocr_image.py,然后运行 python ocr_image.py。
import base64
import os
from pathlib import Path
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OCR_API_KEY", "EMPTY"),
base_url=os.environ["OCR_BASE_URL"].rstrip("/"),
timeout=180.0,
max_retries=2,
)
def recognize_image(image_path, prompt="Text Recognition:", max_tokens=4096):
path = Path(image_path)
mime = {".png": "image/png", ".jpg": "image/jpeg",
".jpeg": "image/jpeg"}.get(path.suffix.lower())
if mime is None:
raise ValueError("示例仅接受 PNG / JPEG,请先转换图片格式")
image_b64 = base64.b64encode(path.read_bytes()).decode("ascii")
response = client.chat.completions.create(
model=os.environ.get("OCR_MODEL", "minicpm-ocr"),
messages=[{"role": "user", "content": [
{"type": "image_url", "image_url": {
"url": f"data:{mime};base64,{image_b64}"
}},
{"type": "text", "text": prompt},
]}],
temperature=0.01,
top_p=0.001,
max_tokens=max_tokens,
stream=False,
extra_body={
"top_k": 1,
"chat_template_kwargs": {"enable_thinking": False},
"mm_processor_kwargs": {
"downsample_mode": "4x", "max_slice_nums": 9
},
},
)
if not response.choices:
raise RuntimeError("服务没有返回 choices")
choice = response.choices[0]
if choice.finish_reason == "length":
raise RuntimeError("OCR 输出已截断:增加输出预算或缩小识别区域")
if choice.finish_reason != "stop":
raise RuntimeError(f"请检查异常结束状态:{choice.finish_reason}")
text = choice.message.content or ""
if not text.strip():
raise RuntimeError("OCR 结果为空,请检查图片、提示词和服务日志")
return text
if __name__ == "__main__":
print(recognize_image("sample.png"))
图像放在文本提示词之前。mm_processor_kwargs 依赖服务端启用 --trust-request-mm-kwargs;当前启动配置已包含此项。普通接入可以先保持镜像的视觉参数,不必反复修改。
5.2 按任务使用标准 Prompt
| 任务 | Prompt(保留冒号) | 处理建议 |
|---|---|---|
| 文本 | Text Recognition: | 读取识别文本;保留原始换行供后续校验 |
| 表格 | Table Recognition: | 原始表示以模型返回为准;整篇文档建议由 SDK 转成 HTML 表格 |
| 公式 | Formula Recognition: | 检查 LaTeX 的括号、上下标与完整性 |
| 印章 | Seal Recognition: | 检查印章文字与方向 |
| 图表 | Chart to Table: | 核对数值、单位与图例对应关系 |
5.3 小例子:从票据抽取 JSON 字段
沿用上面的 recognize_image(),将提示词替换为字段抽取指令:
import json
keys = ["store_name", "invoice_no", "total_amount", "date"]
kie_prompt = (
'Extract the "store_name", "invoice_no", "total_amount", "date". '
'Format the output as a JSON object with keys: '
'"store_name", "invoice_no", "total_amount", "date". '
'If an item is not in the image, use "###".'
)
raw = recognize_image("invoice.png", prompt=kie_prompt, max_tokens=2048)
data = json.loads(raw) # 无效 JSON 应记录为待复核,不能静默忽略
if not isinstance(data, dict) or set(data) != set(keys):
raise ValueError("字段结构不符合预期")
print(data)
两处字段名必须一致。### 表示图片中缺失的信息,不应当作真实业务值。金额、日期、票号还需校验格式和业务规则;生产系统应保留原图与原始响应以便复核。
六、方式二:通过 CLI 将 PDF 转为 Markdown
6.1 创建独立的客户端环境
SDK 与远程 vLLM 服务使用独立环境。以下版本沿用官方推理指南的可复现安装示例:
pip install uv
uv venv .minicpm-ocr --python 3.12 --seed
source .minicpm-ocr/bin/activate
python -m pip install "minicpm-ocr[all]==1.0.2"
按客户端环境二选一安装 PaddlePaddle,不要在同一环境同时安装 CPU 和 GPU 两个包:
# CPU 版面检测
python -m pip install "paddlepaddle==3.2.1"
# 兼容的 Linux NVIDIA GPU 客户端:GPU 版面检测
python -m pip install "paddlepaddle-gpu==3.3.0" \
-i https://www.paddlepaddle.org.cn/packages/stable/cu129/
GPU wheel、驱动与系统架构应匹配;没有对应 wheel 的开发机可改用兼容的 Linux 客户端。先运行 minicpmvocr parse --help 核对当前版本参数,再准备待解析的 doc.pdf。
6.2 先解析一页,再处理全文
export PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK=True
minicpmvocr parse \
--backend vllm \
--device cpu \
--server-url "${OCR_SERVER_URL}" \
--model "${OCR_MODEL}" \
--workers 4 \
--set engine.api_key="${OCR_API_KEY}" \
--set engine.max_model_len=16384 \
--set input.limit=1 \
--input doc.pdf \
--output doc.md
检查 doc.md 的阅读顺序、表格、公式和缺漏。通过后移除 --set input.limit=1 解析全文。客户端有兼容的 NVIDIA GPU 时,将 --device cpu 改为 --device cuda:0;远程识别仍由共绩节点的 GPU 执行。
初始使用 4 个识别请求并发是保守的调试建议,并非服务端并发上限。稳定后可尝试 8、16、32,比较每分钟完成页数、请求尾延迟、错误率与显存,再选择合适的值。
6.3 固定版面模型路径
首次运行会准备 PP-DocLayoutV3 权重。生产环境建议提前下载并固定目录,避免工作目录变化导致重复下载:
mkdir -p ./ocr-assets
curl -fL -o ./ocr-assets/PP-DocLayoutV3_infer.tar \
https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/PP-DocLayoutV3_infer.tar
tar xf ./ocr-assets/PP-DocLayoutV3_infer.tar -C ./ocr-assets
export OCR_LAYOUT_MODEL_DIR="$(pwd)/ocr-assets/PP-DocLayoutV3_infer"
export PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK=True
后续解析命令可显式追加 --layout-model "${OCR_LAYOUT_MODEL_DIR}"。禁用模型源检查不等于下载好了权重;离线环境必须提前准备完整模型目录。
6.4 常用参数
| 参数 | 用途与建议 |
|---|---|
| —server-url | vLLM 服务根地址,不带 /v1;每次命令显式提供 |
| —model | 显式提供 minicpm-ocr,与模型列表一致 |
| —device | 仅选择本地版面检测设备 |
| —workers | 文档块识别请求总并发,按实测逐步调整 |
| —layout-model | 使用提前准备好的版面模型目录 |
| —set engine.max_model_len=16384 | 与当前服务上下文保持一致 |
| —set pipeline.merge_cross_page_tables=true | 按需开启跨页表格合并;验收表头、列对齐与误合并 |
| —set engine.api_key=… | 服务启用鉴权后传入真实密钥 |
多个实例应使用相同模型名和权重;SDK 支持重复传入 --server-url,其 workers 是多个实例共享的请求总并发。共绩任务本身已经有统一访问地址时,先使用任务地址即可。
七、方式三:通过 Python SDK 集成文档解析
完成第六节的依赖安装后,可保存下列代码为 parse_pdf.py。示例使用 CPU 版面检测、4 个请求并发、16384 上下文,并可按需合并跨页表格。
import os
from importlib.resources import files
from pathlib import Path
from minicpmvocr import (
assemble_document_markdown,
load_config,
load_prompt_map,
pipelined_run,
)
from minicpmvocr.layouts.download import ensure_layout_model
def parse_pdf_to_markdown(pdf_path="doc.pdf", output_path="doc.md"):
layout_path = Path(os.environ.get(
"OCR_LAYOUT_MODEL_DIR", "models/PP-DocLayoutV3_infer"
)).resolve()
layout_model = ensure_layout_model(layout_path)
config_path = files("minicpmvocr").joinpath("configs/minicpmvocr.yaml")
cfg = load_config(str(config_path), overrides={
"input": {"file_path": str(Path(pdf_path).resolve())},
"engine": {
"type": "vllm",
"server_url": os.environ["OCR_SERVER_URL"].rstrip("/"),
"model_name": os.environ.get("OCR_MODEL", "minicpm-ocr"),
"api_key": os.environ.get("OCR_API_KEY", "EMPTY"),
"max_model_len": 16384,
},
"layout": {
"model_dir": str(layout_model),
"device": os.environ.get("OCR_LAYOUT_DEVICE", "cpu"),
},
"pipeline": {
"overlap_layout": True,
"max_workers": 4,
"merge_cross_page_tables": True,
},
})
pipelined_run(cfg)
markdown, page_count, merge_count = assemble_document_markdown(
Path(cfg["_meta"]["run_dir"]),
load_prompt_map(cfg),
merge_tables=cfg["pipeline"]["merge_cross_page_tables"],
delimiter="\n\n<!-- page-break -->\n\n",
)
if not markdown.strip():
raise RuntimeError("解析结果为空,请检查逐页产物和日志")
target = Path(output_path)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(markdown, encoding="utf-8")
print(f"页数={page_count},跨页表格合并={merge_count},输出={target}")
return markdown
if __name__ == "__main__":
os.environ.setdefault("PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK", "True")
parse_pdf_to_markdown()
执行:python parse_pdf.py。首次可先在 input 配置中加入 "limit": 1 调试,验收后删除。
默认中间产物位于 runs/<run_tag>/,包含逐页结果及运行记录;可用 OCR_RUN_ROOT 或 run.run_root 设置持久化目录。CLI 使用 --run-tag my_task_001,SDK 使用 "run": {"run_tag": "my_task_001"} 固定任务目录。已核对 1.0.2 源码:同一页的 output/<image_id>.md 与 results_json/<image_id>.json 均存在时,流水线会跳过该页识别。续跑必须保持输入、配置和模型版本一致;修改输入或希望重新识别时使用新的 run_tag,避免误用旧结果。