大多数浏览器和
Developer App 均支持流媒体播放。
-
开发优质的 Siri 媒体交互体验
揭开为音乐和音频 app 设计 Siri 体验的艺术:我们将向你介绍如何设计出色的交互功能,以及如何提供定制词汇,以让 Siri 的响应更准确、更个性化。我们还将介绍如何利用 Apple 内部的 Siri 团队使用的方法来调试常见错误并测试你的 intents。
资源
相关视频
Tech Talks
WWDC20
- 为智能设计 发现新机遇
- 使用 watchOS 快捷指令创建快速交互
- 将 SiriKit Media Intent 扩展到更多平台
- 将你的操作列入快捷指令 app 里
- 智能化设计 与系统交朋友
- 智能设计 想人所想
- 智能设计 进化的 App
- 破解和处理 Siri 常见错误
- 给你的 intent(意图)授权
- 评估和优化 app 语音交互
- 集成你的 app 和 Wind Down
- SiriKit 与快捷指令的新增功能
WWDC19
-
下载
(你好 2020 全球开发者大会)
你好 欢迎莅临全球开发者大会 (开发优质的 Siri 媒体交互体验) 你好 我是 Danny Mandel 今天我想谈谈我们如何确保 你的 SiriKit Media app 拥有最优质的 Siri 体验 (优质的 Siri 体验) 我们为什么重视质量? 我认为我们都希望能够 创造出用户喜欢的应用 没有用户会喜欢声音劣质的应用 另外 由于语音助手的信任障碍 比传统用户界面更大 因此 为了维持使用率 需要进一步提升可靠性 如果你计划 花时间构建 SiriKit Media 支持 就一定要力求做到最优 (当用户说播放时…) 听上去可能有点傻气 (…播放点什么) 当用户要求播放某个内容时 我们可以做的 最重要的就是播放 试想下 用户首次通过声音 使用 app 时兴奋不已 但如果播放失败 他们可能再也不会尝试 因此 当你计划投入 Siri 工程资源时 首要任务是搭建强健的回放堆栈 (…很快地播放什么) 我们想做的第二件事是 确保能够快速进行回放 过去一年内 我们了解到 SiriKit Media app 中 最常见的失败场景是回放超时
尤其是在 CarPlay 车载等环境中 快速进行回放极为重要 在 CarPlay 车载等场景中 当用户在途中打开免提时 会对回放超时更加挑剔 如果 app 无法快速 进行回放 将会被淘汰出局 因此 为了改善这一情况 今年我们添加了提升性能的新选项 请务必查看 “将 SiriKit Media Intents 扩展到多个平台” 了解相关内容 (…快速、完美地播放) 另一个创建优质体验的方法是 帮助 Siri 理解听众的偏好 可以借助 Siri 用户词汇表 API 实现这一目的 同时 也可以借助 Siri 全局词汇表 API 帮助 Siri 理解 app 产品目录 关于这两个话题 稍后我们将进行深入探讨 (…快速、近乎完美地播放) 当你选择了完美的播放内容之后 你必须允许用户能够以不同方式提出要求 app 支持的话语越多 用户就越有可能 乐于在日常生活中使用 app 一直以来 Siri 都允诺自己是智能助手 而只有当它支持多种自然语言的话语时 才称得上是真正的智能 (常见话语) 让我们了解下 所有 SiriKit Media app 中 一些最常见的自然语言模式 什么是近乎完美? 从根本意义上来说 近乎完美 是指在不了解用户想法时进行最佳猜测 (50%…或者更多) 超过一半的 SiriKit Media 需求都 遵循这种模式 例如 “启动 app” “app 播放音乐”等简单需求 当用户没有明确表达想法时 这些就构成了一般场景 最重要的、不会出错的使用场景 确保能够处理这一场景后 便可以立刻给大量听众提供服务 app 中近乎完美的含义 将由你来决定 但是 需要确保 app 能够 让用户体验到极致的惊喜和愉悦 (流行话语) 通过零媒体查询 或者包含了音乐媒体类型的媒体查询 可以知道用户询问的是一般场景 下一个重要的使用场景是 “播放音乐” 这时用户会明确指出希望播放的标题 但不会告知媒体类型的名称 因此 可能是某首歌 某个专辑 某位艺术家 某个播客 你无法确定 因此 在实现这一场景时 必须适应不同的媒体类型 并进行十分广泛的查询
媒体查询对象中只会填充 媒体名称 帮助你理解这种需求 可以看到像这样的查询占比约为 30% 列表往下是更为精细的查询 包含了艺术家和其他查询字段 因此 需要确保能够支持 包含艺术家的混合查询 在这一场景中 你将获得填充的媒体名称 其包含了用户询问的标题 及其想要查询的艺术家的姓名 当查询类型明确时 使用率会开始下降 可以看到 这种模式的使用率约占需求的 5% 最后一个高使用率的类别是播放列表 因此 需要确保能够支持播放列表查询 同样地 当查询明确时 使用率 约为 5% 当用户话语中包含播放列表查询时 mediaName 属性将填充播放列表查询 媒体类型属性将设置为“播放列表” INMediaSearch 中 还包括其他很多的查询字段 因此 这份列表并不完整 然而 正如你所看到的 仅仅通过这四种使用场景 就已经捕获了 SiriKit Media 当前流量的 90% 以上 你可以尽可能多地 针对 app 添加合理的字段 但是 由于用户 最有可能使用这些流行场景 因此 优先考虑这些场景的 工程和质量十分合理 最后需要注意 我们知道 Siri 支持性能越强大 用户就越有可能提出复杂的需求 因此 在提升 Siri 性能时 需要考虑到使用模式可能发生变化 这不是件坏事 这意味着用户 喜欢通过 Siri 使用你的 app 刚刚 我们了解了流行话语 知道了如何通过意图 捕获绝大多数的使用率 现在 让我们了解下调试程序中的意图 了解每个场景中的媒体查询
我们在这里设置一个断点 便于查看媒体查询
第一个话语是空的播放场景 “启动 ControlAudio”
查看断点 可以看到 这一场景中没有媒体查询 这可以理解 因为我们并未明确任何查询标准 现在 让我们看下 在 ControlAudio 中“播放音乐”
可以看到 这一场景对音乐媒体类型进行了设置 其他字段是空的 举例来说 在 ControlAudio 中 “播放 Special Disaster Team”
正如我们所期望的 媒体名称是“Special Disaster Team” 现在 让我们试试更复杂的场景 添加标题和艺术家
可以看到 媒体名称是“也许” 同样地 艺术家姓名是“Special Disaster Team” 同时 不填充其余字段 最后 检查下播放列表 我们尝试在 ControlAudio 中 ”播放全球开发者大会播放列表“
由于话语中 已经包含”播放列表“这个单词 因此可以看到 播放列表媒体类型成为一种参数 同时 媒体名称是全球开发者大会 这些意图能够帮助 在意图处理程序中 捕获绝大多数的 Siri 流量 现在 让我们 深入了解下 SiriKit Media 词汇表功能 Siri 的 natural language processing 为机器学习系统 意味着系统中建构了概率模型 能够在用户要求 Siri 执行特定操作时 尝试预测用户的意图 (音频功能内置训练) 同时 这也意味着 该模型已经经过训练 能够识别出特殊功能 (流派、媒体类型、媒体种类、日期) 例如 无需教导 Siri 了解 音乐流派、媒体类型 种类以及发布日期 因为 Siri 模型已经接受相关训练 能够识别出用户询问的功能 (一般而言 该模型十分可取) 一般而言 从工程和使用率的 角度出发 该模型十分优秀 工程上 它意味着每次新 app 采用 SiriKit Media Intents 时 无需进行额外的模型训练 而使用率上 该模型也十分出色 因为当用户知道如何 使用一款 SiriKit Media app 时 就代表他们能够 使用所有的 SiriKit Media app 然而 和所有系统一样 该系统有时也会带来问题 (你好 Siri…) (在 CONTROLAUDIO 中 播放 70 年代经典朋克歌曲) 例如 ControlAudio 中包含 “70 年代经典朋克歌曲“的播放列表” Siri 模型会默认进行语法分析 判断出媒体发布日期是 20 世纪 70 年代 媒体名称是“经典朋克歌曲” ControlAudio 接收的意图 并不会完全映射该播放列表的名称 (70 年代经典朋克歌曲) 为了解决这一问题 我们选择将额外词汇表同步进 Siri 便于 Siri 明白“70 年代经典朋克歌曲” 就是 ControlAudio 中某播放列表的名称 现在 让我们了解下 实现这一目标的两种方法 (用户词汇表应用于个性化条目) 一种方法是 使用 Siri 用户词汇表功能 教导 Siri 有关产品目录的内容 用户词汇表功能可以 应用于仅针对某用户的条目 而不是应用于所有 app 用户 你可以借助 INVocabulary API 与 Siri 共享词汇表 让我们快速了解下
获得 INVocabulary 共享实例引用
添加想要储存的值 创建顺序集 需要注意 由于 Siri 会重点关注顺序 因此需要确保 重要值位于顺序集的开头
之后 在想要储存的类型上 调用 setVocabularyStrings 在这一场景中 字符串是 mediaPlaylistTitle 使用 INVocabulary 时 通常会关注播放列表等 用户创建的内容 然而 你也可以借助 INVocabulary 对 speech-and-natural-language 识别 设置使其偏向于音乐艺术家或订阅播客 (仅包含 不属于 Siri 内置产品目录的内容) 另一方面 全局词汇表适用于 所有 app 用户可以使用的内容 与用户词汇表不同的是 由于全局词汇表为静态内容 因此 可以将其打包为 plist 并作为 app 的一部分进行分发 注意 只需要包含 app 特有的内容 无需添加流行音乐或博客实体 这是因为 通常 Siri 已经 将这些内容识别为 NL 模型的一部分 屏幕中显示的是全局词汇表 plist 范例 首先需要注意 该范例将数据同步进 INPlayMediaIntent.playlistTitle 其次要注意 词汇表条目的标识符为 “70 年代朋克赞歌” 而标识符则是 INMediaSearch medianame 字段中 由 Siri 发送的值 该标识符将充当密钥 帮助 Siri 明白 “70 年代朋克赞歌” 与 app 产品目录中的某播放列表相匹配 (词汇表类型 用户词汇表符号 全局此汇表属性列表密钥) 正如你所看到的 词汇表的类型多种多样 便于你根据应用支持的媒体类型加以使用 可以看到 用户词汇表中数据类型的命名 与全局词汇表中密钥的命名并不相同 但两者最终都会同步到 Siri 的同一位置 音乐 app 包含 播放列表标题、音乐艺术家的姓名 有声读物 app 包含 有声书的标题及其作者姓名 而广播与播客 app 则可以使用显示标题类型 让我们了解下 Siri 词汇表功能会如何 影响 Siri 传达给 app 的意图 例如 Siri 将“播放 70 年代朋克经典歌曲” 视为意图进行传达 但该意图可能并不符合 app 的期望 让我们看下调试程序中的意图
现在我们开始设置 scheme 用该话语进行测试
尝试运行
可以看到 最终显示的意图并不完全匹配 我们所期望的播放列表标题 音乐流派是”朋克“ 时间是 70 年代 让我们尝试解决这一问题 我们将该词汇表添加进 AppDelegate 关于这段代码 有一点需要注意 该词汇表是顺序集 这是因为 Siri 会优先考虑 列表开头的条目 由于 Siri 能够识别的条目的数量有限 因此需要确保最重要的条目位于列表开头 现在已经设置完成 让我们尝试运行 ControlAudio app 并确保其能够与 Siri 服务器同步 需要注意 在实际场景中 由于会受到网络和电力状况的影响 词汇表可能 需要一段时间才能同步到 Siri 服务器
同步完成 现在使用同样的话语 再次运行意图处理程序 查看屏幕显示的意图
首先需要注意 媒体查询中 已经设置了播放列表的媒体类型 Siri 已经识别出用户词汇表 并自动设置了媒体类型 另外 媒体名称符合我们的期望 也包含”70 年代经典朋克歌曲“ 完整字符串 这就是有关用户词汇表的内容 现在让我们了解下全局词汇表 在此提醒 用户词汇表以特定用户为基础 而全局词汇表 plist 有所不同 其可以供所有 app 用户使用 因此 我们首先需要 将词汇表 plist 添加进 ControlAudio
让我们看下
需要重点关注词汇表密钥参数 首先 需要查看参数名称 在这一场景中 由于明确规定了播放列表标题的词汇表 因此可以看到 相应的值为 INPlayMediaIntent.playlistTitle
可以看到 参数词汇表 包含词汇表条目标识符的值 该标识符为 Intents Media Search 中将接收的值 在这一场景中 我们对之前范例中的值进行了调整 改为“70 年代的朋克赞歌“ 现在 全局词汇表文件已添加完成 让我们尝试运行 看看意图处理程序将发生什么
再次查看意图 可以看到播放列表媒体类型
以及符合我们期望的 70 年代朋克赞歌播放列表标题
现在 我们已经了解 如何借助 Siri 全局词汇表 确保 Siri 了解 对所有 app 用户至关重要的实体 在媒体 app 中 正在播放控制 是 Siri 比较常见的使用场景 因此 有必要对其进行测试 iOS 的正在播放支持功能 借由 MPRemoteCommandCenter 加以实现 (App 支持命令 IOS 负责其余操作) 其工作原理是 针对特定的正在播放命令 注册相应的命令处理程序 正在播放命令的范例包括 ”播放“、”暂停“ ”下一曲目“以及”上一曲目“ 其基本理念是 如果控制中心 或 CarPlay 载正在播放屏幕中 存在某一按键 那么就必须存在相应的正在播放命令类 目前 我们创建了一个优质的范例代码项目 对正在播放 API 的细节进行了深入探讨 名为“Becoming a Now Playable App” 如果你对其运作方式感兴趣 我建议你查看了解
Siri 作为简单的语音界面 建立于特定的正在播放命令之上 这种方式由来已久 算不上新颖 当向 Siri 提出 正在播放命令时 会发生什么? (“下一曲目”) 和大多数 Siri 命令一样 Siri 首先会识别用户话语 在这一场景中 用户话语是“下一曲目” 一旦 Siri 执行操作 便向 MPRemoteCommandCenter 发送命令 在这一场景中 由于用户话语是“下一曲目” 因此 系统将发送下一曲目命令 最后 系统将调用 app 注册的下一曲目命令处理程序 便于你处理下一曲目命令 请注意 由于此实现 与处理按键并无不同 因此 Siri 处理按键和正在播放按键 将拥有相同的实现 在多数场景中 这一点可以接受 只需要在实现的过程中牢记便可 现在 让我们了解一些命令 并学习如何借助 Siri 加以调用 请注意 这里的话语范例只是一部分 为执行此类操作 Siri 能够为多种自然语言话语提供支持 在实现正在播放支持功能的过程中 Siri 将负责执行相关操作 你无需担心 你只需要确保 MPRemoteCommandCenter 能够与 Siri 正常工作
第一是暂停命令 对 Siri 说“暂停”将调用该命令 由 MPRemoteCommandCenter 负责执行
第二是播放命令 用于音乐暂停时继续播放音乐 对 Siri 说“继续”将调用该命令 由 MPRemoteCommandCenter 负责执行
第三是上一曲目命令 对 Siri 说“上一曲目”将调用该命令 系统将负责执行
第四是下一曲目命令 对 Siri 说“下一曲目”将调用该命令 系统将负责执行 让我们继续 第五是快进命令 当说出“快进”时 系统将执行命令 快进的有趣之处在于 用户可以向 Siri 指出快进时间 (“快进45秒”) Siri 会将其打包进命令间隔属性 第六是后退命令 当说出“后退”时 系统将执行命令 (“后退一分钟”) 和快进一样 用户同样可以指出后退的时间 其将包含在命令间隔属性中 虽然还有一些命令并未包含在锁定屏幕中 但仍然需要有所了解 changeRepeatMode 和 changeShuffleMode 带有是否应该启动或禁用的参数 同时需要注意 这两个模式仅用于 启动或关闭循环播放和随机播放设置 可以使用 INPlayMediaIntent 启动随机播放或循环播放 同样 更改播放速度也带有参数 便于指出想要的播放速度 例如 “减缓播放速度” 、“半速播放”等等 如同通过执行“正在播放”命令 控制“正在播放”交互的方法一样 同样可以通过 在 MPNowPlayingInfoCenter 中设置属性 来帮助 Siri 回答 用户提出的有关播放内容的问题 (回答正在播放的相关问题) 支持属性 包括 MPMediaItemPropertyTitle 其可以通过说“这是哪首歌?”加以使用 MPMediaItemPropertyArtist 其可以通过说“这是哪个乐队?”加以使用 MPMediaItemPropertyAlbumTitle 同样 其可以 通过询问“这是哪个专辑?”加以使用 如果出现错误 有三种方法供你选择 告知用户不支持该命令 你可能会针对 app 借助多种方法达到这一目的 (抱歉 无法快进 切勿执行命令) 不执行命令是最简单的方法 即使 MPRemoteCommandCenter 中 没有安装处理程序 Siri 仍然会礼貌指出错误
另外 还可以暂时禁用命令 (禁用命令) 这在你通常提供支持的环境中可能有用 但在某些环境中则不可用 例如 当广告支持的音乐流媒体 播放广告时 可用禁用 nextTrackCommand
最后 还可以判定命令失败 之后 系统将生成一般的错误对话框 (判定命令失败) 通常 只有在特殊场景中才需要你执行此操作 Siri 会负责处理 最后 用户都知道 什么是优质的 SiriKit Media 体验 (队列 回放) 这是所有语音集成面临的挑战 有时 用户并不知道 为了实现优质的语音体验 你付出了多少努力 因为看不见就想不到 (说:“后退15秒” 说:“删除这一集”) 但是 我们仍然注意到 用户在了解相关功能后 愿意使用 Siri 的可能性大大增加 可能性有多大呢? 我们发现 当用户对 app 的功能有一定了解后 Siri 的用户参与度增至十倍 当然 你会希望以个人风格展现功能 并使其与 app 的整体外观、感观相匹配 然而 当你希望用户 只需询问 Siri 便可轻松使用 app 时 便一定会展开详细的介绍 感谢观看 我们真诚希望 你的 app 以及 Siri 都能够给用户带来优质体验 因此 请始终谨记 用户只会使用优质的 Siri 体验 希望 2020 全球开发者大会让你有所收获
-
-
5:46 - resolveMediaItems method
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)) } }
-
10:21 - User vocabulary
let vocabulary = INVocabulary.shared() let playlistNames = NSOrderedSet(objects: "70s punk classics") vocabulary.setVocabularyStrings(playlistNames, of: .mediaPlaylistTitle)
-
11:28 - Global vocabulary example
<?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>
-
13:07 - Resolve media items method
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)) } }
-
13:31 - User vocabulary syncing
// Set our playlist title in user vocabulary so we get the proper Siri intent let vocabulary = INVocabulary.shared() let playlistNames = NSOrderedSet(objects: "70s punk classics") vocabulary.setVocabularyStrings(playlistNames, of: .mediaPlaylistTitle)
-
14:57 - Global vocabulary example
<?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>
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。