1.2GB 离线语音 Agent 真正值得复用的不是 908msTL;DR场景2026-07-18 Hugging Face 社区文章 2026-07-17speech-androidCommitf01841a报告一台 Galaxy S23 Ultra、CPU-only 的离线语音 Agent 测得 Speech End → 首音频 908ms、Resident Memory 1,116MB PSS、首次下载约 620MB。结论908ms 和 1,116MB 是高度条件化结果不能外推到所有 Android真正可迁移的是 4 阶段职责拆分、状态感知 Tool Router、顺序执行降峰、可观测时间锚点组成的系统合同。产出每阶段可独立测量的输入输出、可被外置状态机约束的能力边界、流式首帧与 Turn Trace 协议、边云升级沿用同一 Tool Schema 与结果协议的设计清单。版本矩阵功能 / 组件状态说明HF 社区文章 “We fit a full offline voice agent into 1.2 GB on Android”✅ 已验证2026-07-18T17:07:31.683Z 发布2026-07-18T21:52:21.897Z 修改作者 Ivanaufklarer指定提交soniqo/speech-androidCommitf01841a✅ 已验证标题 “perf(control-demo): stop decoding redundant spoken replies”4 个开源组件VAD / EOU / Router / TTS✅ 已验证4 个 soniqo 模型仓库均存在Silero-VAD-v5-ONNX / Parakeet-EOU-120M-ONNX-INT8 / FunctionGemma-270M-LiteRT-LM / Pocket-TTS-100M-ONNX-INT8Galaxy S23 Ultra CPU-only 2026-07-17 测量条件✅ 已验证HF 文章 #measured-on-the-phone 锚点明确217ms Speech End → Final Transcript✅ 已验证原文测量表294ms 12 次平均 Routing-only Decode✅ 已验证原文测量表179ms TTS 首个音频✅ 已验证原文测量表908ms Speech End → 首音频✅ 已验证原文测量表1,116MB PSS Resident App Memory✅ 已验证原文测量表约 620MB 首次下载✅ 已验证原文 #try-it 锚点8 项工具按联系人拨号 / 拨号码 / 查联系人 / 查音乐 / 播放音乐 / 停止音乐 / 调媒体音量 / 列出能力✅ 已验证ControlTools.ktL23-L136状态过滤仅musicPlaying暴露stop_music✅ 已验证ControlTools.ktL152-L169单 Tool Call 唯一性约束✅ 已验证ControlTools.ktL152-L169单轮 Conversation 新运行时不累积 KV Cache✅ 已验证LiteRtLmRuntime.ktL18-L56endOfSpeechSilenceSec 0.8秒✅ 已验证ControlAgentActivity.ktL327-L340SpeechEnded与TranscriptionCompleted事件分流✅ 已验证ControlAgentActivity.ktL421-L434路由路径遥测输入 / 可用工具 / 调用数量 / 选中工具✅ 已验证ControlAgentActivity.ktL473-L520MemoryMonitor从/proc/self/smaps_rollup读 PSS250ms 周期✅ 已验证MemoryMonitor.ktL5-L67拨号走ACTION_DIAL系统界面不静默呼叫✅ 已验证ControlAgentActivity.ktL679-L707Pocket TTS 80ms 帧固定输出✅ 已验证原文 #the-loop217294179 908简单相加成立❌ 不成立文章明确指出 4 个数字的统计口径不同相加得 690ms与 908ms 相差 218ms 来自不同计时锚点1.2GB 模型下载体积❌ 不成立约 620MB 是落盘资产1,116MB PSS 是运行进程内存峰值内存 最大单模型≈ 327MB❌ 不成立1,116MB PSS 明显高于任一单模型权重工具面 8 项覆盖所有设备能力❌ 不成立仅媒体 / 联系人 / 拨号 8 项是压缩到适配 270M Router的能力空间第三方组件版本号Silero VAD v5、Parakeet-EOU 120M、Pocket TTS 100M 的具体 commit/tag⚠️ 未公开HF 模型卡片仅显示更新时间未给具体版本号217ms 阶段的样本数与置信区间⚠️ 未公开原文未披露12 次平均 Routing 准确率 / 参数正确率 / 长尾⚠️ 未公开原文未披露8 工具集是否覆盖真实使用中的所有意图⚠️ 边界外推文章已明确 8 工具是为 270M 路由裁剪的动作空间外推需重新训练1,116MB PSS 数字的设备 / 温控 / 后台进程 / Android 版本条件⚠️ 测量条件未给原文仅给 Galaxy S23 Ultra CPU-onlyLiteRT-LM / ONNX Runtime / 加速路径的具体配置NNAPI delegate、XNNPack、CPU 线程数⚠️ 未公开仓库app配置未列FunctionGemma 270M 327MB Base 9.5MB LoRA的拆分精度⚠️ 推算文章表述 327MB 9.5MB LoRA未列出 ONNX 量化后单文件尺寸on-device speech SDK for Android — ASR, TTS, VAD, and noise cancellation powered by ONNX Runtime with Qualcomm NNAPI acceleration✅ 已验证GitHub Repo description 元数据**摘要**一台 Galaxy S23 Ultra 上的离线语音 Agent 曾测得 908ms 首音频和1,116MB PSS。但真正值得复用的不是这两个数字而是四阶段职责、状态感知Tool Schema、顺序执行和可观测时间锚点组成的系统合同。**关键词**离线语音 Agent、Android、VAD、EOU、Tool Router、端云协同目录测试条件与数字边界四阶段职责而不是模型清单状态感知的能力边界为什么阶段数字不能简单相加顺序执行与内存账本可迁移的系统合同与端云升级908ms 是这个案例里最容易传播、也最容易被误读的数字。它看起来像一句结论只要选对小模型Android 手机就能在一秒内完成离线语音交互。真正成立的结论要窄得多在一台 Galaxy S23 Ultra 上一条面向有限设备控制任务、CPU-only、四阶段顺序执行的语音流水线曾被原作者测得从用户停止说话到首个播出音频为 908ms。它不是所有 Android 都能一秒响应的证明更不是一个通用离线对话 Agent 的基准。这个案例值得迁移的也不是把四个模型名称原样搬进另一个 App。它真正提供了一种系统拆法把语音活动检测、话轮结束与转写、状态感知的工具路由、流式语音合成拆成可独立测量、替换和约束的组件把开放式生成缩成有限 Tool Schema把设备状态和权限变成运行时能力边界把用户感知延迟拆成从 Speech End、Final Transcript、Tool Call 到 First Presented Audio 的一组时间锚点。以下内容基于 Hugging Face 社区作者文章和speech-android指定提交进行分析没有在 Galaxy S23 Ultra 上复现。性能数字均保留为原作者报告不改写为本文实测。先钉死 908ms 的测试条件原文是 Hugging Face 的 Community Article不是 Hugging Face 官方 Benchmark。文章发表于 2026 年 7 月 18 日测量条件写明为 Galaxy S23 Ultra、2026 年 7 月 17 日、speech-androidCommitf01841a、CPU-only原文测试条件与测量表指定提交。在这组条件下原作者报告Speech End 到 Final Transcript 为 217msTool Routing 为 12 次平均 294ms而且只计算 Routing DecodeTTS 首个音频为 179msSpeech End 到首个播出音频为 908msResident App Memory 为 1,116MB PSS原文测量表。首次运行下载的模型文件合计约 620MB原文下载说明。约 620MB 下载和1,116MB PSS描述的不是同一件事。前者是落盘资产体积后者是运行进程在测量时的比例集大小。权重映射、推理运行时、工作区、音频缓冲、Android/JVM 对象、原生堆、共享库和内存分配碎片都可能进入运行时内存账本。因此1.2GB 这个标题数字更接近运行中的 App 内存量级而不是 APK 大小也不是四个模型文件的简单求和。四个阶段不是模型清单而是四种职责阶段原案例组件与体积系统职责可替换边界VADSilero VAD v5约 2MBONNX Runtime判断当前音频帧是否含语音驱动监听、打断和分段输入 PCM输出语音活动事件或概率EOU / ASRParakeet-EOU 120M INT8约 153MB流式转写并参与判断这一轮话是否已经结束输入语音片段输出 partial、final transcript 与话轮事件Tool RouterFunctionGemma 270M327MB Base 加 9.5MB LoRA在有限工具集合中生成结构化调用不承担开放域聊天输入规范化文本、可用工具和状态输出单个 Tool CallStreaming TTSPocket TTS 100M INT8约 126MB将确定后的短回复持续产出音频帧让播放早于完整合成结束输入文本输出可立即播放的音频流这里必须区分 VAD 和 EOU。VAD 解决的是声学问题这一小段波形里有没有人在说话。EOU 解决的是交互问题用户是否已经表达完这一轮意图。一个人说把音量调到……后停顿半秒VAD 可能只看到静音EOU 还需要结合转写内容、停顿长度和任务语法判断这是思考停顿还是话轮结束。反过来没有 VAD系统会持续把背景声送进后续链路打断与录音状态也更难管理。指定提交中的 Android 配置把endOfSpeechSilenceSec设为 0.8 秒并保留SpeechEnded与TranscriptionCompleted两类不同事件ControlAgentActivity.kt语音配置与事件事件处理。这说明端侧语音 Agent 不是一个听见静音就调用 LLM的黑箱而是一组由声学事件、转写完成事件和状态机共同推进的阶段。这种拆分还改变了故障定位方式。VAD 误报会造成背景声触发或漏掉开头EOU 过早会截断尚未说完的命令过晚则直接增加等待ASR 错误会把正确意图变成错误文本Router 错误表现为工具名或参数错误工具执行可能因为权限、资源或系统状态失败TTS 即使已生成音频也可能卡在播放器启动或首帧呈现。把所有问题合并成一个Agent 成功率或端到端耗时只能看到结果无法找到修复点。流水线架构的价值正在于每个边界都能记录输入、输出、置信、耗时和失败原因并允许用局部回放数据单独回归。Router 的核心不是 270M而是状态感知的能力边界FunctionGemma 在这里不是缩小版通用助手。指定提交定义的工具面只有八项按联系人拨号、拨号码、查联系人、查音乐、播放音乐、停止音乐、调媒体音量、列出能力ControlTools.kt工具声明。它接收的是经过规范化的短命令不接收长对话历史每次生成都使用新的单轮 Conversation避免 KV Cache 随命令累积LiteRtLmRuntime.kt单轮运行时。更重要的是运行时不会把全部工具无条件交给模型。代码读取musicPlaying只有音乐正在播放时才暴露stop_music随后要求解析结果必须恰好包含一个 Tool Call零个或多个都拒绝执行ControlTools.kt状态过滤与单调用约束。紧凑提示词只携带当前可用函数名和playing/idle状态而不是把无限设备能力塞进上下文CompactPrompt.kt。实际路由路径也会记录输入、可用工具、调用数量和最终选择形成可检查的路由遥测ControlAgentActivity.kt路由与执行。这是一条比换更大的端侧模型更可迁移的原则能力集合应由系统状态生成而不是由模型自行想象。可以把它抽象为available_tools f(device_state, permission, user_context, risk_policy, connectivity)原项目公开代码只实现了其中一部分状态过滤主要示例是音乐播放状态与 Android 权限。把用户上下文、风险策略和网络状态加入这个函数是基于其结构的工程推导不是原项目已经实现的事实。代码还把决定做什么和如何向用户确认分开。路由运行时在模型开始生成say参数时就停止继续解码设备执行层根据真实联系人、媒体检索结果或音量值生成确定性反馈LiteRtLmRuntime.ktRouting-only DecodeControlTools.kt结果感知反馈。这减少了无价值的文本生成也避免模型在联系人不存在、媒体检索失败时仍说出成功话术。模型负责选择结构化动作系统负责验证参数、执行动作和陈述真实结果。这条路线能够在小模型上成立与任务边界高度相关。联系人、媒体和音量控制有明确的动词、参数与设备状态回复通常只有一句执行结果可以由本地 API 验证也不需要检索开放知识。原文明确说明 LoRA 针对精确 Tool Schema 和紧凑设备状态序列化进行训练运行时再只提供当前有效工具原文 Router 说明。因此270M 模型的任务不是理解世界并规划一切而是在一个经过产品设计压缩的动作空间里完成结构化分类与参数提取。把工具从八项扩到几百项、加入多轮追问、开放问答或跨应用复杂规划Prompt 长度、歧义、生成长度、内存和可靠性都会变化原有延迟不能沿用。这也意味着 Tool Schema 本身是需要版本管理的产品接口。工具名、参数类型、必填项、枚举范围、权限前置条件和状态可见性一旦改变LoRA 训练分布、Prompt 序列化、解析器和执行器都可能失配。迁移时应把 Schema 版本写入 trace 和模型包元数据建立兼容性测试旧模型遇到新工具应看不到它新模型遇到旧执行器应被拒绝参数越界应由执行层截断或失败而不是让模型用自然语言自行补救。217、294、179 不能相加还原 908把 217ms、294ms 和 179ms 相加得到 690ms距离 908ms 还有 218ms。这个差值不能直接命名为框架开销因为四个数字的统计口径并不一致。217ms 是 Speech End 到 Final Transcript。294ms 是 12 次 Routing-only Decode 的平均值不是同一条 908ms 端到端样本里的必然路由耗时。179ms 是 TTS 首个音频指标而代码同时跟踪模型首帧回调、AudioTrack 播放启动和首帧实际呈现它们不是同一个时间点。908ms 则从 Speech End 锚定到首个播出音频天然还覆盖转写事件交接、文本规范化、状态读取、Prompt 构造、解析、工具执行、TTS 调度、播放器启动与系统调度等路径。指定提交的端到端计算把turnAnchorMs放在 Speech End把 TTS 开始前已经发生的时间与首音频时间合并为roundMs之后还会用 AudioTrack 的 first presentation 时间更新指标ControlAgentActivity.kt端到端时间锚点首帧呈现。在没有原始逐轮日志的情况下正确做法是保留这些口径差异而不是用三个阶段数字倒算第四个数字。同样12 次 Routing 只说明一个很小样本中的平均延迟。它没有给出工具选择准确率、参数正确率、长尾延迟、热降频后的稳定性也没有覆盖口音、噪声、远场和多语言条件因此不能被写成可靠性证明。顺序执行降低峰值叠加不等于内存只看最大模型原文把顺序执行列为控制内存的重要设计阶段一个接一个运行避免多个推理工作区、激活张量和临时缓冲同时达到峰值原文内存解释。这条方向成立但不能进一步推导成整条流水线的峰值内存等于最大单模型。模型权重和推理引擎可能在不同阶段之间保持加载VAD 与音频管线可能持续驻留Android Runtime、LiteRT-LM、ONNX Runtime、播放器和 App 状态也会共同占用内存。项目中的 MemoryMonitor 从/proc/self/smaps_rollup读取进程 PSS并以 250ms 周期更新峰值MemoryMonitor.kt。原作者报告的 1,116MB PSS 本身就明显高于任何一个单独下载模型。因此顺序执行应被理解为减少工作区峰值重叠而不是把总内存公式简化为max(model_size)。真正可迁移的是系统合同第一份可迁移资产是阶段合同。VAD、EOU/ASR、Router、TTS 都应有独立输入输出、错误语义、超时、取消机制和指标。这样换 ASR 不必重写工具执行换 Router 不必改音频播放升级 TTS 也不应改变权限策略。模型只是合同的一种实现。第二份资产是外置状态机。端侧小模型的能力不应靠更长 Prompt 补足而应靠更窄的候选空间、显式状态和执行前校验。对于移动端设备控制权限未授予、媒体不存在、当前没有播放、联系人查找失败都应先在系统层形成状态再决定哪些工具可见、哪些参数可接受、哪些动作必须终止。指定代码中的拨号通过ACTION_DIAL打开系统拨号界面而不是静默直接呼叫ControlAgentActivity.kt拨号执行这也说明高影响动作可以保留系统确认界面。第三份资产是面向感知延迟的流式设计。Pocket TTS 以固定 80ms 帧持续输出播放可以在完整回复合成结束前开始原文流水线说明。类似思路也适用于 ASR partial、Router 的结构化早停和工具执行后的短确认。优化目标不应只写总耗时而应分别记录 Speech End、Final Transcript、Route Ready、Action Done、TTS First Chunk、Playback Start、First Presented Audio 和 Response Complete。只有时间锚点固定跨设备、跨版本和跨模型比较才有意义。第四份资产是可观测性。端侧问题常常不是模型单点失败而是音频焦点、热降频、线程调度、播放器缓冲、权限状态和模型加载共同造成。路由输入、可见工具、选中工具、解析失败原因、各阶段耗时、当前与峰值 PSS、首帧回调与首帧呈现之间的差值都应进入同一 Turn Trace。这样才能区分模型慢“模型选错”“工具执行慢和声音已经生成但没有及时播出”。从端侧案例推导边云升级协议以下是从该架构继续推导的边云设计不是原项目事实。最稳妥的升级方式不是把端侧流水线替换成云端大模型而是保持同一套 Tool Schema、状态快照和结果协议本地优先完成 VAD、EOU/ASR、低风险路由和短 TTS当本地没有可用工具、解析失败、置信不足、任务需要开放知识或者策略要求更高等级确认时再把规范化文本、允许的工具子集、必要状态和 trace id 发送到云端。云端返回的仍然应是受约束 Tool Call 或明确的不可执行结果设备侧继续做权限检查和最终执行。工具还应按风险分级。调节媒体音量可以低摩擦执行查找联系人或音乐属于读取型动作应避免在语音反馈中泄露过多结果拨号、发消息、支付、门锁和车辆控制属于更高风险动作需要确认、系统 UI、二次认证或完全禁止离线自动执行。风险分级不是让模型更谨慎地想而是让运行时改变可见工具、参数范围和确认协议。最后复现实验应先复制测量方法再复制结论。至少要固定设备 SoC、Android 版本、CPU/NPU 路径、冷启动或热启动、模型与运行时版本、线程数、温控状态、电量、音频输入方式、说话距离、噪声条件、语言与口音、命令集合、样本数和 P50/P95/P99。没有这些条件“1.2GB”“CPU-only”908ms都只是一个特定系统切片。验收指标也应从模型能不能跑升级为任务是否可控地完成。一条完整语音控制用例至少包含是否正确起听、是否在正确位置结束、最终文本是否保留关键实体、是否只暴露允许工具、是否生成唯一合法调用、参数是否通过校验、动作是否真实成功、反馈是否与真实结果一致、首音频是否在预算内出现、失败时是否保持安全状态。只有这些指标同时成立端侧 Agent 才是一个系统而不是四个能够单独演示的模型。这个案例证明的是对于受限的设备控制任务端侧语音 Agent 可以由多个小型、专职、可测量的组件组成并通过状态感知的 Tool Router 把模型能力关进明确边界。它没有证明手机里已经装下万能 Agent也没有证明所有手机、语言和声学环境都能复制同一组数字。真正值得复用的不是 908ms而是让每一毫秒、每一个工具和每一次状态转换都可解释、可替换、可拒绝。FAQ908ms 能代表所有 Android 手机吗不能。它绑定 Galaxy S23 Ultra、CPU-only、指定提交和原作者测试条件。1.2GB 是模型下载体积吗不是。约 620MB 是首次下载量1,116MB 是测试时进程 PSS。为什么不直接放一个更大的端侧模型有限设备控制更需要职责分离、状态约束和可拒绝执行单纯扩大模型不能替代这些系统边界。错误速查卡症状根因定位修复把 217294179 当作端到端总耗时4 个数字统计口径不同217 是 Speech End→Final Transcript 单样本294 是 12 次 Routing Decode 平均179 是 TTS 首个音频908 是 Speech End→首音频不能用局部数机械还原端到端核对测量表的样本数与计时锚点统一用roundMs firstAudioMs - turnAnchorMs这种事件轴定义禁止倒算宣称1.2GB 模型下载把 1,116MB PSS 当成模型文件总大小查 PSS 与下载说明是否被混用明确区分落盘资产≈620MB“与运行时进程 PSS1,116MB”分别报告推论峰值内存 最大单模型≈327MB顺序执行降的是工作区叠加不是把总内存公式简化用MemoryMonitor读/proc/self/smaps_rollup把 PSS 拆为权重 运行时 工作区 缓冲 Android Runtime 共同占比套用 908ms 到所有 Android把 Galaxy S23 Ultra CPU-only 2026-07-17 Commit f01841a 当成通用 Android查测试条件表任何数字迁移前先复制测量方法至少固定 SoC / Android 版本 / CPU vs NPU / 温控 / 后台进程12 次 Routing 平均 ≈ 可靠性证明小样本平均延迟不蕴含准确率、参数正确率、长尾稳定性看样本数 / 重复运行 / 失败分布增加重复运行、统计 P50/P95/P99、报告工具选择与参数正确率Router 在空闲态也暴露stop_music工具面被无条件全部塞入提示词查available_tools是否经过状态过滤引入f(device_state, permission, user_context, risk_policy, connectivity)函数模型同时输出 0 个或多个 Tool Call 时仍被采纳缺少唯一性约束查解析器是否要求恰好一个 Tool Call解析失败直接拒绝执行并在 trace 中标注未生成唯一调用把单轮模型重复用于长命令Conversation 与 KV Cache 随命令累积看运行时是否每次新建 Conversation路由层固化单轮、规范化短命令长命令先摘要再路由VAD/EOU/ASR 错误被合并报为端到端成功率流水线各阶段没有独立指标查每个边界是否记录输入/输出/置信/耗时/失败原因引入阶段级 Trace 与回归测试每个阶段可单独回放TTS 已生成音频但首帧未播放被记为模型慢区分不了模型首帧回调、AudioTrack 启动、实际首帧呈现看是否同时跟踪这三个时间点用 AudioTrack 的 first presentation 时间更新指标拨号被静默直接呼叫缺少风险分级与系统确认查拨号是否走ACTION_DIAL高影响动作统一走系统 UI 或二次认证LoRA 升级后旧执行器失配Tool Schema 没有版本管理查工具名/参数/必填项是否变更把 Schema 版本写入 trace 和模型包元数据建立兼容性测试8 工具模型被扩到几百项工具仍期待 270M 性能270M 的能力空间被训练分布锁死看 LoRA 训练数据与 Schema 是否同步变化扩工具时重新训练 LoRA、更新 Prompt 序列化、做兼容性测试EOU 过早截断命令 / VAD 漏报开头 / ASR 实体错误阶段之间没有分流SpeechEnded与TranscriptionCompleted事件查事件分发与 EOU 触发条件区分声学事件与转写完成事件EOU 失败时仍保留 partial结论端侧万能 Agent 已实现把 8 项设备控制推成开放对话看工具集合与回复是否被验证明确任务边界对话类任务不能直接套用本架构误以为 LiteRT-LM / ONNX Runtime 加速路径可默认等价加速配置NNAPI delegate、XNNPack、CPU 线程数未公开查 app build 配置任何外推需先复制仓库app模块的具体配置Galaxy S23 Ultra 上 1,116MB PSS 被外推到所有手机没有锁定 SoC / Android 版本 / 后台进程复现实验至少固定 1 款参考设备 1 款不同 SoC 比对把 294ms 12 次平均当成每次路由的 SLA12 次只是过去 12 次的平均没有 P95 / P99跑重复 N≥50报告均值 P95 / P99 失败分布作者武子康的个人博客原文链接https://huggingface.co/blog/aufklarer/offline-voice-agent-android仓库指定提交https://github.com/soniqo/speech-android/commit/f01841a