Jetson Orin NX 本地 AI Agent,决策模型和大模型如何分工?Laya + Qwen 实测 32.8 秒
一句英文任务,在 Jetson Orin 上跑完一次真实的航班查询,总共 32.8 秒。其中负责挑动作的 Laya 只用掉 0.5 秒,负责理解任务的 Qwen 用掉 3.5 秒,剩下二十多秒都花在浏览器操作、页面等待、核对和保存上。全程本地推理,人没有接管。

录屏里这条任务做了 5 次浏览器动作,最后停在匹配航班的票价选项层。它不下单,也不支付。
数字看完了,问题还在。程序要点一个按钮的时候,是让大模型把这个动作写成一句话再解析,还是从程序已经列好的动作里挑一个?这篇把两条路的机制拆开,再给出 Jetson 上的实测配置、安装步骤和两个能自己复现的 demo。
本文实测设备是 reComputer Industrial J4012(Jetson Orin NX 16GB),系统 JetPack 6.2.1、CUDA 12.6,代码在 jetson-browser-assistant。
一、两条路,区别在动作由谁挑
假设你让助手去查航班。它读完任务、打开页面,接下来得决定做什么。页面上可能是出发地、目的地两个输入框,一个日期选择器,还有一个 Search 按钮。程序能把这些控件列成可选动作,但总得有人挑一个。
传统文本生成模型的做法是读任务和页面描述,然后把这个动作写出来。比如它返回一句「点击 Search」,程序解析这段文字,去执行点击。页面变了以后,助手再观察一次,继续问下一步。
生成这段答案分两步。Prefill 阶段处理描述任务和页面的输入,decode 阶段逐个 Token 生成答案,后面的 Token 依赖前面已经输出的内容。一个任务里要连做几次动作,这两步就重复几遍。
这条路能应付各种说法,也能生成新文字。但程序已经知道有哪些动作可选时,还有一个更窄的问法,这些动作里,下一个该做哪个。

上面一行是自回归模型先做 prefill、再逐 Token 解码出动作文字,下面一行是决策模型给现成的候选打分。这张图讲机制,不比较速度。
两条路都要处理输入,都要算。文本模型也能只输出一个简短的动作 ID,光看机制图判断不了谁更快,实际延迟取决于模型、输入长度、候选数量和具体实现。
这个项目两个模型都用上了。Qwen 负责理解用户的要求,需要写字的时候由它来写。Laya 反复从当前候选里挑操作和目标。工具由程序提供,动作由程序执行,结果也由程序核对。
二、决策模型拿到的三样东西
拿白板上的一个例子。画布上有个绿色的框,标签是 Camera,目标是把它的底色改成蓝色,标签和位置都保留。这一次的目标由程序直接交给 Laya,原文如下。
Make the Camera rectangle background blue. Keep its text and position.
程序把当前情况整理成三份输入。
| 输入 | Camera 这个例子里是什么 |
|---|---|
| 状态 State | Camera 现在是绿色,标签和位置已知 |
| 目标 Goal | 把 Camera 变蓝,标签和位置保留 |
| 候选 Candidates | 此刻可用的红、绿、蓝、黄四个按钮 |
三样缺一不可。状态说的是现在什么样,目标说的是想要什么样,候选说的是程序此刻能做什么。观察漏掉一个控件,或者正确选项根本没进候选列表,模型再准也选不出来。
本项目固定使用 laya-browser v19s,编码器是 mmBERT-base,后面接一个决策头。编码器结合上下文读这些文字,绿色是现在的颜色,蓝色是想要的颜色,保留位置是一条约束。决策头给候选打分,返回选中的那个。更细的设置见文末的固定配置链接。

文字形式的状态、目标和候选进入 mmBERT-base 双向编码器和决策头,分别输出操作选择与点击目标选择。这个实现不把截图像素送进模型。
候选描述每次请求都可能变。这一次选项 3 是蓝色按钮,wrapper 把它映射成动作 ID color_blue;下一次换了页面或画布,3 可能指别的东西。映射由程序按最新一次的观察重新建立。
这次返回把「要做什么」和「对谁做」拆成两个问题,操作选了 CLICK,目标选了选项 3。两个问题可以放进同一批计算,不必固定先推操作再推目标。

一次真实的 Camera 决策,操作选了 CLICK,概率 0.9785,点击目标选了蓝色,概率 0.9721。图上两个色块按保存的场景数值重画,不是截图。
程序点完蓝色按钮后重新读了一次画布,确认 Camera 已经变蓝,标签和位置都还在。
分数只表示模型在这一组候选里更偏向哪个。同一份记录里,目标那一项还有一个 confidence 字段,值是 0.8859。这两个数都不能保证整条任务成功,单条例子也说明不了新任务上的准确率或校准水平。
候选多了,计算量跟着涨。候选集合较大时,固定版本的 wrapper 会分组打分,再拿各组胜出者比一次,所以一次 API 请求背后可能不止一轮模型计算。接口耗时里还有请求准备和传输,不只是模型本身的时间。
三、选出来只是第一步,核对由程序负责
模型推理的时候页面可能已经变了,一次点击也可能没起作用。所以助手跑的是一个循环,观察、选择、核对、执行、再观察。

Qwen 理解要求,Laya 在可用动作里选,执行和核对交给程序。
动手之前,程序先确认选中的目标还在当前页面或画布上。鼠标键盘由 Playwright 执行。做完之后再读一次真实状态,检查要求有没有满足。模型自己返回一个 DONE,不能当作完成凭证。
适配器负责补上具体能力,比如在日历里选完整年月日、拖动白板上的一个对象。白板的核对器还会查对象身份、标签、颜色、位置和箭头绑定,所以在原对象上面盖一个新的框,过不了「保留原对象」这一关。这些工具和检查都写在应用里,加一个新工具就得真写代码。
四、在 Jetson Orin NX 上跑起来
两个演示用同一套配置,模型、浏览器和桌面共用一份内存。
| 组件 | 实测配置 |
|---|---|
| 硬件 | reComputer Industrial J4012,Jetson Orin NX 16GB |
| 系统 | Ubuntu 22.04.5,JetPack 6.2.1,L4T 36.4.3 |
| Python / CUDA | Python 3.10.12,CUDA 12.6 |
| 决策模型 | laya-browser v19s,PyTorch 2.7.0 CUDA,原始 wrapper,fast=False |
| 文本模型 | Qwen3.5-4B-MTP-GGUF,Q4_K_M |
| 文本推理 | CUDA llama.cpp,8192 Token 上下文,单并发,关闭 thinking,MTP 深度 2 |
| 浏览器 / 白板 | Playwright 1.64.1,ARM64 Chromium,Excalidraw 0.18.1 |
在 Jetson 桌面上用普通用户打开终端,执行下面三行。
git clone https://github.com/zibochen6/jetson-browser-assistant.git
cd jetson-browser-assistant
./setup.sh
安装脚本会检查设备,准备独立的 uv 环境和匹配的 CUDA 版 PyTorch,安装 Chromium,下载固定版本的模型,编译 llama.cpp,再把服务预热好。不要加 sudo 运行,需要装系统库时脚本会自己提权。兼容性、下载和排错见文末的 README 与安装说明。
五、什么场景值得用决策模型
值得考虑的场景有几个共同点,用户的目标会变,当前状态会变,但可用的工具是有限的。航班筛选、改白板上已有的对象都属于这一类。应用这边仍然要负责观察当前状态、把有意义的选项摆出来、做完再去核对。
任务固定、每次都走同一条路的场景,确定性脚本往往更省事。需要开放式回答或新写文字的时候,生成式模型依旧有用。
有一条要说清楚,这个项目没有做同条件的脚本基线,所以这些演示不能证明模型方案比脚本更快或更好维护。
当前版本只覆盖 Trip.com 上的一条有限流程和一张小尺寸白板。其他网站、自由绘图、语音和工业控制都要另外开发和验证。Jetson 的 16GB 是 CPU 与 GPU 共享内存,别的程序和 swap 都会吃掉可用容量。
六、时间花在哪,两个 demo 怎么跑
录屏里这条航班任务总耗时 32.8 秒,Laya 决策累计约 0.5 秒,Qwen 解析约 3.5 秒,剩下的时间在浏览器操作、页面等待、核对和保存上。

三根条是各自独立的计数,不是完整的分项拆分,这次任务还复用了页面上已有的搜索条件,所以不能当成从空白页逐个填表的耗时。
另外做过一组等待逻辑的对照。模型预先加载、浏览器用独立的空白 profile、改掉页面等待判定之后,广州到北京这条前后都成功的任务从 119.4 秒降到 41.6 秒。动的是等待逻辑,模型没换过。
查询航班
启动助手,等提示符 Models ready 和 Task> 出现。
./launch.sh
输入下面这条任务,日期换成未来某一天。
Find one-way flights from Shenzhen to Shanghai on 2026-11-13 for 1 adult in economy. Nonstop only, lowest price first. Show up to 3 options and open the fare options for one matching flight.
启动脚本会同时打开真实网站和一个只读的 Live Flow 窗口,只想要网站时用 ./launch.sh --no-visualize。完成后检查实际候选、Query verified 和 Fare options opened 三项输出。全程停在票价选项层,不会下单或支付。
在同一张白板上连续修改
先结束或取消正在跑的任务,再打开一张空白画布。
./launch-whiteboard.sh --open examples/whiteboard/blank.excalidraw
--open 会替换当前画布,已经导出的文件保留。等到 Task> 后输入下面这条。
Draw three labeled boxes: Camera on the left, Jetson in the center, and Display on the right. Connect Camera to Jetson and Jetson to Display with arrows.
第一条跑完后不要重新打开空白文件,直接提交第二条。
Make Jetson green. Add a box labeled Robot Arm below Jetson and connect Jetson to Robot Arm. Keep everything else.

第二句跑完的样子,Jetson 变绿,下方多了一个连好箭头的 Robot Arm,画布上原有的对象都还在。
每条白板任务完成后都会存下可编辑的 scene.excalidraw、一张 PNG、一张截图和一份核对报告,更多可用指令见文末的白板指南。
计时从按下回车开始,包含解析、必要的浏览器打开、推理、界面操作、等待、核对和保存。模型加载、预热和输入等待不计入,人工等待算在总时间里。
任务进行中按 Ctrl+C 取消这一条。空闲时输入 exit 或 quit 退出提示符,模型和浏览器继续留着。./status.sh 查服务,./stop.sh 停止项目服务和模型。航班记录在 runs/,白板记录在 whiteboard-runs/。
七、常见问题
决策模型和让大模型只输出一个动作 ID,区别在哪
输出空间被限定在程序给出的候选里,不用逐 Token 生成一整个答案。输入那一步照样要算,所以真实耗时还是取决于模型大小、输入长度和候选数量。
0.9785 这样的分数能当成功率看吗
不能。它只表示在这一组候选里模型更偏向哪个,confidence 是另一个字段,单条记录也说明不了校准水平。
候选编号为什么每次都要重新映射
页面和画布一变,同一个编号指向的东西就变了。映射由程序按最新一次观察重建,这也是为什么状态、目标、候选三份输入必须来自同一时刻。
换成别的网站能用吗
当前只覆盖 Trip.com 上的一条有限流程。城市选择器、日历这类控件各有各的写法,适配器要单独写代码并验证。
决策只要 0.5 秒,那慢在哪
慢在浏览器操作、页面等待、核对和保存。上面那组 119.4 秒到 41.6 秒的对照,改的全是等待判定,模型没换。
16GB 内存够吗
模型、Chromium 和桌面共用同一份内存。跑白板演示时实测占用 12.29 GiB,另有 2577 MiB swap,其中包含系统里其他程序,实际余量要按自己的环境看。
为什么不让模型自己说任务完成
模型返回 DONE 只代表它认为做完了。完成与否由程序重新读一次真实状态来判定,白板还会核对对象身份、颜色、位置和箭头绑定。
八、参考资料
更多推荐

所有评论(0)