FoundationModels: On-Device LLM (iOS 26)
Muster für die Integration von Apples On-Device-Sprachmodell in Apps mit dem FoundationModels-Framework. Deckt Textgenerierung, strukturierte Ausgabe mit @Generable, benutzerdefinierte Tool-Aufrufe und Snapshot-Streaming ab – alles lokal auf dem Gerät für Privatsphäre und Offline-Unterstützung.
Wann zu aktivieren
- Erstellung KI-gestützter Funktionen mit Apple Intelligence auf dem Gerät
- Generieren oder Zusammenfassen von Text ohne Cloud-Abhängigkeit
- Extrahieren strukturierter Daten aus natürlicher Spracheingabe
- Implementierung benutzerdefinierter Tool-Aufrufe für domänenspezifische KI-Aktionen
- Streamen strukturierter Antworten für Echtzeit-UI-Updates
- Benötigen datenschutzfreundlicher KI (keine Daten verlassen das Gerät)
Kernmuster – Verfügbarkeitsprüfung
Überprüfen Sie immer die Modellverfügbarkeit, bevor Sie eine Sitzung erstellen:
struct GenerativeView: View {
private var model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
ContentView()
case .unavailable(.deviceNotEligible):
Text("Das Gerät ist nicht für Apple Intelligence berechtigt")
case .unavailable(.appleIntelligenceNotEnabled):
Text("Bitte aktivieren Sie Apple Intelligence in den Einstellungen")
case .unavailable(.modelNotReady):
Text("Das Modell wird heruntergeladen oder ist nicht bereit")
case .unavailable(let other):
Text("Modell nicht verfügbar: \(other)")
}
}
}
Kernmuster – Basissitzung
// Einmalige Antwort: jedes Mal eine neue Sitzung erstellen
let session = LanguageModelSession()
let response = try await session.respond(to: "Welcher Monat ist gut für einen Paris-Besuch?")
print(response.content)
// Mehrere Antworten: Sitzung für Gesprächskontext wiederverwenden
let session = LanguageModelSession(instructions: """
Du bist ein Kochassistent.
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:
- Definieren Sie die Rolle des Modells ("Du bist ein Mentor")
- Geben Sie an, was zu tun ist ("Hilf, Kalendereinträge zu extrahieren")
- Legen Sie Stilpräferenzen fest ("Antworte so kurz wie möglich")
- Fügen Sie Sicherheitsmaßnahmen hinzu ("Antworte mit 'Das kann ich nicht helfen' bei gefährlichen Anfragen")
Kernmuster – Geführte Generierung mit @Generable
Generieren Sie strukturierte Swift-Typen anstelle von Rohstrings:
1. Definieren Sie einen @Generable-Typ
@Generable(description: "Basisprofilinformationen über eine 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: "Generiere 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)– numerischer Bereich.count(3)– Array-Elementanzahldescription:– semantische Anleitung für die Generierung
Kernmuster – Tool-Aufruf
Lassen Sie das Modell 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 einem bestimmten Begriff entsprechen, und gib eine Liste der Ergebnisse 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 einige Pasta-Rezepte")
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 {
// Behandle spezifischen Tool-Fehler
}
}
Kernmuster – Snapshot-Streaming
Streamen von strukturierten Antworten für Echtzeit-UI mit PartiallyGenerated-Typen:
@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 Entwurfsentscheidungen
| Entscheidung | Begründung |
|---|---|
| Ausführung auf dem Gerät | Datenschutz – keine Daten verlassen das Gerät; funktioniert offline |
| 4.096-Token-Grenze | 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 partieller Zustand |
@Generable-Makro | Kompilierzeit-Sicherheit für strukturierte Generierung; generiert automatisch PartiallyGenerated-Typ |
| Einzelanfrage 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
- Überprüfen Sie immer
model.availabilityvor dem Erstellen einer Sitzung – behandeln Sie alle Nichtverfügbarkeitsfälle - Verwenden Sie
instructionsum das Modellverhalten zu steuern – sie haben Vorrang vor Aufforderungen - Überprüfen Sie
isRespondingbevor Sie eine neue Anfrage senden – Sitzungen bearbeiten eine Anfrage gleichzeitig - Greifen Sie auf
response.contentfür Ergebnisse zu – nicht auf.output - Teilen Sie große Eingaben in Blöcke auf – die 4.096-Token-Grenze gilt für Anweisungen + Aufforderung + Ausgabe zusammen
- Verwenden Sie
@Generablefür strukturierte Ausgaben – stärkere Garantien als das Parsen von Rohstrings - Verwenden Sie
GenerationOptions(temperature:)um die Kreativität anzupassen (höher = kreativer) - Überwachen Sie mit Instruments – verwenden Sie Xcode Instruments, um die Anforderungsleistung zu profilieren
Anti-Patterns zu vermeiden
- Sitzungen erstellen, ohne zuerst
model.availabilityzu überprüfen - Eingaben senden, die das 4.096-Token-Kontextfenster überschreiten
- Gleichzeitige Anfragen an eine einzelne Sitzung versuchen
.outputanstelle von.contentverwenden, um auf Antwortdaten zuzugreifen- Rohstring-Antworten parsen, wenn strukturierte Ausgabe mit
@Generablefunktionieren würde - Komplexe mehrstufige Logik in einer einzigen Aufforderung aufbauen – in mehrere fokussierte Aufforderungen aufteilen
- Annehmen, dass das Modell immer verfügbar ist – Geräteberechtigung und -einstellungen variieren
Wann zu 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-Aufrufe (Suche, Berechnung, Nachschlagewerk)