← 所有文章

Apple Foundation Models:端侧 LLM 框架详解

Foundation Models 框架让应用得以直接、免费、离线地调用为 Apple Intelligence 提供动力的那个端侧大语言模型1。无需 API 密钥,没有按 token 计费,没有网络往返,数据也不会离开设备。过去要实现这一类功能,往往意味着一次云端 LLM 调用加一轮隐私审查;如今其成本几乎可以归零。代价在于能力:端侧模型体量小,上下文窗口有限,框架本身也对它该做什么、不该做什么划下了明确的界线。摸清这些界线,就是这场游戏的全部。

本文是这一框架本身的参考资料:您真正会调用的类型、让它值得一用的那个核心特性,以及您应当就此打住、转向更强大方案的那个临界点。

太长不看

  • LanguageModelSession 是入口。创建一个会话,调用 respond(to:),拿回文本。多轮对话的上下文保存在会话中;单轮任务则每次都用一个全新的会话2
  • 引导式生成才是使用这个框架的理由。 给某个 Swift 类型加上 @Generable 注解,模型便会直接返回这个类型——已填充完毕、经过类型校验——而不是一段还得您自己解析的字符串3
  • Tool 协议让模型可以在生成过程中调用您的代码,去获取数据或执行某个动作,再把结果融回它的回答里4
  • 动手之前,务必先检查 SystemLanguageModel.default.availability。在不符合条件的设备上、在 Apple Intelligence 关闭时、或在模型尚未下载完成期间,这个模型都是不存在的5
  • 上下文窗口是真实存在且偏小的限制。SystemLanguageModel.default.contextSize 报告的是提示词与响应共享的那份 token 预算6。要么提前为它做好规划,要么会话就会抛出异常。
  • 需要 iOS 26 以及一台支持 Apple Intelligence 的设备。低于这条底线,这个框架根本就不存在。

这个框架是什么,又不是什么

Foundation Models 不是对某个云端接口的封装。模型就驻留在设备上,随操作系统一同分发,并运行于神经网络引擎之上。正是这一个事实,决定了 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 的。它在模型工作时挂起,并在请求超出上下文窗口、模型不可用、或内容被防护栏拒绝时抛出异常。这三种情形里的每一种,都是您要切实处理的真实分支,而非可以无视的边角情况。

要让界面响应灵敏,就用流式而非等待。streamResponse(to:) 会在模型生成内容的同时逐步产出局部输出,从而把三秒钟的卡顿,变成随成型而逐渐浮现的文字7

引导式生成:让这个框架值回票价的特性

下面这部分,才配得上入场费。大多数 LLM 集成,会把三分之一的代码花在哄着模型吐出合法的 JSON,另外三分之二则用来防备它终究还是出错的那些时刻。Foundation Models 把这些活儿统统删掉。

给某个 Swift 类型加上 @Generable 注解,请会话生成它,模型便会返回这个类型的一个实例——已填充完毕、类型安全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 Intent,是形态相同的两个不同表面:一项有名称、有描述、有类型的能力。把这项能力设计一次,您就能通过两者把它一并暴露出去。

可用性:那道您跳不过去的检查

模型并非随时都在。在不支持 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。在用户即将操作的某个界面上,这值得一做;若只是投机性地调用它,则是一种浪费。

上下文窗口,以及它何时不再够用

SystemLanguageModel.default.contextSize 报告的是模型在其中工作的那份 token 预算,而这份预算是共享的:提示词加响应,两者合起来必须装得下6。相对于云端模型,这个数字偏小,而在真实输入上,您会很快感受到它的存在。一份长文档、一整段聊天记录、一个臃肿的工具返回结果:其中任何一个都可能撑爆预算,让 respond 抛出异常。

随之而来的有两种失败模式,而两者都该由您来预防。其一是缓慢的渗透:一个多轮会话不断累积记录,直到某一轮再追加便溢出。应对之道是为不相关的工作另起新会话,并让每一轮的输入保持精简。其二是单次的超大请求:一份 20 页的 PDF 装不下,没有商量余地。把它切块,对每一块做摘要,再基于这些摘要去推理(LLM 工程师都很熟悉的 map-reduce),要么就接受这项任务对端侧模型而言形态不对。

对于这个框架真正要紧的那个决定——何时留在端侧、何时该离开——上下文窗口是最干净利落的信号。

何时不该使用 Foundation Models

这个框架免费、私密、又能离线,这让人忍不住处处都想伸手去用。请克制。遇到下列情形时,越过它去够别的:

  • 您需要真正的推理能力,或宽广的世界知识。 端侧模型生来就小。开放式推理、代码生成和深度分析,属于前沿云端模型。拿这些去问端侧模型,得到的会是自信而错误的答案。
  • 输入装不进上下文窗口,而切块又会摧毁原意。有些任务必须一次看到全部。
  • 您需要一个由自己掌控的模型: 某个特定检查点、一次微调、自定义权重、跨操作系统更新都保持确定的版本。Apple 按它自己的时间表分发并更新模型,而非您的。
  • 您处于 iOS 26 以下,或在一台不符合条件的设备上。 框架根本就不在那儿,而每一次运行时,可用性检查都会这么告诉您。

对于这个框架未能覆盖的那些端侧场景(一个自定义模型、您自己的权重、在设备上训练),下面那一层是 Core ML 与 Apple 的 MLX。而对于那些确实需要规模的场景,把一个云端 LLM 放在隐私边界之后,仍是诚实的答案。Foundation Models 并不是这两者中任何一个的替代品。对于在您手头已有的文本上做专注型语言工作,它是恰当的首选;而对于此外的一切,它都是错误的选择。

这个框架所奖赏的本领,不是提示词雕琢功夫,而是对范围的品味:把模型擅长的任务喂给它,设计出恰好捕捉您所需之物的 @Generable 类型,并认出工作量超出设备承载的那一刻。带着这些直觉去构建,端侧模型便能免费完成多得惊人的真实工作。无视它们,您交付的功能就会在每一个输入恰好多出一个 token 的用户那里崩坏。



  1. Apple Developer, “Foundation Models” framework overview. Apple describes the framework as access to the on-device model that powers Apple Intelligence, suited to focused language tasks such as text generation, summarization, classification, and structured output rather than open-ended reasoning or world knowledge. 

  2. Apple Developer, “LanguageModelSession” and “Generating content and performing tasks with Foundation Models”. A session holds multi-turn context; Apple’s guidance is to create a new session for each distinct single-turn interaction. 

  3. Apple Developer, “Generable” and “Prompting an on-device foundation model”. The @Generable macro lets the framework return a populated, type-checked Swift value rather than a string. 

  4. Apple Developer, “Tool” protocol. Defines protocol Tool<Arguments, Output>: Sendable with required name, description, and parameters: GenerationSchema, plus call(arguments:) async throws -> Output. The Arguments type conforms to ConvertibleFromGeneratedContent and is typically declared @Generable

  5. Apple Developer, “SystemLanguageModel.Availability” and its UnavailableReason. Cases: .available and .unavailable(...) with reasons deviceNotEligible, appleIntelligenceNotEnabled, and modelNotReady. SystemLanguageModel.default.isAvailable is the convenience boolean. 

  6. Apple Developer, “SystemLanguageModel.contextSize”. An instance property (reached through SystemLanguageModel.default) documented as the maximum context size, representing the total tokens across input prompt and generated response. 

  7. Apple Developer, “LanguageModelSession.streamResponse(to:)”. Streams partial generated output as the model produces it, for incremental UI updates. 

  8. Apple Developer, “Guide(description:_:)”. A peer macro that attaches a natural-language description and optional constraints (numeric ranges, regular-expression guides) to a @Generable property. Requires iOS 26.0+. 

  9. Apple Developer, “respond(to:schema:includeSchemaInPrompt:options:)”. includeSchemaInPrompt defaults to true; Apple’s discussion recommends keeping the default unless the model already knows the expected format. 

  10. Apple Developer, “LanguageModelSession.prewarm()”. Asks the framework to load model resources ahead of a known upcoming request to reduce first-response latency. 

  11. Author’s related analysis: On-Device LLMs with Apple’s Foundation Models, Custom Adapters for Foundation Models, Foundation Models Use Cases, and Agentic Workflows on Foundation Models. The App Intents and tool-surface argument is developed in App Intents Are Apple’s New API to Your App

相关文章

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

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

3 分钟阅读

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

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

3 分钟阅读

当维护者就是攻击者:jqwik 1.10.0

jqwik 1.10.0会在Maven输出中发出破坏性的提示注入字符串。ANSI转义会把它从人类视线中隐藏。维护者是有意加入的。

2 分钟阅读