概述
作者: Eivind Holt,2026 年 7 月 代码仓库: github.com/eivholt/qai-nemotron 目标平台: Qualcomm Dragonwing IQ-9075 EVK / QCS9075 / Hexagon v73。硬件由 Qualcomm 慷慨赞助 🙏模型: mistralai/Ministral-3-3B-Instruct-2512 Q4_K_M GGUF,在设备端 NPU 上运行 能够在边缘设备上运行的大型语言模型已经越过了一个界限:它们可以用工具推理来取代传统的硬编码控制流逻辑。它们可以检查本地状态、选择动作、执行工具,并对结果作出反应。 本教程将该循环构建为一个运行在 Qualcomm Dragonwing IQ9075 EVK 上的小型控制台应用程序。 示例是一个出库运输协调器。它读取一个待处理的运单,检查承运人和装货码头,并从以下三种结果中选择一种:
- 安排一个合规的承运人和码头;
- 当临时问题可能得到解决时,暂缓运输;或
- 当不存在安全方案时,上报升级处理。

相关教程
在 Dragonwing IQ9075 上部署 NVIDIA Llama-3.1-Nemotron-Nano-8B-v1 更详细地介绍了 EVK、WSL2 存储、Qualcomm AI Hub 凭证、QAIRT 安装以及 Genie 基础知识。 Dragonwing IQ9075 上的代理型 LLM 对比了多种模型的函数调用和物流行为。这个基准测试工作促成了本文使用的模型和客户端设计。示例中包含哪些内容
实现代码位于代码仓库的以下路径:
运行时包含四个场景:
为什么选择 Ministral 3B
示例使用 Mistral Ministral-3-3B-Instruct-2512 及其官方 Q4_K_M GGUF 权重。在之前的 IQ9075 对比测试中,这个模型是在有界工具使用方面最稳定的加速器后端选择。它在共享的非 Web BFCL 选择上得分 132/170,在严格的自定义医院物流测试中通过 9/14 个案例。 IQ9075 Q4 包在保存的代理配置文件中生成约 5 tokens/s。这比某些 QAI Hub W4A16 模型慢,但该模型在多轮工具使用中更可靠。因此模型选择应考虑完成的工作量,而不仅是 tokens/s。 Mistral 官方的使用说明要求在源模型使用 vLLM 托管时使用原生 Mistral 工具调用解析器。Genie 本身不提供该 OpenAI 适配器。代码仓库中的mistral_tool 渲染器和解析器在常驻 C++ 服务和 genie-t2t-run 两侧都执行相同的转换。
各部分如何协作
运行路径故意保持精简: 两个 HTTP 层都只监听127.0.0.1。Python 适配器使用原生 Mistral 模板完整渲染一次请求,将该文本通过 Qualcomm C++ GenieAPIService 中的身份提示发送,解析返回的模型文本,并将其作为普通的 OpenAI 兼容响应交给 Pydantic AI。Pydantic AI 然后直接执行所选的工具,或者通过 MCP 发现并调用它,然后将结果包含在下一次模型回合中。
C++ 进程一次性加载模型和 QNN 上下文。它为每个 HTTP 请求重置 Genie 对话,因为 Python 适配器已经提供了完整对话,但该重置不会重新加载模型。将渲染保留在一个地方,也避免在模型的原生 Mistral 提示之上再套用一个通用服务模板。
代码仓库还保留了围绕 genie-t2t-run 构建的第二个高度可检查的桥接。它为每个模型回合启动一个新的 CLI 进程,并记录提示、原始响应、解析器决策和 Genie 配置文件。这使它成为模型集成测试和解析器调试的有用工具,而常驻 C++ 路径是更好的实时演示后端。
资源预期
已验证的导出使用 QAIRT 2.47,在桌面 PC 上耗时约 25 分钟。
请为完整的构建预留至少 45 到 50 GB 的快速桌面存储。成功的桌面机器有 192 GB 内存,但未捕捉到导出的峰值 RAM,因此这项工作并未确立更小的下限。生成的包本身可以轻松容纳在 EVK 的系统内存中。
准备桌面导出环境
创建一个隔离的环境:2.47.0.260601,请使用 qairt-vm fetch 打印的目录。
在此环境中将 qairt-dev 包保持为 Python 导入的主导。不要 source 完整 SDK 的 envsetup.sh;它可能会前置第二个 qairt Python 树,从而遮蔽构建器包。上面两个路径导出提供了构建器所需的桌面二进制文件和共享库。
下载官方 GGUF
发布方在官方 Mistral 仓库中提供了Ministral-3-3B-Instruct-2512-Q4_K_M.gguf。
为 IQ9075 编译并导出
在激活的qairt-dev-gguf 环境中运行仓库辅助脚本:
dsp_arch:v73;soc_model:77;cores:1 并导出两个 QNN 上下文二进制文件。成功的输出以 EXPORT_SUMMARY JSON 行结尾。
可部署的包为:
在 EVK 默认版旁并列安装 QAIRT 2.47
将 2.47 与/opt/qairt/current 并列可避免修改开发板上现有的每个模型。设置当前 EVK 地址,然后创建目标目录:
PATH、LD_LIBRARY_PATH 和 ADSP_LIBRARY_PATH。
复制模型和示例
在桌面机的仓库根目录下:~/qairt-env.sh。它在启动器添加 2.47 路径之前提供正常的设备环境。
在 EVK 上准备代理环境
打开 SSH shell:genie_config.agent.json,seed 为 42,temperature 为 0,top-k=1,top-p=1.0。它还写入 C++ 服务的身份 prompt.json,并在 artifacts/ 下创建到 tokenizer、后端扩展和 QNN 上下文二进制文件的根级相对链接。这些链接消耗的存储微不足道,并适配示例服务的按 basename 查找。原始导出的配置和产物不会被修改。
构建常驻 C++ 服务
实时演示使用 Qualcomm 的开源GenieAPIService。已验证的构建将 qai-appbuilder 固定在提交 86ce07a,以便源代码和命令保持可重现:
CLI/CLI.hpp 之类的头文件时会停止。这个小型仓库补丁移除了一个被 EVK 上 GCC 13 编译器拒绝的无效额外 std:: 限定,并将服务的硬编码绑定地址从 0.0.0.0 改为 127.0.0.1。它不会添加模型模板或工具解析器。
在 EVK 上配置和构建:
QNN_SDK_ROOT 导出。嵌套的 AppBuilder 构建从环境读取它,同时针对 EVK 上已安装的 QNN 开发头文件进行编译。启动时,脚本会选择该模型包所需的并列 QAIRT 2.47 库。
只应用这一个小源补丁的干净检出在 EVK 上以 2 分 20.6 秒完成了所有四个严格直接工具场景。这证实了实时演示不依赖于模型比较工作中其他地方使用的早期实验性 C++ 模型模板或工具解析器补丁。
启动本地模型服务器
使用第一个 EVK shell 启动常驻实时演示后端:GenieAPIService,加载一次模型,等待就绪,然后在端口 8001 上启动模型特定的 Python 适配器。适配器使用原生 Mistral 工具模板并暴露每个解析出的调用。当模型发出额外调用时,它不会静默地只保留第一个调用。
对于可检查的模型集成和解析器回归测试,先停止 C++ 服务器,然后启动 CLI 桥接:
genie-t2t-run。不要同时运行两个后端,因为它们共享端口 8001 和 HTP 资源。
在没有模型的情况下测试 Python 工具
打开第二个 EVK shell:运行代理
默认使用直接 Pydantic 函数工具。从常规案例开始:天气封路示例
以下记录来自常驻 C++ 运行器。仅重新排列了空白和终端标签以便于阅读 JSON。完整的系统指令展示在下一节。 用户输入:危险品示例
危险品案例演练了另一种非调度结果。它使用相同的用户输入:自主代理循环
Pydantic AI 将普通的 Python 函数注册为模型工具。模型永远不会直接执行 Python 或 shell 代码。所有六个工具在每一轮都保持可用:get_pending_shipmentget_shipping_optionsschedule_shipmenthold_shipmentescalate_shipmentnotify_dispatch
sequential=True 可防止并发的状态更改调用。它不隐藏工具,也不规定下一个动作。模型在对话中接收每个工具结果,并选择下一步。
返回决策就绪的观察结果
第一个自主版本返回一个混合的承运人列表。Ministral 通常能完成任务,但严格的重复运行暴露了两个注意力失误:在天气案例中它尝试了明显被拒绝的选项,而有一次即使存在可用卡车,它也在关注一辆容量不足的厢式车后暂缓了一个常规运单。 添加更多针对特定案例的提示文本会让测试更容易通过,但也不那么通用。相反,模拟选项服务被改进得更像一个真实的物流服务。它执行确定性的兼容性检查,并将记录分组为:schedule、hold 或 escalate。它提供决策就绪的事实,将协调策略留给模型。这是一个重要的边缘代理模式:确定性系统应该计算它们可以精确计算的事实,而 LLM 负责处理变化的工作流和意图。
工具在一个当前运单上操作,因此下游调用不要求模型重复其运单 ID。移除冗余的标识符转写修复了一个真实的 MCP 验证失败,同时没有降低决策自主性。
通过 MCP 运行相同的工具
同一个控制台客户端可以通过 Model Context Protocol 发现并执行全部六个工具:shipping_agent/mcp_server.py 会作为独立的 stdio 进程为每个场景自动启动。它拥有模拟状态,并为严格评估器持久化每次执行的调用。没有 MCP 工具裁剪。
有一个微妙的差异很重要。Pydantic 的直接注册发出简洁的 JSON schema,而原始 MCP 发现添加了诸如 get_shipping_optionsArguments 和 Shipment Id 等 schema 标题。一次早期的 MCP 运行将 SHP-1001 复制为 SHP-1000,最终恢复过来,但因为多出一个被拒绝的调用而正确地未通过严格评分。
客户端使用 Pydantic AI 的 PreparedToolset 包装发现的工具,并只规范化其面向模型的 JSON schema:
更安全的渐进披露选项
某些部署应仅暴露在当前工作流阶段有效的动作。Pydantic AI 可以动态准备工具列表:在代码中保留硬性验证
自主性并不要求运营系统接受不可能的动作。调度工具仍然强制执行普通的软件规则:"dispatch_notified": false;只有 notify_dispatch 才返回 true。这种真实的状态可防止模型将升级或调度当作已通知处理。这是一个 API 设计改进,而不是隐藏的下一步或场景特定的提示。
严格评分使用记录的动作台账,而不是 LLM 评委。运行会因错误的处置、错误的承运人或码头、被拒绝的动作、多余的动作、错误的顺序或缺少调度通知而失败。禁用 Pydantic 工具重试,因此不能在该台账之外静默修复格式错误的调用。
已验证的结果
最终应用程序在物理 IQ9075 HTP 上进行了验证,temperature 为 0,top-k 为 1,top-p 为 1,并对两种传输运行相同的四个场景。下表中的每一行都包含两次完整的重复,暴露所有模型发出的调用,使用零次自动工具重试,并要求准确的动作台账。
常驻服务将直接工具的总墙钟时间减少约 23%,MCP 减少约 22%。这是工作流时间而非原始解码吞吐量:每个案例需要四个生成工具的请求,加上第五个用于其最终总结的请求。C++ 进程在这些请求之间保持模型和 QNN 上下文加载状态;CLI 路径每次都初始化一个新的对话进程。
每个案例都遵循以下确切的四调用路径之一:
早期的 CLI 设置只保留第一个解析出的调用。这看起来有帮助,直到一次原始的天气响应显示一次有效的
hold_shipment 调用后跟一个虚构的 notify_shipment 调用。静默丢弃第二个调用让运行看起来比模型的实际行为更好。最终的 all 策略暴露两者,上面的每个结果都在这个更严格的规则下重新运行。
四个通用变更使完全暴露的运行具有可重复性:
- 模型面向的空助手内容渲染为空字符串,而不是字面上的
null。 - 提示要求恰好一次工具调用,然后暂停等待其结果。
- 暂缓和升级的描述区分临时恢复与无兼容选项。
- 处置结果明确报告尚未通知调度。
在一次模型回合中试验多个工具
主套件有意要求每个模型回合调用一个工具。这是可靠的,但它留下了一个有用的问题未解答:这个模型能否识别独立的工作、一次请求多个工具,并且在采取依赖于它们结果的动作之前仍然等待? 单独的多工具案例 将组合的选项工具替换为三个只读观察:get_pending_shipmentget_carrier_optionsget_dock_options
schedule_shipment、hold_shipment、escalate_shipment 和 notify_dispatch。只有在模型将独立的运单、承运人和码头结果组合后,冷链运单才能被调度。它的额外指令是关于依赖处理的,而不是关于答案:
sequential=True。请求还设置了 parallel_tool_calls=True。针对常驻 C++ 适配器运行该案例:
COLD-1 调度 REEFER-2。只有在该动作成功之后,它才在第三个回合中通知调度。第四个回合返回最终的文本总结,未调用任何工具。
评估器报告两个独立的结果。task_passed 需要准确的五个工具、正确的承运人和码头、成功的最终状态和调度通知。batching_passed 另外要求在一次助手响应中至少有两次独立的读取调用,并拒绝任何混合了依赖性状态更改调用的响应。这意味着完全顺序的运行可以完成运输任务,但仍然可能未通过批处理实验。
所有四次运行都发出了相同的模型级工具批次,进行了准确的五次调用,并达到了正确的状态。为三个 Python 读取函数打印的执行顺序可能会有所不同,因为它们并发运行;模型的原始调用顺序仍然可以在
TOOL BATCHES 和适配器的原始请求记录中找到。
这是一次成功的有限演示,而不是证明每一个并行计划都是安全的。模型并没有预先发出整个工作流,也不应该:调度选择依赖于观察结果,而通知依赖于成功的调度。有用的多工具行为只并行化真正独立的调用,并将依赖动作留在后续回合。
生成并执行协调代码
代码生成是与工具调用不同形式的自主性。工具调用代理选择一个或多个声明的动作,接收结果,并决定后续模型回合的操作;代码生成代理写出一个受限程序,可以在沙箱内执行循环、计算和多个 API 调用。该程序可以被测试、批准、缓存和复用,而无需再次请求模型,但它也引入了关于恶意代码、验证和缓存选择的独立关注点。这种方法在 TODO:发布后更新链接 在 Dragonwing IQ9075 上构建代码生成制造代理 中介绍。前沿代理平台增加了什么
成功的多工具案例并不意味着这个小型模型能匹敌当前的前沿代理。它展示了一项有用但有限的技能:模型能够一起请求独立的信息,检查结果,然后选择下一步动作。前沿平台可以处理比这种逐步循环更复杂的工作流。在下面的几节中,我们概述了前沿模型和 API 中代理能力的现状。虽然它们与可部署到边缘的模型之间的差距很大,但后者似乎正朝着前沿模型的能力汇聚。了解这些能力有助于为较小模型即将面对的情况做好准备,也能为聪明的代理型 AI 工程提供灵感。截至 2026 年 7 月,GPT-5.6 系列 将更强的代理训练模型与 Responses API 中的编排功能结合起来。这一区别很重要:一些能力存在于模型中,一些存在于托管运行时中,最先进的工作流则依赖于两者。
延迟工具发现
传统的函数调用请求将每一个工具名称、描述和参数 schema 都放在提示中。大型 MCP 部署可能会花费数万个 token 来描述从未使用的工具。 OpenAI 的工具搜索 代之以给模型提供轻量级的命名空间或 MCP 服务器描述。标记为defer_loading: true 的函数仅在模型搜索它们时才加载。所选定义随后被附加到对话中。这有时被称为延迟工具发现。它减少了提示大小,保留了更多的提示缓存,并让一个代理能够浏览一个大得多的工具目录。
这与仅仅对小型模型隐藏大部分工具不同。模型必须理解一个两阶段协议:搜索适当的能力、检查发现的 schema,然后正确调用它。目前只有 GPT-5.4 及后续的 OpenAI 模型支持该协议。因此,IQ9075 演示直接呈现一小组稳定的工具集。自定义客户端可以构建工具搜索层,但这些边缘模型尚未针对其协议进行训练或验证。
程序化工具调用
程序化工具调用 允许 GPT-5.6 生成 JavaScript,在一个隔离的 V8 运行时(谷歌的高性能 JavaScript 引擎)内协调工具。程序可以并发运行独立调用、使用循环和条件、将一个结果传递给另一个调用,并在返回给模型之前减少大型中间输出。它没有通用的文件系统、子进程、包安装或直接网络访问;它只能通过应用程序允许的工具访问外界。 当控制流可预测时,这可以替代许多模型往返。而运输实验仍会将每个工具结果返回给模型以做出新决定。在本地重现程序化工具调用需要沙箱运行时以及能够可靠地根据类型化工具输入和输出生成编排代码的模型。早期的边缘实验发现,直接的、描述良好的函数调用比再增加一个生成代码协议要可靠得多。模型引导的多代理工作
GPT-5.6 的ultra 模式默认协调四个代理。Responses API 还提供了一个多代理测试版,其中根模型可以创建子代理、赋予它们有界任务、交换消息、等待结果,并综合它们的发现。独立的上下文允许多个代理并发调查代码库的独立部分、比较假设或进行研究,而不会挤占一个对话。
这与前面的多工具案例不同。该案例有一个模型上下文和三个并发执行的 Python 函数。真正的多代理执行需要多个活动的推理流和上下文历史。让多个 QNN 模型上下文保持驻留,或反复交换它们,会给 EVK 内存和 HTP 调度带来更大压力。将子代理串行化是可行的,但会失去主要的延迟收益,并且仍然让一个 3B 模型负责委派和综合。
长视野状态和更广泛的动作面
GPT-5.6 Sol 模型 拥有 105 万 token 的上下文窗口、最多 128,000 个输出 token、通过max 可调的推理强度、结构化输出,以及一个包含 Web 和文件搜索、代码解释、托管 shell、补丁应用、计算机使用、技能、MCP 和工具搜索的 Responses API 工具面。这些工具共同让代理能够检索公开或私有信息、操作图形界面、执行沙箱代码、修改文件,并加载可复用的过程,而无需将每一项能力都定义为自定义函数。该平台还支持对话状态、后台执行 和上下文压缩 以支持长时间运行的工作。
本教程中的 Ministral 包被编译为 4,096 token 的上下文。增大该编译时的限制会消耗更多内存,并使导出和运行时的资源压力更大。更重要的是,仅仅增大 token 限制并不能教会模型在长工作流中保持连贯、从部分失败中恢复、管理审批或使用附加的托管协议。
为何差距仍然存在
缺失的能力不是单一的模型大小限制:- 训练: 前沿模型经过训练和评估,能够处理长工具工作流、延迟 schema、代码编排、计算机使用以及从失败中恢复。
- 模型容量: 委派、协议选择、中间状态跟踪和最终综合在 3B 或 8B 模型中竞争有限的注意力。
- 运行时支持: Genie 和本教程的适配器暴露聊天补全和函数调用,而不是 Responses API 的工具搜索、程序、子代理、压缩和托管工具项类型。
- 内存和计算: 百万 token 上下文和并发的模型代理远远超出了这个导出的 EVK 设置的实际内存和延迟预算。
- 量化: Q4 或 W4A16 部署使这些模型在设备上变得可行,但可能会进一步降低精确、低频率协议 token 和复杂结构化输出上的可靠性。
结论
一个实用的边缘代理无需接收任意代码或硬件访问就可以有意义地实现自主。这个示例将全部六个工作流工具交给一个 3B NPU 托管模型,让它独立选择和排序动作,并将硬性运营规则和条件保留在确定性服务中。 最终的直接和 MCP 运行显示,当模型收到其原生提示格式、简洁的工具 schema 和决策就绪的观察时,这种级别的本地自主性在 IQ9075 上已经可行。当部署需要更狭窄的安全范围时,仍然可以使用渐进披露。故障排除
QNN 上下文错误 5000
如果原始响应包含:openai_genie_server 进程。
2.47 路径必须出现在 /opt/qairt/current 之前。
设备创建失败 14001
QNN 设备创建错误可能是过期的运行时或资源冲突,而非糟糕的答案。停止残留的 Genie 进程,验证没有其他测试在使用 HTP,并重试。如果设备仍然不可用,请重启 EVK。缺少 libatomic
如果 Genie 报告libatomic.so.1: cannot open shared object file,请将 QAIRT 编译器运行时库目录包含到 LD_LIBRARY_PATH 中。第一篇教程中的基础 qairt-env.sh 应在需要该兼容性库时同时包含 QAIRT 目标目录和 lib/aarch64-oe-linux-gcc8.2。
C++ 服务找不到包文件
固定的服务在包根目录查找上下文二进制文件,并按 basename 解析部分配置路径。正常的 QAIRT GGUF 导出会将这些文件保存在artifacts/ 下。重新运行:
C++ /status 返回错误
在固定的服务版本中,即使模型健康,/status 也可能在序列化其响应时失败。启动器有意探测 http://127.0.0.1:8911/ 以检测 C++ 就绪。适配器启动后,请使用 http://127.0.0.1:8001/health。不要将损坏的 status 响应视为推理失败。
有效的 JSON 但没有 Pydantic 工具调用
Genie 返回文本,而不是 OpenAI 工具对象。桥接必须使用原生 Mistral 渲染器和mistral_tool 解析器。通用的 JSON 解析器或另一个模型的聊天模板会将看起来有效的响应变成不可用的调用。
输出过长或不终止
确认使用了原生 Mistral[INST]...[/INST] 包裹和 EOS token。之前没有原生包裹的原始提示运行了超过 11 分钟;而正确包裹的冒烟请求在几秒内完成。
停止演示
在服务器 shell 中使用Ctrl+C。模拟状态仅存在于一次控制台运行中,因此每个场景在下次调用时都会重新开始。
