博客 • Foundation Models

Apple Intelligence 2026 开发者生态变化:Foundation Models、App Intents 与 JSON 数据交换机制详解

2026 年,同一个 App 会同时接到两套入口:设备上的 LanguageModelSession 按类型解码出一份内容,系统则调用 App Intent 的 perform()。要落盘的不是这两份里的任何一份原样,而是你自己的交换信封。

产品叙事见 iPhone 18 为什么不只是硬件。Siri 会不会变成 Agent,见 Siri AI 会成为 AI Agent 吗。本篇只写信封:哪些字段要落盘,以及什么情况下不能执行。

先记住这一点:框架把 Generable 收成 schema,再解码成值。这只发生在模型会话里。你要保存、对账、再决定要不要执行,靠的是另一份信封。下面按「两份合同 → 信封字段 → 一份 JSON → 四种停」拆开。

两份合同不是一次调用

设备上的模型怎样按类型填值,见 Generable。类型会收成 schema,模型填完,框架再解码。静态类型用 @Generable。没有静态类型时,会话可以按 GenerationSchema 返回 GeneratedContent。两种结果写进信封时都叫 content。

App Intent 是另一条调用链。参数写在 @Parameter 上,说明见 为 App Intent 添加参数。系统调用 perform() 时没有 LanguageModelSession,也就没有 Generable 的 content。

  1. 1
    会话产出内容

    respond(to:generating:) 返回 Response。生成还没结束时,对应的是 PartiallyGenerated,字段可以仍为空。这时还没有一份能执行的参数。

  2. 2
    投影出参数

    只把 Intent 接受的字段抄进 parameters。模型多写的 rationale 留在 content。两份对象可以同名,但不能是同一个对象。

  3. 3
    先写信封再执行

    source、sessionId、schemaVersion、complete、content、intent、parameters 落成一份 JSON。complete 不是 true,就不调用 perform()。

信封里有哪些字段

下面这些键是你自己的信封,不是 Apple 解码结果里的字段。

字段谁来写作用
source你的 Appfoundation-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 里判断。

生成完成后的信封。rationale 留在 content,parameters 里没有它。
{
  "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。操作改成用 Schema 校验。
{
  "$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

生成未完成。quantity 还没有,parameters 保持 null。
{
  "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 ToolApp Intent
谁调用你的 LanguageModelSessionSiri、快捷指令、Spotlight
入参Generable 的 Arguments@Parameter 上的参数
你实现的方法call(arguments:)perform()
信封里的 sourcefoundation-modelsapp-intent

和另外几篇怎么分工

产品与 Siri 两篇动作字段那篇本篇
回答为什么不只是硬件;Siri 会不会变成 Agent哪些字段能进这个动作模型值和动作参数之间落哪一份 JSON
不要在这里找又一篇发布稿AppEnum 和实体 id 的类型对照把 content 原样当成 perform() 的入参

在 JSONNote 里对上这封信

  1. 1
    选对操作

    打开 JSON Schema。操作选「用 Schema 校验」。第一栏贴信封,第二栏贴上面的 Schema。source 只能是 foundation-models 或 app-intent。complete 必须是布尔。parameters 必须是对象或 null。

  2. 2
    撞三条坏数据

    source 写成 siri,或 complete 写成字符串 true,或删掉 parameters。这三条都会失败。未完成那封信(complete 为 false,parameters 为 null)会通过。通过也不要调用 perform()。

  3. 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。

返回博客