我对端侧小模型的兴趣不是来自“把大模型塞进手机”这类口号而是来自一个很具体的需求做一个离线会议助手。它要在笔记本和手机上工作能在没有网络的会议室里生成本地摘要能先把敏感信息在设备上处理掉必要时再把脱敏后的材料交给云端兜底。这个需求听起来不大却能把端侧 AI 的难点都翻出来模型下载、冷启动、NPU 和 CPU fallback、音频转写后的本地摘要、隐私缓存、云端兜底开关、设备碎片化、发布灰度、回滚和验收。端侧小模型不是云端大模型的迷你版它更像一个必须长期住在用户设备里的功能模块。下面我用这个离线会议助手做主线记录一套可以复制的工程方案。它不是某个真实产品的内部实现所有名称、URL、token 和客户信息都用公开示例替代。先把产品边界写窄离线会议助手第一版只承诺四件事。它能在本地处理一段会议音频转写文本生成 5 到 8 条要点。它能识别并遮蔽明显的手机号、邮箱、身份证样式字段、合同号样式字段。它能在用户同意后把脱敏摘要和片段索引发给云端让云端生成更完整的纪要。它能在没有网络时保存草稿等用户确认后再同步。它不承诺自动识别所有发言人不承诺做法律级事实核验不承诺离线回答最新政策也不承诺把两小时会议完整压进一个小模型上下文。边界写窄不是保守而是端侧产品必须对资源和信任负责。第一版的本地链路是这样的音频文件或实时转写文本 - 本地分段器 - 隐私预处理器 - 小模型摘要器 - 本地草稿和片段索引 - 用户确认 - 可选云端兜底这里的小模型不是唯一主角。分段、脱敏、缓存、权限和兜底一样重要。端侧 AI 做得好常常不是因为模型特别聪明而是每一步都知道自己该做多少。一个请求长什么样本地接口没有必要模仿云端 OpenAI API但保留结构化请求会让调试和回放方便很多。会议助手内部给本地推理服务发这样的请求{ request_id: local_meeting_20260615_001, mode: offline_summary, device_context: { platform: macos, power: battery, network: offline, accelerator: ane, memory_pressure: normal }, privacy_policy: { redact_before_cache: true, allow_cloud_fallback: false, retention_hours: 24 }, input: { meeting_title: 产品周会, language: zh-CN, segments: [ { start_ms: 12000, end_ms: 42600, speaker: S1, text: 我们本周先把离线摘要做成 beta不要默认上传音频。 }, { start_ms: 43100, end_ms: 86500, speaker: S2, text: 如果用户打开云端增强只上传脱敏后的摘要和片段索引。 } ] }, output: { max_bullets: 8, include_actions: true, include_risks: true } }返回也要能解释本地做了什么{ request_id: local_meeting_20260615_001, status: ok, summary: [ 离线摘要 beta 阶段不默认上传音频。, 云端增强需要用户主动开启并且只接收脱敏后的摘要和片段索引。 ], actions: [ { owner: 未识别, text: 补充云端增强的授权弹窗文案。, evidence_segment_ids: [2] } ], redactions: [ { type: email, count: 1, replacement: [EMAIL_1] } ], runtime: { model: meeting-summarizer-1.8b-q4, backend: ane, input_tokens: 1462, output_tokens: 214, prefill_ms: 382, decode_ms: 1040, peak_memory_mb: 1180, cloud_fallback_used: false } }这类结构化返回有两个好处。产品可以把“云端未使用”“已脱敏”“本地草稿保存 24 小时”清楚地展示给用户工程也可以在不记录原文的情况下知道模型是否跑在加速器上、耗时多少、是否触发 fallback。设备能力探测比模型榜单更早端侧项目最容易犯的错是先挑一个看起来分数不错的模型再想办法让它跑起来。会议助手反过来做。先定义设备矩阵再选模型。第一版只覆盖三类设备设备档位目标允许策略高端笔记本10 分钟会议 15 秒内出草稿本地摘要、脱敏、可选云端增强普通手机10 分钟会议 30 秒内出草稿本地短摘要、低电量降级低端或旧设备只做隐私预处理和片段索引不强行本地生成长摘要设备探测在功能入口就发生而不是推理失败后才补救{ device_id_hash: dev_anon_7f2a, platform: android, os_version: 15, app_version: 1.4.0, accelerators: [nnapi, cpu], recommended_backend: nnapi, available_memory_mb: 3260, battery_level: 0.42, thermal_state: nominal, model_profile: meeting-lite }如果设备只能稳定跑meeting-lite界面就不要给用户承诺“完整纪要”。可以给“本地要点”和“云端增强”两个清晰选择。端侧 AI 的体验很大一部分来自诚实不是来自把所有能力都塞进一个按钮。模型包不只是一个文件会议助手用了两个本地模型包。一个很小负责隐私预处理和意图分类一个稍大负责会议摘要草稿。它们和 tokenizer、prompt 模板、后处理规则绑定发布。模型清单像这样models: - id: privacy-filter-280m-q8 role: privacy_preprocess size_mb: 210 min_app_version: 1.4.0 backends: [ane, nnapi, cpu] checksum: sha256:example-privacy-filter retention: bundled - id: meeting-summarizer-1.8b-q4 role: offline_summary size_mb: 1260 min_app_version: 1.4.0 backends: [ane, nnapi, directml, cpu] checksum: sha256:example-meeting-summarizer download: wifi_only: true resume: true keep_previous_version: true prompts: summary_template_version: summary-v6 action_template_version: action-v3 postprocess: schema_version: meeting-note-v2这里最容易被低估的是版本绑定。新模型可能要求新的 tokenizer也可能改变输出格式。只替换权重文件不更新 prompt 和后处理很容易让客户端解析失败。端侧发布比服务端更麻烦因为坏版本已经到了用户设备上不是重启一个 Pod 就能消失。所以模型包必须有签名、校验、最小 App 版本、可回滚的上一版本。下载也要可恢复不能让用户在会议前卡在一个 1GB 文件上。本地摘要不是把整场会议塞进去小模型上下文有限移动设备更有限。会议助手不把整场会议一次性喂给模型而是先做分段再做局部摘要最后合并。一个 45 分钟会议可能被拆成 20 到 40 个片段。分段器用简单规则和轻量模型混合静音超过一定时间、话题关键词变化、发言人变化、片段 token 超过上限。每段生成一个局部草稿{ segment_id: 12, time_range: 00:18:22-00:21:05, input_tokens: 1180, local_summary: 团队决定 beta 阶段默认只保存本地草稿云端增强需要用户主动确认。, actions: [ 补充授权弹窗, 把云端增强的上传字段写入隐私说明 ], risk_flags: [privacy_notice_required] }全局摘要只读取这些局部草稿和少量证据片段而不是读取完整原文。这样做牺牲了一部分跨段推理能力但换来稳定的内存、可解释的证据和更低的隐私风险。端侧小模型适合做“足够好的草稿”不是替人拍板。会议纪要这种场景尤其要保留证据片段让用户能点回去看原话。没有证据的漂亮总结在线上看起来聪明在实际工作里很危险。隐私预处理在缓存之前“数据不出设备”不等于隐私问题自动解决。会议助手会在本地缓存草稿、片段索引和模型中间结果。如果缓存里保留了手机号、邮箱、合同号风险仍然在设备上长期存在。所以我们把隐私预处理放在缓存之前。原始转写文本只保存在受保护的临时区默认 24 小时过期。进入持久缓存的内容必须先脱敏。privacy: raw_transcript: storage: protected_tmp ttl_hours: 24 sync: false redacted_segments: storage: encrypted_local_db ttl_days: 30 sync: optional cloud_payload: allowed_fields: - redacted_summary - action_items - evidence_segment_ids - language denied_fields: - raw_audio - raw_transcript - speaker_voiceprint脱敏规则不要只靠大模型。确定性规则负责邮箱、手机号、证件号样式、银行卡样式小模型负责识别“这可能是客户名称、内部项目代号、合同编号”这类上下文信息。两者都不完美所以界面上要允许用户查看将要上传的内容。云端兜底的请求因此长这样{ request_id: cloud_enhance_20260615_001, consent_id: consent_local_9a21, source: offline_meeting_assistant, payload: { language: zh-CN, redacted_summary: [ 团队决定 beta 阶段默认只保存本地草稿。, [PROJECT_1] 需要补充授权弹窗和隐私说明。 ], action_items: [ 补充授权弹窗, 确认云端增强上传字段 ], evidence_segment_ids: [3, 12, 18] } }注意这里没有原始音频、没有完整转写、没有真实人名邮箱。云端拿到的是本地预处理后的材料。这个链路不如“全量上云”聪明但它更容易被用户和企业安全团队接受。云端兜底是正常路径不是失败路径很多端侧方案把云端兜底写成“本地失败才调用云端”。会议助手里不这么设计。云端增强是一条用户可见的正常路径本地先出草稿用户看过脱敏内容后选择是否增强。本地模式下用户得到的是要点、待办和风险提示。云端增强模式下用户可以得到更完整的章节、决策背景、冲突点整理和格式化纪要。二者不是谁替代谁而是成本、隐私和质量的不同选择。工程上也不能做双倍浪费。云端增强不从零处理会议而是接收本地摘要、待办、片段索引和少量已脱敏证据。这样云端上下文更短费用更低用户也知道上传了什么。如果企业策略禁止云端功能仍然可用只是不展示增强入口{ policy: local_only, features: { offline_summary: true, privacy_preprocess: true, cloud_enhance: false, share_redacted_note: true }, message_code: cloud_disabled_by_policy }这个错误语义比“网络不可用”重要。用户需要知道是离线、策略禁止、低电量降级还是设备不支持。指标只记录运行状态不记录会议内容端侧 AI 也需要观测否则线上问题会变成客服截图。难点是不能把用户会议内容传回去。会议助手只上报匿名运行指标和失败原因。第一版指标字段如下字段用途eventmodel_loaded、summary_completed、fallback_used等app_version排查客户端版本问题model_id模型版本和量化版本backendane、nnapi、directml、cpudevice_tier高端、中端、低端的匿名档位network_state在线、离线、弱网battery_state插电、电池、低电量thermal_state正常、温热、过热降级input_token_bucket只记录分桶不记录文本latency_ms端到端耗时prefill_ms输入处理耗时decode_ms生成耗时peak_memory_mb峰值内存fallback_reasonCPU 回退、模型缺失、策略禁止等redaction_count_bucket脱敏数量分桶不含具体值一条上报像这样{ event: summary_completed, app_version: 1.4.0, model_id: meeting-summarizer-1.8b-q4, backend: nnapi, device_tier: mid, network_state: offline, battery_state: battery, thermal_state: nominal, input_token_bucket: 8k-16k, latency_ms: 23840, prefill_ms: 6920, decode_ms: 11860, peak_memory_mb: 1420, fallback_reason: none, cloud_fallback_used: false }这里故意不上传会议标题、发言人、摘要文本、具体脱敏值。端侧功能的观测要克制宁可少一点也不要把信任优势毁掉。排障记录为什么一台手机突然慢了三倍试点时遇到过一个典型问题同一个 10 分钟会议样本高端笔记本 11 秒出草稿中端手机有时 24 秒有时接近 70 秒。模型没变输入也没变。排查不是从模型质量开始而是从指标分桶开始。慢请求有三个共同点backendcpu、thermal_statewarm、fallback_reasonaccelerator_compile_failed。也就是说NNAPI 编译失败后回退到了 CPU手机又处于温热状态频率下降延迟自然翻倍。继续查本地日志发现失败集中在一个算子组合上。模型里有一段动态 shape对某个驱动版本不稳定。解决方式不是让用户重试而是给这类设备下发meeting-lite配置限制片段长度并禁用触发问题的图优化。配置像这样device_overrides: - match: platform: android accelerator: nnapi driver_family: example-driver-31 model_profile: meeting-lite max_segment_tokens: 900 graph_optimizations: dynamic_shape_fusion: false fallback: allow_cpu: true max_latency_ms: 45000这个问题说明端侧排障有自己的节奏。服务端慢了可以看 Pod、看 GPU、看队列。端侧慢了要看设备温度、驱动、后端、内存压力、是否插电、是否刚下载完模型。没有匿名指标团队只能在少数测试机上猜。发布模型灰度要像客户端灰度会议助手的发布不是把模型文件放到 CDN 就结束。第一版发布分成四层客户端代码、模型清单、模型文件、远程策略。任何一层都可能需要回滚。一个可执行的发布步骤如下。先发布客户端1.4.0只带隐私过滤小模型摘要模型按需下载。功能入口默认对 5% beta 用户开放。rollout: app_min_version: 1.4.0 audience: beta percent: 5 default_mode: local_summary cloud_enhance_default: off再下发模型清单但不自动下载大模型。用户进入会议助手时如果设备符合条件且在 Wi-Fi 下才提示下载。download_policy: meeting-summarizer-1.8b-q4: wifi_only: true require_battery_level_gte: 0.3 allow_metered_network: false keep_previous_version: true接着扩大到 20%观察模型加载成功率、下载失败率、摘要完成率、CPU fallback 比例、低电量降级比例。这里的核心不是 DAU而是端侧运行健康。最后才开放云端增强。云端增强必须单独灰度因为它牵涉授权文案、脱敏 payload、服务端成本和企业策略。cloud_enhance: percent: 10 require_user_consent: true upload_payload: redacted_only max_payload_tokens: 6000 blocked_when: - enterprise_policy_local_only - redaction_confidence_low这个节奏慢一些但端侧功能需要这种耐心。坏模型一旦下载到大量设备回收成本比服务端高得多。回滚用户设备上的坏版本不会自动消失端侧回滚分四种情况。如果只是策略问题比如云端增强授权文案不够清楚关闭远程开关即可features: cloud_enhance: enabled: false reason: consent_copy_revision如果是模型质量问题把模型清单指回上一版并保留已下载的新模型但不再选择它。不要立刻删除避免用户在弱网下反复下载。models: meeting_summary_active: meeting-summarizer-1.8b-q4-20260610 meeting_summary_blocked: - id: meeting-summarizer-1.8b-q4-20260615 reason: action_item_regression如果是某类设备的运行时问题只对该设备族降级到 lite 模型或 CPU 路径。不要全量回滚否则会牺牲其他设备上已经稳定的体验。device_overrides: - match: device_tier: mid backend: nnapi os_version_lte: 15 force_model: meeting-summarizer-lite-900m-q4如果是客户端解析崩溃才需要紧急发版并通过远程策略关闭相关模型能力。端侧系统要默认假设“有一部分用户暂时不会升级”所以旧客户端兼容性要保留一段时间。回滚演练也要做。验收不是写一段文档而是在测试设备上真的把模型从新版本切回旧版本确认草稿还能打开缓存不会损坏用户不会丢会议记录。验收端侧 AI 要看体验也要看不打扰会议助手第一版的验收线写得很具体。指标验收标准高端笔记本 10 分钟会议草稿P95 小于 15 秒中端手机 10 分钟会议草稿P95 小于 30 秒本地隐私预处理邮箱、手机号样式召回率大于 99%云端 payload不包含原始音频和完整原文模型冷启动热启动小于 2 秒冷启动小于 8 秒CPU fallback 比例支持设备上小于 5%低电量降级电量低于 20% 时不启动大模型崩溃率beta 用户不高于基线 0.1 个百分点回滚耗时远程策略 5 分钟内生效用户可见控制能查看本地保存、上传内容和清除缓存质量验收也不能只看自动分数。我们准备了一组会议样本短站会、产品评审、客户访谈、技术排障、带大量数字的项目同步。每个样本都有人类参考摘要检查三类问题关键决策有没有漏待办有没有编造敏感字段有没有外泄。端侧还有一个特殊验收不打扰。模型下载不能抢用户流量低电量时不能硬跑手机发热时不能继续生成长摘要用户关闭云端增强后不能偷偷上传。很多 AI 功能失败在太积极而端侧功能尤其要学会克制。这套方案真正改变了什么做完这个离线会议助手我对端侧小模型的判断更朴素了。它不是云端大模型的替代品。它更适合做第一层离数据近、响应快、隐私敏感、可以离线、成本稳定。它负责把原始材料变成更干净、更短、更有结构的中间结果。云端负责更复杂的推理和更完整的表达但前提是用户知道并同意哪些数据会离开设备。开发上也要换心态。服务端 AI 项目常常围绕吞吐、队列和 GPU 成本转端侧 AI 项目则绕不开电量、温度、包体、下载、驱动、缓存、权限和回滚。NPU 不是自动加速按钮小模型也不是随便放进 App 就能用。最可靠的路径是从一个窄场景开始。比如会议助手只先做好本地摘要、隐私预处理和云端增强确认。把请求样例、配置、指标、排障、发布、回滚和验收都跑通以后再谈更多能力。端侧 AI 最后拼的不是一句“本地运行”而是它在用户设备上长期、稳定、可解释地运行。当一个功能能在断网时给出可用草稿在联网时清楚说明上传内容在低电量时主动降级在出问题时能回滚到旧模型我才会觉得它真的进入了产品阶段。否则它只是一次演示。参考资料W3C Web Neural Network APIWebNN 介绍Microsoft WebNN Overview