博客 • AI / App Intents

iOS 27 Siri AI 开放更多能力后,App Actions 的 JSON 数据到底如何流转?从用户指令到 API 执行完整解析

一条 Siri 指令能执行,是因为它先变成 App Intent 的参数,再在 AppIntent 的 perform() 里决定走本机还是打你的 API。系统不把整段语音原样发给服务器。

Siri 算不算 Agent,写在 上一篇。本篇只跟一条指令:短语怎么对上 Intent,参数怎么变成 JSON,校验通过之后才调用 API。

先记住这一点:语音不是请求体。Intent 填好的参数才是。下面按「短语 → 参数类型 → 实体 id → 你的 JSON → API」走一遍。

一条指令经过的五步

官方入口是 App Intents。说明见 App Intents 入门。Siri、快捷指令和应用内按钮走的是同一套 Intent,差别在谁填参数。

  1. 1
    短语命中

    你在 AppShortcutsProvider 里登记口语句。系统用这句话选中某个 Intent,不是把原话交给模型自由发挥。

  2. 2
    填参数

    Intent 上的 @Parameter 告诉系统要什么值、必填还是可选。缺了会向用户追问,而不是猜一个发出去。

  3. 3
    解析实体

    用户数据用 EntityQuery 按 id 解析。参数里放的是实体,不是一段描述它的文本。

  4. 4
    执行

    perform() 里才读到已经解析好的值。本机能做完的就在这里做完。

  5. 5
    回给 Siri

    用 IntentResult 或对话返回结果。失败也要有一句能说出口的原因,不要只在服务器日志里留错。

参数分三种

参数类型见 为 App Intent 添加参数。选错类型,后面的 JSON 就会不稳定。

类型适合进 JSON 时
字符串、数字、日期数量、备注这种简单输入原样放进 parameters
AppEnum编译期就固定的选项,例如到店取还是配送用枚举值,不要把展示文案当主键
AppEntity会变的用户数据,例如某件商品只放解析后的 id

固定集合用 AppEnum。会随用户增删的用 AppEntity 加查询。把会变的列表写成枚举,选项一过期,短语就对不上。

实体只带走 id

系统要的是可显示、可查询的实体。你的数据库仍是数据源。实体上至少要有稳定 id 和给人看的名字。

解析完成后你能记下来的实体字段。id 才是后续 API 的键。
{
  "entity": "OrderItem",
  "id": "sku_42",
  "display": "Oat milk"
}

查询实现 entities(for identifiers:),用 id 找回对象。找不到就不要编一个 id 继续。

你自己的参数 JSON

下面这份是你在 perform() 里落盘、再决定要不要打 API 的契约。它不是 Apple 的请求格式,系统也不会替你读这个文件。

一条已经解析完的下单指令。resolved 为 true 才表示实体查询成功。
{
  "intent": "StartOrder",
  "source": "siri",
  "parameters": {
    "item_id": "sku_42",
    "quantity": 1
  },
  "resolved": true
}

什么时候打 API

  1. 1
    本机能完成

    只改本机状态的动作,在 perform() 里做完,不必出网。

  2. 2
    先校验

    要用服务器时,先用 Schema 校验上面的 JSON。缺 item_id、quantity 小于 1,都停在设备上。

  3. 3
    再出网

    校验通过且 resolved 为 true,才把 parameters 发给你的 API。服务器再按 id 查自己的记录,不信任展示名。

  4. 4
    失败说人话

    API 拒绝或超时,用 Intent 对话告诉用户没完成。不要让 Siri 说已经成功。

第三方 App 怎么声明 Intent、失败怎么回,是下一篇的题目。本篇停在这条数据路径。

和旧文怎么分工

Siri 是不是 Agent本篇
回答系统会不会自己规划并调用工具一条指令从短语到 API 经过哪些 JSON
不要在这里找又一篇产品发布稿把语音原文当成可以落盘的请求体

硬件和系统功能的产品叙事见 iPhone 18 为什么不只是硬件。那边不拆参数字段。

在 JSONNote 里检查这条 JSON

  1. 1
    格式化

    用 JSON 格式化 打开这份参数记录,确认没有尾逗号。

  2. 2
    用 Schema 卡住缺字段

    把下面的 Schema 贴进 JSON Schema。缺 intent、item_id,或 quantity 不是正整数,都不应发到 API。

  3. 3
    再验一条样例

    用 JSON 校验 贴一条真实样例。重点看 item_id 是不是空字符串。

参数记录的 JSON Schema。source 只允许 siri、shortcut、app。
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": false,
  "required": ["intent", "source", "parameters", "resolved"],
  "properties": {
    "intent": { "type": "string", "minLength": 1 },
    "source": { "enum": ["siri", "shortcut", "app"] },
    "resolved": { "type": "boolean" },
    "parameters": {
      "type": "object",
      "required": ["item_id", "quantity"],
      "properties": {
        "item_id": { "type": "string", "minLength": 1 },
        "quantity": { "type": "integer", "minimum": 1 }
      }
    }
  }
}

常见问题

系统会把整段语音发给我的服务器吗?

不会按这条路径。服务器收到的应是你在 perform() 里组装、并校验过的参数 JSON。语音留在设备侧的识别过程里。

参数里为什么不直接写商品名?

名称会改、会重复。AppEntity 用稳定 id,查询用这个 id 找回对象。API 也只认 id。

Schema 通过就可以打 API 吗?

还要看 resolved。形状对了但实体没解析完,说明用户还没选定对象。这时打 API 会把一次追问变成一次错误下单。

本机动作和出网动作怎么分?

只改本机数据的留在 perform()。要进你的后端的,先写参数 JSON、校验,再请求。两种都要用 IntentResult 把结果说回给用户。

这和快捷指令里手填参数是同一条路吗?

是同一套 AppIntent。差别是谁填参数:Siri 按短语填,快捷指令由用户或上一个动作填。后面的实体解析、JSON 和 API 门闩相同。

总结

一条指令的数据路径是:短语选中 Intent,参数按类型填入,实体收成 id,你写成 JSON,校验通过才打 API,再用对话把结果交回 Siri。

直接结论:不要上传原始语音;id 没解析完就不请求;失败要说回给用户,而不是只写服务器日志。

下一步:先用 Schema 卡住这条参数 JSON,再接到 perform()。

返回博客