Apple Foundation Models实战解析:iOS应用中端侧大模型(LLM)开发完全指南#
苹果为什么要把大模型塞进iOS#
一年前的WWDC 2025上,苹果通过一个名为 Foundation Models 的新框架,开放了驱动Apple Intelligence的同一个端侧语言模型。不需要API密钥,没有按token计费,用户数据也不会离开设备。模型早已存在于手机上,从iOS 26 / macOS 26 / iPadOS 26 / visionOS开始,只需几行Swift代码就能调用它。
这不是营销话术——它实实在在改变了哪些功能值得放进App里做。我是一名一线iOS开发者,这篇文章不是主题演讲的复述:而是我亲手验证过的API、实际会碰到的限制,以及关于端侧大模型何时是正确选择、何时不是的坦诚判断。
到2026年年中,这个框架已经又走过了一轮迭代:WWDC 2026上,苹果发布了第三代Apple Foundation Models(AFM 3),同时还单独公布了在同一套Swift API中接入第三方大模型供应商的能力——这也是专门一场分享「Bring an LLM provider to the Foundation Models framework」的主题。这是一个重要信号:Foundation Models正在从"自家模型的封装"变成"端侧与混合推理的统一接口"。
基础端侧模型是稠密架构,参数量约30亿(苹果在当前这一代中称之为AFM 3 Core)。它针对一组特定任务做了调优:摘要、实体抽取、文本理解与润色、简短对话、简短的创意生成。它不是通用聊天机器人,也不能替代搜索引擎——苹果官方文档明确说明,它并非设计用来作为通用世界知识的来源。
在部分设备上——当前这一代里指配备12GB内存的高端iPhone——可以使用扩展模型:AFM 3 Core Advanced,参数量约200亿,采用稀疏架构,每次请求只激活10亿到40亿参数。对开发者而言,这意味着同一行代码在不同设备上运行的其实是明显不同的"大脑",不应该期待iPhone 15 Pro和iPhone 17 Pro给出完全一致的输出质量。
与模型交互主要围绕两个类型展开:
SystemLanguageModel— 模型本身、其可用性以及专用模式(useCase,例如开箱即用的标签生成与实体抽取模式.contentTagging)的入口。LanguageModelSession— 对话会话,真正发送请求的地方,维护历史记录(transcript)、指令和工具。
可用性分级:三种"不能用"的原因,不是同一个问题#
关于Foundation Models的文章最常见的错误,是把"不可用"当成单一状态来处理。实际上 SystemLanguageModel.default.availability 会返回三种本质不同的原因之一,把它们混为一谈,几乎必然会做出一个让人感觉"坏掉了"的功能:
import FoundationModels
func checkFoundationModelsAvailability() -> String {
switch SystemLanguageModel.default.availability {
case .available:
return "✅ 模型已就绪"
case .unavailable(.deviceNotEligible):
// A13或更早芯片 — 此功能在该设备上永远不会出现
return "❌ 设备不支持Apple Intelligence"
case .unavailable(.appleIntelligenceNotEnabled):
// 设备符合条件,但用户未在设置中开启Apple Intelligence
return "⚠️ 请在设置中开启Apple Intelligence"
case .unavailable(.modelNotReady):
// 模型仍在下载 — 这是临时状态
return "⏳ 模型正在下载,请稍后再试"
case .unavailable(let reason):
return "❓ 不可用:\(reason)"
}
}deviceNotEligible 是永久状态:设备太旧,应该隐藏功能,不要反复提示。appleIntelligenceNotEnabled 是用户可控的设置项,一次礼貌的引导提示是合理的。modelNotReady 是模型下载中的临时状态,应该重试而不是报错。把这三种场景合并成一个笼统的"功能不可用"界面,是用户抱怨"AI坏了"最常见的根源。
截至本文撰写时支持的平台是:iOS、iPadOS、macOS、visionOS。watchOS和tvOS不在框架覆盖范围内。
结构化输出:用@Generable代替手写JSON解析#
在Foundation Models出现之前,获取结构化输出意味着让模型返回JSON,自己手写解析代码,还要祈祷它不会在花括号前后多塞几个字。@Generable 宏彻底消除了这一整类bug:它在编译期生成schema,保证模型返回的就是真正的Swift类型的值。
import FoundationModels
@Generable
struct TripSummary {
@Guide(description: "简短的行程标题,不超过6个词")
var title: String
@Guide(description: "行程的主要亮点", .count(3))
var highlights: [String]
@Guide(description: "整体心情评分,1到5分")
var moodScore: Int
}
func summarizeTrip(notes: String) async throws -> TripSummary {
let session = LanguageModelSession(
instructions: "你是一个简要总结旅行笔记的助手。"
)
let response = try await session.respond(
to: "用户笔记:\(notes)",
generating: TripSummary.self
)
return response.content
}@Guide 为字段附加的不只是给模型看的自然语言描述,还有 .count()、.maximumCount() 等编程约束——它们是真正在收窄生成空间,而不只是"礼貌地请求"。
流式响应与工具调用#
为了让界面更有响应感,结构化输出可以分段流式返回——@Generable 生成的类型会自动附带一个字段全部可选的"部分"版本:
func streamTripSummary(notes: String) async throws {
let session = LanguageModelSession()
let stream = session.streamResponse(
to: "根据这些笔记总结一次旅行:\(notes)",
generating: TripSummary.self
)
for try await partial in stream {
// partial: TripSummary.PartiallyGenerated — 字段都是可选的,
// 逐步填充,便于实时更新界面
print(partial)
}
}如果模型需要prompt里没有的数据——比如用户当前所在城市——它可以调用你通过 Tool 协议描述的工具:
struct CurrentLocationTool: Tool {
let name = "getCurrentLocation"
let description = "返回用户当前所在的城市"
@Generable
struct Arguments {
@Guide(description: "是否需要精确到街区级别")
var preciseArea: Bool
}
func call(arguments: Arguments) async throws -> ToolOutput {
let city = arguments.preciseArea ? "里斯本,阿尔法马区" : "里斯本"
return ToolOutput(city)
}
}
let session = LanguageModelSession(
tools: [CurrentLocationTool()],
instructions: "当需要用户所在城市时,使用getCurrentLocation。"
)模型会根据工具描述自行判断是否调用它——这与许多人在传统NLP流程中习惯的手动意图路由,是完全不同的思维模型。
边界在哪里:上下文、任务与硬件#
最主要的实际限制是会话的上下文窗口大小。根据苹果关于端侧模型上下文窗口管理的技术说明TN3193,一个会话的上限大约是4096个token,涵盖指令、历史记录和留给回复的空间。这比GPT或Claude这类云端模型小了几个数量级,意味着长文档、冗长的聊天历史或体量较大的few-shot提示词,根本塞不进一个会话——需要做摘要、裁剪,或者开启新会话。
一旦接近上限,模型可能在正式超出之前就开始出错——它可能确实没有足够空间生成回复。实用的做法是:为回复本身预留余量,不要把整个窗口都用在输入上。
第二个限制是质量。它不是通用聊天机器人,也不是世界知识的来源:一个30亿(甚至稀疏的200亿)参数的模型,擅长收窄和转换已有的文本,而不擅长回答"给我讲讲……"这类开放式问题。第三个限制是硬件碎片化:扩展模型AFM 3 Core Advanced并非在所有支持Apple Intelligence的设备上都可用,只有内存足够的设备才能用上。设计UX时要确保降级到基础模型不会破坏使用流程。
端侧大模型还是云端:一张诚实选择的对照表#
| 维度 | 端侧(Foundation Models) | 云端大模型(API) |
|---|---|---|
| 数据隐私 | 数据从不离开设备 | 数据发送到供应商服务器 |
| 成本 | 免费,没有token账单 | 按token/请求计费 |
| 离线支持 | 无网络也能工作 | 需要联网 |
| 延迟 | 低,无需网络往返 | 取决于网络与服务器排队情况 |
| 复杂任务质量 | 有限(摘要、抽取、短文本) | 在推理与知识方面明显更强 |
| 上下文窗口 | 每次会话约4096个token | 数万到数十万token |
| 可用性 | 仅限支持Apple Intelligence的设备 | 任何联网设备 |
| 模型定制 | 无法微调权重 | 可微调、可设system prompt、可选模型 |
这张表给出的实际结论是:端侧大模型是一个面向特定、有限任务的工具,适合对隐私要求高、成本要求为零的场景——而不是云端API的通用替代品。
不是每个任务都需要大模型——附上手清单#
在开发MeteoHealth(/projects/meteohealth/)——一款把天气状况和身体感受关联起来的应用——的过程中,我有意选择了端侧的经典统计引擎,而不是任何ML或大模型方案。气压、湿度与用户症状之间的相关性,是用确定性的统计方法计算出来的:结果可复现、可解释,也不消耗一个token的生成式文本。
这不是因为资源不足而做的妥协,而是根据任务选择合适工具的主动决策。即便是端侧大模型,也会在最需要透明逻辑的地方引入不确定性——比如"气压下降了X百帕,偏头痛风险上升了"这类判断。Foundation Models非常适合输入是非结构化文本、输出是结构化数据或同样是文本的任务:总结笔记、抽取实体、生成标题、应用内的简短对话助手。对于数值序列的确定性计算,它并不需要,在只需要算术运算的地方硬塞一个大模型,只是没有收益的复杂度。
接入Foundation Models之前的检查清单#
- 分别检查三种
unavailable原因——它们需要不同的UX处理方式,而不是一个统一的错误界面。 - 用
@Generable/@Guide代替手写JSON解析——这能消灭一整类bug。 - 提前为上下文窗口(约4096个token)做预算,而不是等会话开始报错才想起来。
- 不要指望一个30亿参数的模型有百科全书式的知识——它是文本转换工具,不是通用聊天机器人。
- 在引入大模型之前,先问一句:不用生成式模型的普通代码能不能解决问题?有时候统计方法比生成更好用。
Foundation Models不是为了炒作而炒作:一个免费、私密、可离线运行、几行Swift代码就能用上的大模型,是开发者期待多年的东西。正因如此,更要精准地使用它——用在它真正胜过替代方案的地方,而不是因为"我们也得有AI"。
参考链接:



