← 返回文章

在 Jetson AGX Orin 上部署 TensorRT Edge-LLM:从 Qwen FP16 到 VS Code Agent

记录在 x86 CPU-only 主机上导出 Qwen 模型、在 Jetson AGX Orin 上构建 TensorRT engine、扩展到 32K 上下文、启动 OpenAI 兼容服务,并与 Ollama Gemma4 做真实速度对比。

#TensorRT Edge-LLM#Jetson AGX Orin#Qwen#VS Code Agent#TensorRT#本地大模型

在 Jetson AGX Orin 上部署 TensorRT Edge-LLM:从 Qwen FP16 到 VS Code Agent

这次实践的目标不是简单把一个模型“跑起来”,而是验证一条可以长期使用的本地推理链路:

Hugging Face checkpoint
        │
        ▼
x86 Ubuntu CPU-only 主机导出 ONNX
        │
        ▼
Jetson AGX Orin 本机构建 TensorRT engine
        │
        ▼
TensorRT Edge-LLM C++ runtime
        │
        ▼
OpenAI-compatible server
        │
        ▼
VS Code Chat / Agent

最终测试了三种路线:

  • Qwen3-8B-FP16 + TensorRT Edge-LLM;
  • Qwen2.5-Coder-14B-Instruct-FP16 + TensorRT Edge-LLM;
  • Gemma4 8B Q4_K_M + Ollama,作为速度参照。

先给结论:Qwen3-8B FP16 的真实 Decode 速度约为 10.8 tokens/s,Qwen2.5-Coder-14B FP16 约为 5.7 tokens/s,Gemma4 Q4_K_M 约为 30.66 tokens/s。TensorRT Edge-LLM 的优势主要在于 TensorRT 优化、原生 C++ runtime、可控的 engine 和服务链路,而不是让 FP16 模型超过 4-bit 模型的纯速度。

一、实际环境

Jetson 侧没有预先假定软件版本,而是先检查系统:

cat /etc/nv_tegra_release
nvcc --version
dpkg-query -W nvidia-jetpack nvidia-l4t-core 2>/dev/null
dpkg -l | grep -Ei 'tensorrt|nvinfer'
python3 --version
cmake --version
gcc --version
git --version
free -h
df -h /
nvpmodel -q
sudo jetson_clocks --show

本次推理设备的环境为:

项目 实际版本或状态
设备 Jetson AGX Orin Developer Kit 64GB
CPU 架构 aarch64
JetPack 6.2
L4T R36.4.3
Ubuntu 22.04.5 LTS
Kernel 5.15.148-tegra
CUDA Toolkit 12.6
TensorRT 10.3.0
Python 3.10
GCC 11.4
CMake 3.22
TensorRT Edge-LLM v0.10.0
Git commit 71dd1bae032e70771265917ec74d3ff4cad07a10
Power mode MAXN

Jetson 使用统一内存,主要监控工具是:

tegrastats --interval 1000

性能测试前可以锁定频率:

sudo jetson_clocks
nvpmodel -q

二、整体部署策略

这次选择 TensorRT Edge-LLM v0.10.0,没有为了安装它升级 JetPack、CUDA 或 TensorRT,也没有切换到早期的 TensorRT-LLM Jetson 分支。

export 和 engine build 分开进行:

  • x86 主机负责 checkpoint 到 ONNX 的导出;
  • Jetson 负责加载 ONNX、调用 TensorRT 和 Edge-LLM plugin 构建 engine;
  • 最终运行时主要依赖 engine、tokenizer、chat template 和 plugin。

官方资料:

三、x86 CPU-only 主机完成 export

导出阶段不需要 NVIDIA GPU。CPU-only 主机可以运行 exporter,只是速度较慢、内存占用较高。

以 FP16 模型为例:

cd /path/to/TensorRT-Edge-LLM-export
source .venv/bin/activate

export HF_HUB_OFFLINE=1
export TMPDIR=/path/to/large-disk/tmp
mkdir -p "$TMPDIR"

set -o pipefail
time tensorrt-edgellm-export \
  /path/to/models/Qwen3-8B-FP16 \
  /path/to/models/Qwen3-8B-FP16/onnx \
  2>&1 | tee /path/to/models/Qwen3-8B-FP16/export.log

echo "export_status=${PIPESTATUS[0]}"

Qwen2.5-Coder-14B-Instruct-FP16 使用同样的 export 方式,只需替换输入和输出目录。

成功的 onnx/llm 目录至少应包含:

onnx/llm/
├── config.json
├── embedding.safetensors
├── model.onnx
├── model.onnx.data
├── processed_chat_template.json
├── tokenizer.json
└── tokenizer_config.json

model.onnx.data 是 ONNX external data 权重文件,不能只复制 model.onnx。如果在另一台机器上构建 engine,复制整个 onnx/llm 目录即可。

四、Jetson 侧编译

cd /home/wooley/TensorRT-Edge-LLM

export TRT_PACKAGE_DIR=/usr
export CUDA_CTK_VERSION=12.6
export ENABLE_CUTE_DSL=ALL
export CMAKE_BUILD_PARALLEL_LEVEL=8

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j8

关键产物为:

build/libNvInfer_edgellm_plugin.so
build/examples/llm/llm_build
build/examples/llm/llm_inference
build/examples/llm/llm_bench

如果需要 server,还要编译 pybind runtime:

source .torch-jetpack62-venv/bin/activate
python experimental/server/setup_pybind.py build_ext --inplace

五、构建 TensorRT engine

Qwen3-8B FP16:

./build/examples/llm/llm_build \
  --onnxDir /home/wooley/models/Qwen3-8B-FP16/onnx/llm \
  --engineDir /home/wooley/models/Qwen3-8B-FP16/engine \
  --maxBatchSize 1 \
  --maxInputLen 4096 \
  --maxKVCacheCapacity 8192 \
  2>&1 | tee logs/qwen3_8b_fp16_engine_build.log

Qwen2.5-Coder-14B 的 8K 输入版本:

./build/examples/llm/llm_build \
  --onnxDir /home/wooley/models/Qwen2.5-Coder-14B-Instruct-FP16/onnx/llm \
  --engineDir /home/wooley/models/Qwen2.5-Coder-14B-Instruct-FP16/engine-8k \
  --maxBatchSize 1 \
  --maxInputLen 8192 \
  --maxKVCacheCapacity 16384 \
  2>&1 | tee logs/qwen25_coder_14b_fp16_engine_build_8k.log

为了给 VS Code Agent 更大的工作区上下文,又构建了 32K 总上下文版本:

./build/examples/llm/llm_build \
  --onnxDir /home/wooley/models/Qwen2.5-Coder-14B-Instruct-FP16/onnx/llm \
  --engineDir /home/wooley/models/Qwen2.5-Coder-14B-Instruct-FP16/engine-32k \
  --maxBatchSize 1 \
  --maxInputLen 32256 \
  --maxKVCacheCapacity 32768 \
  2>&1 | tee logs/qwen25_coder_14b_fp16_engine_build_32k.log

这表示:

32256 输入 tokens + 512 输出 tokens = 32768 总上下文

模型的 max_position_embeddings 是 32768,因此不能同时使用 32768 输入 tokens 和额外的生成空间。

构建时的 OOM

14B engine build 的 CPU 内存需求比运行时高很多。一次构建曾被 Linux OOM killer 终止,增加本地 NVMe swap 后成功:

sudo fallocate -l 64G /swapfile-edgellm
sudo chmod 600 /swapfile-edgellm
sudo mkswap /swapfile-edgellm
sudo swapon /swapfile-edgellm

free -h
swapon --show

swap 主要服务于 build 阶段。正常推理时不应依赖 swap,否则延迟会明显恶化。

六、C++ runtime 推理

使用统一的测试输入:

{
  "batch_size": 1,
  "temperature": 0.7,
  "top_p": 0.8,
  "top_k": 20,
  "max_generate_length": 256,
  "apply_chat_template": true,
  "enable_thinking": false,
  "requests": [
    {
      "messages": [
        {
          "role": "user",
          "content": "请用简洁但专业的语言解释蒙特卡洛树搜索的基本原理,以及它为什么适合围棋程序。"
        }
      ]
    }
  ]
}

真实推理测试使用 llm_inference,而不是只看带 --profile 的层级 profiling:

export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH
export EDGELLM_PLUGIN_PATH=/home/wooley/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

./build/examples/llm/llm_inference \
  --engineDir /home/wooley/models/Qwen3-8B-FP16/engine \
  --inputFile /home/wooley/models/Qwen3-8B-AWQ/input_cn.json \
  --outputFile /home/wooley/models/Qwen3-8B-FP16/output_cn.json \
  --warmup 10 \
  --dumpProfile \
  --profileOutputFile /home/wooley/models/Qwen3-8B-FP16/profile_cn.json \
  2>&1 | tee logs/qwen3_8b_fp16_inference.log

14B 测试只需把 --engineDir 换成 engine-32k。

七、真实速度对比

测试结果如下:

模型 输入 tokens 输出 tokens Prefill Decode 峰值统一内存
Qwen3-8B FP16 + Edge-LLM 35 256 335.4 tok/s 10.8 tok/s 14.59 GB
Qwen2.5-Coder-14B FP16 + Edge-LLM 52 256 251.1 tok/s 5.7 tok/s 24.75 GB
Gemma4 8B Q4_K_M + Ollama 33 256 183.3 tok/s 30.66 tok/s Ollama 显示约 3.9 GB

Qwen 两个模型使用的是相同中文文字,但 tokenizer 不同,所以输入 token 数不同。三者都生成了 256 tokens,因此 Decode 对比更可靠。

Gemma4 的测试在另一台 Jetson Orin 上进行,Ollama 显示它是 GGUF、Q4_K_M、8B,并且 100% 使用 GPU。因此这个结果是工程参考,不是完全相同软件环境下的实验室对照。

结论很清楚:

  • Gemma4 4-bit 的 Decode 约是 Qwen3-8B FP16 的 2.8 倍;
  • Qwen3-8B FP16 约是 Qwen2.5-Coder-14B FP16 的 1.9 倍;
  • 14B 的优势是代码能力和模型容量,不是速度;
  • 量化格式、模型规模、kernel 和上下文长度对 Orin 速度影响很大。

八、启动 OpenAI-compatible server

cd /home/wooley/TensorRT-Edge-LLM
source .torch-jetpack62-venv/bin/activate

export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH
export EDGELLM_PLUGIN_PATH=/home/wooley/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

python -m experimental.server \
  --model /home/wooley/models/Qwen2.5-Coder-14B-Instruct-FP16/engine-32k \
  --host 0.0.0.0 \
  --port 8001

检查服务:

curl http://127.0.0.1:8001/health
curl http://127.0.0.1:8001/v1/models

当前返回的模型 ID 是 engine-32k。

九、VS Code Custom Endpoint

VS Code 配置如下:

{
  "name": "TensorRT Edge-LLM",
  "vendor": "customendpoint",
  "apiType": "chat-completions",
  "models": [
    {
      "id": "engine-32k",
      "name": "Qwen2.5-Coder-14B-Instruct-FP16",
      "url": "http://JETSON_HOST:8001/v1/chat/completions",
      "toolCalling": true,
      "vision": false,
      "contextWindow": 32768,
      "maxInputTokens": 32256,
      "maxOutputTokens": 512
    }
  ]
}

JETSON_HOST 替换成 Jetson 的局域网地址。maxInputTokens 不能填写 128000,因为 UI 配置不会扩展 engine 的真实上下文。

十、Tool calling 的问题

第一次从 VS Code 发送带工具的请求时,server 返回:

transformers is required for tool-aware chat templates.

在 server venv 中安装仓库需要的 Transformers 和新版 Pillow:

source /home/wooley/TensorRT-Edge-LLM/.torch-jetpack62-venv/bin/activate

python -m pip install \
  --no-cache-dir \
  --retries 10 \
  --timeout 120 \
  -i https://pypi.tuna.tsinghua.edu.cn/simple \
  "transformers==5.14.1" \
  "Pillow>=10.0.0"

仅安装 Transformers 仍可能失败,因为 Jetson 系统自带的旧 Pillow 缺少 PIL.Image.Resampling,导致 AutoProcessor 导入失败。安装完成后必须重启 server。

随后 VS Code 又出现了:

No lowest priority node found

这个错误发生在 VS Code Copilot 客户端内部,不是 Jetson 返回的错误。server 日志显示请求已经是 200 OK。

Agent 模式会把大量工具的名称、描述、参数 JSON Schema、MCP 工具和扩展工具一起注入上下文。32K engine 能解决模型本身的上下文限制,但不能无限容纳所有工具定义、工作区内容、对话历史和输出空间。

处理方式是:

  1. 在 VS Code 工具配置中关闭不需要的 MCP 和扩展工具;
  2. 只保留文件读取、文件编辑、终端和代码搜索;
  3. 为本地模型建立一个只包含少量工具的 custom agent;
  4. 更新 VS Code 和 Copilot 扩展;
  5. 普通聊天测试时临时关闭 toolCalling。

这个客户端异常也有公开记录:VS Code Issue #322299。

十一、32K engine 的速度和内存代价

只把 engine 最大容量从 16K 提高到 32K,不会让短 prompt 立刻慢四倍。真正影响速度的是实际使用的 KV cache 长度:

  • 2K~4K 上下文通常与 8K engine 接近;
  • 8K 上下文开始增加 Prefill 时间;
  • 16K~32K 上下文会明显增加 Prefill 时间,Decode 也会随着 KV 读取量增加而下降;
  • 最大容量增加本身主要增加内存占用,实际长上下文才会明显增加计算量。

Qwen2.5-Coder-14B 从 16K KV 容量扩展到 32K,KV cache 预计增加约 3GB。64GB AGX Orin 可以承受,但仍应观察统一内存、温度、功耗和长时间稳定性。

十二、最终判断

Qwen3-8B FP16 链路已经稳定跑通,适合作为 Edge-LLM 的基础模型和质量基线。Qwen2.5-Coder-14B FP16 的 32K engine 适合复杂代码上下文,但约 5.7 tok/s 的 Decode 速度会让交互明显变慢。

如果目标是最快的日常本地聊天,4-bit Ollama 模型更有优势;如果目标是研究 TensorRT engine、保持 FP16 质量、使用原生 C++ runtime,TensorRT Edge-LLM 更合适。

对于 VS Code Agent,最现实的方案是使用 8B 模型负责快速交互,14B 模型负责复杂代码理解,并严格限制工具列表,避免把 32K 上下文消耗在工具 Schema 上。后续还应继续验证长上下文、连续多轮工具调用、异常恢复和热稳定性。

Conversation

留言

加载中

正在读取留言…

Leave a note

留下你的想法

留言提交后会先进入审核,审核通过才会公开显示。