博客 • App Intents

iOS 27 AI Agent 如何调用第三方 App?App Intents、App Actions、API 与 JSON 参数验证完整教程

系统调用第三方 App 时,入口是你声明的 AppIntent。参数进 perform() 之后、API 之前,先落成一份 JSON。类型、枚举或必填对不上,就返回失败记录,这次调用停在 App 里。

一条指令怎么走到 API,见 App Actions 的 JSON 路径。Siri 会不会变成 Agent,见 Siri AI 会成为 AI Agent 吗。本篇只写 App 侧:声明哪些参数、用 Schema 挡住什么、失败时返回哪一份 JSON。

先记住这一点:系统把调用交到你的 App,不代表参数已经能用。下面按「声明合同 → 一份 Schema → 三条失败 → 形状通过仍停 → 失败怎么回」拆开。

先声明参数合同

第三方 App 对外的入口写在 AppIntent 上。你给出标题,并实现 perform()。系统能发现这个动作,是因为你声明了它。声明本身不证明这次参数可以打 API。

每个参数用 @Parameter 标出。说明见 为 App Intent 添加参数。这份示例只收三样:商品 id、数量、履约方式。名称、说明文字和模型多写的键都不在合同里。

  1. 1
    钉住 Intent 名

    候选 JSON 里的 intent 只能是 PlaceOrderIntent。别的名字表示这次调用找错了动作。

  2. 2
    列出三个参数

    itemId 是字符串,quantity 是整数,fulfillment 只能是 pickup 或 delivery。缺任何一项,这份候选就不完整。

  3. 3
    perform() 里先校验

    校验失败就返回下面的失败记录。通过,并且 id 能解析、数量至少为 1,才进入 API。

同一组字段写成 Schema

下面的候选和 Schema 是你的契约,不是 Apple 的线上格式。JSONNote 的 JSON Schema 会检查类型、枚举和必填。数量至少为 1、id 必须存在,这层 Schema 表达不了,要在 App 里再判断。

能进入下一步的候选。fulfillment 必须是 pickup 或 delivery。
{
  "intent": "PlaceOrderIntent",
  "parameters": {
    "itemId": "sku_42",
    "quantity": 1,
    "fulfillment": "pickup"
  }
}
第一栏贴候选,第二栏贴这份 Schema。操作改成用 Schema 校验。
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["intent", "parameters"],
  "properties": {
    "intent": { "enum": ["PlaceOrderIntent"] },
    "parameters": {
      "type": "object",
      "required": ["itemId", "quantity", "fulfillment"],
      "properties": {
        "itemId": { "type": "string" },
        "quantity": { "type": "integer" },
        "fulfillment": { "enum": ["pickup", "delivery"] }
      }
    }
  }
}

参数类型怎么对齐,见 App 动作字段与 JSON Schema。本篇不重复那张表,只写校验失败时 App 返回什么。

三条会失败的候选

候选里改了什么为什么失败
fulfillment 写成 ship不在 pickup 和 delivery 里。
quantity 写成字符串 1合同要的是整数。
删掉 itemId必填缺了,候选不完整。

把 quantity 改成 0,类型仍是整数,这份 Schema 会通过。通过不等于可以下单。0 件商品要在 App 里拒绝。

形状通过仍要停

数量小于 1 时留下的失败记录。dialog 是说给用户的那一句。
{
  "intent": "PlaceOrderIntent",
  "status": "rejected",
  "reason": "invalid_parameter",
  "field": "quantity",
  "dialog": "Quantity must be at least 1."
}

itemId 是非空字符串时,Schema 也通过。接着要在你的数据里解析这个 id。解析不到,就再写一份 rejected,field 写成 itemId。不要用商品名造一个 id,也不要打 API。

失败怎么回

缺必填时,用参数上的 requestValue 再问一次,不要拿空值去打 API。值非法时,让 perform() 停住,并用 IntentDialog 写出用户能看懂的那一句。上面的 JSON 是你为这次决定留下的记录。

情况记录里的 statusAPI
缺 fulfillmentneeds_value再问一次,不打 API
fulfillment 是 shiprejected说明只能是 pickup 或 delivery
quantity 是 0rejected形状合法也停
itemId 解析不到rejected不用商品名顶替 id

needs_value 表示还要问。rejected 表示这次候选不能执行。两条都不进入 API。用户看见的是 dialog 那一句,日志里留下整份 JSON,之后才能对上是哪一个字段被挡住。

和另外几篇怎么分工

指令路径那篇交换信封那篇本篇
回答短语怎么走到 API模型值和参数之间落哪一份 JSONApp 如何声明、校验、把失败回回去
不要在这里找再走一遍语音到 API 的步骤content 和 parameters 两份对象用商品名顶替实体 id

在 JSONNote 里挡住这次调用

  1. 1
    选对操作

    打开 JSON Schema。操作选「用 Schema 校验」。第一栏贴候选,第二栏贴上面的 Schema。intent 只能是 PlaceOrderIntent。fulfillment 只能是 pickup 或 delivery。quantity 必须是整数。

  2. 2
    撞三条坏数据

    fulfillment 写成 ship,或 quantity 写成字符串 1,或删掉 itemId。这三条都会失败。quantity 改成 0 会通过。通过之后仍要写 rejected,不打 API。

  3. 3
    对比 1 和 0

    打开 JSON 对比 Diff。第一栏贴 quantity 为 1 的 parameters,第二栏把 quantity 改成 0。差的是这个数。0 仍能通过 Schema,所以挡住 API 的是失败记录,不是这次形状检查。

常见问题

系统已经把参数交给 App,还要再校验吗?

要。系统交来的是一次调用,不是已经校验过的 API 请求。perform() 开头先看这份 JSON。不过就不继续。

Schema 通过就可以打 API 吗?

还不行。这份 Schema 只看类型、枚举和必填。quantity 为 0 会通过。itemId 是字符串也会通过。数量至少为 1,并且 id 能解析,才打 API。

缺参数时应该返回错误还是再问一次?

再问一次。status 写成 needs_value,用 requestValue 向用户要这个值。问到合法值之前不打 API。值到了但非法,才写成 rejected。

和「一条指令的 JSON 路径」是同一篇吗?

不是。那篇跟着一条指令从短语走到 API。本篇停在 App 里面:参数合同、Schema,以及失败时返回的 JSON。API 只有在 rejected 和 needs_value 都没有出现时才发生。

失败记录是 Apple 的返回格式吗?

不是。Apple 用 requestValue 和 IntentDialog 把结果交给系统和用户。这份 JSON 是你自己的日志:status、reason、field、dialog。留下它,才能在 JSONNote 里复核这次为什么没有打 API。

总结

第三方 App 被调用时,先有一份参数合同,再有一份能失败的 JSON。失败记录把这次调用停在 API 之前,并留下用户能看懂的一句 dialog。

直接结论:先声明 Intent 参数;用 Schema 卡住类型、枚举和必填;缺值返回 needs_value;非法值返回 rejected;这两种都不打 API;数量小于 1 或 id 不存在,形状通过也要停。

下一步:先让候选参数通过 Schema,再决定这次调用是继续还是返回失败。

返回博客