Apple Foundation Models:端侧 LLM 框架详解
Foundation Models 框架让应用直接、免费、离线地调用驱动 Apple Intelligence 的那个端侧大语言模型1。不需要 API 密钥,没有按 token 计费,没有网络往返,也没有任何数据离开设备。过去这类功能意味着一个云端 LLM 加一轮隐私评审,如今成本几乎归零。代价在能力:端侧模型体量小,上下文窗口有限,框架还对自己做什么、不做什么划出了硬边界。摸清这些边界,才是全部功夫所在。
本文是这个框架本身的参考手册:真正会调用的类型、让它值得一用的那一个特性,以及应当止步、转向更大模型的临界点。
要点速览
LanguageModelSession是入口。创建一个会话,调用respond(to:),拿回文本。多轮上下文保存在会话里;单轮任务每次都新建会话2。- 引导式生成才是使用这个框架的理由。 用
@Generable标注一个 Swift 类型,模型返回的就是填好值、通过类型检查的该类型实例,而不是一段还要自己解析的字符串3。 Tool协议允许模型在生成过程中调用您的代码,去取数据或执行操作,再把结果并入回答4。- 动手之前先检查
SystemLanguageModel.default.availability。设备不符合条件、Apple Intelligence 未开启、模型仍在下载时,模型都不存在5。 - 上下文窗口是实打实的限制,而且很小。
SystemLanguageModel.default.contextSize给出提示词与响应共用的 token 预算6。端侧预算为 4K token;Private Cloud Compute 上的模型把它提升到 32K14。提前规划,否则会话会抛出错误。 - 需要 iOS 26 以及支持 Apple Intelligence 的设备。低于这条底线,框架根本不存在。iOS 27 测试版在同一套 API 上扩展了图像输入、按请求控制工具调用,以及 Private Cloud Compute 上的服务器端模型121314。
这个框架是什么,不是什么
Foundation Models 不是对云端接口的封装。模型就在设备上,随操作系统一起分发,跑在 Neural Engine 上。仅此一点,就决定了这套 API 的每一个设计取舍,也决定了您使用它时的每一个判断。
您能得到的是:文本生成、摘要、分类、抽取、短文本改写和结构化输出,全部在端侧完成,全部免费。您得不到的是:前沿模型。Apple 打造这个端侧模型,是为了应用内边界清晰的语言任务,而不是开放式推理,不是长文档分析,也不是可供随意提问的世界知识。Apple 自己就是这么说的。这个定位很重要——它设定了预期,而 API 本身并不会阻止您越界1。
让您少踩坑的心智模型是:把端侧模型当成一位快速、私密、免费的实习生——极擅长打磨文字,极不擅长掌握事实。给它素材,给它明确的任务。别问它无从回答的问题。
LanguageModelSession:入口
每一次交互都从一个会话开始。
import FoundationModels
let session = LanguageModelSession()
let response = try await session.respond(to: "Summarize this review in one sentence: \(reviewText)")
print(response.content)
会话持有对话状态。每次调用 respond(to:) 都会追加到正在累积的记录中,因此长期保留的会话记得之前发生过什么。做聊天功能,这正是您要的。而对彼此独立的一次性任务(摘要这段、给那段分类),则应每次调用都新建会话,免得陈旧上下文渗进来,白白吃掉 token 预算2。
respond(to:) 是 async throws 的。模型工作期间它会挂起;当请求超出上下文窗口、模型不可用,或安全护栏拒绝了内容时,它会抛出错误。这三种情况都是需要真正处理的分支,而不是可以忽略的边缘情况。
想让 UI 有响应感,就用流式输出,而不是干等。streamResponse(to:) 会随着模型生成不断吐出片段,把三秒的卡顿变成边成形边浮现的文字7。
引导式生成:撑起整个框架的特性
这才是值回票价的部分。多数 LLM 集成,三分之一的代码用来哄模型吐出合法的 JSON,剩下三分之二用来防备它照样失手。Foundation Models 直接删掉了这些活儿。
用 @Generable 标注一个 Swift 类型,让会话生成它,模型返回的就是该类型的实例,值已填好且类型安全3:
@Generable
struct Recipe {
@Guide(description: "The dish name")
let title: String
@Guide(description: "Ingredients, each as 'quantity item'")
let ingredients: [String]
@Guide(description: "Total minutes, start to finish", .range(5...240))
let minutes: Int
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "A weeknight pasta for two.",
generating: Recipe.self
)
let recipe = response.content // a Recipe, not a String
不用解析。不用 JSONDecoder。不用为畸形输出写重试循环。@Guide 宏约束单个字段:一段模型会当作指令来读的描述,外加可选的限制条件,比如数值范围,或输出必须匹配的正则表达式8。框架不是客客气气地请模型给一个 5 到 240 之间的数字,而是直接约束解码过程,让这个字段不可能是别的样子。
它强加的这套纪律才是真正的价值所在。您先用 Swift 设计输出类型,由编译器把关。模型填的是您定义好的契约,而不是丢给您一段需要逆向解读的文字。对于信息抽取、表单填充,以及任何把语言变成数据的功能,引导式生成就是演示品与可上线代码之间的分界线。
有一个开关值得记住:respond(to:generating:) 的 includeSchemaInPrompt 默认为 true,会把您的类型结构注入提示词,引导模型往那个形状靠。除非模型已经从训练数据或会话中较早的轮次里知道了这个格式,否则请保持开启;为了省 token 而对一个模型从未见过的格式关掉它,得到的只会是垃圾9。
工具调用:让模型够得着您的代码
引导式生成塑造输出,工具调用改变输入。所谓工具,就是您代码中的一段逻辑,模型可以在生成中途调用它,去获取自己没有的信息或执行某个操作,然后带着结果继续把回答写完4。
工具需遵循 Tool 协议:一个 name,一段供模型判断何时调用的 description,一个 @Generable 的 Arguments 类型,以及真正干活的 call(arguments:) 方法4:
struct FindContacts: Tool {
let name = "findContacts"
let description = "Find a specific number of contacts from the address book"
@Generable
struct Arguments {
@Guide(description: "How many contacts to return", .range(1...10))
let count: Int
}
func call(arguments: Arguments) async throws -> [String] {
// Fetch contacts, return formatted names.
}
}
let session = LanguageModelSession(tools: [FindContacts()])
let response = try await session.respond(to: "Draft a dinner invite to three of my contacts.")
流程是这样:模型判断自己需要联系人,带着一个已校验的 count 调用您的工具,您返回数据,模型再用真实姓名写出邀请。参数经由同一套引导式生成机制送达,已通过类型检查,因此您永远不必从自由文本里解析模型的意图。工具描述是您唯一能左右模型何时调用它的抓手,所以写它时要像写一份函数文档——一位毫无其他背景的工程师读完就得正确使用。
这里也是 Foundation Models 与整个智能体叙事的接缝所在。端侧模型调用的工具,与 Apple Intelligence 调用的 App Intent11,是形状相同的两种界面:一项具名、有描述、有类型的能力。能力只需设计一次,就能同时通过两条路径暴露出去。
可用性:不能跳过的检查
模型并非总在。不支持 Apple Intelligence 的设备上没有,用户关掉功能时没有,操作系统还在下载模型资源的那段时间里也没有。如果您发布的代码默认模型一定存在,那么对于一批您从未测试过的用户,它会崩溃、悄悄降级,或者干脆卡死。
检查 SystemLanguageModel.default.availability,并按原因分支处理5:
switch SystemLanguageModel.default.availability {
case .available:
// Show the intelligence feature.
case .unavailable(.deviceNotEligible):
// Hide it. This device will never have the model.
case .unavailable(.appleIntelligenceNotEnabled):
// Prompt the user to turn on Apple Intelligence.
case .unavailable(.modelNotReady):
// Downloading or otherwise not ready yet. Try again later.
case .unavailable(let other):
// Unknown reason. Fail closed.
}
这三种原因要求三种不同的产品反应,把它们混为一谈,是这类功能显得“坏掉了”的最常见原因。deviceNotEligible 是永久性的:把功能藏起来,别反复骚扰用户。appleIntelligenceNotEnabled 是用户可控的设置:给一次提示是合理的。modelNotReady 是暂时的:稍后重试,不要报错。请以对待正常路径同样的用心去构建不可用路径,因为对相当一部分设备来说,那是唯一的路径。
当模型可用,而您已经预见到马上会有请求时,对会话调用 prewarm() 可以预热模型,让第一次真实响应来得更快10。在用户即将操作的界面上,这很划算;漫无目的地投机调用,则是浪费。
实操:一个文件写完一个完整功能
把上面这些部件拼起来,就是一个真实功能,代码量还不如多数网络层为单个接口写的多。下面这个例子是一屏完整、可编译的 SwiftUI 界面,把自由格式的会议记录转成结构化的待办事项:可用性检查、@Generable 输出类型、一次引导式生成调用,以及三条不可用分支的处理。每一个符号都来自上文记录过的框架接口2358。
import SwiftUI
import FoundationModels
@Generable
struct ActionItems {
@Guide(description: "One-sentence summary of the meeting")
let summary: String
@Guide(description: "Concrete follow-up tasks, each starting with a verb")
let tasks: [String]
@Guide(description: "How urgent the follow-ups are overall", .anyOf(["low", "medium", "high"]))
let urgency: String
}
struct MeetingNotesView: View {
@State private var notes = ""
@State private var result: ActionItems?
@State private var errorMessage: String?
var body: some View {
Form {
TextField("Paste meeting notes", text: $notes, axis: .vertical)
.lineLimit(6...12)
Button("Extract action items") {
Task { await extract() }
}
.disabled(notes.isEmpty)
if let result {
Section(result.summary) {
ForEach(result.tasks, id: \.self) { Text($0) }
Text("Urgency: \(result.urgency)")
}
}
if let errorMessage {
Text(errorMessage).foregroundStyle(.secondary)
}
}
}
private func extract() async {
switch SystemLanguageModel.default.availability {
case .available:
do {
let session = LanguageModelSession()
let response = try await session.respond(
to: "Extract the action items from these notes: \(notes)",
generating: ActionItems.self
)
result = response.content
} catch {
errorMessage = "The model could not process these notes."
}
case .unavailable(.appleIntelligenceNotEnabled):
errorMessage = "Turn on Apple Intelligence in Settings to use this feature."
case .unavailable(.modelNotReady):
errorMessage = "The model is still downloading. Try again shortly."
case .unavailable:
errorMessage = "This feature needs an Apple Intelligence-capable device."
}
}
}
这么小的示例里有三处细节值得留意。输出类型就是 API:ActionItems 精确定义了这个功能产出什么,而 urgency 上的 @Guide 约束意味着这个字符串不可能返回三个允许值之外的东西8。会话按次创建,因为每次抽取彼此独立;保留会话只会把之前的记录拖进 token 预算2。三条不可用分支给出的是三种不同的用户体验,而不是一个笼统的错误提示——这正是“诚实降级的功能”与“看起来坏了的功能”之间的差别。把这个文件贴进 iOS 26 项目,在支持 Apple Intelligence 的设备上运行,它就能跑。
上下文窗口,以及它不够用的那一刻
SystemLanguageModel.default.contextSize 报告模型可用的 token 预算,而这份预算是共用的:提示词加响应必须一起塞得下6。相对云端模型,这个数字很小,面对真实输入很快就能感觉到。一份长文档、一整段聊天记录、一个臃肿的工具返回结果——任何一项都可能撑爆预算,让 respond 抛出错误。
随之而来的是两种失败模式,防住它们是您的责任。第一种是缓慢累积:多轮会话不断堆积记录,直到多出一轮就溢出。应对办法是为不相关的任务另起会话,并保持每轮输入精简。第二种是单次请求过大:一份 20 页的 PDF 就是塞不下,没有别的说法。要么分块、对每块做摘要,再基于摘要推理(LLM 工程师熟悉的 map-reduce),要么承认这个任务的形状本就不适合端侧模型。
对于这个框架,真正重要的决策是什么时候留在端侧、什么时候离开——而上下文窗口是判断这一点最干净的信号。数字如今已经公开:端侧模型在 4K token 的预算内工作,Private Cloud Compute 上的服务器端模型把这个数字提到 32K14。关于分块的那些建议,都请配上这组数字来理解。
iOS 27 测试版带来了什么
以上描述的都是 iOS 26 中发布的框架,而且至今依然成立。iOS 27 测试版在同一套接口上向四个方向做了扩展,没有一个会打破 iOS 26 的心智模型12。
提示词可以带图。 端侧模型获得了视觉能力:把图像附件与文本一并放进提示词,模型会就两者作答。新增类型是 Attachment、ImageAttachmentContent 和 ImageReference,附件接受 UIImage、NSImage、CGImage、Core Image 类型、CoreVideo 像素缓冲区以及文件 URL1213。图像不限尺寸和宽高比,但它们花的是与文本同一份 token 预算,因此端侧那 4K 的窗口很快就会成为设计约束13。完整讲解见 iOS 27 中的 Foundation Models 图像输入。
工具调用装上了阀门。 GenerationOptions 新增了可按请求设置的 toolCallingMode,用来控制模型如何与您挂载的工具交互;Vision 框架还直接提供了现成的 OCRTool 和 BarcodeReaderTool 实现,挂到会话上即可,不必自己写识别代码15。行为细节见 iOS 27 中的工具调用控制。
更大的模型,只差一行。 PrivateCloudComputeLanguageModel 用同一套 API 去调用 Apple 在 Private Cloud Compute 上的服务器端模型,需要相应的授权,并带来端侧模型所没有的 32K 上下文窗口和推理能力1214。引导式生成和工具的用法完全不变;切换模型就是会话的 model 参数。
会话获得了更多控制面。 测试版新增了 ContextOptions、TranscriptErrorHandlingPolicy、动态配置(DynamicInstructions、LanguageModelSession.DynamicProfile),以及一套自定义语言模型提供方协议(LanguageModel、LanguageModelExecutor),让会话去驱动您自己提供的模型,而非系统模型12。watchOS 也在 27.0 加入了受支持的平台列表12。
该记住的定位是:iOS 26 的代码在 iOS 27 上照常编译、行为一致。测试版拓宽的是提示词能承载什么、模型能在哪里运行;框架的本质并没有变。
什么时候不该用 Foundation Models
这个框架免费、私密、离线,诱人到让人处处都想用它。请克制。遇到下面这些情况,应当越过它:
- 您需要真正的推理能力或广博的世界知识。 端侧模型的小是设计使然。开放式推理、代码生成和深度分析属于云端的前沿模型。让端侧模型干这些,只会得到自信而错误的答案。
- 输入塞不进上下文窗口,而分块会破坏语义。有些任务必须一次看到全部内容。
- 您需要一个自己能掌控的模型: 特定的检查点、微调版本、自定义权重,或跨系统更新的确定性版本管理。Apple 按自己的节奏发布和更新模型,不按您的。
- 您的目标版本低于 iOS 26,或设备不符合条件。 框架根本就不在那里,可用性检查每次运行都会如实告诉您。
至于这个框架覆盖不到的端侧场景(自定义模型、自有权重、在设备上训练),下面还有几层:固定的转换后模型用 Core ML,开放权重模型和您自己的微调版本用 MLX,需要显式控制专门化与调度时,则用 iOS 27 的 Core AI。至于真正需要规模的场景,Private Cloud Compute 或隐私边界之后的云端 LLM 仍然是诚实的答案。Foundation Models 不能替代其中任何一个。对于您手上已有文本的、边界清晰的语言任务,它是正确的第一选择;除此之外的一切,它都是错的选择。
这个框架奖励的能力不是提示词技巧,而是对边界的品味:把它擅长的任务喂给它,设计出恰好捕捉所需信息的 @Generable 类型,并且识别出工作量超出设备承受范围的那一刻。带着这些直觉去构建,端侧模型能免费替您完成出人意料的大量实际工作。忽视它们,您交付的功能就会在每一位输入多出一个 token 的用户那里崩掉。
常见问题
Apple 的 Foundation Models 框架可以免费使用吗?
可以。这个框架让应用直接、免费、离线地调用驱动 Apple Intelligence 的那个端侧模型。不需要 API 密钥,没有按 token 计费,也没有网络往返1。
Foundation Models 对设备和 iOS 版本有什么要求?
需要 iOS 26 以及支持 Apple Intelligence 的设备。低于这条底线,框架不存在;即使系统版本满足要求,在不符合条件的设备上、Apple Intelligence 被关闭时,或模型正在下载时,模型同样不存在。使用前请务必检查 SystemLanguageModel.default.availability5。
如何得到结构化、类型安全的输出,而不是一段字符串?
用 @Generable 标注一个 Swift 类型,模型返回的就是填好值、通过类型检查的该类型实例,而不是一段还要自己解析的字符串。这种引导式生成,正是让整个框架值得一用的那一个特性3。
Apple 端侧模型的上下文窗口有多大?
SystemLanguageModel.default.contextSize 报告 token 预算,该预算由提示词和生成的响应共用6。端侧模型提供 4K token,Private Cloud Compute 上的模型提供 32K14。长文档和长多轮对话历史会超出端侧预算,因此请为这个上限提前规划,否则会话会抛出错误。
Foundation Models 能离线工作吗?它会把数据发给 Apple 吗?
它完全在设备上、依托 Neural Engine 运行。没有数据离开设备,也不需要网络往返——正因如此,它适合那些过去必须依赖云端 LLM 和一轮隐私评审的功能1。
端侧模型能在生成过程中调用我自己的代码吗?
可以。Tool 协议允许模型在生成期间调用您的代码去获取数据或执行操作,再把结果并入回答4。
什么时候不该使用 Foundation Models?
当您需要前沿模型时就应越过它:开放式推理、代码生成、长文档分析或世界知识。Apple 打造这个端侧模型是为了应用内边界清晰的语言任务,向它索取通用智能,只会得到自信而错误的答案1。
iOS 27 为 Foundation Models 增加了什么?
iOS 27 测试版增加了图像输入(提示词中的附件,可由 UIImage、CGImage、像素缓冲区等创建)、通过 GenerationOptions 按请求控制工具调用、现成的 Vision 工具 OCRTool 与 BarcodeReaderTool,以及用于以同一套 API 调用 Apple 32K 上下文服务器端模型的 PrivateCloudComputeLanguageModel12131415。iOS 26 的代码无需改动即可运行。
-
Apple Developer,“Foundation Models” framework overview。Apple 将该框架描述为对驱动 Apple Intelligence 的端侧模型的访问途径,适用于文本生成、摘要、分类和结构化输出这类边界清晰的语言任务,而非开放式推理或世界知识。 ↩↩↩↩↩
-
Apple Developer,“LanguageModelSession” 与 “Generating content and performing tasks with Foundation Models”。会话持有多轮上下文;Apple 的建议是为每一次独立的单轮交互创建新会话。 ↩↩↩↩
-
Apple Developer,“Generable” 与 “Prompting an on-device foundation model”。
@Generable宏让框架返回一个填好值、经过类型检查的 Swift 值,而不是字符串。 ↩↩↩↩ -
Apple Developer,“Tool” protocol。定义了
protocol Tool<Arguments, Output>: Sendable,要求实现name、description和parameters: GenerationSchema,以及call(arguments:) async throws -> Output。Arguments类型遵循ConvertibleFromGeneratedContent,通常声明为@Generable。 ↩↩↩↩ -
Apple Developer,“SystemLanguageModel.Availability” 及其
UnavailableReason。取值为.available与.unavailable(...),后者的原因包括deviceNotEligible、appleIntelligenceNotEnabled和modelNotReady。SystemLanguageModel.default.isAvailable是便捷的布尔值。 ↩↩↩↩ -
Apple Developer,“SystemLanguageModel.contextSize”。这是一个实例属性(通过
SystemLanguageModel.default访问),文档将其定义为最大上下文尺寸,代表输入提示词与生成响应的 token 总量。 ↩↩↩ -
Apple Developer,“LanguageModelSession.streamResponse(to:)”。随模型生成流式输出片段,用于增量更新 UI。 ↩
-
Apple Developer,“Guide(description:_:)”。这是一个 peer 宏,为
@Generable属性附加自然语言描述和可选约束(数值范围、正则表达式引导)。需要 iOS 26.0 及以上。 ↩↩↩ -
Apple Developer,“respond(to:schema:includeSchemaInPrompt:options:)”。
includeSchemaInPrompt默认为true;Apple 在说明中建议保持默认值,除非模型已经知道预期格式。 ↩ -
Apple Developer,“LanguageModelSession.prewarm()”。在已知的请求到来之前,请求框架预先加载模型资源,以降低首次响应延迟。 ↩
-
作者的相关分析:用 Apple Foundation Models 实现端侧 LLM、为 Foundation Models 定制适配器、Foundation Models 的应用场景 和 在 Foundation Models 上构建智能体工作流。关于 App Intents 与工具界面的论述,见 App Intents 是 Apple 通往您应用的新 API。 ↩
-
Apple Developer,截至 2026 年 7 月的 “Foundation Models” 框架主题。标记为 27.0 版本 beta 的类型包括:
Attachment、ImageAttachmentContent和ImageReference(提示词附件);ContextOptions与TranscriptErrorHandlingPolicy;DynamicInstructions与LanguageModelSession.DynamicProfile(动态配置);带com.apple.developer.private-cloud-compute授权的PrivateCloudComputeLanguageModel;以及自定义提供方接口LanguageModel、LanguageModelCapabilities和LanguageModelExecutor。该框架的平台列表新增 watchOS 27.0(beta)。 ↩↩↩↩↩↩↩ -
Apple,WWDC26 session 241,“What’s new in the Foundation Models framework”。图像附件“可由多种类型创建,包括 UIImage、NSImage、CGImage、Core Image 类型、CoreVideo 像素缓冲区以及文件 URL”;“模型支持任意尺寸和宽高比的图像”,并且“更大的图像会消耗更多 token 并带来更高的延迟”。 ↩↩↩↩
-
Apple,WWDC26 session 319,“Build with the new Apple Foundation Model on Private Cloud Compute”。“端侧模型提供 4k,使用 PCC 则可获得 32K”;该演讲演示了只改一行代码就从端侧模型切换到 PCC 服务器端模型,引导式生成与工具调用在两者上的表现完全一致。 ↩↩↩↩↩↩
-
Apple Developer,“GenerationOptions.ToolCallingMode”(iOS 27 beta;包括
toolCallingMode属性与init(samplingMode:temperature:maximumResponseTokens:toolCallingMode:)构造器),以及 Vision 框架的 “OCRTool” 与 “BarcodeReaderTool”(iOS 27 beta),二者均遵循 Foundation Models 的Tool协议。 ↩↩