无 GPU 跑通 Nemotron 离线语音助手
作者在 8 核虚拟 CPU、16GB 内存的 Xeon 虚拟机上,以官方四位量化 GGUF 运行 Nemotron 3.5 Bolt E4B,搭建了包含工具调用的离线语音助手。该模型本身仅处理文本,语音识别与合成需由独立组件承担。
AI 深度解读
没有 GPU,一台 8 vCPU、16 GB 内存的 Xeon 虚拟机,能不能跑起带工具调用的离线语音助手?作者用 NVIDIA Nemotron 3.5 Bolt E4B 做了一次小规模实测:工具选对了,整轮语音交互约 4.5 秒,但日期参数会出错。
这篇最有用的地方,是把模型速度、语音识别、工具参数和内存占用放进同一条链路里看。它展示了一个可行原型,也说明:要让小模型好用,有些工作应该交给代码。
「4GB」模型为什么要下载 6.18 GB
据原文,NVIDIA 在 10 月 23 日发布 Nemotron 3.5 Bolt E4B。它基于 Gemma 4 E4B,由 NVIDIA 通过 NeMo RL、采用 GRPO 强化学习进行后训练,重点优化工具调用、指令遵循和多轮对话。总参数约 8B,「有效」参数约 4B,不能直接当成普通的 4B 模型理解。
原文列出的上下文长度是 64K tokens,但它只接收和输出文本。语音助手还得另接语音识别(STT)与语音合成(TTS)。许可证也要分开看:NVIDIA 版本采用 OpenMDW-1.1,基础 Gemma 4 权重采用 Apache 2.0。
作者使用官方仓库 nvidia/NVIDIA-Nemotron-3.5-Bolt-E4B-4GB 中的 Q4_K_M GGUF。文件实际为 6.18 GB,下载和磁盘预算要按约 6.2 GB 算。
「4GB」对应的是运行时的内存表现。模型包含较大的逐层嵌入查找表,每个 token 只读取少量行;Linux 下 llama.cpp 又会通过内存映射访问文件,因此不必让全部权重一直驻留内存。
作者刚加载时看到 RSS 约 9.6 GB,运行一段时间后,报告模型约占 4 GB,语音组件约占 0.7 GB,合计约 4.7 GB。文件大小、刚加载时的 RSS 和稳定运行后的内存占用,不能混成一个指标。
测试机器支持 AVX-512、VNNI,无 swap、无 GPU;运行时是 llama.cpp CPU build b11913。这些条件比「8 核 CPU」更具体,换一台机器未必能复现同样的速度。
工具选对了,不代表动作就执行对了
作者用 llama-server 提供兼容 OpenAI 的接口,启动命令如下:
llama-server -m Nemotron-3.5-Bolt-E4B-Q4_K_M.gguf --alias nemotron-bolt \
--host 127.0.0.1 --port 8000 -c 8192 -np 1 -t 8 --jinja --metrics
这里要看 --jinja,以及 -c 8192:模型支持 64K 上下文,这次服务实际配置的是 8192。原文给出的推荐采样设置为 temperature 1.0、top_p 0.95,思考模式则通过聊天模板切换。
模型加载耗时 2.5~6 秒。作者使用 llama-bench,以 8 线程重复测试 3 次,生成速度约为每秒 11 tokens。回复一两句话尚可,长回答就容易拖慢体验。
工具测试包含 20 条提示,关闭思考后重复运行 3 轮。其中 15 条需要调用工具,5 条应该直接回答或说明无法完成。最终工具选择正确 60/60,参数完全正确率为 95%。
原文称定义了五个 mock 工具,但只列出四个签名:calculate(expression)、create_event(title, date, time, duration_min)、set_timer(minutes, label)、control_light(room, state, brightness)。后面的语音实验又出现 get_weather(city="Pune"),工具配置没有完整交代。
具体案例比总分更能说明能力边界:
- 「45 秒」被转换为
minutes: 0.75,「一个半小时」被转换为duration_min: 90。 - 「卧室灯调暗到 30%」得到
state: "on"、brightness: 30。 - 「4999 的 18% GST」被转换为
4999 * 0.18。 - 五类无需工具的请求均未误触发调用,包括没有对应工具的订披萨请求。
问题出在日历。系统提示给定当天为周二 2026-10-27、时区 IST,用户要求周五 11 点安排一小时的 sprint planning。模型连续三轮都选对工具,却把日期填成 2026-11-06,正确日期应为 2026-10-30。
开启思考后,这个日期答对了,代价是约 350 字符的推理和该请求 17.6 秒的耗时。整组测试每轮大约慢了 5 倍,只多修正了 20 条中的一个参数。
结构化输出能被解析,只能证明格式合格,不能证明参数符合用户意图。
作者因此建议关闭思考,把日期解析交给代码:提前传入「Friday = 2026-10-30」,或者让工具接收「Friday」后自行解析。
约 4.5 秒的语音交互,时间花在哪里
完整链路是:Piper 生成模拟用户输入的 WAV,faster-whisper 转写,Bolt 选择工具,工具返回结果,Bolt 再生成一句回答,最后由 Piper 合成语音。
语音识别分别测试 base.en 和 small.en,均使用 CPU int8;语音合成使用 en_US-lessac-medium。作者测了六条语音,给出的整轮交互结论约为 4.5 秒。
主要耗时来自两次 LLM 调用:一次决定动作,一次组织回答。Piper 生成一段约 4 秒的回复音频,只需 70~100 ms。作者认为 small.en 多花的约半秒值得,因为识别错误会让后续流程偏离用户要求。
两个例子说明了这种差别。base.en 把 Pune 听成 Pooner,Bolt 仍能调用 get_weather(city="Pune");但它把「讲一个关于编译器的单行笑话」识别成「告诉我你想做什么」,模型只能顺着错误文本介绍自己的能力。换成 small.en 后才正确讲出笑话。
不过,这些都是 Piper 生成的干净英语语音,没有真实房间噪声、口音或中文输入。模型能纠正部分地名,不代表能恢复已经丢失的整句意图。
另外,首 token 时间也容易误导。约 500 tokens 的系统提示和工具 schema,让首次请求等待了 6.4 秒;之后 llama.cpp 缓存了提示前缀,只需处理新消息,普通文本首 token 为 0.36~0.6 秒。
工具调用则要等解析完成才输出,其首 token 时间接近整次调用准备完成的时间。开启思考时,即使很快收到推理 token,用户也还没拿到可播报的答案。语音体验应看实际可用结果的延迟。
我的判断
我认可作者的取舍:让小模型负责识别意图、选择工具,把日期、单位和精确数字交给确定性代码。这比单纯开启思考更适合对延迟敏感的短指令助手。
但这些结果只能支持「值得做原型」。20 条提示、3 次重复属于冒烟测试,不能当成广泛可靠性的证明。重复运行也没有增加请求类型,而且实验只涉及单轮单工具调用,没有测试并行调用或工具链。
原文引用 NVIDIA 给出的 Q4_K_M 成绩:BFCL-v3 为 67.3,ToolSandbox 为 74.1。这是厂商报告,不是作者在这台 CPU 机器上复现的结果。模型卡涉及的 H100、RTX 5090、DGX Spark,也不能拿来解释本次延迟。
稳定内存约 4.7 GB 同样需要带条件理解:测试使用 8192 上下文、单并发和特定 CPU,没有证明更长上下文或并发下仍是这个数。作者还记录过后台任务造成的波动:首轮中位耗时为 3.6 秒,较干净的第三轮为 2.1 秒。
相比固定命令解析,小模型的优势是接受更自然的表达;相比 GPU 服务,CPU 方案的吸引力是离线运行和硬件门槛。原文没有给出这些方案的对照数据,因此不能据此断言成本更低或综合效果更好。
能拿来做什么
- 用 Node 编写工具处理层时,把相对日期、时间和单位转换集中到代码中,传入明确的当前日期与时区,并在执行前校验参数。
- 涉及计算结果时,直接用工具返回值填充回复模板。原文中同一个结果出现过「约 900」和「899.82」,
temperature 1.0下不要依赖模型稳定复述精确数字。 - 为本地语音助手分别记录冷启动、缓存命中、工具准备完成和最终音频生成时间,并重复测试,避免把一次首 token 成绩当成交互速度。
- 做语音原型时先保持回复简短,再用真实目标用户的录音评估识别模型。原文只验证了英语合成语音,中文、口音和噪声环境都需要另测。