Highlight
这场 session 用 SiriKit Media 的高频播放请求、
INMediaSearch调试、Siri 词表和 Now Playing 命令,说明音频 App 如何把语音播放做成可依赖的日常入口。
核心内容
音频 App 接入 Siri 后,最危险的失败场景很朴素:用户第一次说 “play your app”,结果没有任何内容播放。Danny Mandel 在开场直接把播放成功率放到最高优先级,因为语音交互的信任门槛比传统界面更高;第一次失败之后,用户很可能不会再尝试。(00:37)
第二个质量问题是启动速度。逐字稿里特别点名 CarPlay:人在车里免手操作时,Siri 对超时更严格,App 如果没有足够快地开始播放,系统会结束这次处理。媒体 App 的 Siri 工程投入,应该先落在稳定播放栈和低延迟启动上。(01:07)
这场 session 的核心判断来自真实流量分布。超过一半的 SiriKit Media 请求是泛化播放,比如 “play your app”;约 30% 只给出媒体名称;带艺术家的复合搜索约 5%;播放列表查询约 5%。把这四类请求做好,就能覆盖当时超过 90% 的 SiriKit Media 流量。(02:31)
剩下的质量来自 Siri 能不能听懂你的内容。通用模型已经理解音乐类型、媒体类型、排序和发行日期,但它不知道用户自己的歌单,也不知道你的 App 里独有的节目或播放列表。INVocabulary 和全局 vocabulary plist 负责把这些名字同步给 Siri;MPRemoteCommandCenter 和 MPNowPlayingInfoCenter 则让 Siri 的播放控制和”正在播放什么”问题走同一套 Now Playing 基础设施。(08:23)
详细内容
用 INMediaSearch 覆盖高频播放请求
(05:46)Intent Handler 里最关键的入口是 resolveMediaItems(for:with:)。Siri 会把用户的话解析成 INPlayMediaIntent,媒体查询条件放在 intent.mediaSearch 上。泛化播放可能没有 mediaSearch;“play music” 会只带 mediaType;“play Special Disaster Team” 会只带 mediaName;“play Maybe Sometime by Special Disaster Team” 会同时带标题和艺术家;“play my WWDC playlist” 会把 mediaType 设成 playlist,并把 mediaName 设成 WWDC。
func resolveMediaItems(for intent: INPlayMediaIntent, with completion: @escaping ([INPlayMediaMediaItemResolutionResult]) -> Void) {
let mediaSearch = intent.mediaSearch
resolveMediaItems(for: mediaSearch) { optionalMediaItems in
guard let mediaItems = optionalMediaItems else {
return
}
completion(INPlayMediaMediaItemResolutionResult.successes(with: mediaItems))
}
}
关键点:
intent.mediaSearch是 Siri 给 App 的结构化查询,调试时先看哪些字段有值。- 示例里的第二个
resolveMediaItems(for:)是 App 自己的搜索逻辑,负责把INMediaSearch映射到真实媒体项。 - 空的
mediaSearch不能直接当失败处理,session 明确说泛化播放占比最高,App 应该准备默认播放策略。 - 返回
INPlayMediaMediaItemResolutionResult.successes(with:)代表这次解析拿到了可播放候选项,下一步才进入播放处理。
用用户词表修正个人内容识别
(10:01)Siri 的自然语言模型会识别常见音乐概念,但用户自己的歌单名容易被拆错。session 里的例子是 ”70s punk classics”:默认模型可能把 70s 识别成发行年代,把 punk classics 识别成媒体名称。把这个歌单名同步到用户词表后,Siri 会把它作为播放列表标题交给 App。
let vocabulary = INVocabulary.shared()
let playlistNames = NSOrderedSet(objects: "70s punk classics")
vocabulary.setVocabularyStrings(playlistNames, of: .mediaPlaylistTitle)
关键点:
INVocabulary.shared()取得当前 App 的用户词表入口。NSOrderedSet的顺序有意义,Siri 会优先考虑排在前面的词条。.mediaPlaylistTitle告诉 Siri 这些字符串属于媒体播放列表标题。- 用户词表适合个人内容,比如用户创建的播放列表,也能用于偏向用户的艺人或订阅播客名称。
- 词表同步到 Siri 服务可能受网络和电量条件影响,真实 App 需要给同步留出时间。
用全局词表描述 App 目录
(11:28)全局词表适合所有用户都会看到的 App 目录实体。它是随 App 分发的静态 plist,不需要把热门音乐或常见播客都列进去;session 明确说这些通用实体通常已经在 Siri 的模型里。你只需要列出对自己 App 特有、且模型容易误解的名称。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ParameterVocabularies</key>
<array>
<dict>
<key>ParameterNames</key>
<array>
<string>INPlayMediaIntent.playlistTitle</string>
</array>
<key>ParameterVocabulary</key>
<array>
<dict>
<key>VocabularyItemSynonyms</key>
<array>
<dict>
<key>VocabularyItemPhrase</key>
<string>70s punk anthems</string>
</dict>
</array>
<key>VocabularyItemIdentifier</key>
<string>70s punk anthems</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>
关键点:
ParameterNames指向INPlayMediaIntent.playlistTitle,表示这些词条要影响播放列表标题字段。VocabularyItemIdentifier是 App 会从INMediaSearch.mediaName收到的值。VocabularyItemPhrase是 Siri 用来匹配用户说法的短语。- 全局词表适合 App 目录中固定存在的歌单、节目或其他媒体实体。
- 用户词表和全局词表的命名方式不同,但最终都会帮助 Siri 填充媒体 intent 的对应字段。
把 Siri 播放控制接到 Now Playing
(16:29)Now Playing 控制由 MPRemoteCommandCenter 实现。Siri 听到 “pause”、“resume”、“next track”、“previous track” 这类命令后,会把它们路由到同一套 command handler。这个设计让语音控制、控制中心按钮和 CarPlay Now Playing 屏幕共享实现。
关键 API:
MPRemoteCommandCenter.pauseCommand处理暂停。MPRemoteCommandCenter.playCommand处理恢复播放。previousTrackCommand和nextTrackCommand处理上一首、下一首。skipForwardCommand和skipBackwardCommand会把用户说的跳转时长放到 command 的interval属性。changeRepeatMode和changeShuffleMode只处理打开或关闭状态;如果要用随机或重复模式开始播放,session 要求走INPlayMediaIntent。
(20:11)Siri 回答”这是什么歌”这类问题时,依赖 MPNowPlayingInfoCenter。至少要填好 MPMediaItemPropertyTitle、MPMediaItemPropertyArtist 和 MPMediaItemPropertyAlbumTitle,否则 Siri 没有足够上下文回答正在播放的内容。
错误处理也要走系统语义。没有安装某个 command handler 时,Siri 会给出错误对话;临时不支持时可以禁用 command;异常失败时再返回失败结果。这样做比在 App 里自定义一套语音错误话术更稳。
核心启发
-
做什么:给 SiriKit Media 做一张播放请求 QA 矩阵。 为什么值得做:session 给出的四类高频请求覆盖了超过 90% 的当时流量,先测这些场景比平均铺开所有字段更有效。 怎么开始:在 Intent Handler 的
resolveMediaItems(for:with:)打断点,逐个测试泛化播放、只含媒体名、媒体名加艺术家、播放列表四种 utterance,记录INMediaSearch上哪些字段被填充。 -
做什么:为用户创建的歌单同步
INVocabulary。 为什么值得做:用户自己的名称最容易被自然语言模型误拆,”70s punk classics” 这种名字会被拆成年代、类型和媒体名。 怎么开始:在歌单创建、重命名和删除后更新INVocabulary.shared(),用NSOrderedSet把最常用或最重要的名字放在前面,并把类型设为.mediaPlaylistTitle。 -
做什么:把 App 独有的公共目录写进全局 vocabulary plist。 为什么值得做:全局词表能让 Siri 在安装 App 后理解所有用户共享的节目、歌单或频道名,尤其适合品牌化、缩写化、容易被拆词的名称。 怎么开始:列出 App 目录中固定存在且 Siri 容易误判的媒体实体,只为这些实体配置
ParameterVocabularies,不要把通用音乐流派或热门艺人塞进 plist。 -
做什么:把 Now Playing 命令作为 Siri 测试的一部分。 为什么值得做:Siri 的 pause、resume、next track、skip forward 会直接路由到
MPRemoteCommandCenter,这里的 bug 会同时影响语音、控制中心和 CarPlay。 怎么开始:用同一套测试脚本检查 command handler、禁用状态、失败状态和 skip interval,再用MPNowPlayingInfoCenter验证 Siri 能回答标题、艺人和专辑问题。 -
做什么:在 App 内提示用户可用的 Siri 播放方式。 为什么值得做:逐字稿结尾提到,用户知道 Siri 功能存在后,Siri engagement 最高可提升到十倍。语音入口隐藏在界面外,教育本身就是功能质量的一部分。 怎么开始:在播放页、歌单页或首次连接车载场景中展示具体句式,比如 “Ask Siri to play your WWDC playlist in ControlAudio”,并保持文案和你的 intent 支持范围一致。
关联 Session
- Expand your SiriKit Media Intents to more platforms — 继续讲 SiriKit Media 在 HomePod、Apple TV 等平台上的播放接入和替代结果。
- What’s new in SiriKit and Shortcuts — 总览 2020 年 SiriKit 与 Shortcuts 的 compact Siri UI、对话设计和 action 组织更新。
- Evaluate and optimize voice interaction for your app — 从设计角度评估语音交互质量,适合作为本文 QA 矩阵的上游方法论。
- Empower your intents — 讲 intent 处理、in-app intent handling 和富对话呈现,补足媒体 intent 之外的 SiriKit 体验。
- Decipher and deal with common Siri errors — 深入 Siri 错误语义,适合接在本文的失败状态与 command 禁用策略之后阅读。
评论
GitHub Issues · utterances