iOS 26 调用 Apple Intelligence 本地模型:Foundation Models 实践

概述

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开发工程师

微鲤技术团队

微鲤技术团队承担了中华万年历、Maybe、蘑菇语音、微鲤游戏高达3亿用户的产品研发工作,并构建了完备的大数据平台、基础研发框架、基础运维设施。践行数据驱动理念,相信技术改变世界。