8.2 KiB
8.2 KiB
| name | description |
|---|---|
| foundation-models-on-device | 適用於 Apple FoundationModels 框架的裝置端 LLM 指南 — 包含文字建構、使用 @Generable 進行引導式建構、工具呼叫以及 iOS 26+ 的快照串流。 |
FoundationModels:裝置端 LLM (iOS 26)
使用 FoundationModels 框架將 Apple 的裝置端語言模型整合至 App 中的模式。內容涵蓋文字建構、使用 @Generable 的結構化輸出、自定義工具呼叫以及快照串流 (Snapshot Streaming) — 所有功能皆在裝置端執行,以保護隱私並支援離線作業。
何時啟用
- 使用 Apple Intelligence 裝置端模型開發 AI 驅動的功能。
- 在不依賴雲端的情況下建構或總結文字。
- 從自然語言輸入中擷取結構化資料。
- 針對領域特有 (Domain-specific) 的 AI 行為實作自定義工具呼叫。
- 為了即時 UI 更新而串流結構化回應。
- 需要極高隱私保護的 AI 功能 (資料不離開裝置)。
核心模式 — 可用性檢查 (Availability Check)
在建立會話 (Session) 之前,請務必檢查模型可用性:
struct GenerativeView: View {
private var model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
ContentView()
case .unavailable(.deviceNotEligible):
Text("此裝置不符合 Apple Intelligence 使用資格")
case .unavailable(.appleIntelligenceNotEnabled):
Text("請在設定中啟用 Apple Intelligence")
case .unavailable(.modelNotReady):
Text("模型正在下載或尚未就緒")
case .unavailable(let other):
Text("模型不可用:\(other)")
}
}
}
核心模式 — 基礎會話 (Basic Session)
// 單次對話 (Single-turn):每次都建立新會話
let session = LanguageModelSession()
let response = try await session.respond(to: "去巴黎旅遊的最佳月份是幾月?")
print(response.content)
// 多輪對話 (Multi-turn):重複使用會話以保留上下文
let session = LanguageModelSession(instructions: """
你是一位烹飪助手。
請根據食材提供食譜建議。
建議應簡明扼要且具備實作性。
""")
let first = try await session.respond(to: "我有雞肉和米飯")
let followUp = try await session.respond(to: "那素食的選項呢?")
設定指令 (Instructions) 的重點:
- 定義模型角色 (例如:「你是一位導師」)。
- 指定具體任務 (例如:「幫助擷取行事曆事件」)。
- 設定風格偏好 (例如:「盡可能簡短地回應」)。
- 增加安全措施 (例如:「針對危險請求,請回應『我無法協助處理此事』」)。
核心模式 — 使用 @Generable 的引導式建構
建構結構化的 Swift 型別,而非原始字串:
1. 定義 Generable 型別
@Generable(description: "關於一隻貓的基本檔案資訊")
struct CatProfile {
var name: String
@Guide(description: "貓的年齡", .range(0...20))
var age: Int
@Guide(description: "一句話描述貓的個性")
var profile: String
}
2. 要求結構化輸出
let response = try await session.respond(
to: "生成一隻可愛的流浪貓資訊",
generating: CatProfile.self
)
// 直接存取結構化欄位
print("名稱:\(response.content.name)")
print("年齡:\(response.content.age)")
print("描述:\(response.content.profile)")
支援的 @Guide 約束 (Constraints)
.range(0...20)— 數值範圍。.count(3)— 陣列元素數量。description:— 提供生成內容的語義化指引。
核心模式 — 工具呼叫 (Tool Calling)
讓模型調用自定義程式碼來執行領域特定任務:
1. 定義工具 (Tool)
struct RecipeSearchTool: Tool {
let name = "recipe_search"
let description = "根據給定的關鍵字搜尋食譜並回傳結果清單。"
@Generable
struct Arguments {
var searchTerm: String
var numberOfResults: Int
}
func call(arguments: Arguments) async throws -> ToolOutput {
let recipes = await searchRecipes(
term: arguments.searchTerm,
limit: arguments.numberOfResults
)
return .string(recipes.map { "- \($0.name): \($0.description)" }.joined(separator: "\n"))
}
}
2. 建立帶有工具的會話
let session = LanguageModelSession(tools: [RecipeSearchTool()])
let response = try await session.respond(to: "幫我找一些義大利麵食譜")
3. 處理工具錯誤
do {
let answer = try await session.respond(to: "尋找番茄湯的食譜。")
} catch let error as LanguageModelSession.ToolCallError {
print(error.tool.name)
if case .databaseIsEmpty = error.underlyingError as? RecipeSearchToolError {
// 處理特定工具錯誤
}
}
核心模式 — 快照串流 (Snapshot Streaming)
使用 PartiallyGenerated 型別為即時 UI 串流結構化回應:
@Generable
struct TripIdeas {
@Guide(description: "未來旅行的點子")
var ideas: [String]
}
let stream = session.streamResponse(
to: "有哪些令人興奮的旅行點子?",
generating: TripIdeas.self
)
for try await partial in stream {
// partial 類型為 TripIdeas.PartiallyGenerated (所有屬性皆為 Optional)
print(partial)
}
SwiftUI 整合範例
@State private var partialResult: TripIdeas.PartiallyGenerated?
@State private var errorMessage: String?
var body: some View {
List {
ForEach(partialResult?.ideas ?? [], id: \.self) { idea in
Text(idea)
}
}
.overlay {
if let errorMessage { Text(errorMessage).foregroundStyle(.red) }
}
.task {
do {
let stream = session.streamResponse(to: prompt, generating: TripIdeas.self)
for try await partial in stream {
partialResult = partial
}
} catch {
errorMessage = error.localizedDescription
}
}
}
關鍵設計決策
| 決策 | 理由 |
|---|---|
| 裝置端執行 | 隱私保護 — 資料不離開裝置;支援離線工作 |
| 4,096 Token 限制 | 裝置端模型的物理約束;大數據需跨會話切割處理 |
| 快照串流 (而非 Deltas) | 對結構化輸出更友善;每個快照都是完整的局部狀態 |
@Generable 巨集 |
結構化生成的編譯時安全性;自動建構 PartiallyGenerated 型別 |
| 每個會話單次請求 | isResponding 可防止併發請求;需要時可建立多個會話 |
使用 response.content |
正確的 API 名稱 — 始終透過 .content 屬性獲取結果 |
最佳實踐
- 建立會話前務必檢查
model.availability— 妥善處理各種不可用的情況。 - 使用
instructions指導模型行為 — 它們的優先權高於提示詞 (Prompts)。 - 發送新請求前檢查
isResponding— 會話一次僅處理一個請求。 - 存取
response.content獲取結果 — 而非使用舊有的.output。 - 將大型輸入切割為多個區塊 — 4,096 Token 限制包含指令、提示詞與輸出的總和。
- 針對結構化輸出使用
@Generable— 比解析原始字串更具可靠保障。 - 使用
GenerationOptions(temperature:)調整創意度 (數值越高越具備創意性)。 - 使用 Instruments 監控效能 — 使用 Xcode Instruments 分析請求效能。
應避免的反模式
- 未先檢查
model.availability就建立會話。 - 發送超過 4,096 Token 上下文窗口的輸入內容。
- 嘗試在單一會話上併發發起多個請求。
- 使用
.output而非.content來獲取回應資料。 - 在可以使用
@Generable結構化輸出的情況下,仍手動解析原始字串回應。 - 在單一提示詞中建置過於複雜的多步驟邏輯 — 應將其拆分為多個聚焦的提示詞。
- 假設模型始終可用 — 裝置資格與使用者設定各有不同。
適用情境
- 針對隱私敏感型 App 提供裝置端文字建構。
- 從使用者輸入中擷取結構化資料 (表單、自然語言指令)。
- 必須支援離線工作的 AI 輔助功能。
- 逐步顯示生成內容的串流 UI。
- 透過工具呼叫執行領域特定 AI 行為 (搜尋、運算、查閱)。