在 NVIDIA DGX Spark 上让 RAGFlow DeepDoc 真正使用 GPU:ONNX Runtime aarch64/CUDA 13 源码编译实录
在 NVIDIA DGX Spark 上让 RAGFlow DeepDoc 真正使用 GPU:ONNX Runtime aarch64/CUDA 13 源码编译实录
本文以技术爱好者的视角,记录一次在 NVIDIA DGX Spark 上运行 RAGFlow 0.26.4 源码,并让 DeepDoc OCR 真正使用 GPU 的完整实验。
文章目录
- 在 NVIDIA DGX Spark 上让 RAGFlow DeepDoc 真正使用 GPU:ONNX Runtime aarch64/CUDA 13 源码编译实录
-
- 摘要
- 1. 为什么要做这次实验
- 2. 实验环境
- 3. 先理解 RAGFlow 文档解析进程
- 4. 为什么 RAGFlow 在 aarch64 上安装了 CPU ORT
- 5. 为什么单独建立编译环境
- 6. 创建 ONNX Runtime 编译环境
- 7. 下载 ONNX Runtime 源码
- 8. 六次构建尝试概览
- 9. 踩坑一:外部依赖下载失败
- 10. 踩坑二:CUDA 13 头文件警告被当作错误
- 11. 踩坑三:wheel 生成成功,但 CUDA Provider 缺少符号
- 12. 从 ORT CMake 源码定位根因
- 13. 踩坑四:关闭 contrib ops 不是正确方案
- 14. 踩坑五:只使用 121-virtual 仍然失败
- 15. 最终解法:75;121;121-virtual
- 16. 最终成功的完整编译命令
- 17. 长期保存 wheel
- 18. 安装到 RAGFlow 项目环境
- 19. 准备 DeepDoc 模型
- 20. RAGFlow 源码适配:优先检测 ORT CUDA Provider
- 21. 防止 uv sync 再次安装 CPU ORT
- 22. PyCharm 中运行 RAGFlow 后端
- 23. 推荐启动顺序
- 24. 五层验证法
- 25. 哪些 CPU 日志不代表 OCR 回退
- 26. Task Executor 的 libodbc.so.2 错误
- 27. 为什么 GPU 利用率很高,文档仍然可能解析得慢
- 28. 常见故障快速判断
- 29. 日常运行清单
- 30. 什么时候需要重新编译
- 31. 本次实验得到的几个认识
- 32. 结语
- 参考项目
摘要
RAGFlow 的 DeepDoc 文档解析依赖 ONNX Runtime 执行 OCR 模型。在常见的 Linux x86_64 环境中,可以直接安装官方 onnxruntime-gpu wheel;但 NVIDIA DGX Spark 使用 ARM64(aarch64)架构,并搭载计算能力 12.1 的 GB10 GPU。在 RAGFlow 0.26.4 的原始依赖条件下,aarch64 会被解析为 CPU 版 onnxruntime,即使设置了 DEVICE=gpu,DeepDoc OCR 仍然只能在 CPU 上运行。
本次实验采用 ONNX Runtime 1.27.1、CUDA 13.0、cuDNN 9.13 和 Python 3.13,从源码构建 Linux aarch64 GPU wheel。构建过程中先后遇到依赖下载中断、CUDA 13 头文件警告被当作错误、wheel 能生成但 CUDA Provider 缺少 MoE 符号、关闭 contrib ops 后链接失败,以及 121-virtual 被错误归一化等问题。
最终通过组合使用:
CMAKE_CUDA_ARCHITECTURES=75;121;121-virtual
成功生成可运行的 aarch64 CUDA wheel。实际验证结果为:
ONNX Runtime: 1.27.1
Device: GPU
Providers: ['CUDAExecutionProvider', 'CPUExecutionProvider']
DeepDoc 的文本检测和文本识别模型均成功创建 CUDA Session,并完成真实 OCR 推理。
关键词: RAGFlow、DGX Spark、GB10、DeepDoc、ONNX Runtime、CUDA 13、aarch64、OCR、PyCharm
1. 为什么要做这次实验
我最初遇到的现象并不复杂:RAGFlow 前端、API Server 和 Task Executor 都能启动,文档也能进入解析队列,但解析速度并不理想。Task Executor 的环境变量已经设置为:
DEVICE=gpu
然而启动日志仍然显示 OCR 使用 CPU。
这很容易让人把问题归因于 RAGFlow 的 GPU 开关、PyCharm 环境变量,或者 CUDA 驱动。但进一步检查实际 Python 解释器后,我发现真正的问题是 ONNX Runtime 包本身:
ONNX Runtime: 1.23.2
Device: CPU
Providers: ['AzureExecutionProvider', 'CPUExecutionProvider']
也就是说,当前环境里安装的是 CPU 版 onnxruntime。DEVICE=gpu 只能表达“希望使用 GPU”,不能凭空给 CPU wheel 增加 CUDAExecutionProvider。
这次实验的目标因此被明确为:
- 为 Linux aarch64、Python 3.13、CUDA 13 构建 ONNX Runtime GPU wheel。
- 让 RAGFlow DeepDoc 的
det.onnx和rec.onnx使用 CUDA Provider。 - 防止后续执行
uv sync时又被 CPU ORT 覆盖。 - 让 API Server 和 Task Executor 能在 PyCharm 中稳定组合运行。
- 使用真实 OCR 推理验证,而不是只看安装命令是否成功。
2. 实验环境
本次实验环境如下:
| 项目 | 版本或规格 |
|---|---|
| 设备 | NVIDIA DGX Spark |
| GPU | NVIDIA GB10 |
| CPU/GPU 架构 | ARM64 / aarch64 |
| GPU 计算能力 | 12.1,即 sm_121 |
| 操作系统 | Ubuntu 24.04 LTS |
| NVIDIA Driver | 580.159.03 |
| CUDA | 13.0 |
| cuDNN | 9.13 |
| Python | 3.13 |
| RAGFlow | 0.26.4 源码 |
| ONNX Runtime | 1.27.1 源码 |
| IDE | PyCharm aarch64 版本 |
开始前先检查机器基线:
uname -m
nvidia-smi
nvcc --version
预期至少确认:
uname -m -> aarch64
GPU -> NVIDIA GB10
CUDA -> 13.0
DGX Spark 采用 CPU/GPU 统一内存架构。部分版本的 nvidia-smi 不会像传统独立显卡那样显示完整的显存总量和进程显存统计,这不等于 GPU 不可用。
3. 先理解 RAGFlow 文档解析进程
源码运行 RAGFlow 时,至少要区分下面三部分:
| 组件 | 作用 |
|---|---|
api/ragflow_server.py |
提供后端 API 和页面请求服务 |
rag/svr/task_executor.py |
从队列领取并执行文档解析任务 |
web |
提供前端页面,不执行 OCR |
只启动 API Server 时,文件可以上传,也可以进入任务队列,但没有 Task Executor 消费任务,文档会长期停在等待状态。
DeepDoc OCR 真正运行在 Task Executor 进程里。因此,排查 GPU 时必须检查 Task Executor 使用的:
- Python 解释器;
DEVICE环境变量;- ONNX Runtime 包;
- CUDA/cuDNN 动态库路径;
- DeepDoc 模型文件。
不能只检查 API Server,也不能只看前端页面。
4. 为什么 RAGFlow 在 aarch64 上安装了 CPU ORT
RAGFlow 0.26.4 的原始依赖条件大致如下:
"onnxruntime==1.23.2; sys_platform == 'darwin' or platform_machine != 'x86_64'",
"onnxruntime-gpu==1.23.2; sys_platform != 'darwin' and platform_machine == 'x86_64'",
这两条 marker 的含义是:
- macOS 或非 x86_64 平台安装 CPU ORT;
- 非 macOS 的 x86_64 平台安装 GPU ORT。
DGX Spark 是 aarch64,因此会命中第一条规则:
platform_machine != 'x86_64'
最终安装:
onnxruntime==1.23.2
这不是 RAGFlow “识别不到显卡”,而是依赖解析阶段就选择了 CPU 包。
在继续之前,可以使用项目实际解释器检查:
python -c 'import sys, onnxruntime as ort; print(sys.executable); print(ort.__version__); print(ort.get_device()); print(ort.get_available_providers())'
如果输出中没有:
CUDAExecutionProvider
那么继续修改 DEVICE、任务并发数或显存参数都没有意义,首先必须解决 ORT GPU wheel。
5. 为什么单独建立编译环境
我没有直接在 RAGFlow 项目的 .venv 中编译 ONNX Runtime,而是另外创建了一个 Conda 环境。
这样做有三个好处:
- CUDA Toolkit、cuDNN、编译器和构建工具集中在一个环境中。
- ORT 编译过程不会污染 RAGFlow 的业务依赖。
- 编译完成后只需要把 wheel 安装到项目
.venv。
需要注意,wheel 是否可用主要取决于:
- 操作系统和 CPU 架构;
- Python ABI;
- CUDA/cuDNN 版本;
- GPU 架构;
- 编译时启用的 Execution Provider。
Conda 环境只是承载工具链,不是硬件能力的来源。
6. 创建 ONNX Runtime 编译环境
创建独立环境:
conda create -n ragflow-ort-gpu python=3.13 -y
conda activate ragflow-ort-gpu
安装 CUDA Toolkit 和 cuDNN:
conda install -y -c nvidia \
cuda-toolkit=13.0.0 \
cudnn=9.13.0.50
安装构建工具:
python -m pip install -U \
cmake \
ninja \
numpy \
packaging \
protobuf \
setuptools \
wheel
本次成功环境中的关键版本为:
Python: 3.13.14
CMake: 4.4.0
Ninja: 1.13.0
CUDA nvcc: 13.0.48
cuDNN: 9.13.0.50
GCC/G++: 14.3.0
NumPy: 2.5.1
检查工具链:
python --version
cmake --version
ninja --version
nvcc --version
gcc --version
g++ --version
如果环境中没有可用的 C/C++ 编译器,应先通过 Conda 或系统包管理器补齐,再开始 ORT 构建。
7. 下载 ONNX Runtime 源码
下载官方 ONNX Runtime 1.27.1 源码,并初始化全部子模块:
git clone --recursive --branch v1.27.1 \
https://github.com/microsoft/onnxruntime.git \
onnxruntime-v1.27.1
cd onnxruntime-v1.27.1
git submodule update --init --recursive
git rev-parse HEAD
本次实验使用的官方提交为:
df2ba1cf8108aa63627cf4cdf8f807880b938616
建议把源码、构建日志和最终 wheel 分开管理:
- 源码可以随时重新克隆;
- 构建日志用于追踪参数和错误;
- 成功 wheel 应复制到长期稳定的产物目录;
- 产物目录中同时保存版本说明和 SHA-256。
文章不限定具体磁盘路径,只要这个位置不会被临时清理即可。
8. 六次构建尝试概览
这次编译并不是一次成功。完整过程大致如下:
| 次数 | CUDA 架构或方案 | 结果 | 主要问题 |
|---|---|---|---|
| 1 | 121 |
CMake 配置失败 | 下载 Abseil 时 GitHub HTTP/2/TLS 中断 |
| 2 | 121 |
CUDA 编译失败 | CUDA 13 头文件警告被 -Werror 当作错误 |
| 3 | 121 + 关闭警告即错误 |
wheel 生成,但运行失败 | CUDA Provider 缺少 MoE 符号 |
| 4 | 121 + --disable_contrib_ops |
链接失败 | GetFusedActivationAttr 未定义 |
| 5 | 121-virtual |
CMake 配置失败 | 被归一化成非法架构字符串 |
| 6 | 75;121;121-virtual |
成功 | CUDA Provider 和真实 DeepDoc OCR 均通过 |
下面逐项分析。
9. 踩坑一:外部依赖下载失败
第一次配置时,CMake 需要下载 Abseil 等第三方依赖。日志中出现:
status_code: 56
Failure when receiving data from the peer
OpenSSL SSL_read ... unexpected eof while reading
Failed receiving HTTP2 data
这类错误发生在依赖下载阶段,与 CUDA 架构设置没有直接关系。
正确处理方式是:
- 保留已经下载的依赖和构建缓存;
- 检查 GitHub 网络连接或代理;
- 网络恢复后重新执行相同构建命令;
- 不要因为下载失败随意修改 CUDA 架构参数。
判断原则很简单:如果还没有进入 C/C++ 或 CUDA 编译,就不要先怀疑 sm_121。
10. 踩坑二:CUDA 13 头文件警告被当作错误
第二次构建已经进入 CUDA 编译,但在 CUDA 13 自带头文件中失败:
cuda_fp4.hpp: error: unused parameter 'fp4_interpretation'
[-Werror=unused-parameter]
这里真正终止构建的不是语法错误,而是:
-Werror
它把第三方 CUDA 头文件中的警告升级成了错误。
解决方法是在 ORT 构建参数中增加:
--compile_no_warning_as_error
这个参数只是不再把警告当作错误,不会屏蔽真正的编译失败。
11. 踩坑三:wheel 生成成功,但 CUDA Provider 缺少符号
加入 --compile_no_warning_as_error 后,纯 sm_121 构建成功生成了 wheel。
这时最容易产生一个误判:
wheel 文件已经生成,所以 GPU 版本已经编译成功。
但实际安装并加载 CUDA Provider 时,出现了与下面符号相关的错误:
MoeGemmRunner ... getConfigs
也就是说:
- wheel 打包成功;
- Python 甚至可能能够
import onnxruntime; - 但
libonnxruntime_providers_cuda.so内部仍缺少实现; - 创建 CUDA Session 时才真正暴露问题。
这一步让我确认,验证 ORT GPU 不能只看三个东西:
pip install 成功
import onnxruntime 成功
wheel 文件存在
必须继续创建 CUDA Provider Session,并运行真实模型。
12. 从 ORT CMake 源码定位根因
继续检查 ONNX Runtime 1.27.1 的构建逻辑,关键文件包括:
cmake/onnxruntime_cuda_source_filters.cmake
cmake/onnxruntime_providers_cuda.cmake
cmake/external/cuda_configuration.cmake
其中,ORT 对 contrib_ops/cuda/llm/ 下的 LLM/MoE CUDA 源码建立了独立对象库。源码注释明确说明:
The LLM directory contains kernels with minimum SM75 support.
同时使用了类似下面的过滤条件:
onnxruntime_filter_cuda_archs(
_ort_llm_cuda_architectures
MIN_SM 75
EXCLUDE_SM120_REAL
)
过滤逻辑的关键部分是:
if(_FCA_EXCLUDE_SM120_REAL
AND _arch_num GREATER_EQUAL 120
AND _arch MATCHES "-real$")
continue()
endif()
纯粹设置:
CMAKE_CUDA_ARCHITECTURES=121
会被 ORT 归一化成:
121-real
对于通用 LLM/MoE 对象库,121-real 又会被 EXCLUDE_SM120_REAL 排除。最终这个对象库没有可用架构,相关实现没有完整编入 CUDA Provider,而其他位置仍可能引用这些模板符号。
这就解释了为什么纯 sm_121 wheel 能生成,却在加载 Provider 时出现 MoE 未定义符号。
13. 踩坑四:关闭 contrib ops 不是正确方案
为了绕开 MoE/LLM 相关符号,我曾尝试:
--disable_contrib_ops
结果在链接 libonnxruntime.so 时出现:
undefined reference to
onnxruntime::GetFusedActivationAttr(...)
这说明简单关闭 contrib ops 会破坏当前构建目标的依赖闭合。即使通过进一步删减让它勉强链接,也会损失大量算子支持,不适合作为 RAGFlow 的通用 ORT 运行库。
因此,这条路线被放弃。
14. 踩坑五:只使用 121-virtual 仍然失败
既然 ORT 的注释说明 SM120+ 的 real 架构会被排除,而 virtual/PTX 会被保留,一个自然想法是只使用:
CMAKE_CUDA_ARCHITECTURES=121-virtual
但在本次 ORT 1.27.1 构建中,它又被归一化成了类似:
121-virtual-real;121-virtual
其中 121-virtual-real 不是合法的 CMake CUDA 架构字符串,因此配置阶段直接失败。
所以,单独使用 121-virtual 也没有解决问题。
15. 最终解法:75;121;121-virtual
最终使用的架构组合为:
CMAKE_CUDA_ARCHITECTURES=75;121;121-virtual
ORT 归一化后的结果是:
75-real;121-real;121-virtual
三个值分别承担不同角色:
15.1 75-real
使通用 LLM/MoE 对象库至少拥有一个满足 MIN_SM 75、且不会被 EXCLUDE_SM120_REAL 排除的 real 架构,从而把缺失实现链接进 Provider。
15.2 121-real
为 GB10 生成原生 sm_121 代码。
15.3 121-virtual
保留 SM121 的 PTX,使相关路径可以在 GB10 上通过驱动 JIT 编译。
这里的 75 并不意味着 GB10 只能按 SM75 运行。最终 wheel 同时包含 SM121 原生代码和 PTX。75-real 的主要作用,是满足 ORT 1.27.1 当前 LLM/MoE 对象库的架构过滤逻辑。
这是一种针对当前 ORT 源码实现的工程性解决方案。升级 ONNX Runtime 后,应重新检查相关 CMake 逻辑,而不是永久照搬这个组合。
16. 最终成功的完整编译命令
首先进入前面创建的 Conda 环境:
conda activate ragflow-ort-gpu
定义几个通用变量。请自行替换,不要直接保留尖括号:
export ORT_SRC="<ONNX Runtime 1.27.1 源码目录>"
export BUILD_LOG_DIR="<构建日志长期保存目录>"
配置 CUDA/cuDNN 环境:
export CUDA_HOME="$CONDA_PREFIX"
export CUDNN_HOME="$CONDA_PREFIX"
export CUDAToolkit_ROOT="$CONDA_PREFIX"
export PATH="$CONDA_PREFIX/bin:$PATH"
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$CONDA_PREFIX/targets/sbsa-linux/lib:${LD_LIBRARY_PATH:-}"
执行构建:
cd "$ORT_SRC"
mkdir -p "$BUILD_LOG_DIR"
./build.sh \
--config Release \
--update \
--build \
--build_wheel \
--skip_tests \
--parallel 5 \
--cmake_generator Ninja \
--compile_no_warning_as_error \
--use_cuda \
--cuda_home "$CONDA_PREFIX" \
--cudnn_home "$CONDA_PREFIX" \
--cuda_version=13.0 \
--build_dir aarch64-conda-linux-gnu \
--cmake_extra_defines \
'CMAKE_CUDA_ARCHITECTURES=75;121;121-virtual' \
"CUDAToolkit_ROOT=$CONDA_PREFIX" \
"CUDA_CUDA_LIBRARY=$CONDA_PREFIX/lib/stubs/libcuda.so" \
2>&1 | tee "$BUILD_LOG_DIR/ort-1.27.1-cuda13-sm75-sm121-vptx.log"
几个注意点:
- 架构列表必须整体加引号,否则 shell 会把分号当作命令分隔符。
--parallel 5是本次实验使用的并行度,可根据内存压力调整。--skip_tests只跳过 ORT 完整单元测试,不能跳过后续 Provider 和真实模型验证。- 构建阶段使用 CUDA Toolkit 中的
libcuda.sostub,运行阶段仍由 NVIDIA 驱动提供真实驱动库。 - 如果 Conda Toolkit 的目录结构不同,应先用
find检查libcuda.so和 cuDNN 库的位置。
成功日志中应出现类似:
Original CMAKE_CUDA_ARCHITECTURES : 75;121;121-virtual
CMAKE_CUDA_ARCHITECTURES: 75-real;121-real;121-virtual
CUDA compiler identification is NVIDIA 13.0.48
Build complete
17. 长期保存 wheel
构建成功后,从构建输出目录找到:
onnxruntime_gpu-1.27.1-cp313-cp313-linux_aarch64.whl
不要让 RAGFlow 的依赖配置长期引用临时 build 目录。建议:
- 创建一个稳定的构建产物目录;
- 按
ORT版本-CUDA版本-GPU架构-Python版本分类; - 将 wheel 复制到该目录;
- 保存 SHA-256;
- 保存对应的构建日志和 ORT Git 提交号。
示例:
export WHEEL_STORE="<长期保存 wheel 的目录>"
mkdir -p "$WHEEL_STORE"
find "$ORT_SRC" -path '*/Release/dist/onnxruntime_gpu-*.whl' -print
cp "<上一步找到的 wheel>" "$WHEEL_STORE/"
sha256sum "$WHEEL_STORE"/onnxruntime_gpu-*.whl
wheel 文件名中的含义:
cp313 -> CPython 3.13
linux_aarch64 -> Linux ARM64
onnxruntime_gpu -> 含 GPU Execution Provider 的发行包
18. 安装到 RAGFlow 项目环境
编译环境与 RAGFlow 运行环境不是同一个环境:
编译环境:Conda ragflow-ort-gpu
运行环境:RAGFlow 项目 .venv
定义运行解释器和 wheel:
export RAGFLOW_HOME="<RAGFlow 源码根目录>"
export RAGFLOW_PYTHON="$RAGFLOW_HOME/.venv/bin/python"
export ORT_WHEEL="<长期保存目录中的 ORT GPU wheel>"
卸载 CPU/GPU ORT 旧包,再安装自编译 wheel:
uv pip uninstall --python "$RAGFLOW_PYTHON" \
onnxruntime \
onnxruntime-gpu
uv pip install --python "$RAGFLOW_PYTHON" \
--reinstall \
--no-deps \
"$ORT_WHEEL"
立即验证:
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$CONDA_PREFIX/targets/sbsa-linux/lib:${LD_LIBRARY_PATH:-}"
"$RAGFLOW_PYTHON" -c \
'import onnxruntime as ort; print(ort.__version__); print(ort.get_device()); print(ort.get_available_providers())'
预期:
1.27.1
GPU
['CUDAExecutionProvider', 'CPUExecutionProvider']
19. 准备 DeepDoc 模型
DeepDoc OCR 需要下载以下模型或资源:
det.onnx
rec.onnx
layout.onnx
tsr.onnx
ocr.res
updown_concat_xgb.model
主要来源:
Hugging Face: InfiniFlow/deepdoc
Hugging Face: InfiniFlow/text_concat_xgb_v1.0
下载完成后,放入 RAGFlow 约定的项目相对目录:
rag/res/deepdoc
可以使用 huggingface_hub 下载:
cd "$RAGFLOW_HOME"
.venv/bin/python - <<'PY'
from huggingface_hub import snapshot_download
snapshot_download(
repo_id="InfiniFlow/deepdoc",
local_dir="rag/res/deepdoc",
)
snapshot_download(
repo_id="InfiniFlow/text_concat_xgb_v1.0",
local_dir="rag/res/deepdoc",
allow_patterns=["updown_concat_xgb.model"],
)
PY
如果缺少 updown_concat_xgb.model,可能出现:
XGBoostError: No such file or directory
rag/res/deepdoc/updown_concat_xgb.model
这个模型用于版面上下文拼接,不是 ONNX OCR 模型,但缺失时仍会中断文档解析。
20. RAGFlow 源码适配:优先检测 ORT CUDA Provider
仅安装 GPU wheel 还不够。RAGFlow 0.26.4 的部分 GPU 检测逻辑依赖 PyTorch,可能在没有 PyTorch 时把 DeepDoc 判断为 CPU。
我的处理原则是:
DEVICE=cpu时明确使用 CPU;DEVICE=gpu时优先检查 ORT 的CUDAExecutionProvider;- ORT CUDA 不可用时,再保留 PyTorch 检测作为后备。
20.1 修改 deepdoc/vision/ocr.py
在 cuda_is_available() 中加入 ORT Provider 检测:
def cuda_is_available():
if os.environ.get("DEVICE", "cpu") == "cpu":
return False
target_id = 0 if device_id is None else device_id
try:
if "CUDAExecutionProvider" in ort.get_available_providers():
return True
except Exception:
pass
try:
pip_install_torch()
import torch
if torch.cuda.is_available() and torch.cuda.device_count() > target_id:
return True
except Exception:
return False
return False
这样,DeepDoc 可以直接基于 ORT CUDA 创建 Session,不必为了判断显卡而额外安装完整 PyTorch。
20.2 修改 common/settings.py
Task Executor 初始化设备数时,也优先检查 ORT:
def check_and_install_torch():
global PARALLEL_DEVICES
if os.environ.get("DEVICE", "cpu") == "cpu":
PARALLEL_DEVICES = 0
return
try:
import onnxruntime as ort
if "CUDAExecutionProvider" in ort.get_available_providers():
PARALLEL_DEVICES = 1
logging.info("found 1 gpu via ONNX Runtime CUDA provider")
return
except Exception:
pass
# 保留项目原有的 PyTorch 检测逻辑作为后备
单 GPU 的 DGX Spark 设置为 1 即可。不要为了提高并发把 PARALLEL_DEVICES 伪造成 2,它表示设备数量,不是任务线程数。
21. 防止 uv sync 再次安装 CPU ORT
手工安装 wheel 后还有一个隐藏问题:再次运行 uv sync 时,uv 会根据 pyproject.toml 和 uv.lock 恢复声明的依赖。
如果项目仍使用原始 marker,dry-run 可能显示:
- onnxruntime-gpu==1.27.1
+ onnxruntime==1.23.2
也就是说,刚刚安装的 GPU wheel 又会被 CPU ORT 覆盖。
21.1 调整平台 marker
可以将依赖拆分为:
"onnxruntime==1.23.2; sys_platform == 'darwin' or (platform_machine != 'x86_64' and (sys_platform != 'linux' or platform_machine != 'aarch64'))",
"onnxruntime-gpu==1.23.2; sys_platform != 'darwin' and platform_machine == 'x86_64'",
"onnxruntime-gpu==1.27.1; sys_platform == 'linux' and platform_machine == 'aarch64'",
含义是:
- Linux aarch64 使用自编译的 1.27.1 GPU wheel;
- Linux x86_64 继续使用项目原有 GPU 版本;
- macOS 和其他非目标平台继续使用 CPU ORT。
21.2 为 uv 配置本地 wheel
在 [tool.uv.sources] 中为 Linux aarch64 指向长期保存的 wheel。
下面只是结构示例,YOUR_WHEEL_STORE 必须替换成真实、长期有效的位置:
[tool.uv.sources]
onnxruntime-gpu = [
{ path = "YOUR_WHEEL_STORE/onnxruntime_gpu-1.27.1-cp313-cp313-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64'" },
]
如果希望避免暴露个人目录,也可以在项目中约定一个不提交 Git 的稳定相对目录,例如:
[tool.uv.sources]
onnxruntime-gpu = [
{ path = "vendor/wheels/onnxruntime_gpu-1.27.1-cp313-cp313-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64'" },
]
此时要保证该文件在每次 uv sync 前已经存在。
21.3 更新锁文件并验证
cd "$RAGFLOW_HOME"
uv lock
uv lock --check --offline
uv sync --locked --offline --dry-run
确认 dry-run 不再出现:
卸载 onnxruntime-gpu 1.27.1
安装 onnxruntime 1.23.2
确认无误后再执行:
uv sync --locked --offline
然后重新检查 Provider。不要把“lock 解析成功”等同于“GPU Provider 仍然存在”。
22. PyCharm 中运行 RAGFlow 后端
为了让文档上传、排队和解析都完整工作,建议建立三个运行配置。
22.1 RAGFlow API Server
Name: RAGFlow API Server
Script: api/ragflow_server.py
Parameters: 留空
Working directory: RAGFlow 源码根目录
Interpreter: 项目 .venv/bin/python
环境变量:
PYTHONPATH=<RAGFlow 源码根目录>
DEVICE=gpu
NLTK_DATA=<NLTK 数据目录>
LITELLM_LOCAL_MODEL_COST_MAP=True
LD_LIBRARY_PATH=<Conda编译环境>/lib:<Conda编译环境>/targets/sbsa-linux/lib
不建议给脚本额外添加 --debug。需要断点时直接使用 PyCharm 的 Debug 按钮,避免 Flask reloader 再创建一套进程。
22.2 RAGFlow Task Executor
Name: RAGFlow Task Executor
Script: rag/svr/task_executor.py
Parameters: -i 0
Working directory: RAGFlow 源码根目录
Interpreter: 项目 .venv/bin/python
环境变量与 API Server 相同。
同一个 -i 0 不要启动两份。旧 worker 没有停止时又启动一个同编号 worker,会使日志和任务归属变得难以判断。
22.3 Compound 配置
创建 Compound:
Name: RAGFlow Backend All
包含: RAGFlow API Server
包含: RAGFlow Task Executor
以后先启动数据库、Redis、对象存储和搜索引擎,再运行 RAGFlow Backend All。
23. 推荐启动顺序
23.1 启动基础服务
cd "$RAGFLOW_HOME"
docker compose -f docker/docker-compose-base.yml up -d
docker compose -f docker/docker-compose-base.yml ps
确认以下服务正常:
MySQL
Redis
MinIO
Elasticsearch 或项目选用的文档引擎
23.2 启动后端
在 PyCharm 中运行:
RAGFlow Backend All
23.3 启动前端
cd "$RAGFLOW_HOME/web"
npm run dev
24. 五层验证法
我把验证分成五层。任何一层失败,都不要直接进入下一层。
24.1 第一层:包和 Provider
"$RAGFLOW_PYTHON" -c \
'import onnxruntime as ort; print(ort.__version__); print(ort.get_device()); print(ort.get_available_providers())'
目标:
1.27.1
GPU
['CUDAExecutionProvider', 'CPUExecutionProvider']
24.2 第二层:动态库
定位 ORT C API 目录:
ORT_CAPI_DIR=$("$RAGFLOW_PYTHON" -c \
'import pathlib, onnxruntime as ort; print(pathlib.Path(ort.__file__).parent / "capi")')
ldd "$ORT_CAPI_DIR/libonnxruntime_providers_cuda.so" | grep 'not found'
正常情况下,最后一条命令没有输出。
如果出现缺失库,优先检查:
LD_LIBRARY_PATH;- CUDA Toolkit 运行库;
- cuDNN;
- wheel 的 Python/架构 ABI;
- NVIDIA 驱动。
24.3 第三层:DeepDoc Session
在 RAGFlow 根目录执行:
DEVICE=gpu "$RAGFLOW_PYTHON" - <<'PY'
from deepdoc.vision.ocr import load_model
model_dir = "rag/res/deepdoc"
for name in ("det", "rec"):
session, _ = load_model(model_dir, name, 0)
print(name, session.get_providers())
PY
期望:
det ['CUDAExecutionProvider', 'CPUExecutionProvider']
rec ['CUDAExecutionProvider', 'CPUExecutionProvider']
24.4 第四层:真实 OCR
准备一张带有清晰文字的测试图片,然后执行:
export OCR_TEST_IMAGE="<OCR 测试图片>"
DEVICE=gpu "$RAGFLOW_PYTHON" - <<'PY'
import os
import cv2
from common import settings
from deepdoc.vision.ocr import OCR
settings.PARALLEL_DEVICES = 1
image = cv2.imread(os.environ["OCR_TEST_IMAGE"])
if image is None:
raise RuntimeError("无法读取 OCR_TEST_IMAGE")
ocr = OCR()
result = ocr(image)
print(result)
PY
本次实验使用包含 RAGFLOW GPU OCR 的图片,识别结果为:
RAGFLOW GPU OCR
confidence: 0.9708765745162964
真实推理能够验证:
- 模型可以加载;
- CUDA kernel 可以执行;
- 文本检测和识别链路完整;
- 不是只注册了一个不可用的 Provider 名称。
24.5 第五层:uv sync 后复测
执行一次真实或 dry-run 同步后,再次重复前四层检查。
尤其要确认:
onnxruntime-gpu 仍为 1.27.1
CUDAExecutionProvider 仍在
det/rec Session 仍以 CUDA 为第一 Provider
25. 哪些 CPU 日志不代表 OCR 回退
GPU OCR 正常时,日志中仍可能出现 CPU 字样。
25.1 updown_cnt_mdl initialized on CPU
这是 XGBoost 排版拼接模型,不是 det.onnx 或 rec.onnx。它在 CPU 上运行是正常设计。
25.2 Some nodes were not assigned
ONNX Runtime 可能把 shape 等轻量算子分配到 CPU。这是 Provider 的正常图优化,不代表整个模型回退到 CPU。
25.3 /sys/class/drm/card0 警告
可能看到:
Failed to detect devices under "/sys/class/drm/card0"
如果 CUDA Provider 可以创建、Session Provider 顺序正确、真实 OCR 已成功,这条设备发现警告通常不影响推理。
25.4 libtinfo.so.6 提示
Conda 动态库路径可能让系统 shell 输出:
libtinfo.so.6: no version information available
如果编译和运行均正常,它通常只是 Conda 与系统库版本信息不一致的提示。
判断 GPU OCR 的依据应是:
Provider + Session + 真实推理
而不是日志中是否完全没有 CPU 字样。
26. Task Executor 的 libodbc.so.2 错误
Task Executor 启动时还可能出现:
ImportError: libodbc.so.2: cannot open shared object file
通常是 Python 环境中安装了 pyodbc,但操作系统或 Conda 环境缺少 unixODBC runtime。
它主要影响依赖 ODBC 的 SQL Server 或 Agent SQL 工具,并不等同于 DeepDoc GPU OCR 故障。但启动阶段长期保留 ImportError 并不理想,应补齐依赖。
可以选择系统包管理器:
sudo apt update
sudo apt install -y unixodbc libodbc2
也可以安装到 Conda 环境:
conda install -n ragflow-ort-gpu -c conda-forge unixodbc
如果采用 Conda 方案,PyCharm 中需要保留该环境的 LD_LIBRARY_PATH。
验证:
"$RAGFLOW_PYTHON" -c 'import pyodbc; print(pyodbc.version)'
27. 为什么 GPU 利用率很高,文档仍然可能解析得慢
成功启用 ORT GPU 后,我又遇到了第二个容易误判的问题:
- Task Executor 已占用一部分 GPU 统一内存;
- GPU 利用率很高;
- 继续提交文档,进度却仍然缓慢;
- 增加任务数后,Task Executor 的显存占用没有线性增长。
对解析日志分段计时后发现,某个 13 页样例的 DeepDoc 阶段大致为:
OCR: 6.98 秒
Layout analysis: 3.15 秒
Table analysis: 0 秒
Text merge: 0.14 秒
真正慢的是后续 VLM 图片描述。RAGFlow 把文档图片提交给本地视觉语言模型,而该模型使用了很大的上下文,并且只有单并发 runner,多个图片描述请求因此排队。
这说明“文档解析”并不是一个单一算子,而是一条流水线:
PDF 读取
-> OCR
-> Layout
-> Table
-> 文本合并
-> 图片/VLM 描述
-> Embedding
-> 索引写入
ORT GPU 主要解决 DeepDoc OCR 模型执行问题,不会自动加速外部 VLM 服务、Embedding 服务、数据库写入和网络请求。
27.1 为什么占用约 2GB 后不再增加
多个任务通常共享同一个 Task Executor 进程和已经加载的 ORT 模型。模型权重不会因为任务数量增加而重复加载,所以显存或统一内存占用不一定线性增加。
这不是“程序只被允许使用 2GB”,而是当前模型和 ORT arena 的实际需求。
RAGFlow 中还可能配置类似:
OCR_GPU_MEM_LIMIT_MB=2048
这个值控制 ORT CUDA Provider 的内存 arena 上限,不代表整台 DGX Spark 的 GPU 内存上限。盲目提高它通常不会让小型 OCR 模型变快。
27.2 更有效的优化方向
如果日志证明瓶颈在 VLM,可以考虑:
- 不需要图片描述时关闭默认 VLM;
- 使用更小的视觉语言模型;
- 将超大的上下文窗口降到 8192 或 16384;
- 降低单请求内存后,再测试 VLM 并发 2;
- 分别记录 OCR、Layout、VLM、Embedding 的耗时。
当 GPU 已接近满算力时,单纯增加显存占用并不会提高速度。
28. 常见故障快速判断
28.1 上传文件后完全不动
检查 Task Executor 是否启动:
pgrep -af task_executor.py
没有 worker 时,先启动 Task Executor。
28.2 ORT 只有 CPU Provider
"$RAGFLOW_PYTHON" -c \
'import sys, onnxruntime as ort; print(sys.executable); print(ort.__version__); print(ort.get_available_providers())'
重点检查:
- PyCharm 是否使用项目
.venv; - 是否安装了 CPU
onnxruntime; uv sync是否覆盖了 GPU wheel;- wheel 是否与 CPython 3.13/aarch64 匹配。
28.3 Provider 在列表中,但 Session 创建失败
echo "$LD_LIBRARY_PATH"
ldd "$ORT_CAPI_DIR/libonnxruntime_providers_cuda.so" | grep 'not found'
重点检查 CUDA、cuDNN、驱动和 ABI。
28.4 updown 模型缺失
ls -lh rag/res/deepdoc/updown_concat_xgb.model
缺失时从 InfiniFlow/text_concat_xgb_v1.0 下载。
28.5 uv sync 想删除 GPU ORT
uv sync --locked --offline --dry-run
如果计划删除 onnxruntime-gpu 1.27.1、安装 CPU ORT,应先修复平台 marker、本地 source 和 lock,不要直接执行真实同步。
28.6 OCR 很快,但进度长时间停在中后段
按阶段搜索日志:
grep -E \
'OCR finished|Layout analysis|Visual model|describe_with_prompt|Task done' \
<Task Executor 日志> \
| tail -n 100
如果 OCR finished 很快出现,而 Visual model 后长时间无进展,瓶颈在 VLM,不在 ORT OCR。
29. 日常运行清单
启动前
[ ] 基础服务全部健康
[ ] ORT wheel 的长期保存位置仍然有效
[ ] 项目 .venv 中是 onnxruntime-gpu 1.27.1
[ ] CUDAExecutionProvider 存在
[ ] DeepDoc 模型完整
[ ] PyCharm 的 DEVICE=gpu 和 LD_LIBRARY_PATH 正确
启动顺序
1. MySQL、Redis、MinIO、Elasticsearch
2. RAGFlow API Server
3. RAGFlow Task Executor
4. RAGFlow Web 前端
启动后
[ ] 只有一个 task_executor.py -i 0
[ ] 日志出现 ORT CUDA Provider
[ ] det.onnx 和 rec.onnx 使用 GPU
[ ] 上传一个小 PDF 做端到端验证
30. 什么时候需要重新编译
以下任一条件变化后,应重新编译或至少完整复测:
- Python ABI 从 CPython 3.13 变为其他版本;
- CUDA Toolkit 主版本变化;
- cuDNN 主版本变化;
- ONNX Runtime 版本变化;
- GPU 架构变化;
- 系统从 aarch64 迁移到 x86_64,或反向迁移;
- NVIDIA 驱动对 PTX JIT 的支持发生变化;
- ORT 修复或重构了 SM120+ LLM/MoE 架构过滤逻辑。
重新编译后,建议始终重复:
wheel 安装
-> Provider 检查
-> 动态库检查
-> DeepDoc Session
-> 真实 OCR
-> uv sync 后复测
31. 本次实验得到的几个认识
31.1 环境变量不是运行时能力
DEVICE=gpu 只是配置意图。真正的 GPU 能力来自:
正确的 wheel + CUDA Provider + 动态库 + 驱动 + 可运行模型
31.2 构建成功不等于运行成功
生成 wheel 只是编译链的一个结果。动态链接错误往往要到 Provider 创建或模型推理时才出现。
31.3 平台 marker 是部署的一部分
手工安装一次 GPU wheel 不能解决长期维护问题。pyproject.toml、tool.uv.sources 和 uv.lock 必须表达同一个平台决策。
31.4 要从源码理解架构过滤
75;121;121-virtual 不是随机尝试出来的“神秘参数”,而是由 ORT 1.27.1 中 MIN_SM 75、EXCLUDE_SM120_REAL 和 PTX 保留逻辑共同决定的。
31.5 性能问题要分阶段测量
OCR、Layout、VLM、Embedding 和索引写入属于不同组件。只看 UI 的总进度,无法判断真正的瓶颈。
31.6 统一内存不能套用传统显存直觉
DGX Spark 的统一内存模型与传统独立 GPU 不同。进程没有继续增加“显存占用”,不等于被限制,也不等于没有继续计算。
32. 结语
作为一个喜欢追根究底的技术爱好者,这次实验中最有价值的并不是得到了一条可以复制的编译命令,而是建立了一套排查顺序:
先确认包
再确认 Provider
再确认动态库
再确认 Session
最后做真实推理
如果只停留在 DEVICE=gpu、nvidia-smi 或 wheel 文件是否存在,很容易在错误方向上反复调整参数。
最终,Linux aarch64、CUDA 13 和 GB10 并不是不能运行 RAGFlow DeepDoc GPU OCR。真正的难点在于:官方依赖默认选择 CPU ORT,而 ORT 1.27.1 的 SM120+ LLM/MoE 构建过滤逻辑又让纯 sm_121 架构组合产生了运行时缺失符号。
通过源码定位后,使用:
75-real + 121-real + 121-virtual
同时满足了对象库链接、GB10 原生代码和 PTX JIT 三方面需求。再配合 RAGFlow 的 ORT Provider 检测、uv 平台依赖和 PyCharm 双进程配置,最终实现了可重复启动、可验证、不会被依赖同步轻易覆盖的 GPU OCR 环境。
对于类似的 ARM GPU 软件适配问题,我认为最可靠的方法仍然是:记录版本、保存日志、阅读构建源码、设计分层验证,并把一次成功变成能够重复的工程配置。
参考项目
- RAGFlow:https://github.com/infiniflow/ragflow
- ONNX Runtime:https://github.com/microsoft/onnxruntime
- ONNX Runtime CUDA Execution Provider:https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html
- NVIDIA CUDA Toolkit Documentation:https://docs.nvidia.com/cuda/
- InfiniFlow DeepDoc Models:https://huggingface.co/InfiniFlow/deepdoc
版本提示:本文结论基于 RAGFlow 0.26.4、ONNX Runtime 1.27.1、CUDA 13.0、Python 3.13 和 NVIDIA GB10。升级任一组件后,应重新检查官方依赖、CMake 架构过滤逻辑和真实推理结果。
更多推荐
所有评论(0)