WWDC Quick Look 💓 By SwiftGGTeam
Meet the Foundation Models framework

Meet the Foundation Models framework

观看原视频

Highlight

Foundation Models framework 提供 30 亿参数、每参数 2 位量化的端侧大模型 Swift API,覆盖 macOS、iOS、iPadOS 与 visionOS,离线可用。


核心内容

过去想在 App 里调大模型,要么打云端 API(隐私、延迟、账单都是问题),要么自己捆一个开源模型进 App(包大小直接膨胀几个 GB)。再加上模型输出是非结构化文本,开发者只能在 prompt 里写「请返回 JSON」,再写一堆容错解析——模型一句话顺序不对就崩。

WWDC25 推出的 Foundation Models framework 把驱动 Apple Intelligence 的端侧模型直接开放给开发者。这个模型 30 亿参数、每参数 2 位量化,内置在系统里,App 不需要打包它。所有数据留在设备上,离线可用。它面向摘要、抽取、分类、内容生成这类设备级任务,不适合做世界知识或复杂推理(02:57)。

框架围绕四件事解决落地问题。Guided Generation@Generable 标注 Swift struct,模型直接输出类型安全的对象,底层用 constrained decoding 在数学上保证结构合法。Snapshot Streaming 把流式增量做成一连串 PartiallyGenerated 快照,每个属性都是 Optional,配合 SwiftUI 状态绑定就能逐字浮现。Tool Calling 让模型自主决定调用哪个 Swift 函数,参数也走 @GenerableStateful Session 维护多轮 transcript,并提供 contentTagging 等专用 adapter。


详细内容

Guided Generation:用宏代替 prompt 工程

定义一个想让模型生成的类型,加上 @Generable@Guide05:32):

@Generable
struct SearchSuggestions {
    @Guide(description: "A list of suggested search terms", .count(4))
    var searchTerms: [String]
}

关键点:

  • @Generable 宏告诉框架这个 struct 是可生成的目标类型,自动衍生编码协议与 PartiallyGenerated 镜像类型。
  • @Guide(description:) 把字段的语义用自然语言写在类型定义旁,模型据此理解字段含义。
  • .count(4) 是结构性约束,强制数组长度恰为 4,由 constrained decoding 在 token 层面保证。

调用时直接告诉 session 要生成什么类型(05:51):

let response = try await session.respond(
    to: prompt,
    generating: SearchSuggestions.self
)
print(response.content)

关键点:

  • prompt 不再需要描述输出格式,只描述意图。
  • generating: 参数把类型作为 schema 传给底层解码器。
  • response.content 是强类型 SearchSuggestions,直接交给 SwiftUI 视图。

Snapshot Streaming:每帧一个完整快照

传统 delta streaming 要求开发者自己拼 token,对结构化输出极不友好。框架改成 snapshot 模式(09:40):

let stream = session.streamResponse(
    to: "Craft a 3-day itinerary to Mt. Fuji.",
    generating: Itinerary.self
)

for try await partial in stream {
    print(partial)
}

关键点:

  • streamResponse 返回 AsyncSequence,每个元素是 Itinerary.PartiallyGenerated
  • PartiallyGenerated 中所有属性都是 Optional,随生成进度逐步填充。
  • 在 SwiftUI 里把 partial 存进 @State,视图自动随生成刷新(10:05)。
  • 字段在 struct 里的声明顺序就是生成顺序,把 summary 放最后一项往往能拿到更高质量的总结(11:00)。

Tool Calling:让模型调你的 Swift 函数

定义一个符合 Tool protocol 的类型(13:42):

struct GetWeatherTool: Tool {
    let name = "getWeather"
    let description = "Retrieve the latest weather information for a city"

    @Generable
    struct Arguments {
        @Guide(description: "The city to fetch the weather for")
        var city: String
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        let places = try await CLGeocoder().geocodeAddressString(arguments.city)
        let weather = try await WeatherService.shared.weather(for: places.first!.location!)
        let temperature = weather.currentWeather.temperature.value

        let content = GeneratedContent(properties: ["temperature": temperature])
        return ToolOutput(content)
    }
}

关键点:

  • namedescription 是模型用来判断何时调用此工具的依据,写法要面向模型可读。
  • Arguments 必须 @Generable,模型生成的参数也是类型安全的,避免乱拼字符串。
  • call 是开发者的 Swift 代码,可以打 WeatherKit、CoreLocation 这类系统服务。
  • ToolOutput 既能传结构化 GeneratedContent,也能传自然语言字符串。

把工具挂到 session 上即可(15:03):

let session = LanguageModelSession(
    tools: [GetWeatherTool()],
    instructions: "Help the user with weather forecasts."
)
let response = try await session.respond(to: "What is the temperature in Cupertino?")

关键点:

  • 模型自主决定是否调用 getWeather、传哪个城市。
  • 工具输出会被框架插回 transcript,模型基于此再生成最终自然语言回复。

Stateful Session 与可用性检查

session 内部维护 transcript,多轮调用自动带历史上下文(17:46)。运行前必须检查可用性(19:56):

struct AvailabilityExample: View {
    private let model = SystemLanguageModel.default

    var body: some View {
        switch model.availability {
        case .available:
            Text("Model is available")
        case .unavailable(let reason):
            Text("Reason: \(reason)")
        }
    }
}

关键点:

  • 只有开启 Apple Intelligence 且地区支持的设备上模型才可用。
  • .unavailable(reason) 给出降级理由,UI 要据此切换到非 AI 路径。

核心启发

  • 做什么:把现有 App 里的「JSON parsing + 正则修补」全部换成 @Generable 类型。

    • 为什么值得做:constrained decoding 从结构上消除了模型输出格式错误的可能,省下大量容错代码。
    • 怎么开始:挑一个最常出错的 LLM 解析点,把目标类型加上 @Generable,prompt 里删掉所有「请返回 JSON…」的指令,跑一遍 Xcode Playground 验证输出。
  • 做什么:用 snapshot streaming 把「等待 5 秒」改造成「逐字浮现」的 SwiftUI 动画。

    • 为什么值得做:感知延迟比真实延迟更影响留存,snapshot 与 SwiftUI @State 天然契合,几行代码就能让生成过程变得可见。
    • 怎么开始:把现有 respond(to:) 调用换成 streamResponse,用 Itinerary.PartiallyGenerated? 存 state,给字段加 .transition(.opacity)
  • 做什么:把 App 内部已有的 Swift 函数包装成 Tool,让模型成为路由器。

    • 为什么值得做:避免在 prompt 里堆「如果用户问天气就……」,由模型自主决定是否调用,逻辑更干净,也能调用 WeatherKit、CoreLocation 等本地能力。
    • 怎么开始:先选一个无副作用的纯查询函数(天气、汇率、库存),实现 Tool protocol,挂到 session 的 tools: 数组,prompt 只描述用户意图。
  • 做什么:内容标签场景直接用 SystemLanguageModel(useCase: .contentTagging) 专用 adapter。

    • 为什么值得做:专用 adapter 在标签、情感、动作抽取上的准确率比通用模型高一档,调用方式与通用模型一致。
    • 怎么开始:把笔记、评论、聊天日志这类输入跑一遍 Top3ActionEmotionResult,对比通用 prompt 的效果。

关联 Session

评论

GitHub Issues · utterances