WWDC Quick Look 💓 By SwiftGGTeam
What's new in the Foundation Models framework

What's new in the Foundation Models framework

观看原视频

Highlight

Apple 将 Foundation Models 框架开源,新增多模态理解、Private Cloud Compute 大模型接入、动态 Profile 构建 Agent 体验,以及 fm CLI 和 Python SDK,让开发者在设备端和云端都能调用大模型能力。

核心内容

从封闭到开放:框架全面开源

去年 Apple 推出 Foundation Models 框架时,开发者只能在 iOS/macOS 应用内调用系统内置的 on-device 模型。能力有限,扩展性也受限。

今年 Apple 把框架核心代码完全开源,还推出了 Foundation Models Framework Utilities 包,在 OS 版本之间持续更新实验性功能。这意味着 Swift 开发者可以在 Linux 服务器上运行这套代码,不再受限于 Apple 平台。

模型选择从「单一」变成「任意」

以前一个 LanguageModelSession 只能绑定系统自带的 on-device 模型。推理能力有限,上下文窗口只有几千 token,遇到复杂任务就捉襟见肘。

今年 Apple 引入 LanguageModel 协议,任何本地或远程模型都能接入框架。系统自带三种实现:

  • SystemLanguageModel:设备端模型,上下文 8192 token,新增 Vision 能力
  • PrivateCloudComputeLanguageModel:云端大模型,上下文 32000 token,支持推理
  • CoreAILanguageModel / MLXLanguageModel:在 Apple Neural Engine 或 Mac GPU 上运行开源模型

第三方模型也一并支持。Anthropic 和 Google 都发布了 Swift Package,导入后初始化模型、传入 session,下游代码完全不变。

多模态:图片直接进 Prompt

设备端模型现在能看懂图片了。不需要预处理、不需要裁剪到固定尺寸,直接把 UIImageNSImageCGImage、Core Image 类型、CVPixelBuffer 或文件 URL 塞进 prompt 就行。

let response = try await session.respond {
    "What animal is this?"
    Attachment(UIImage(...))
}

模型支持任意尺寸和宽高比的图片。图片越大,消耗的 token 越多,延迟也越高。开发者需要根据场景权衡分辨率。

云端模型零配置接入

Private Cloud Compute(PCC)模型是 Apple Intelligence 背后的同款大模型。32K 上下文、内置推理能力、无需 API Key、无需账户认证。

开发者只需要换一行代码:

let model = PrivateCloudComputeLanguageModel()
let session = LanguageModelSession(model: model)

let response = try await session.respond(
    to: "Recommend a craft that doesn't require scissors.",
    contextOptions: ContextOptions(reasoningLevel: .light)
)

reasoningLevel 控制模型思考深度:.light 响应快,.deep 质量高但消耗更多计算资源。

PCC 对首年下载量低于 200 万的开发者免费。用户每天有固定额度,iCloud+ 订阅用户额度更高。watchOS 27 也支持 PCC,手腕上就能跑大模型。

Agent 体验:动态 Profile 管理上下文

一个 Agent 应用通常要在不同任务间切换。分析图片时用一种指令和工具集,头脑风暴时换另一种。以前需要手动创建多个 session、手动迁移对话历史,代码冗长且容易出错。

动态 Profile(Dynamic Profile)用声明式 API 解决这个问题。一个 struct conform 到 DynamicProfile 协议,根据应用状态返回不同的 Profile

struct CraftProfile: LanguageModelSession.DynamicProfile {
    let states: CraftProjectStates

    var body: some DynamicProfile {
        switch states.mode {
        case .craftAnalysis:
            Profile {
                Instructions { /* 分析指令 */ }
                RecordImageAnalysisTool()
                SwitchModeTool(states: states)
            }
        case .brainstorm:
            Profile {
                Instructions { /* 头脑风暴指令 */ }
                BrainstormRecordTool()
            }
            .model(states.privateCloudCompute)
            .reasoningLevel(.deep)
        }
    }
}

let session = LanguageModelSession(profile: CraftProfile())

Profile 切换时,框架自动保留对话历史,开发者只需关注业务逻辑。分析阶段用设备端模型快速响应,头脑风暴阶段自动切到 PCC 深度推理,全程无缝。

系统工具:Vision + Spotlight 内置集成

框架新增两个 Vision 工具和一个搜索工具:

  • BarcodeReaderTool:读取条形码信息
  • OCRTool:从图片提取结构化文本
  • Spotlight 搜索工具:基于 Core Spotlight 索引实现本地 RAG(检索增强生成)

这些工具不需要额外配置,直接加入 session 的 tools 数组即可使用。

Token 用量透明化

使用第三方模型按 token 计费,开发者需要清楚知道消耗了多少。Session 和 response 新增了 usage 属性:

print(response.usage.input.totalTokenCount)
print(response.usage.input.cachedTokenCount)
print(response.usage.output.totalTokenCount)
print(response.usage.output.reasoningTokenCount)

可以精确追踪输入 token、缓存命中 token、输出 token 和推理 token 的数量。

Mac 开发者工具:fm CLI 和 Python SDK

macOS 27 引入了 fm 命令行工具,终端里直接调用 on-device 模型和 PCC:

fm chat "What does valley fold mean in origami?"
fm generate --image IMG_1234.jpg "Suggest a descriptive filename"

可以嵌入 shell 脚本做文档摘要、信息提取、内容生成。

Python 开发者也有官方 SDK:

import apple_fm_sdk as fm

model = fm.SystemLanguageModel()
is_available, reason = model.is_available()

if is_available:
    session = fm.LanguageModelSession(model=model)
    response = await session.respond(prompt="Hello!")
    print(response)

Python SDK 覆盖 Swift 框架的核心功能,从 prompt 到结构化响应只需几行代码。

详细内容

设备端模型上下文查询

02:46

iOS 26.4 开始,开发者可以查询模型上下文大小和 token 数量,根据硬件能力动态调整:

let model = SystemLanguageModel()
print(model.contextSize)
// 8192

let count = try await model.tokenCount(for: "What are the Japanese characters for origami?")
print(count)

关键点:

  • contextSize 返回当前模型的最大上下文长度(单位:token)
  • tokenCount(for:) 计算指定文本的 token 数量,用于预估请求是否会超出上下文限制
  • 不同设备上的 on-device 模型可能有不同的 context size,建议运行时查询而非硬编码

图片附件类型

03:52

let response = try await session.respond {
    "What animal is this?"
    Attachment(UIImage(...))
}

关键点:

  • Attachment() 支持 UIImageNSImageCGImage、Core Image 类型、CVPixelBuffer、文件 URL
  • 图片尺寸和宽高比不受限制,框架自动处理
  • 大图消耗更多 token,建议根据场景选择合适分辨率

Token 用量检查

08:45

let response = try await session.respond(
    to: "Recommend a craft that doesn't require scissors.",
    contextOptions: ContextOptions(reasoningLevel: .light)
)

print(response.usage.input.totalTokenCount)
print(response.usage.input.cachedTokenCount)
print(response.usage.output.totalTokenCount)
print(response.usage.output.reasoningTokenCount)

关键点:

  • usage.input.totalTokenCount:本次请求消耗的输入 token 总数
  • usage.input.cachedTokenCount:从缓存读取的输入 token 数,命中缓存可降低成本
  • usage.output.totalTokenCount:模型生成的输出 token 总数
  • usage.output.reasoningTokenCount:用于推理过程的输出 token 数(仅 reasoning 模型)

动态 Profile:模式切换与对话历史保留

11:55

@Observable
final class AppStates {
    var mode: Mode
}

let appStates: AppStates
var session: LanguageModelSession?

func updateSession() {
    let originalTranscript = session?.transcript.dropFirstInstructions() ?? Transcript()

    switch appStates.mode {
    case .craftAnalysis:
        session = LanguageModelSession(
            tools: [
                RecordImageAnalysisTool(),
                SwitchModeTool(states: appStates)
            ],
            instructions: "Analyze the user's craft project...",
            transcript: originalTranscript
        )
    case .brainstorm:
        session = LanguageModelSession(
            tools: [
                RecordBrainstormRecordTool(),
            ],
            instructions: "Brainstorm some ideas...",
            transcript: originalTranscript
        )
    }
}

struct SwitchModeTool: Tool {
    let description = "Switch to a different mode."
    let states: AppStates

    @Generable
    struct Arguments {
        let mode: Mode
    }

    func call(arguments: Arguments) async throws -> some PromptRepresentable {
        appStates.mode = arguments.mode
        return "Successfully switched to \(arguments.mode)."
    }
}

withObservationTracking {
    appStates.mode
} onChange: {
    updateSession()
}

关键点:

  • transcript.dropFirstInstructions() 保留对话历史但丢弃旧指令,避免指令累积
  • SwitchModeTool 让模型能主动调用工具切换应用模式
  • @Generable 标记的参数结构体让框架自动生成参数解析逻辑
  • withObservationTracking 监听模式变化,自动重建 session

声明式 Dynamic Profile

12:42

struct CraftProfile: LanguageModelSession.DynamicProfile {
    var body: some DynamicProfile {
        Profile {
            Instructions {
                """
                You are an expert crafting assistant. \
                Record craft project image analyses   \
                using the recordImageAnalysis tool.
                """
            }
            RecordImageAnalysisTool()
        }
    }
}

let session = LanguageModelSession(profile: CraftProfile())

关键点:

  • DynamicProfile 协议要求实现 body 属性,返回 some DynamicProfile
  • Profile 构建器内可以放置 Instructions 和任意 Tool
  • Session 初始化时传入 profile 实例,框架自动管理上下文

带模型切换的 Dynamic Profile

14:36

struct CraftProfile: LanguageModelSession.DynamicProfile {
    let states: CraftProjectStates

    var body: some DynamicProfile {
        switch states.mode {
        case .craftAnalysis:
            Profile {
                Instructions { /* ... */ }
                RecordImageAnalysisTool()
                SwitchModeTool(states: states)
            }
        case .brainstorm:
            Profile {
                Instructions { /* ... */ }
                BrainstormRecordTool()
            }
            .model(states.privateCloudCompute)
            .reasoningLevel(.deep)
        }
    }
}

关键点:

  • .model() modifier 切换底层模型,如从设备端切到 PCC
  • .reasoningLevel(.deep) 要求模型做深度推理,适合创意生成等复杂任务
  • 条件分支决定当前激活的 Profile,框架处理切换逻辑
  • 对话历史在切换时自动保留

Python SDK 基础用法

18:29

import apple_fm_sdk as fm

model = fm.SystemLanguageModel()

is_available, reason = model.is_available()

if is_available:
    session = fm.LanguageModelSession(model=model)
    response = await session.respond(prompt="Hello!")
    print(response)

关键点:

  • apple_fm_sdk 是 Python 端的包名
  • is_available() 检查模型是否可在当前设备上运行
  • LanguageModelSession 的 API 与 Swift 端保持一致
  • 支持异步调用,与 Python asyncio 生态兼容

核心启发

1. 做一个「看懂图片」的日记应用

  • 做什么:用户拍照后,应用自动识别图中物品、场景、文字,生成结构化标签存入日记。
  • 为什么值得做SystemLanguageModel 新增 Vision 能力,Attachment 直接接收 UIImageOCRToolBarcodeReaderTool 提取文字和条码信息。全程设备端运行,不上传云端。
  • 怎么开始:创建 LanguageModelSession,在 respond 闭包里同时传入文字 prompt 和 Attachment(UIImage),配合 OCRToolBarcodeReaderTool 处理提取结果。

2. 做一个「会搜索」的个人知识助手

  • 做什么:用户用自然语言提问,助手检索本地文档、邮件、笔记后生成回答。
  • 为什么值得做:框架内置 Spotlight 搜索工具,直接基于 Core Spotlight 索引实现本地 RAG。不需要自建向量数据库,不需要嵌入模型,几行代码就能让模型「记住」用户的本地文件。
  • 怎么开始:在 session 的 tools 数组中加入 Spotlight 搜索工具,用户提问时模型会自动调用它检索相关内容,再把结果作为上下文生成回答。

3. 做一个「自动升级」的创意写作助手

  • 做什么:写大纲阶段用设备端模型快速响应,扩写阶段自动切换到 PCC 深度推理。
  • 为什么值得做:Dynamic Profile 让模型切换变成声明式配置。大纲阶段走 SystemLanguageModel 保证低延迟,扩写阶段用 .model(PrivateCloudComputeLanguageModel()).reasoningLevel(.deep) 提升内容质量,对话历史自动保留。
  • 怎么开始:定义一个 conform 到 DynamicProfile 的 struct,用 switch 根据写作阶段返回不同的 Profile,在扩写分支上加 .model().reasoningLevel() modifier。

4. 用 fm CLI 批量处理文件

  • 做什么:写 shell 脚本批量重命名图片、总结 PDF、提取文档关键信息。
  • 为什么值得做:macOS 27 自带 fm 命令,终端里直接调用 on-device 模型和 PCC,不需要写 Swift 代码,不需要配置 API Key。
  • 怎么开始:安装 macOS 27 后,在终端运行 fm chat "你的问题"fm generate --image 图片.jpg "指令",把命令嵌入 shell 脚本做批量处理。

5. 做一个跨平台的 Swift 服务端 AI 应用

  • 做什么:用 Swift 写后端 AI 服务,同时服务 iOS App 和 Web 前端。
  • 为什么值得做:Foundation Models 框架开源后支持 Linux。CoreAILanguageModelMLXLanguageModel 可以在服务器上跑开源模型,统一的 LanguageModel 协议让客户端和服务端共享同一套接口。
  • 怎么开始:在 Linux 服务器上引入开源的 Foundation Models 框架,用 CoreAILanguageModel 加载模型,暴露 HTTP API。iOS 端用第三方模型 package 接入同一个协议,保持接口一致。

关联 Session

评论

GitHub Issues · utterances