Apple Foundation Models : le framework de LLM embarqué, expliqué
Le framework Foundation Models donne à une application un accès direct, gratuit et hors ligne au même grand modèle de langage embarqué qui alimente Apple Intelligence1. Aucune clé d’API, aucune facturation au token, aucun aller-retour réseau, aucune donnée qui quitte l’appareil. Pour toute une catégorie de fonctionnalités qui exigeaient autrefois un LLM dans le cloud et une revue de confidentialité, le coût se rapproche désormais de zéro. La contrepartie tient à la capacité : le modèle embarqué est petit, sa fenêtre de contexte est limitée et le framework trace des lignes nettes autour de ce qu’il accepte et refuse de faire. Connaître ces lignes, c’est tout l’enjeu.
Ceci est la référence pour le framework lui-même : les types que vous appelez réellement, la seule fonctionnalité qui justifie son usage, et le point où vous devriez vous arrêter pour atteindre quelque chose de plus grand.
En bref
LanguageModelSessionest le point d’entrée. Créez-en une, appelezrespond(to:), récupérez du texte. Le contexte multi-tours vit dans la session ; un travail en un seul tour reçoit une nouvelle session à chaque fois2.- La génération guidée est la raison d’utiliser ce framework. Annotez un type Swift avec
@Generableet le modèle renvoie ce type, rempli et validé par le compilateur, au lieu d’une chaîne que vous devez analyser3. - Le protocole
Toolpermet au modèle d’appeler votre code en cours de génération pour récupérer une donnée ou effectuer une action, puis d’intégrer le résultat dans sa réponse4. - Vérifiez
SystemLanguageModel.default.availabilityavant toute chose. Le modèle est absent sur les appareils non éligibles, lorsqu’Apple Intelligence est désactivé, ou pendant son téléchargement5. - La fenêtre de contexte est bien réelle et petite.
SystemLanguageModel.default.contextSizeindique le budget de tokens partagé entre l’invite et la réponse6. Prévoyez-le, sinon la session lève une erreur. - Nécessite iOS 26 et un appareil compatible Apple Intelligence. En dessous de ce seuil, le framework n’existe pas.
Ce qu’est le framework, et ce qu’il n’est pas
Foundation Models n’est pas une enveloppe autour d’un point de terminaison dans le cloud. Le modèle vit sur l’appareil, est livré avec le système d’exploitation et s’exécute sur le Neural Engine. Ce seul fait gouverne chaque décision de conception dans l’API et chaque décision que vous prenez en l’utilisant.
Ce que vous obtenez : génération de texte, résumé, classification, extraction, réécriture de formes courtes et sortie structurée, le tout embarqué et gratuit. Ce que vous n’obtenez pas : un modèle de pointe. Apple a conçu le modèle embarqué pour des tâches de langage ciblées au sein d’une application, pas pour du raisonnement ouvert, pas pour l’analyse de longs documents, pas pour des connaissances générales que vous pourriez interroger. Apple le dit explicitement, et ce cadrage compte, car il fixe des attentes que l’API vous laisserait sinon transgresser1.
Le modèle mental qui vous évite les ennuis : traitez le modèle embarqué comme un stagiaire rapide, discret et gratuit, excellent pour mettre en forme du texte et exécrable pour connaître des faits. Donnez-lui de la matière et une tâche claire. Ne lui posez pas de questions auxquelles il n’a aucun moyen de répondre.
LanguageModelSession : le point d’entrée
Chaque interaction commence par une session.
import FoundationModels
let session = LanguageModelSession()
let response = try await session.respond(to: "Summarize this review in one sentence: \(reviewText)")
print(response.content)
La session conserve l’état de la conversation. Chaque appel à respond(to:) s’ajoute à la transcription en cours, de sorte qu’une session que vous gardez se souvient de ce qui a précédé. Pour une fonctionnalité de chat, c’est exactement ce que vous voulez. Pour des tâches ponctuelles indépendantes (résumer ceci, classer cela), créez une nouvelle session par appel afin qu’un contexte périmé ne s’y infiltre pas et ne grignote pas votre budget de tokens2.
respond(to:) est async throws. La méthode se suspend pendant que le modèle travaille et lève une erreur lorsque la requête dépasse la fenêtre de contexte, lorsque le modèle est indisponible, ou lorsque les garde-fous rejettent le contenu. Chacun de ces cas est une véritable branche que vous gérez, non un cas limite que vous ignorez.
Pour une interface réactive, diffusez en flux plutôt que d’attendre. streamResponse(to:) produit une sortie partielle à mesure que le modèle la génère, ce qui transforme une attente de trois secondes en un texte qui apparaît au fur et à mesure de sa formation7.
Génération guidée : la fonctionnalité qui justifie le framework
Voici la partie qui vaut le prix du billet. La plupart des intégrations de LLM consacrent un tiers de leur code à extraire du JSON valide d’un modèle, et les deux autres tiers à se prémunir contre les cas où il échoue malgré tout. Foundation Models supprime ce travail.
Annotez un type Swift avec @Generable, demandez à la session de le générer, et le modèle renvoie une instance de ce type, remplie et de type sûr3 :
@Generable
struct Recipe {
@Guide(description: "The dish name")
let title: String
@Guide(description: "Ingredients, each as 'quantity item'")
let ingredients: [String]
@Guide(description: "Total minutes, start to finish", .range(5...240))
let minutes: Int
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "A weeknight pasta for two.",
generating: Recipe.self
)
let recipe = response.content // a Recipe, not a String
Pas d’analyse. Pas de JSONDecoder. Pas de boucle de relance pour une sortie mal formée. La macro @Guide contraint chaque champ : une description que le modèle lit comme une instruction, et des limites facultatives comme une plage numérique ou une expression régulière à laquelle la sortie doit correspondre8. Le framework ne demande pas gentiment au modèle un nombre entre 5 et 240 ; il contraint le décodage pour que le champ ne puisse pas revenir autrement.
La discipline que cela impose est la véritable valeur. Vous concevez d’abord le type de sortie, en Swift, sous le contrôle du compilateur. Le modèle remplit un contrat que vous avez défini au lieu de renvoyer une prose que vous devez décortiquer. Pour l’extraction, le remplissage de formulaires et toute fonctionnalité qui transforme le langage en données, la génération guidée fait la différence entre une démo et du code prêt à livrer.
Un réglage qu’il vaut la peine de connaître : respond(to:generating:) fixe par défaut includeSchemaInPrompt à true, ce qui injecte la forme de votre type dans l’invite pour orienter le modèle vers elle. Laissez-le activé, sauf si le modèle connaît déjà le format par son entraînement ou par les tours précédents de la session ; le désactiver pour économiser des tokens sur un format que le modèle n’a jamais vu, c’est ainsi qu’on récupère n’importe quoi9.
Appel d’outils : laisser le modèle atteindre votre code
La génération guidée façonne ce qui sort. L’appel d’outils change ce qui entre. Un outil est un fragment de votre code que le modèle peut invoquer en cours de génération pour aller chercher une information dont il ne dispose pas ou effectuer une action, puis poursuivre sa réponse en s’appuyant sur le résultat4.
Un outil se conforme au protocole Tool : un name, une description que le modèle lit pour décider quand l’appeler, un type Arguments annoté @Generable, et une méthode call(arguments:) qui fait le travail4 :
struct FindContacts: Tool {
let name = "findContacts"
let description = "Find a specific number of contacts from the address book"
@Generable
struct Arguments {
@Guide(description: "How many contacts to return", .range(1...10))
let count: Int
}
func call(arguments: Arguments) async throws -> [String] {
// Fetch contacts, return formatted names.
}
}
let session = LanguageModelSession(tools: [FindContacts()])
let response = try await session.respond(to: "Draft a dinner invite to three of my contacts.")
Le déroulé : le modèle décide qu’il a besoin de contacts, appelle votre outil avec un count validé, vous renvoyez des données, et le modèle rédige l’invitation à l’aide de vrais noms. Les arguments arrivent validés par la même mécanique de génération guidée, vous n’avez donc jamais à extraire l’intention du modèle d’un texte libre. La description de l’outil est votre seul levier sur le moment où le modèle y recourt ; rédigez-la donc comme la documentation d’une fonction qu’un autre ingénieur (sans aucun autre contexte) doit lire et utiliser correctement.
C’est aussi la couture où Foundation Models rejoint le reste de l’histoire des agents. Un outil que le modèle embarqué appelle et un App Intent qu’Apple Intelligence appelle sont des surfaces différentes ayant la même forme : une capacité nommée, décrite et typée. Concevez la capacité une fois et vous pouvez l’exposer par les deux voies.
Disponibilité : la vérification qu’on ne peut pas sauter
Le modèle n’est pas toujours là. Il est absent sur les appareils qui ne prennent pas en charge Apple Intelligence, lorsque l’utilisateur l’a désactivé, et pendant la fenêtre où le système d’exploitation télécharge encore les ressources du modèle. Livrez du code qui suppose que le modèle existe et il plantera, se dégradera silencieusement ou se figera pour une partie de vos utilisateurs sur laquelle vous n’avez jamais testé.
Vérifiez SystemLanguageModel.default.availability et branchez selon la raison5 :
switch SystemLanguageModel.default.availability {
case .available:
// Show the intelligence feature.
case .unavailable(.deviceNotEligible):
// Hide it. This device will never have the model.
case .unavailable(.appleIntelligenceNotEnabled):
// Prompt the user to turn on Apple Intelligence.
case .unavailable(.modelNotReady):
// Downloading or otherwise not ready yet. Try again later.
case .unavailable(let other):
// Unknown reason. Fail closed.
}
Les trois raisons exigent trois réponses produit différentes, et les confondre est la manière la plus courante de donner à ces fonctionnalités un air défectueux. deviceNotEligible est permanent : masquez la fonctionnalité, ne harcelez pas. appleIntelligenceNotEnabled est un réglage que l’utilisateur contrôle : une invite unique est légitime. modelNotReady est temporaire : réessayez, n’affichez pas d’erreur. Construisez le chemin d’indisponibilité avec le même soin que le chemin nominal, car pour une part réelle des appareils c’est le seul chemin.
Lorsque le modèle est disponible et que vous savez qu’une requête arrive, prewarm() sur la session préchauffe le modèle pour que la première vraie réponse arrive plus vite10. Cela en vaut la peine sur un écran où l’utilisateur s’apprête à agir, mais c’est du gaspillage si vous l’appelez de façon spéculative.
La fenêtre de contexte, et le point où elle ne suffit plus
SystemLanguageModel.default.contextSize indique le budget de tokens dans lequel le modèle travaille, et ce budget est partagé : l’invite et la réponse réunies doivent y tenir6. Ce nombre est petit comparé à un modèle dans le cloud, et vous le ressentez vite sur des entrées réelles. Un long document, un historique de chat complet, un résultat d’outil volumineux : chacun peut faire exploser le budget et faire lever une erreur à respond.
Deux modes de défaillance en découlent, et il vous revient de les prévenir tous deux. D’abord, la dérive lente : une session multi-tours accumule la transcription jusqu’à ce qu’un tour de plus la fasse déborder. Maîtrisez-la en démarrant de nouvelles sessions pour les travaux sans rapport et en gardant l’entrée de chaque tour légère. Ensuite, la requête unique surdimensionnée : un PDF de 20 pages ne tient pas, point final. Découpez-le, résumez les morceaux, puis raisonnez sur les résumés (le map-reduce que les ingénieurs en LLM connaissent bien), ou acceptez que la tâche soit de la mauvaise forme pour un modèle embarqué.
La fenêtre de contexte est le signal le plus net pour la décision qui compte vraiment avec ce framework : quand rester embarqué et quand le quitter.
Quand ne pas utiliser Foundation Models
Le framework est gratuit, privé et hors ligne, ce qui donne envie d’y recourir partout. Résistez. Allez au-delà lorsque :
- Vous avez besoin d’un vrai raisonnement ou d’une large étendue de connaissances générales. Le modèle embarqué est petit par conception. Le raisonnement ouvert, la génération de code et l’analyse approfondie relèvent d’un modèle de pointe dans le cloud. Les demander au modèle embarqué produit des réponses assurées et fausses.
- L’entrée ne tient pas dans la fenêtre de contexte et le découpage en détruirait le sens. Certaines tâches doivent tout voir d’un seul coup.
- Vous avez besoin d’un modèle que vous maîtrisez : un point de contrôle précis, un fine-tune, des poids personnalisés, un versionnage déterministe au fil des mises à jour de l’OS. Apple livre et met à jour le modèle selon son calendrier, pas le vôtre.
- Vous êtes en dessous d’iOS 26 ou sur un appareil non éligible. Le framework n’est tout simplement pas là, et la vérification de disponibilité vous le dira à chaque exécution.
Pour les cas embarqués que le framework ne couvre pas (un modèle personnalisé, vos propres poids, l’entraînement sur l’appareil), la couche en dessous est Core ML et le MLX d’Apple. Pour les cas qui exigent véritablement de l’échelle, un LLM dans le cloud derrière une frontière de confidentialité reste la réponse honnête. Foundation Models ne remplace ni l’un ni l’autre. C’est le bon premier réflexe pour un travail de langage ciblé sur du texte que vous détenez déjà, et le mauvais réflexe pour tout le reste.
La compétence que ce framework récompense n’est pas l’art de l’invite. C’est le jugement sur le périmètre : confier au modèle des tâches où il excelle, concevoir des types @Generable qui capturent exactement ce dont vous avez besoin, et reconnaître le moment où le travail dépasse les capacités de l’appareil. Bâtissez avec ces instincts et le modèle embarqué accomplit une quantité étonnante de vrai travail, gratuitement. Ignorez-les et vous livrez une fonctionnalité qui casse pour chaque utilisateur dont l’entrée a dépassé d’un seul token la limite.
FAQ
Le framework Foundation Models d’Apple est-il gratuit ?
Oui. Le framework donne à une application un accès direct, gratuit et hors ligne au même modèle embarqué qui alimente Apple Intelligence. Il n’y a aucune clé d’API, aucune facturation au token et aucun aller-retour réseau1.
Quels appareils et quelle version d’iOS Foundation Models exige-t-il ?
Il nécessite iOS 26 et un appareil compatible Apple Intelligence. En dessous de ce seuil, le framework n’existe pas, et même sur un OS pris en charge le modèle est absent sur les appareils non éligibles, lorsqu’Apple Intelligence est désactivé, ou pendant le téléchargement du modèle. Vérifiez toujours SystemLanguageModel.default.availability avant de l’utiliser5.
Comment obtenir une sortie structurée et de type sûr plutôt qu’une chaîne ?
Annotez un type Swift avec @Generable et le modèle renvoie ce type, rempli et validé par le compilateur, au lieu d’une chaîne que vous devez analyser. Cette génération guidée est la seule fonctionnalité qui justifie l’usage du framework3.
Quelle est la fenêtre de contexte du modèle embarqué d’Apple ?
SystemLanguageModel.default.contextSize indique le budget de tokens, partagé entre l’invite et la réponse générée6. Il est petit par conception : les longs documents et les longs historiques multi-tours le dépasseront. Prévoyez la limite, sinon la session lève une erreur.
Foundation Models fonctionne-t-il hors ligne, et envoie-t-il des données à Apple ?
Il s’exécute entièrement sur l’appareil, sur le Neural Engine. Aucune donnée ne quitte l’appareil et aucun aller-retour réseau n’est requis, ce qui le rend adapté aux fonctionnalités qui exigeaient autrefois un LLM dans le cloud et une revue de confidentialité1.
Le modèle embarqué peut-il appeler mon propre code en cours de génération ?
Oui. Le protocole Tool permet au modèle d’invoquer votre code pour récupérer une donnée ou effectuer une action pendant la génération, puis d’intégrer le résultat dans sa réponse4.
Quand ne devrais-je pas utiliser Foundation Models ?
Allez au-delà lorsque vous avez besoin d’un modèle de pointe : raisonnement ouvert, génération de code, analyse de longs documents ou connaissances générales. Apple a conçu le modèle embarqué pour des tâches de langage ciblées au sein d’une application ; lui demander une intelligence générale produit des réponses assurées et fausses1.
-
Apple Developer, aperçu du framework « Foundation Models ». Apple décrit le framework comme un accès au modèle embarqué qui alimente Apple Intelligence, adapté à des tâches de langage ciblées telles que la génération de texte, le résumé, la classification et la sortie structurée, plutôt qu’au raisonnement ouvert ou aux connaissances générales. ↩↩↩↩↩
-
Apple Developer, « LanguageModelSession » et « Generating content and performing tasks with Foundation Models ». Une session conserve le contexte multi-tours ; Apple recommande de créer une nouvelle session pour chaque interaction distincte en un seul tour. ↩↩
-
Apple Developer, « Generable » et « Prompting an on-device foundation model ». La macro
@Generablepermet au framework de renvoyer une valeur Swift remplie et validée par le compilateur plutôt qu’une chaîne. ↩↩↩ -
Apple Developer, protocole « Tool ». Définit
protocol Tool<Arguments, Output>: Sendableavec les éléments requisname,descriptionetparameters: GenerationSchema, ainsi quecall(arguments:) async throws -> Output. Le typeArgumentsse conforme àConvertibleFromGeneratedContentet est généralement déclaré@Generable. ↩↩↩↩ -
Apple Developer, « SystemLanguageModel.Availability » et son
UnavailableReason. Cas :.availableet.unavailable(...)avec les raisonsdeviceNotEligible,appleIntelligenceNotEnabledetmodelNotReady.SystemLanguageModel.default.isAvailableest le booléen de commodité. ↩↩↩ -
Apple Developer, « SystemLanguageModel.contextSize ». Une propriété d’instance (accessible via
SystemLanguageModel.default) documentée comme la taille de contexte maximale, représentant le total des tokens entre l’invite d’entrée et la réponse générée. ↩↩↩ -
Apple Developer, « LanguageModelSession.streamResponse(to:) ». Diffuse en flux la sortie partielle générée à mesure que le modèle la produit, pour des mises à jour incrémentales de l’interface. ↩
-
Apple Developer, « Guide(description:_:) ». Une macro pair qui attache une description en langage naturel et des contraintes facultatives (plages numériques, guides d’expression régulière) à une propriété
@Generable. Nécessite iOS 26.0+. ↩ -
Apple Developer, « respond(to:schema:includeSchemaInPrompt:options:) ».
includeSchemaInPromptest par défaut àtrue; Apple recommande de conserver la valeur par défaut sauf si le modèle connaît déjà le format attendu. ↩ -
Apple Developer, « LanguageModelSession.prewarm() ». Demande au framework de charger les ressources du modèle en amont d’une requête connue à venir afin de réduire la latence de première réponse. ↩
-
Analyse connexe de l’auteur : Les LLM embarqués avec Foundation Models d’Apple, Adaptateurs personnalisés pour Foundation Models, Cas d’usage de Foundation Models et Workflows agentiques sur Foundation Models. L’argument sur les App Intents et la surface d’outils est développé dans App Intents, la nouvelle API d’Apple vers votre application. ↩