概述
Apple Intelligence的Foundation Models框架是在 WWDC 2025 正式发布的重要API,它让开发者能够直接调用运行在本地设备上的Apple Intelligence本地模型。Apple Intelligence本地的模型是一个 3B 参数的小模型,能够在保证隐私安全的前提下执行一些相对简单的信息摘要,提取,分类等相对简单的文本处理任务。
核心特性
- 本地运行: 所有数据处理都在设备本地进行,保护用户隐私
- 离线可用 :无需网络连接即可运行
- Swift原生支持:完美集成Swift语言特性
- 多平台支持 :支持iOS、iPadOS、macOS和visionOS
一、环境准备
系统要求
iOS 26 或更高版本
Apple Intelligence 支持的设备
设备必须在设置中打开Apple Intelligence
导入框架
import FoundationModels
检查模型可用性
在创建会话之前,检查模型在当前设备和区域是否可用:
import FoundationModels
// 检查模型可用性
if SystemLanguageModel.default.availability == .available {
// 可以创建会话
print("Foundation Models 可用")
} else {
// 处理不可用情况
print("Foundation Models 不可用")
}
// 检查模型支持的语言
let supportedLanguages = SystemLanguageModel.default.supportedLanguages
guard supportedLanguages.contains(Locale.current.language) else {
// Show message
return
}
二、基础使用
最简单的调用方式
使用 LanguageModelSession 初始化一个 session 并调用模型即可,同时初始化 session 时可以指定一些指令,使得模型能更准确的响应你的问题:
import FoundationModels
import Playgrounds
func respond(userInput: String) async throws -> String {
let session = LanguageModelSession(instructions: """
You are a professional tour guide.
Respond to the tourist’s question.
"""
)
do {
let response = try await session.respond(to: userInput)
return response.content
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session, with some history from the previous session.
}
}
会话一次只能处理一个请求,如果在前一个请求完成之前再次调用它,则会导致运行时错误。检查 isResponding 在发送新请求之前验证会话是否完成了前一个请求的处理。
context window size上下文窗口大小限制了模型可以为会话实例处理多少数据,系统模型最多支持4096个Token。不同语言一个Token对应不同数的字符,例如中文一个Token对应一个字符。在单个会话中,指令、所有提示和所有输出中所有令牌的总和计入上下文窗口大小。
如果你的会话处理了大量超过上下文窗口的token,框架会抛出错误LanguageModelSession.GenerationError.exceededContextWindowSize(_:)。当您遇到错误时,请启动一个新的会话并尝试缩短提示。如果您需要处理单个上下文窗口限制无法容纳的大量数据,请将数据分成更小的块,在单独的会话中处理每个块,然后组合结果。另外,可以根据上一个会话的历史摘要,生成新的会话,保持会话上下文的连贯性。
其他 GenerationError 错误类型:
// 表示会话所需的资产不可用的错误
case assetsUnavailable(LanguageModelSession.GenerationError.Context)
// 表示会话未能从模型输出反序列化有效的可生成类型的错误
case decodingFailure(LanguageModelSession.GenerationError.Context)
// 表示会话已达到其上下文窗口大小限制的错误
case exceededContextWindowSize(LanguageModelSession.GenerationError.Context)
// 表明系统的安全护栏是由提示中的内容或模型生成的响应触发的
case guardrailViolation(LanguageModelSession.GenerationError.Context)
// 表明您的会话已受到速率限制
case rateLimited(LanguageModelSession.GenerationError.Context)
// 会话拒绝请求时发生的错误
case refusal(LanguageModelSession.GenerationError.Refusal, LanguageModelSession.GenerationError.Context)
// 如果您试图使会话响应第二个提示,而它仍在响应第一个提示,则会发生错误
case concurrentRequests(LanguageModelSession.GenerationError.Context)
// 使用了具有不受支持模式的生成指南
case unsupportedGuide(LanguageModelSession.GenerationError.Context)
// 发生错误的上下文
struct Context
// 语言模型产生的拒绝
struct Refusal
为了获得最佳的提示结果,可尝试不同的生成选项,影响模型的运行时参数:GenerationOptions
// init(sampling: GenerationOptions.SamplingMode?, temperature: Double?, maximumResponseTokens: Int?)
// sampling:模型在生成响应时选择令牌的抽样策略,SamplingMode 一种定义如何从概率分布中采样值的类型
// temperature:影响模型响应的置信度
// maximumResponseTokens:模型在其响应中允许产生的令牌的最大数量
let options = GenerationOptions(temperature: 2.0)
let session = LanguageModelSession()
let prompt = "Write me a story about coffee."
let response = try await session.respond(
to: prompt,
options: options
)
三、格式化输出(Guided Generation)
Foundation Models 框架的一个重要特性是能够确保模型输出特定格式的数据,而不是依赖不可靠的提示词。
定义输出格式
使用 @Generable 和 @Guide 宏来定义输出结构:
import FoundationModels
@Generable
struct SearchSuggestions {
@Guide("搜索建议的景点名称")
let name: String
@Guide(description: "相关的搜索关键词列表 限制4个", .count(4))
let keywords: [String]
@Guide("搜索的分类,如:人文、自然、娱乐等")
let category: String
@Guide("建议的优先级,1-10的数字")
let priority: Int
}
上面的代码中 @Generable 来修饰 SearchSuggestions 这个 struct 表示它是语言模型的一个输出格式类, 然后 @Guide 可以为每个属性做说明和格式限制。
然后在使用 session 调用模型的时候,把 SearchSuggestions 传进去即可:
let prompt = """
Generate a list of suggested search terms for an app about visiting famous landmarks.
"""
let response = try await session.respond(
to: prompt,
generating: SearchSuggestions.self
)
let suggestions = response.content
print("名称: \(suggestions.name)")
print("关键词: \(suggestions.keywords.joined(separator: ", "))")
print("分类: \(suggestions.category)")
print("优先级: \(suggestions.priority)")
模型输出的内容就直接是我们这个自定义的类 SearchSuggestions 了。
四、流式输出 (Streaming)
对于结构化的数据格式输出,比如JSON,一个个Token的输出,会产生语法解析问题。因此,我们当然是期望流式输出是按照一个 JSON 语法单位的形式生成,而且对于长时间的生成任务,使用流式输出可以提供更好的用户体验。
// 为指定类型开启流式响应
let stream = session.streamResponse(
to: "prompt",
generating: SearchSuggestions.self
)
for try await suggestions in stream {
print(suggestions)
}
五、工具调用 (Tool Calling)
Foundation Models 支持工具调用功能,让模型能够调用应用程序的特定功能。我们把应用自身的一些功能函数告诉本地模型,由它来决定是否调用,以及什么时候调用。
定义工具
import CoreLocation
import WeatherKit
// 天气查询工具
struct GetWeatherTool: Tool {
let name = "GetWeather"
let description = "获取指定城市的当前天气信息"
@Generable
struct Arguments {
@Guide("要查询天气的城市名称")
let city: String
}
func call(with arguments: Arguments) async throws -> ToolOutput {
// 使用Core Location将城市名转换为坐标
let geocoder = CLGeocoder()
let placemarks = try await geocoder.geocodeAddressString(arguments.city)
guard let location = placemarks.first?.location else {
return "无法找到城市:\(arguments.city)"
}
// 使用WeatherKit获取天气信息
let weather = try await WeatherService.shared.weather(for: location)
let temperature = weather.currentWeather.temperature.value
let condition = weather.currentWeather.condition.description
return ToolOutput("\(arguments.city)当前天气:\(condition),温度:\(Int(temperature))°C")
}
}
// 日历事件工具
struct CreateCalendarEventTool: Tool {
let name = "CreateCalendarEvent"
let description = "创建新的日历事件"
@Generable
struct Arguments {
@Guide("事件标题")
let title: String
@Guide("事件日期,格式:YYYY-MM-DD")
let date: String
@Guide("事件时间,格式:HH:MM")
let time: String
@Guide("事件描述(可选)")
let description: String?
}
func call(with arguments: Arguments) async throws -> ToolOutput {
// 这里实现创建日历事件的逻辑
// 实际应用中需要使用EventKit框架
return ToolOutput("已创建事件:\(arguments.title),时间:\(arguments.date) \(arguments.time)")
}
}
上面代码创建了一个 GetWeatherTool和CreateCalendarEventTool, 其中GetWeatherTool用于获取指定城市的天气情况,CreateCalendarEventTool用于创建日历事件。 自定义工具需要继承Tool protocol。 其中有name和description属性, 用于给本地模型提供这个工具的名称,以及作用描述。 本地模型根据这个信息来确定它的功能,以及什么时候调用它。
Arguments用于定义这个Tool接收的所有参数,比如GetWeatherTool这里定义了一个city, 并且用@Guide标记了这个参数的说明,这样本地的语言模型就知道如何创建这个参数了。
接下来的call方法就是这个Tool的具体实现,通过给定的城市名称,调用CLGeocoder去解析成地理位置,然后调用苹果的WeatherService去获取天气信息并且返回。
现在Tool 创建好了,再来看看如何把它应用到模型中:
使用工具
class ToolEnabledService {
private var session: LanguageModelSession?
func initializeSession() async throws {
// 创建包含工具的会话
session = try await LanguageModelSession(
tools: [GetWeatherTool(), CreateCalendarEventTool()],
instructions: """
你是一个智能助手,可以帮助用户查询天气和创建日历事件。
当用户询问天气时,使用GetWeather工具。
当用户要创建提醒或安排日程时,使用CreateCalendarEvent工具。
"""
)
}
func handleUserRequest(_ request: String) async throws -> String {
guard let session = session else {
throw AIError.sessionNotInitialized
}
let response = try await session.response(to: request)
return response.content
}
}
// 使用示例
let toolService = ToolEnabledService()
try await toolService.initializeSession()
// 查询天气
let weatherResponse = try await toolService.handleUserRequest("北京今天天气怎么样?")
print(weatherResponse)
// 创建事件
let eventResponse = try await toolService.handleUserRequest("帮我在明天下午3点创建一个会议提醒")
print(eventResponse)
六、会话上下文记忆
上下文保持
在同一个 LanguageModelSession 中会自动保持对话的上下文:
class ContextAwareService {
private var session: LanguageModelSession?
func initializeSession() async throws {
session = try await LanguageModelSession()
}
func continuousChat() async throws {
guard let session = session else {
throw AIError.sessionNotInitialized
}
// 第一轮对话
let response1 = try await session.response(to: "请写一首关于鱼的俳句")
print("AI: \(response1.content)")
// 第二轮对话 - 模型会记住上下文
let response2 = try await session.response(to: "现在写一首关于高尔夫的")
print("AI: \(response2.content)") // 模型知道用户想要另一首俳句
// 查看完整对话历史
print("\n=== 对话历史 ===")
for message in session.transcript {
print("\(message.role): \(message.content)")
}
}
}
以上代码,由于模型会记住上下文,在第二轮对话的提示词中只说了关于高尔夫的,模型也知道提示词的隐含意思是要写一手关于高尔夫的俳句。
其中 session 还提供了一个 transcript 属性,通过它就能看到当前上下文中所有的信息。
七、专用适配器(Use Case Adapters)
Foundation Models 提供了针对特定用例优化的适配器:
内容标签适配器
class ContentTaggingService {
private var session: LanguageModelSession?
func initializeSession() async throws {
// 使用内容标签适配器
let model = SystemLanguageModel(useCase: .contentTagging)
session = try await LanguageModelSession(model: model)
}
func tagContent(_ content: String) async throws -> [String] {
guard let session = session else {
throw AIError.sessionNotInitialized
}
let prompt = "为以下内容生成标签:\(content)"
let response = try await session.response(to: prompt)
// 解析标签
return response.content.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
}
func extractEntities(from text: String) async throws -> [String] {
guard let session = session else {
throw AIError.sessionNotInitialized
}
let prompt = "从以下文本中提取实体:\(text)"
let response = try await session.response(to: prompt)
return response.content.components(separatedBy: "\n").filter { !$0.isEmpty }
}
}
八、实际应用案例
智能笔记应用
import FoundationModels
class IntelligentNotesApp {
private var session: LanguageModelSession?
init() {
Task {
try await initializeAI()
}
}
private func initializeAI() async throws {
session = try await LanguageModelSession(
tools: [SummarizeNotesTool(), ExtractKeywordsTool()],
instructions: "你是一个智能笔记助手,可以帮助用户整理和分析笔记内容。"
)
}
// 笔记摘要功能
func summarizeNote(_ content: String) async throws -> String {
guard let session = session else {
throw AIServiceError.sessionNotInitialized
}
let prompt = "请为以下笔记生成简洁的摘要:\(content)"
let response = try await session.response(to: prompt)
return response.content
}
// 关键词提取
func extractKeywords(from content: String) async throws -> [String] {
guard let session = session else { throw AIServiceError.sessionNotInitialized }
let prompt = "从以下内容中提取关键词:\(content)"
let response = try await session.response(to: prompt)
return response.content.components(separatedBy: ",")
.map { $0.trimmingCharacters(in: .whitespaces) }
}
// 智能分类
@Generable
struct NoteCategory {
@Guide("笔记的主要分类")
let primaryCategory: String
@Guide("次要分类标签")
let tags: [String]
@Guide("重要程度评分 1-10")
let importance: Int
}
func categorizeNote(_ content: String) async throws -> NoteCategory {
guard let session = session else { throw AIServiceError.sessionNotInitialized }
let prompt = "请分析以下笔记内容并进行分类:\(content)"
return try await session.response(to: prompt, generating: NoteCategory.self)
}
}
智能客服助手
class CustomerServiceBot {
private var session: LanguageModelSession?
init() {
Task {
try await setupBot()
}
}
private func setupBot() async throws {
session = try await LanguageModelSession(
tools: [OrderLookupTool(), RefundProcessTool(), FAQSearchTool()],
instructions: """
你是一个专业的客服助手。你需要:
1. 礼貌和耐心地回应客户问题
2. 使用工具查找订单信息和处理退款
3. 如果无法解决问题,建议联系人工客服
"""
)
}
@Generable
struct CustomerIntent {
@Guide("客户意图分类:咨询、投诉、退款、查询订单等")
let intent: String
@Guide("情绪状态:满意、中性、不满、愤怒")
let sentiment: String
@Guide("优先级:低、中、高、紧急")
let priority: String
@Guide("是否需要人工介入")
let needsHumanAgent: Bool
}
func analyzeCustomerMessage(_ message: String) async throws -> CustomerIntent {
guard let session = session else {
throw AIServiceError.sessionNotInitialized
}
let prompt = "分析以下客户消息:\(message)"
return try await session.response(to: prompt, generating: CustomerIntent.self)
}
func respondToCustomer(_ message: String) async throws -> String {
guard let session = session else {
throw AIServiceError.sessionNotInitialized
}
let response = try await session.response(to: message)
return response.content
}
}
结语
Foundation Models 框架为iOS开发者提供了强大的本地AI能力,使得在保护用户隐私的同时能够构建智能应用成为可能。通过合理使用这个框架,可以为用户提供更加个性化和智能的体验。
随着Apple Intelligence生态的不断发展,Foundation Models框架将成为iOS应用开发中不可或缺的重要工具。建议深入学习和实践这个框架,创造更好的智能应用体验。
作者介绍:
- 刘爽 高级IOS开发工程师