← 所有文章

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,一个 @GenerableArguments 类型,以及真正干活的 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

提示词可以带图。 端侧模型获得了视觉能力:把图像附件与文本一并放进提示词,模型会就两者作答。新增类型是 AttachmentImageAttachmentContentImageReference,附件接受 UIImageNSImageCGImage、Core Image 类型、CoreVideo 像素缓冲区以及文件 URL1213。图像不限尺寸和宽高比,但它们花的是与文本同一份 token 预算,因此端侧那 4K 的窗口很快就会成为设计约束13。完整讲解见 iOS 27 中的 Foundation Models 图像输入

工具调用装上了阀门。 GenerationOptions 新增了可按请求设置的 toolCallingMode,用来控制模型如何与您挂载的工具交互;Vision 框架还直接提供了现成的 OCRToolBarcodeReaderTool 实现,挂到会话上即可,不必自己写识别代码15。行为细节见 iOS 27 中的工具调用控制

更大的模型,只差一行。 PrivateCloudComputeLanguageModel 用同一套 API 去调用 Apple 在 Private Cloud Compute 上的服务器端模型,需要相应的授权,并带来端侧模型所没有的 32K 上下文窗口和推理能力1214。引导式生成和工具的用法完全不变;切换模型就是会话的 model 参数。

会话获得了更多控制面。 测试版新增了 ContextOptionsTranscriptErrorHandlingPolicy、动态配置(DynamicInstructionsLanguageModelSession.DynamicProfile),以及一套自定义语言模型提供方协议(LanguageModelLanguageModelExecutor),让会话去驱动您自己提供的模型,而非系统模型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 测试版增加了图像输入(提示词中的附件,可由 UIImageCGImage、像素缓冲区等创建)、通过 GenerationOptions 按请求控制工具调用、现成的 Vision 工具 OCRToolBarcodeReaderTool,以及用于以同一套 API 调用 Apple 32K 上下文服务器端模型的 PrivateCloudComputeLanguageModel12131415。iOS 26 的代码无需改动即可运行。



  1. Apple Developer,“Foundation Models” framework overview。Apple 将该框架描述为对驱动 Apple Intelligence 的端侧模型的访问途径,适用于文本生成、摘要、分类和结构化输出这类边界清晰的语言任务,而非开放式推理或世界知识。 

  2. Apple Developer,“LanguageModelSession”“Generating content and performing tasks with Foundation Models”。会话持有多轮上下文;Apple 的建议是为每一次独立的单轮交互创建新会话。 

  3. Apple Developer,“Generable”“Prompting an on-device foundation model”@Generable 宏让框架返回一个填好值、经过类型检查的 Swift 值,而不是字符串。 

  4. Apple Developer,“Tool” protocol。定义了 protocol Tool<Arguments, Output>: Sendable,要求实现 namedescriptionparameters: GenerationSchema,以及 call(arguments:) async throws -> OutputArguments 类型遵循 ConvertibleFromGeneratedContent,通常声明为 @Generable。 

  5. Apple Developer,“SystemLanguageModel.Availability”UnavailableReason。取值为 .available.unavailable(...),后者的原因包括 deviceNotEligibleappleIntelligenceNotEnabledmodelNotReadySystemLanguageModel.default.isAvailable 是便捷的布尔值。 

  6. Apple Developer,“SystemLanguageModel.contextSize”。这是一个实例属性(通过 SystemLanguageModel.default 访问),文档将其定义为最大上下文尺寸,代表输入提示词与生成响应的 token 总量。 

  7. Apple Developer,“LanguageModelSession.streamResponse(to:)”。随模型生成流式输出片段,用于增量更新 UI。 

  8. Apple Developer,“Guide(description:_:)”。这是一个 peer 宏,为 @Generable 属性附加自然语言描述和可选约束(数值范围、正则表达式引导)。需要 iOS 26.0 及以上。 

  9. Apple Developer,“respond(to:schema:includeSchemaInPrompt:options:)”includeSchemaInPrompt 默认为 true;Apple 在说明中建议保持默认值,除非模型已经知道预期格式。 

  10. Apple Developer,“LanguageModelSession.prewarm()”。在已知的请求到来之前,请求框架预先加载模型资源,以降低首次响应延迟。 

  11. 作者的相关分析:用 Apple Foundation Models 实现端侧 LLM为 Foundation Models 定制适配器Foundation Models 的应用场景在 Foundation Models 上构建智能体工作流。关于 App Intents 与工具界面的论述,见 App Intents 是 Apple 通往您应用的新 API。 

  12. Apple Developer,截至 2026 年 7 月的 “Foundation Models” 框架主题。标记为 27.0 版本 beta 的类型包括:AttachmentImageAttachmentContentImageReference(提示词附件);ContextOptionsTranscriptErrorHandlingPolicyDynamicInstructionsLanguageModelSession.DynamicProfile(动态配置);带 com.apple.developer.private-cloud-compute 授权的 PrivateCloudComputeLanguageModel;以及自定义提供方接口 LanguageModelLanguageModelCapabilitiesLanguageModelExecutor。该框架的平台列表新增 watchOS 27.0(beta)。 

  13. Apple,WWDC26 session 241,“What’s new in the Foundation Models framework”。图像附件“可由多种类型创建,包括 UIImage、NSImage、CGImage、Core Image 类型、CoreVideo 像素缓冲区以及文件 URL”;“模型支持任意尺寸和宽高比的图像”,并且“更大的图像会消耗更多 token 并带来更高的延迟”。 

  14. Apple,WWDC26 session 319,“Build with the new Apple Foundation Model on Private Cloud Compute”。“端侧模型提供 4k,使用 PCC 则可获得 32K”;该演讲演示了只改一行代码就从端侧模型切换到 PCC 服务器端模型,引导式生成与工具调用在两者上的表现完全一致。 

  15. Apple Developer,“GenerationOptions.ToolCallingMode”(iOS 27 beta;包括 toolCallingMode 属性与 init(samplingMode:temperature:maximumResponseTokens:toolCallingMode:) 构造器),以及 Vision 框架的 “OCRTool”“BarcodeReaderTool”(iOS 27 beta),二者均遵循 Foundation Models 的 Tool 协议。 

相关文章

Foundation Models 使用场景:通用与内容标记

iOS 26 Foundation Models 提供了 .general 和 .contentTagging 两种使用场景。运用 Apple 的规则来决定何时提示词优于专门化方案。

3 分钟阅读

Foundation Models 自定义适配器:何时需要训练

iOS 26 Foundation Models 自定义适配器训练 LoRA 权重,导出 .fmadapter 包,通过 Background Assets 交付,并需要 Apple 的授权。

3 分钟阅读

Claude Code Auto Mode Is Not a Security Boundary

Anthropic closed a working auto-mode bypass as Informative: the classifier is best-effort, not a guarantee. What actuall…

10 分钟阅读