ブログ • App Intents

iOS 27 の Agent は第三者 App をどう呼ぶか:API の前に App Intent の 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"
  }
}
1つ目の欄に候補、2つ目の欄にこの 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 を解決する。解決できなければ、field を itemId にした rejected をもう一つ書く。商品名から id を作らず、API も呼ばない。

失敗をどう返すか

必須が欠けているときは、そのパラメータの requestValue でもう一度尋ね、空の値で API を呼ばない。値が不正なときは perform() を止め、IntentDialog でユーザーが読める一文を書く。上の JSON は、この決定のために残す記録だ。

場合記録の statusAPI
fulfillment が無いneeds_valueもう一度尋ね、API は呼ばない
fulfillment が shiprejectedpickup か delivery だけだと伝える
quantity が 0rejected形は合法でも止める
itemId が解決できないrejected商品名で id を代用しない

needs_value は、まだ尋ねる必要があるという意味だ。rejected は、この候補を実行できないという意味だ。どちらも API には進まない。ユーザーが見るのは dialog の一文だ。ログには JSON 全体を残し、後からどのフィールドが止めたかを合わせられるようにする。

ほかの記事との分担

指示の経路の記事交換用の封筒の記事本記事
答えることフレーズがどう API に届くかモデルの値とパラメータの間にどの JSON を残すかApp がどう宣言し、検証し、失敗を返すか
ここで探さない音声から API までの手順をもう一度content と parameters という二つのオブジェクト商品名でエンティティ id を代用すること

JSONNote でこの呼び出しを止める

  1. 1
    操作を選ぶ

    JSON Schema を開く。操作する を Schemaで確認してください にする。1つ目の欄に候補、2つ目の欄に上の Schema を貼る。intent は PlaceOrderIntent だけ。fulfillment は pickup か delivery だけ。quantity は整数。

  2. 2
    壊れたデータを三つぶつける

    fulfillment を ship にする、quantity を文字列 1 にする、または itemId を消す。この三つは失敗する。quantity を 0 にすると通る。通ったあとでも rejected を書き、API は呼ばない。

  3. 3
    1 と 0 を比べる

    JSON 比較Diff。1つ目の欄に quantity が 1 の parameters、2つ目の欄は 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 の形式ではない。Apple は requestValue と IntentDialog で結果をシステムとユーザーに渡す。この JSON は自分のログだ。status、reason、field、dialog。残しておけば、なぜ API を呼ばなかったかを JSONNote で後から合わせられる。

まとめ

第三者 App が呼ばれるとき、先にあるのはパラメータ契約で、次にあるのは失敗できる JSON だ。失敗記録はこの呼び出しを API の前で止め、ユーザーが読める dialog を一文残す。

結論:先に Intent のパラメータを宣言する。Schema で型、列挙、必須を止める。値が無ければ needs_value を返す。値が不正なら rejected を返す。どちらも API は呼ばない。数量が 1 未満、または id が無いときは、形が通っても止める。

次:候補のパラメータを Schema に通し、この呼び出しを続けるか失敗を返すかを決める。

ブログへ戻る