博客 • Foundation Models
Apple Intelligence 2026 開發者生態變化:Foundation Models、App Intents 與 JSON 數據交換機制詳解
2026 年,同一個 App 會同時接到兩套入口:設備上的 LanguageModelSession 按類型解碼出一份內容,系統則調用 App Intent 的 perform()。要落盤的不是這兩份裏的任何一份原樣,而是你自己的交換信封。
產品敘事見 iPhone 18 爲什麼不只是硬件。Siri 會不會變成 Agent,見 Siri AI 會成爲 AI Agent 嗎。本篇只寫信封:哪些字段要落盤,以及什麼情況下不能執行。
- content 是模型解碼出來的那份對象
- parameters 是交給 perform() 的那份對象
- complete 爲 false 時,parameters 寫成 null
- schemaVersion 變了,舊信封不能直接重放
先記住這一點:框架把 Generable 收成 schema,再解碼成值。這隻發生在模型會話裏。你要保存、對賬、再決定要不要執行,靠的是另一份信封。下面按「兩份合同 → 信封字段 → 一份 JSON → 四種停」拆開。
兩份合同不是一次調用
設備上的模型怎樣按類型填值,見 Generable。類型會收成 schema,模型填完,框架再解碼。靜態類型用 @Generable。沒有靜態類型時,會話可以按 GenerationSchema 返回 GeneratedContent。兩種結果寫進信封時都叫 content。
App Intent 是另一條調用鏈。參數寫在 @Parameter 上,說明見 爲 App Intent 添加參數。系統調用 perform() 時沒有 LanguageModelSession,也就沒有 Generable 的 content。
- 1會話產出內容
respond(to:generating:) 返回 Response。生成還沒結束時,對應的是 PartiallyGenerated,字段可以仍爲空。這時還沒有一份能執行的參數。
- 2投影出參數
只把 Intent 接受的字段抄進 parameters。模型多寫的 rationale 留在 content。兩份對象可以同名,但不能是同一個對象。
- 3先寫信封再執行
source、sessionId、schemaVersion、complete、content、intent、parameters 落成一份 JSON。complete 不是 true,就不調用 perform()。
信封裏有哪些字段
下面這些鍵是你自己的信封,不是 Apple 解碼結果裏的字段。
| 字段 | 誰來寫 | 作用 |
|---|---|---|
source | 你的 App | foundation-models 或 app-intent。標明這次值從哪條入口來。 |
sessionId | 你的 App | 只有模型會話纔有,例如 lm_01。系統調用時寫成 null。 |
schemaVersion | 你的 App | 你給 Generable 字段表標的代際。這份示例是 3。字段變了就加一,舊信封不能直接重放。 |
generable | 你的 App | 類型名,例如 OrderDraft。系統直接調用時寫成 null。 |
complete | 你從生成狀態抄下 | false 表示還在生成,字段還會變。true 才允許往下投影並考慮執行。 |
content | 模型解碼結果 | 模型解碼出的對象,可以比動作多字段,例如 rationale。系統調用時寫成 null。 |
intent | 你的 App | 將要調用或系統正在調用的 App Intent 標識,例如 PlaceOrderIntent。 |
parameters | 你的投影,或系統入參 | 交給 perform() 的對象。未完成時寫成 null。系統調用時就是系統傳入的參數。 |
content 和 parameters 可以有相同的字段名,但它們是兩份對象。把 content 原樣送進 perform(),模型多寫的 rationale 會進動作。
一份能落盤的交換 JSON
下面這份信封和 Schema 是你的契約,不是 Apple 的線上格式。JSONNote 的 JSON Schema 會檢查類型、枚舉和必填。complete 爲 true 才允許執行,這層 Schema 表達不了,要在 App 裏判斷。
{
"source": "foundation-models",
"sessionId": "lm_01",
"schemaVersion": 3,
"generable": "OrderDraft",
"complete": true,
"content": {
"itemId": "sku_42",
"quantity": 1,
"fulfillment": "pickup",
"rationale": "User asked for pickup."
},
"intent": "PlaceOrderIntent",
"parameters": {
"itemId": "sku_42",
"quantity": 1,
"fulfillment": "pickup"
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["source", "sessionId", "schemaVersion", "generable", "complete", "content", "intent", "parameters"],
"properties": {
"source": { "enum": ["foundation-models", "app-intent"] },
"sessionId": { "type": ["string", "null"] },
"schemaVersion": { "type": "integer" },
"generable": { "type": ["string", "null"] },
"complete": { "type": "boolean" },
"content": { "type": ["object", "null"] },
"intent": { "type": "string" },
"parameters": { "type": ["object", "null"] }
}
}
rationale 只留在 content。動作參數的類型對照見 App 動作字段與 JSON Schema。本篇不重複那張表。
沒完成就不要 perform
{
"source": "foundation-models",
"sessionId": "lm_01",
"schemaVersion": 3,
"generable": "OrderDraft",
"complete": false,
"content": {
"itemId": "sku_42"
},
"intent": "PlaceOrderIntent",
"parameters": null
}
這封信可以通過上面的 Schema,因爲 parameters 允許 null。形狀通過也不執行。PartiallyGenerated 表示後面的 token 還會改已有字段。等 complete 變成 true,再從完整的 content 投影 parameters。未完成時就算把 parameters 寫成對象,Schema 同樣不會攔住。
| 信封 | 爲什麼停 |
|---|---|
| complete 爲 false | 後面的 token 還會改已有字段。 |
| schemaVersion 不是當前字段表 | 舊 content 對不上現在的 Intent。 |
| foundation-models 的 parameters 多出 content 沒有的鍵 | 投影不能發明模型沒寫過的值。 |
| app-intent 的 content 不是 null | 系統入參不是模型輸出。不要把它寫回 content,僞裝成生成結果。 |
多出來的鍵用 JSON 對比看。這個檢查只用於 source 爲 foundation-models 的信封。系統調用時 content 是 null,不能要求 parameters 的每個鍵都在 content 裏出現過。
Tool 和 App Intent 怎麼交接
Foundation Models 的 Tool 由你的會話調用。說明見 用工具調用擴展生成。call(arguments:) 收到的 Arguments 也是 Generable。先寫入 content,投影成 parameters 之後,才談得上 perform()。
系統直接調用 Intent 時,source 寫成 app-intent,sessionId、generable、content 寫成 null,parameters 用系統已經傳入的值。不要編一個 sessionId,把這次調用僞裝成模型生成。
| Foundation Models Tool | App Intent | |
|---|---|---|
| 誰調用 | 你的 LanguageModelSession | Siri、快捷指令、Spotlight |
| 入參 | Generable 的 Arguments | @Parameter 上的參數 |
| 你實現的方法 | call(arguments:) | perform() |
| 信封裏的 source | foundation-models | app-intent |
和另外幾篇怎麼分工
| 產品與 Siri 兩篇 | 動作字段那篇 | 本篇 | |
|---|---|---|---|
| 回答 | 爲什麼不只是硬件;Siri 會不會變成 Agent | 哪些字段能進這個動作 | 模型值和動作參數之間落哪一份 JSON |
| 不要在這裏找 | 又一篇發佈稿 | AppEnum 和實體 id 的類型對照 | 把 content 原樣當成 perform() 的入參 |
在 JSONNote 裏對上這封信
- 1選對操作
打開 JSON Schema。操作選「用 Schema 校驗」。第一欄貼信封,第二欄貼上面的 Schema。source 只能是 foundation-models 或 app-intent。complete 必須是布爾。parameters 必須是對象或 null。
- 2撞三條壞數據
source 寫成 siri,或 complete 寫成字符串 true,或刪掉 parameters。這三條都會失敗。未完成那封信(complete 爲 false,parameters 爲 null)會通過。通過也不要調用 perform()。
- 3對比兩份對象
打開 JSON 對比 Diff。第一欄貼 content,第二欄貼 parameters。rationale 只該出現在第一欄。第二欄多出來的鍵是投影寫的,不是模型寫的。這個對比只用於 source 爲 foundation-models 的信封。
常見問題
Generable 的輸出能不能直接交給 perform()?
不能。輸出寫進 content。perform() 只接收投影后的 parameters。信封把兩份都留下,用來對賬。
Schema 通過了,就可以執行嗎?
還不行。這份 Schema 只檢查類型、枚舉和必填。complete 爲 false、parameters 爲 null 時形狀仍然合法。執行前要看到 complete 爲 true,並且在模型這條入口上,parameters 沒有 content 裏不存在的鍵。
Tool 和 App Intent 是同一次調用嗎?
不是。Tool 由 LanguageModelSession 調用。App Intent 由 Siri、快捷指令或 Spotlight 調用。同一件業務可以兩邊都做,入參都先落進這份信封。
schemaVersion 變了,舊日誌還能重放嗎?
不能直接重放。版本號是你給 Generable 字段表標的代際。字段變了,舊 content 對不上現在的 Intent。先停住,做一次明確的映射,再決定要不要執行。
和動作字段與 JSON Schema 那篇是同一篇嗎?
不是。那篇把參數類型對齊成一張字段表。本篇寫模型值和那張表之間要落盤的信封:入口、完成狀態、版本,以及 content 和 parameters 分開。
總結
Foundation Models 和 App Intents 是兩套入口。交換信封把入口、完成狀態、schema 版本、content 和 parameters 放在同一份 JSON 裏,是爲了在 perform() 之前能停下來。
直接結論:content 留在信封裏;perform() 只接收 parameters;complete 爲 false 不執行;schemaVersion 對不上不重放;parameters 不能出現 content 裏沒有的鍵。
下一步:先讓這封交換 JSON 通過 Schema,再用對比看 content 和 parameters。