FoundationModels: On-Device LLM (iOS 26)
Patterns zur Integration von Apples On-Device-Sprachmodell in Apps mit dem FoundationModels-Framework. Deckt Textgenerierung, strukturierte Ausgabe mit @Generable, benutzerdefinierten Tool-Aufruf und Snapshot-Streaming ab – alles auf dem Gerät für Privatsphäre und Offline-Betrieb.
Wann aktivieren
- KI-gestützte Funktionen mit Apple Intelligence auf dem Gerät entwickeln
- Text generieren oder zusammenfassen, ohne auf eine Cloud angewiesen zu sein
- Strukturierte Daten aus natürlicher Sprache extrahieren
- Benutzerdefinierte Tool-Aufrufe für domänenspezifische KI-Aktionen implementieren
- Strukturierte Antworten für Echtzeit-UI-Updates streamen
- Datenschutzfreundliche KI benötigen (keine Daten verlassen das Gerät)
Kernmuster – Verfügbarkeitsprüfung
Vor dem Erstellen einer Sitzung immer die Modellverfügbarkeit prüfen:
struct GenerativeView: View {
private var model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
ContentView()
case .unavailable(.deviceNotEligible):
Text("Gerät nicht für Apple Intelligence geeignet")
case .unavailable(.appleIntelligenceNotEnabled):
Text("Bitte Apple Intelligence in den Einstellungen aktivieren")
case .unavailable(.modelNotReady):
Text("Das Modell wird heruntergeladen oder ist noch nicht bereit")
case .unavailable(let other):
Text("Modell nicht verfügbar: \(other)")
}
}
}
Kernmuster – Basissitzung
// Einzeldurchlauf: jedes Mal eine neue Sitzung erstellen
let session = LanguageModelSession()
let response = try await session.respond(to: "Welcher Monat eignet sich gut für einen Paris-Besuch?")
print(response.content)
// Mehrfachdurchlauf: Sitzung für Gesprächskontext wiederverwenden
let session = LanguageModelSession(instructions: """
Du bist ein Koch-Assistent.
Gib Rezeptvorschläge basierend auf den Zutaten.
Halte Vorschläge kurz und praktisch.
""")
let first = try await session.respond(to: "Ich habe Hühnchen und Reis")
let followUp = try await session.respond(to: "Was ist mit einer vegetarischen Option?")
Wichtige Punkte für Anweisungen:
- Rolle des Modells definieren („Du bist ein Mentor“)
- Aufgabe beschreiben („Hilf, Kalenderereignisse zu extrahieren“)
- Stilpräferenzen festlegen („Antworte so kurz wie möglich“)
- Sicherheitsmaßnahmen hinzufügen („Bei gefährlichen Anfragen antworte mit ‚Dazu kann ich nicht helfen‘“)
Kernmuster – Geführte Generierung mit @Generable
Strukturierte Swift-Typen statt roher Zeichenketten generieren:
1. Einen Generable-Typ definieren
@Generable(description: "Grundlegende Profilinformationen einer Katze")
struct CatProfile {
var name: String
@Guide(description: "Das Alter der Katze", .range(0...20))
var age: Int
@Guide(description: "Ein Ein-Satz-Profil über die Persönlichkeit der Katze")
var profile: String
}
2. Strukturierte Ausgabe anfordern
let response = try await session.respond(
to: "Erzeuge eine süße Katze aus dem Tierheim",
generating: CatProfile.self
)
// Direkter Zugriff auf strukturierte Felder
print("Name: \(response.content.name)")
print("Alter: \(response.content.age)")
print("Profil: \(response.content.profile)")
Unterstützte @Guide-Einschränkungen
.range(0...20)– Zahlenbereich.count(3)– Array-Elementanzahldescription:– semantische Leitlinie für die Generierung
Kernmuster – Tool-Aufruf
Das Modell kann benutzerdefinierten Code für domänenspezifische Aufgaben aufrufen:
1. Ein Tool definieren
struct RecipeSearchTool: Tool {
let name = "recipe_search"
let description = "Suche nach Rezepten, die zu einem bestimmten Begriff passen, und gib eine Ergebnisliste zurück."
@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. Sitzung mit Tools erstellen
let session = LanguageModelSession(tools: [RecipeSearchTool()])
let response = try await session.respond(to: "Finde mir ein paar Nudelrezepte")
3. Tool-Fehler behandeln
do {
let answer = try await session.respond(to: "Finde ein Rezept für Tomatensuppe.")
} catch let error as LanguageModelSession.ToolCallError {
print(error.tool.name)
if case .databaseIsEmpty = error.underlyingError as? RecipeSearchToolError {
// Spezifischen Tool-Fehler behandeln
}
}
Kernmuster – Snapshot-Streaming
Strukturierte Antworten für Echtzeit-UI mit PartiallyGenerated-Typen streamen:
@Generable
struct TripIdeas {
@Guide(description: "Ideen für anstehende Reisen")
var ideas: [String]
}
let stream = session.streamResponse(
to: "Was sind einige aufregende Reiseideen?",
generating: TripIdeas.self
)
for try await partial in stream {
// partial: TripIdeas.PartiallyGenerated (alle Eigenschaften Optional)
print(partial)
}
SwiftUI-Integration
@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
}
}
}
Wichtige Designentscheidungen
| Entscheidung | Begründung |
|---|---|
| Ausführung auf dem Gerät | Datenschutz – keine Daten verlassen das Gerät; funktioniert offline |
| 4.096 Token-Limit | Einschränkung des On-Device-Modells; große Daten auf mehrere Sitzungen aufteilen |
| Snapshot-Streaming (keine Deltas) | Strukturierte Ausgabe freundlich; jeder Snapshot ist ein vollständiger Teilzustand |
@Generable-Makro | Kompilierzeitsicherheit für strukturierte Generierung; erzeugt automatisch den PartiallyGenerated-Typ |
| Eine Anfrage pro Sitzung | isResponding verhindert gleichzeitige Anfragen; bei Bedarf mehrere Sitzungen erstellen |
response.content (nicht .output) | Korrekte API – Ergebnisse immer über die .content-Eigenschaft abrufen |
Best Practices
model.availabilityimmer prüfen vor dem Erstellen einer Sitzung – alle Nichtverfügbarkeitsfälle behandelninstructionsverwenden, um das Modellverhalten zu steuern – sie haben Vorrang vor PromptsisRespondingprüfen, bevor eine neue Anfrage gesendet wird – Sitzungen bearbeiten jeweils nur eine Anfrageresponse.contentfür Ergebnisse verwenden – nicht.output- Große Eingaben aufteilen – das 4.096 Token-Limit gilt für Anweisungen + Prompt + Ausgabe zusammen
@Generablefür strukturierte Ausgabe verwenden – stärkere Garantien als das Parsen roher ZeichenkettenGenerationOptions(temperature:)verwenden, um die Kreativität einzustellen (höher = kreativer)- Mit Instruments überwachen – Xcode Instruments zur Profilerstellung der Anfrageleistung nutzen
Anti-Patterns (Vermeidung)
- Sitzungen erstellen, ohne vorher
model.availabilityzu prüfen - Eingaben senden, die das 4.096 Token-Kontextfenster überschreiten
- Gleichzeitige Anfragen an eine einzelne Sitzung stellen
.outputstatt.contentverwenden, um auf Antwortdaten zuzugreifen- Rohe Zeichenkettenantworten parsen, wenn
@Generablestrukturierte Ausgabe möglich wäre - Komplexe mehrschrittige Logik in einen einzigen Prompt packen – in mehrere fokussierte Prompts aufteilen
- Annehmen, dass das Modell immer verfügbar ist – Geräteberechtigung und Einstellungen variieren
Wann verwenden
- On-Device-Textgenerierung für datenschutzsensible Apps
- Strukturierte Datenextraktion aus Benutzereingaben (Formulare, natürliche Sprachbefehle)
- KI-gestützte Funktionen, die offline funktionieren müssen
- Streaming-UI, die generierte Inhalte schrittweise anzeigt
- Domänenspezifische KI-Aktionen über Tool-Aufruf (Suche, Berechnung, Nachschlagen)