使用 Foundation Models 框架构建智能体 App 体验
Build agentic app experiences with the Foundation Models framework
2026年6月10日
一句话判断
Foundation Models 终于从“套壳对话框”进化到了“真正的多 Agent 编排框架”,DynamicProfile 是今年做 AI App 必须拿下的 API。
这场 Session 讲了什么
以前用 LanguageModelSession 搞复杂任务极其痛苦。要么一个模型硬扛导致 Token 爆炸和幻觉,要么开多个 Session 导致上下文割裂,手动拼 Prompt 和摘要写到吐血。Apple 这次直接给出了官方解法:DynamicProfile(动态配置文件)和 DynamicInstructions(动态指令)。
现在你可以在同一个 Session 里,根据 App 的不同阶段动态切换底层模型。比如头脑风暴时用高温度(temperature)的 Private Cloud Compute(私有云计算)模型,审查阶段切到低温度的端侧 SystemLanguageModel(系统语言模型)。配合 historyTransform(历史转换),你还能自动裁剪历史对话,把那些又长又没用的 Tool Call(工具调用)记录丢掉。
这对做复杂 AI 工作流的开发者影响最大。你不再需要自己手写一堆状态机来管理 Prompt 和上下文窗口,Apple 把“上下文工程”做成了声明式的 SwiftUI 风格 API。
值得深挖的点
端云混合调度的官方范式
做 AI App 最大的成本痛点就是模型调度。端侧模型便宜快但笨,云端模型聪明但贵且慢。真实场景必须混合使用,但以前在多个 Session 间传递上下文简直是灾难。DynamicProfile 漂亮地解决了这个问题。它允许你在同一个 LanguageModelSession 中,通过类似 SwiftUI 的 body 声明式地切换模型、温度甚至推理级别(reasoningLevel)。
Apple 在这里的设计取舍很明确:他们选择了让开发者通过 Profile 显式声明边界,而不是在底层做黑盒魔法。当你切换模型或大幅修改历史时,底层的 KV Cache(键值缓存)会失效,导致首字延迟飙升。Apple 没有帮你自动抹平这个差异,而是提供了全新的 Xcode Instrument 让你自己去测。说白了,Apple 给你提供了最灵活的换挡杆,但踩离合的时机得你自己把握。
上下文工程与无状态历史裁剪
长对话必然超出 Context Window(上下文窗口),或者塞满无关的 Tool Call 导致模型变笨。historyTransform 是这次最实用的 API。它的设计非常克制:它是无状态的(stateless),每次请求前跑一遍,只改变发给模型的快照,绝不修改 Session 本身的真实历史。这比直接 mutate 历史安全得多,避免了状态同步的噩梦。
如果你不想自己写裁剪逻辑,Apple 顺手开源了 Foundation Models framework utilities(基础模型框架工具包)。直接用里面的 .rollingWindow(滚动窗口)和 .droppingCompletedToolCalls(丢弃已完成的工具调用)修饰符就行。这里有个大坑:如果你把 transcriptErrorHandlingPolicy(转录错误处理策略)设为 .preserveTranscript 并在 Tool 报错时手动改历史,一定要确保 isResponding == false,否则直接崩溃。
代码片段
1. 声明式切换模型与 Agent 配置
场景:根据 App 当前所处的阶段(头脑风暴或审查),动态切换底层模型和温度。
struct CraftProfile: LanguageModelSession.DynamicProfile {
var orchestrator: CraftOrchestrator
var body: some DynamicProfile {
switch orchestrator.mode {
case .brainstorming:
// 头脑风暴需要创造力,使用云端模型并调高温度
Profile { BrainstormFacilitator(orchestrator: orchestrator) }
.model(orchestrator.pccLanguageModel)
.temperature(1)
case .reviewing:
// 审查阶段需要严谨,使用端侧模型并降低温度
Profile { CraftCoach() }
.model(orchestrator.systemLanguageModel)
.temperature(0.2)
}
}
}
坑:body 会在每次 prompt 时重新计算,确保 orchestrator 的状态变化能正确触发 UI 和 Profile 更新,别在 body 里写耗时逻辑。
2. 使用官方工具包裁剪上下文
场景:对话太长时,自动丢弃已经执行完的 Tool Call,并只保留最近 10 条记录,防止 Token 超限。
import FoundationModelsUtilities
struct CraftProfile: LanguageModelSession.DynamicProfile {
var orchestrator: CraftOrchestrator
var body: some DynamicProfile {
switch orchestrator.mode {
case .reviewing:
Profile { CraftCoach() }
// 丢弃已完成的工具调用记录,减少噪音
.droppingCompletedToolCalls()
// 只保留最近的 10 条历史记录
.rollingWindow(size: .entries(10))
}
}
}
坑:频繁改变历史窗口大小或裁剪逻辑会导致 KV Cache 失效。尽量保持裁剪规则稳定,并用 Xcode 的 Foundation Models Instrument 监控延迟。
最佳实践
已有项目如果还在用单个大 Prompt 塞所有逻辑,或者手动维护多个 Session 传递摘要,建议立刻重构。新项目直接上 DynamicProfile,把不同的业务阶段拆分成独立的 Profile。
在多 Agent 协作时,优先使用 baton-pass(接力)模式:多个 Profile 共享完整的 transcript(对话历史),通过一个 Tool 来切换当前激活的 Profile,由接手的 Profile 输出最终结果。如果是需要隔离上下文的子任务,再用 phone-a-friend(求助)模式,派生一个短生命周期的子 Session。
实战中极容易踩的一个坑是 Tool calling mode(工具调用模式)。当你把模式设为 required(必须调用)时,模型会陷入无限循环调用 Tool 的死胡同。一定要给个退出条件,比如条件化这个模式,或者提供一个 final-answer tool(最终答案工具),让它抛出异常来打破循环。
还有什么值得关注
SessionProperty跨 Profile 共享状态:通过@SessionPropertyEntry声明自定义属性(比如对话摘要),可以在不同的 Profile 和 Tool 之间无缝共享状态,不用再搞全局单例了。transcriptErrorHandlingPolicy新选项:现在 Tool 报错时,你可以选择.preserveTranscript保留错误现场,自己手动修复转录历史,而不是像以前那样只能默认回滚。DynamicInstructions组合能力:可以像 SwiftUI 视图一样嵌套组合指令,比如把OrigamiExpert嵌套在BrainstormFacilitator里,自动拼接 Prompt 和 Tools,代码复用率极高。