博客 • 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。
- Intent 的參數是 App 自己的合同
- Schema 檢查類型、枚舉和必填
- 失敗記成 needs_value 或 rejected
- 這兩種記錄都不進入 API
先記住這一點:系統把調用交到你的 App,不代表參數已經能用。下面按「聲明合同 → 一份 Schema → 三條失敗 → 形狀通過仍停 → 失敗怎麼回」拆開。
先聲明參數合同
第三方 App 對外的入口寫在 AppIntent 上。你給出標題,並實現 perform()。系統能發現這個動作,是因爲你聲明瞭它。聲明本身不證明這次參數可以打 API。
每個參數用 @Parameter 標出。說明見 爲 App Intent 添加參數。這份示例只收三樣:商品 id、數量、履約方式。名稱、說明文字和模型多寫的鍵都不在合同裏。
- 1釘住 Intent 名
候選 JSON 裏的 intent 只能是 PlaceOrderIntent。別的名字表示這次調用找錯了動作。
- 2列出三個參數
itemId 是字符串,quantity 是整數,fulfillment 只能是 pickup 或 delivery。缺任何一項,這份候選就不完整。
- 3perform() 裏先校驗
校驗失敗就返回下面的失敗記錄。通過,並且 id 能解析、數量至少爲 1,才進入 API。
同一組字段寫成 Schema
下面的候選和 Schema 是你的契約,不是 Apple 的線上格式。JSONNote 的 JSON Schema 會檢查類型、枚舉和必填。數量至少爲 1、id 必須存在,這層 Schema 表達不了,要在 App 裏再判斷。
{
"intent": "PlaceOrderIntent",
"parameters": {
"itemId": "sku_42",
"quantity": 1,
"fulfillment": "pickup"
}
}
{
"$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 裏拒絕。
形狀通過仍要停
{
"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 是你爲這次決定留下的記錄。
| 情況 | 記錄裏的 status | API |
|---|---|---|
| 缺 fulfillment | needs_value | 再問一次,不打 API |
| fulfillment 是 ship | rejected | 說明只能是 pickup 或 delivery |
| quantity 是 0 | rejected | 形狀合法也停 |
| itemId 解析不到 | rejected | 不用商品名頂替 id |
needs_value 表示還要問。rejected 表示這次候選不能執行。兩條都不進入 API。用戶看見的是 dialog 那一句,日誌裏留下整份 JSON,之後才能對上是哪一個字段被擋住。
和另外幾篇怎麼分工
| 指令路徑那篇 | 交換信封那篇 | 本篇 | |
|---|---|---|---|
| 回答 | 短語怎麼走到 API | 模型值和參數之間落哪一份 JSON | App 如何聲明、校驗、把失敗回回去 |
| 不要在這裏找 | 再走一遍語音到 API 的步驟 | content 和 parameters 兩份對象 | 用商品名頂替實體 id |
在 JSONNote 裏擋住這次調用
- 1選對操作
打開 JSON Schema。操作選「用 Schema 校驗」。第一欄貼候選,第二欄貼上面的 Schema。intent 只能是 PlaceOrderIntent。fulfillment 只能是 pickup 或 delivery。quantity 必須是整數。
- 2撞三條壞數據
fulfillment 寫成 ship,或 quantity 寫成字符串 1,或刪掉 itemId。這三條都會失敗。quantity 改成 0 會通過。通過之後仍要寫 rejected,不打 API。
- 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,再決定這次調用是繼續還是返回失敗。