可直接把本篇教程链接发给code agent进行全自动化部署
本文记录如何在 Qualcomm X1 板端部署并运行 Qwen3-VL 8B,使用 GenieX + QAIRT 后端完成视觉语言模型推理,并让图像推理实际运行在 HTP/NPU 上。>
这套流程重点解决几个比较容易踩坑的问题:
-
GenieX runner 与板端 glibc 版本不兼容
-
模型文件命名与 GenieX 预期不一致
-
embedding 文件缺失导致模型输出乱码
-
示例图片扩展名与真实格式不一致
-
geniex-bench使用/dev/stdin时可能出现 prompt 短读 -
板端原有 QIRF / ROS 环境中缺少完整的 GenieX 工具
最终使用独立部署目录 + 私有 glibc runtime 的方式完成部署,不修改系统 glibc,也不破坏板端已有 QAIRT / QNN 环境。
最终效果
模型最终部署在:
/home/aidlux/qwen3-vl-8b-geniex-qcs8550
实测验证通过:
-
6 个 LLM shard 全部加载成功
-
Vision Encoder 加载成功
-
图像推理实际运行在 HTP / NPU
-
首 Token 延迟约 0.91 秒
-
解码速度约 7.9 token/s
-
模型能够正常返回可读的图像描述
1. 下载模型以及确认模型 Runtime
进入
通过开发板内置的 MMS 工具获取模型资源及代码包。
检查模型包中的 metadata.json。
需要关注这些字段:
runtime: geniex_qairt
chipset: QCS8550
HTP: v73
precision: w4a16
context length: 4096
supports vision: true
这里最关键的是:
runtime: geniex_qairt
对于这种 Qwen3-VL bundle,不建议直接使用传统的:
genie-t2t-run
来验证视觉能力。
应使用 GenieX:
geniex-bench --plugin qairt --device npu --vlm
在本次部署环境中,板端原有 QIRF / ROS 的 llm_genie 包并不完整,系统 PATH 中也没有可直接使用的 genie-t2t-run 或 geniex-bench。
因此本文采用完全独立的部署目录,不修改已有 systemd 服务,也不覆盖系统 QAIRT。
2. 检查 X1 板端环境
本次使用的板端环境为:
架构:aarch64
系统:Ubuntu 22.04
glibc:2.35
内存:约 14 GiB
Swap:约 7.4 GiB
可以先通过 SSH 检查基本环境:
ssh aidlux 'uname -m; . /etc/os-release; echo "$PRETTY_NAME"; getconf GNU_LIBC_VERSION; df -h /home/aidlux'
重点确认:
-
架构为
aarch64 -
SSH 正常
-
/home/aidlux有足够空间 -
glibc 版本
本次模型约 6.9 GiB,此外还需要存放 GenieX runner 和私有 runtime,因此部署前建议先确认磁盘空间。
注意:
不要为了部署模型去停止已有 systemd / QNN 服务,也不要覆盖系统已有 QAIRT 安装。
3. 下载 GenieX ARM64 Runner
本文使用的 Linux ARM64 runner:
https://qaihub-public-assets.s3.us-west-2.amazonaws.com/qai-hub-geniex/geniex-bench-linux-arm64.tar.gz
下载:
curl -fL --retry 10 --retry-all-errors --retry-delay 2 --max-time 600 \
-o geniex-bench-linux-arm64.tar.gz \
https://qaihub-public-assets.s3.us-west-2.amazonaws.com/qai-hub-geniex/geniex-bench-linux-arm64.tar.gz
不要下载完就直接解压。
先检查文件大小:
stat -c '%s %n' geniex-bench-linux-arm64.tar.gz
检查 gzip:
gzip -t geniex-bench-linux-arm64.tar.gz
再查看 tar 内容:
tar -tzf geniex-bench-linux-arm64.tar.gz | head
如果:
gzip -t geniex-bench-linux-arm64.tar.gz
失败,说明下载文件可能损坏。
本次部署过程中就遇到过归档尾部损坏的问题。
这种情况下不要继续解压或者上传,重新下载到新文件后再次校验。
正常解压后,至少应该看到:
bin/geniex-bench
lib/qairt/libgeniex_plugin.so
lib/qairt/libgeniex-proc-vision.so
lib/qairt/libgeniex_vlm.so
lib/qairt/libgeniex_core.so
4. 解决 glibc 版本不兼容
这是整个部署过程中最关键的兼容问题之一。
X1 板端:
glibc 2.35
而本次使用的官方预编译 GenieX runner 需要:
GLIBC_2.38 / GLIBC_2.39
因此如果直接运行:
geniex-bench
会因为 glibc 版本不足而失败。
不要直接升级系统 glibc
这里不建议替换板端系统 glibc。
更安全的方案是:
在部署目录中准备一套 Ubuntu 24.04 ARM64 用户态 runtime,然后通过私有 loader 启动 GenieX。
本次使用的组件包括:
libc6
libstdc++6
libgcc-s1
gcc-14-base
最终私有 runtime 中至少需要:
/home/aidlux/qwen3-vl-8b-geniex-qcs8550/runtime/lib/ld-linux-aarch64.so.1
5. 使用私有 Loader 启动 GenieX
假设部署目录为:
target=/home/aidlux/qwen3-vl-8b-geniex-qcs8550
设置 loader:
loader=$target/runtime/lib/ld-linux-aarch64.so.1
设置运行时动态库路径:
libs=$target/runner/lib:$target/runner/lib/qairt:$target/runner/lib/qairt/htp-files:$target/runner/lib/llama_cpp:$target/runtime/lib
测试:
"$loader" \
--library-path "$libs" \
"$target/runner/bin/geniex-bench" \
--help
如果可以正常显示 geniex-bench 帮助信息,就说明:
-
私有 glibc loader 可以工作
-
runner 可以正常启动
-
不需要修改系统 glibc
6. 推荐的板端目录结构
建议模型、runner、runtime 全部放在一个独立目录,例如:
/home/aidlux/qwen3-vl-8b-geniex-qcs8550/
├── runner/
│ ├── bin/
│ │ └── geniex-bench
│ └── lib/
├── runtime/
│ └── lib/
│ └── ld-linux-aarch64.so.1
└── model/
└── qnn248_qcs8550_cl4096/
├── metadata.json
├── tokenizer.json
├── embedding_fp32.bin
├── qwen3-vl-8b-vit.serialized.bin
├── 6 个文本 serialized bin
└── ingredients.png
这样做的好处是:
-
不影响系统环境
-
不覆盖已有 QAIRT
-
不修改系统 glibc
-
删除整个目录即可回滚
-
后续定位依赖问题也更简单
模型上传完成后,再进行下面的兼容处理。
7. 创建两个关键模型软链接
进入模型目录:
cd /home/aidlux/qwen3-vl-8b-geniex-qcs8550/model/qnn248_qcs8550_cl4096
创建:
ln -s qwen3-vl-8b-vit.serialized.bin vision_encoder.bin
ln -s embedding_fp32.bin embedding_weights.raw
检查:
ls -l vision_encoder.bin embedding_weights.raw
正确结果应类似:
vision_encoder.bin -> qwen3-vl-8b-vit.serialized.bin
embedding_weights.raw -> embedding_fp32.bin
为什么 embedding_weights.raw 很重要?
本次部署时曾出现一个很容易误判的问题:
-
模型加载成功
-
NPU 也加载成功
-
但最终输出是乱码
补上:
embedding_weights.raw -> embedding_fp32.bin
之后,纯文本推理立刻恢复正常,例如:
The answer is 4.
因此:
如果模型能够运行但输出乱码,优先检查
embedding_weights.raw。
不要为了改名复制一份数 GB 的模型文件,直接使用软链接即可。
8. 修复示例图片扩展名
另一个比较隐蔽的问题是模型目录中的:
ingredients.png
实际文件内容是 JPEG,只是扩展名写成了 .png。
建议保留原文件,再复制一份正确扩展名:
model=/home/aidlux/qwen3-vl-8b-geniex-qcs8550/model/qnn248_qcs8550_cl4096
cp -n "$model/ingredients.png" "$model/ingredients.jpg"
检查文件真实格式:
file "$model/ingredients.png" "$model/ingredients.jpg"
之后进行图像推理时使用:
ingredients.jpg
9. Prompt 不要直接使用 /dev/stdin
geniex-bench 的 prompt 建议通过真实 UTF-8 文件传入。
本次测试发现,把:
/dev/stdin
直接作为 prompt 输入时,可能出现短读等问题。
推荐的 Qwen3-VL prompt:
<|im_start|>system
You are a helpful AI assistant.<|im_end|>
<|im_start|>user
<__image__>Describe the image in one short sentence.<|im_end|>
<|im_start|>assistant
创建文件:
cat > /tmp/qwen3-vl-prompt.txt <<'EOF'
<|im_start|>system
You are a helpful AI assistant.<|im_end|>
<|im_start|>user
<__image__>Describe the image in one short sentence.<|im_end|>
<|im_start|>assistant
EOF
特别注意:
<__image__>
必须存在,否则视觉输入可能无法按预期处理。
10. 最小可运行的图文推理命令
配置路径:
target=/home/aidlux/qwen3-vl-8b-geniex-qcs8550
model=$target/model/qnn248_qcs8550_cl4096
loader=$target/runtime/lib/ld-linux-aarch64.so.1
设置 library path:
libs=$target/runner/lib:$target/runner/lib/qairt:$target/runner/lib/qairt/htp-files:$target/runner/lib/llama_cpp:$target/runtime/lib
设置 GenieX / QAIRT 环境:
export GENIEX_PLUGIN_PATH=$target/runner/lib
export ADSP_LIBRARY_PATH=$target/runner/lib/qairt/htp-files
export GENIEX_LOG=INFO
运行:
"$loader" \
--library-path "$libs" \
"$target/runner/bin/geniex-bench" \
--plugin qairt \
--device npu \
--model "$model" \
--vlm \
--prompt-file /tmp/qwen3-vl-prompt.txt \
--image "$model/ingredients.jpg" \
--n-gen 32 \
--warmup 0 \
--repetitions 1 \
--accuracy
11. 如何判断部署成功
不要只看进程是否启动。
至少确认下面几项:
-
6 个文本 shard 全部加载成功
-
Vision Encoder 加载成功
-
QAIRT VLM 创建成功
-
日志中能够看到 HTP / NPU 推理
-
最终返回正常、可读的文本内容
如果只是:
模型加载成功
但结果乱码或视觉输入失败,仍然不能算部署完成。
12. 建议封装成启动脚本
为了避免每次手动设置环境变量,建议最终封装一个:
run_x1_geniex.sh
脚本至少应该完成以下工作:
-
设置部署根目录
-
设置模型目录
-
接收
--image -
接收
--prompt -
接收
--tokens -
检查私有 loader
-
检查
geniex-bench -
检查
metadata.json -
检查两个模型兼容软链接
-
检查输入图片
-
使用
mktemp创建真实 prompt 文件 -
设置
GENIEX_PLUGIN_PATH -
设置
ADSP_LIBRARY_PATH -
设置
GENIEX_LOG -
通过私有 loader 和
--library-path启动geniex-bench -
固定使用
--plugin qairt --device npu --vlm
上传:
scp run_x1_geniex.sh aidlux:/home/aidlux/qwen3-vl-8b-geniex-qcs8550/
添加执行权限:
ssh aidlux 'chmod +x /home/aidlux/qwen3-vl-8b-geniex-qcs8550/run_x1_geniex.sh'
运行:
ssh aidlux '/home/aidlux/qwen3-vl-8b-geniex-qcs8550/run_x1_geniex.sh'
自定义图片和 prompt:
/home/aidlux/qwen3-vl-8b-geniex-qcs8550/run_x1_geniex.sh \
--image /path/to/image.jpg \
--prompt "请描述这张图片" \
--tokens 32
故障排查
1. 报 GLIBC_2.38 / GLIBC_2.39
如果出现类似:
GLIBC_2.38 not found
或者:
GLIBC_2.39 not found
通常说明:
-
仍然使用了系统 loader
-
或者私有
--library-path不完整
确认 loader:
runtime/lib/ld-linux-aarch64.so.1
并确保以下目录都加入 --library-path:
runner/lib
runner/lib/qairt
runner/lib/qairt/htp-files
runner/lib/llama_cpp
runtime/lib
2. 模型能运行,但输出乱码
先检查:
ls -l "$MODEL/embedding_weights.raw"
它必须指向:
embedding_fp32.bin
也就是:
embedding_weights.raw -> embedding_fp32.bin
这个问题很容易被误认为 tokenizer、量化模型或者 NPU Runtime 出错,建议优先排查。
3. 纯文本正常,但图像推理失败
先检查图片的真实文件格式:
file your-image.png
如果文件内容实际是 JPEG,但文件名使用 .png,可以复制成 .jpg 后重新测试。
例如:
cp -n ingredients.png ingredients.jpg
然后改用:
ingredients.jpg
进行推理。
4. Prompt 处理失败 / 输出为空
不要直接使用:
/dev/stdin
建议创建真实 UTF-8 文件。
同时检查 prompt 是否包含:
<__image__>
5. 找不到 genie-t2t-run
对于:
runtime: geniex_qairt
的视觉模型,这不是阻塞问题。
直接使用 GenieX:
geniex-bench --plugin qairt --device npu --vlm
即可。
6. Runner 解压失败
重新检查:
gzip -t geniex-bench-linux-arm64.tar.gz
以及:
tar -tzf geniex-bench-linux-arm64.tar.gz
任何一个校验失败,都不要继续部署该文件。
重新下载后再测试。
部署检查清单
正式验收之前,可以按下面顺序快速检查:
-
SSH 可以正常连接 X1
-
uname -m返回aarch64 -
磁盘空间足够存放模型、runner 和私有 runtime
-
metadata.json确认QCS8550 -
metadata.json确认HTP v73 -
metadata.json确认runtime: geniex_qairt -
GenieX ARM64 runner 已完整下载
-
gzip -t校验通过 -
X1 glibc 低于 runner 要求时使用私有 ARM64 glibc
-
没有替换系统 glibc
-
runner、runtime、model 位于独立部署目录
-
vision_encoder.bin软链接正确 -
embedding_weights.raw软链接正确 -
输入图片扩展名与真实格式一致
-
使用真实 prompt 文件,而不是
/dev/stdin -
Prompt 中包含
<__image__> -
使用
--plugin qairt -
使用
--device npu -
使用
--vlm -
6 个文本 shard 全部加载成功
-
Vision Encoder 加载成功
-
日志确认使用 HTP / NPU
-
最终输出为正常可读文本
总结
这次部署最值得注意的,其实不是模型本身,而是几个运行时兼容问题:
-
GenieX runner 的 glibc 要求高于 X1 系统版本
-
不要为了运行 runner 替换系统 glibc
-
使用私有 ARM64 loader + library path 可以隔离解决兼容问题
-
embedding_weights.raw缺失可能导致模型加载正常但输出乱码 -
Vision Encoder 文件名需要适配 GenieX
-
图片扩展名必须和真实格式一致
-
VLM prompt 建议写入真实文件
-
geniex_qairt类型的视觉模型应使用geniex-bench + QAIRT + NPU验证
只要把 runtime、模型文件命名和 prompt 这几个坑处理好,Qwen3-VL 就可以在 X1 上通过 GenieX + QAIRT 正常完成 NPU 图文推理。
参考资料
https://www.qualcomm.com/developer/software/gen-ai-inference-extensions
https://github.com/qualcomm/GenieX
