Adaptadores Personalizados de Foundation Models: El Ciclo de Vida que Apple Recomienda Evitar
El tipo SystemLanguageModel.Adapter permite que una app cargue pesos entrenados a medida en el modelo de lenguaje de Apple en el dispositivo.1 El framework lo soporta. Apple ofrece un kit de entrenamiento. Existe un entitlement documentado, un formato de paquete .fmadapter y una integración con Background Assets para descargar el adaptador correcto para el dispositivo correcto.
La propia documentación de Apple sobre el mismo tipo también dice, citando textualmente: “Adapters consume a large amount of storage space and isn’t recommended for most apps.”1 El post anterior de este clúster, Casos de Uso de Foundation Models, cubrió las vías por las que la mayoría de apps debería transitar. Este post es el tercer raíl: el ciclo de vida operacional de entrenar, empaquetar y publicar un adaptador personalizado, y la guía explícita de Apple sobre cuándo no hacerlo.
TL;DR
- Un adaptador personalizado es una matriz de pesos entrenada con LoRA que se superpone al modelo de lenguaje del sistema en el dispositivo.2 El kit de Apple confirma la técnica por su nombre.
- Cada adaptador está vinculado a una única versión del modelo del sistema. Cuando Apple actualiza el modelo base, hay que reentrenar el adaptador.3
- La recomendación de Apple, en sus palabras: “Use the base system model for most prompt engineering, guided generation, and tools. If you need to specialize the model, train a custom Adapter… Use custom adapters only if you’re comfortable training foundation models in Python.”1
- Los assets de los adaptadores son grandes (~160 MB), se distribuyen mediante Background Assets y están protegidos por un entitlement (
com.apple.developer.foundation-model-adapter) que el Account Holder de una membresía del Apple Developer Program solicita para el despliegue.3 - La vía tiene sentido para un conjunto reducido de apps: aquellas que replican un LLM ajustado en servidor en el dispositivo, aquellas con requisitos estrictos de adherencia a estilo/formato/política, o aquellas con techos documentados de prompt engineering en su tarea objetivo.2
Qué Es Realmente un Adaptador
SystemLanguageModel.Adapter es un struct, disponible en iOS, iPadOS, Mac Catalyst, macOS y visionOS, todos 26.0+.4 La descripción de Apple del tipo:
“Use the base system model for most prompt engineering, guided generation, and tools. If you need to specialize the model, train a custom Adapter to alter the system model weights and optimize it for your custom task. Use custom adapters only if you’re comfortable training foundation models in Python.”4
El mecanismo está documentado. La guía de entrenamiento de adaptadores de Apple lo dice directamente:2
“The system model uses a parameter-efficient fine-tuning (PEFT) approach known as LoRA (Low-Rank Adaptation). In LoRA, the original model weights are frozen, and small trainable weight matrices called ‘adapters’ are embedded through the model’s network. During training, only adapter weights are updated, significantly reducing the number of parameters to train.”
LoRA es una técnica pública con un historial publicado de papers.5 La contribución de Apple es el kit, el formato de paquete .fmadapter, el runtime en el dispositivo que carga el adaptador y la fontanería del ciclo de vida para distribuirlo a través de Background Assets.
La Superficie API del Tipo
El struct Adapter expone una superficie reducida:4
init(fileURL: URL) throws: “Creates an adapter from the file URL.” Se usa para pruebas locales en Xcode.init(name: String) throws: “Creates an adapter downloaded from the background assets framework.” Se usa en producción.func compile() async throws: “Prepares an adapter before being used with a LanguageModelSession. You should call this if your adapter has a draft model.”var creatorDefinedMetadata: [String : Any]: valores del campo definido por el creador en los metadatos.static func removeObsoleteAdapters() throws: borra los adaptadores que ya no coinciden con un modelo del sistema actual.static func compatibleAdapterIdentifiers(name: String) -> [String]: obtiene los IDs de los paquetes de assets de adaptadores compatibles.enum AssetError: tipo de error para fallos relacionados con los assets.
SystemLanguageModel tiene un inicializador emparejado para adaptadores: convenience init(adapter: SystemLanguageModel.Adapter, guardrails: SystemLanguageModel.Guardrails), con la descripción “Creates the base version of the model with an adapter.”6
La clave del entitlement, exclusiva del despliegue, es com.apple.developer.foundation-model-adapter: “A Boolean value that indicates whether the app can enable custom adapters for the Foundation Models framework.”6 No la necesitas para entrenar ni para hacer pruebas locales en Xcode; sí la necesitas antes de publicar en la App Store.3
El Criterio de Apple sobre “Cuándo Considerar un Adaptador”
La página del kit expone señales concretas de adopción:2
- “You have a dataset suitable for use with an LLM” o ya usas un LLM ajustado en servidor y quieres paridad en el dispositivo.
- “You need the model to become a subject-matter expert.”
- “You need the model to adhere to a specific style, format, or policy.”
- “Prompt engineering isn’t achieving the required accuracy or consistency for your task.”
- “You want lower latency at inference. If your prompt-engineered solutions require lengthy prompts with examples for every call, an adapter specialized for that task offers minimal prompting.”
La misma guía también enumera los costes que asumes:2
- Un dataset de pares de prompt y respuesta que demuestren tu habilidad objetivo.
- Un proceso para evaluar la calidad de tus adaptadores.
- Un proceso para cargar tus adaptadores en tu app desde un servidor.
Y el impuesto de almacenamiento: “Each adapter will take approximately 160 MB of storage space in your app. Like other big assets, adapters shouldn’t be part of your app’s main bundle because with multiple adapter versions your app will become too big for people to install.”2
La recomendación del framework, reiterada por Apple en dos lugares distintos, es recurrir por defecto al prompt engineering más el tool calling y acudir a los adaptadores solo cuando el criterio anterior se cumpla.
Entrenamiento, en la Forma de Apple
El pipeline de entrenamiento es Python. El kit de Apple incluye código de muestra, los assets del modelo correspondientes a una versión específica del modelo del sistema, utilidades de dataset y el paso de exportación que produce un paquete .fmadapter.2
Requisitos de hardware: “Mac with Apple silicon and at least 32GB memory, or Linux GPU machines.” Python 3.11 o posterior.2
Forma del dataset:2
- El formato es jsonl con campos
role("user"o"assistant") ycontent. - De 100 a 1.000 muestras para tareas básicas.
- 5.000+ muestras para tareas complejas.
- Un
Schema.mden el kit cubre el esquema completo, incluidos campos para guided generation y ganchos de seguridad de IA.
La nota de Apple sobre la calidad de los datos vale la pena citarla: “Focus on quality over quantity. A smaller dataset of clear, consistent, and well-structured samples may be more effective than larger dataset of noisy, low-quality samples.”2
El entrenamiento se invoca desde el punto de entrada train_adapter del kit:
python -m examples.train_adapter \
--train-data /path/to/train.jsonl \
--eval-data /path/to/valid.jsonl \
--epochs 5 \
--learning-rate 1e-3 \
--batch-size 4 \
--checkpoint-dir /path/to/my_checkpoints/
Opcionalmente, después de entrenar el adaptador, puedes entrenar un draft model emparejado.2 Un draft model es una versión más pequeña del modelo base del sistema que permite el speculative decoding, una técnica publicada de aceleración de inferencia.7 El planteamiento de Apple: “If you choose not to train the draft model, speculative decoding will not be available for your adapter’s use case.”2
La Restricción de Versión Única
El hecho operacionalmente más relevante sobre los adaptadores es el vínculo con una versión específica del modelo del sistema:3
“Each adapter is compatible with a single specific system model version. You must train a new adapter for every new base model version. A runtime error occurs if your app runs on a person’s device without a compatible adapter.”
El kit está versionado junto con el modelo. Al momento de escribir, Apple ha publicado dos versiones beta del kit (Beta 0.1.0 y Beta 0.2.0, ambas retiradas) y una versión estable: 26.0.0.2 La declaración de cadencia de releases de Apple: “A new toolkit will be released for every system model update. The system model is shared across iOS, macOS, and visionOS, and system model updates will occur as part of those platforms’ OS updates (though not every OS update will have a model update).”2
La implicación operacional: los equipos de apps que distribuyen adaptadores se suscriben a un ciclo de vida de actualización del modelo que corre a la cadencia de Apple. Cada salto del modelo base es una función forzosa para reentrenar, reevaluar y republicar.
Empaquetado como Asset Packs
El archivo del adaptador es lo bastante grande como para que empaquetarlo dentro de la app esté explícitamente desaconsejado. Apple enruta la entrega del adaptador a través de Background Assets.3
El kit produce paquetes .fmadapter, que el propio kit también empaqueta como asset packs de Background Assets. La herramienta de línea de comandos ba-package de Xcode 16 o posterior hace el trabajo de empaquetado; el kit la invoca.3
Opciones de hosting:3
- Apple-Hosted, Managed. Apple aloja el asset; el SDK gestiona el ciclo de vida de la descarga.
- Self-Hosted, Managed. Tú alojas en tu servidor; el SDK gestiona el ciclo de vida de la descarga.
- Self-Hosted, Unmanaged. Tú alojas y gestionas el ciclo de vida por tu cuenta.
Las claves requeridas en Info.plist varían según la opción de hosting:3 Apple-Hosted Managed necesita BAHasManagedAssetPacks, BAAppGroupID y BAUsesAppleHosting; Self-Hosted Managed necesita las dos primeras; Self-Hosted Unmanaged no necesita ninguna. Cada vía también tiene un target de extensión asset-downloader que Xcode genera.
Elegir el Adaptador Correcto en Tiempo de Ejecución
Cuando se genera el BackgroundDownloadHandler.swift de la extensión asset-downloader, Xcode conecta un callback shouldDownload(_:). El cuerpo de ejemplo de Apple para assets de adaptador:3
func shouldDownload(_ assetPack: AssetPack) -> Bool {
if assetPack.id.hasPrefix("mygameshader") {
return true
}
return SystemLanguageModel.Adapter.isCompatible(assetPack)
}
SystemLanguageModel.Adapter.isCompatible(_:) devuelve true para los asset packs cuyo adaptador coincide con el modelo del sistema actual. El mismo callback también puede dejar pasar otros assets que tu app necesite que no sean adaptadores.
Cargar y Rastrear Descargas
Una vez que el asset está en el dispositivo, la ruta de carga es:3
SystemLanguageModel.Adapter.removeObsoleteAdapters()
let adapter = try SystemLanguageModel.Adapter(name: "myAdapter")
La construcción dispara una descarga si el dispositivo no tiene un adaptador compatible cacheado. La nota de Apple sobre UX: “Because adapters can have a large data size they can take some time to download, especially if a person is on Wi-Fi or a cell network. If a person doesn’t have a network connection, they aren’t able to use your adapter right away.”3
La secuencia de estado proviene de AssetPackManager:3
let assetpackIDList = SystemLanguageModel.Adapter.compatibleAdapterIdentifiers(name: name)
if let assetPackID = assetpackIDList.first {
let statusUpdates = AssetPackManager.shared.statusUpdates(forAssetPackWithID: assetPackID)
for await status in statusUpdates {
switch status {
case .began(let assetPack): ...
case .paused(let assetPack): ...
case .downloading(let assetPack, let progress): ...
case .finished(let assetPack): ...
case .failed(let assetPack, let error): ...
@unknown default: ...
}
}
}
Los cinco casos documentados de DownloadStatusUpdate: .began, .paused, .downloading, .finished, .failed.3 La rama @unknown default del framework es obligatoria porque Apple puede añadir casos en futuras versiones de SDK.
Después de que el estado llegue a .finished, el adaptador está listo para enchufarse a una sesión:
let adaptedModel = SystemLanguageModel(adapter: adapter)
let session = LanguageModelSession(model: adaptedModel)
El Draft Model y su Límite de Tasa
Si tu adaptador se distribuyó con un draft model, llamar a adapter.compile() lo prepara para su uso. La documentación de Apple lo señala como un paso aparte y costoso computacionalmente:3
“The first time a device downloads a new version of your adapter, a call to
compile()fully compiles your draft model and saves it to the device. During subsequent launches of your app, a call tocompile()checks for a saved compiled draft model and returns it immediately if it exists.”
Hay un límite de tasa publicado:3
“Rate limiting protects device resources that are shared between all apps and processes. If the framework determines that a new compilation is necessary, it rate-limits the compilation process on all platforms, excluding macOS, to three draft model compilations per-app, per-day.”
La exclusión de macOS es interesante; el límite se aplica en iOS, iPadOS y visionOS. Apple recomienda ejecutar la compilación dentro de una tarea programada con Background Tasks para que el trabajo no bloquee el lanzamiento de la app.3
Una trampa de desarrollo: “The full compilation process runs every time you launch your app through Xcode because Xcode assigns your app a new UUID for every launch. If you receive a rate-limiting error while testing your app, stop your app in Xcode and re-launch it to reset the rate counter.”3
Pruebas y la Restricción del Simulador
Probar adaptadores requiere un dispositivo físico. Apple es explícito: “Testing adapters requires a physical device and isn’t supported on Simulator.”3
Para pruebas locales en Xcode, inicializas desde una URL de archivo en lugar de un nombre:3
let localURL = URL(filePath: "absolute/path/to/my_adapter.fmadapter")
let adapter = try SystemLanguageModel.Adapter(fileURL: localURL)
El patrón de Apple: importa el archivo local para pruebas, luego retíralo del proyecto antes de publicar la app, ya que los archivos de adaptador son demasiado grandes para empaquetarlos.3
Lo que Esta Vía te Cuesta en Términos Operacionales
Juntando el ciclo de vida, una app que distribuye un adaptador personalizado se compromete a:
- Infraestructura de entrenamiento Python. Una Mac con Apple silicon y al menos 32 GB de memoria, o una máquina GPU con Linux.2
- Una cadencia de reentrenamiento al ritmo de Apple. Cada actualización del modelo del sistema implica un adaptador nuevo y una versión nueva del kit.3
- Una pila de servicio. O bien asset packs alojados por Apple a través de App Store Connect, o tu propio servidor corriendo una integración asset-downloader.3
- Adaptadores por versión en almacenamiento. Múltiples versiones del modelo base en circulación significa múltiples adaptadores alojados, con el dispositivo descargando el que coincida.3
- Una puerta de entitlement. El Account Holder de tu membresía del Apple Developer Program lo solicita; no puedes publicar sin aprobación.2
- El impuesto de 160 MB por versión de adaptador. No en el bundle de la app, sino en el dispositivo del usuario tras la descarga.2
- Pruebas en dispositivo físico. El Simulador no ejecuta adaptadores.3
Ese es el coste operacional en su forma más llana. El beneficio, cuando las señales de adopción se cumplen, es: el modelo en el dispositivo se especializa para tu tarea con prompting mínimo, menor latencia, sin coste de API por llamada y plena localidad de datos. Para apps que ya pagan por inferencia ajustada en servidor y quieren paridad en el dispositivo, ese es el trato en términos llanos.
Conclusiones
- Los adaptadores son LoRA, según la técnica documentada por Apple. Pesos base congelados, pequeñas matrices entrenables a través de la red del modelo, solo los pesos del adaptador se actualizan durante el entrenamiento.2
- Un adaptador por versión del modelo del sistema, sin excepciones. Planifica el reentrenamiento ligado a las releases del SDK.3
- El flujo
.fmadapteres de extremo a extremo. Entrena en Python, empaqueta con el kit, aloja como asset pack de Background Assets, carga por nombre en tu app, compila el draft model en una tarea en segundo plano. - La propia Apple recomienda que la mayoría de las apps no tomen esta vía. Dos páginas distintas de la documentación de Apple lo desaconsejan para la mayoría de los casos de uso.1 Lee el criterio en la página del kit; la respuesta por defecto es “no”.
- Prueba en hardware. El Simulador no ejecuta adaptadores. Diseña el plan de pruebas en torno a sesiones en dispositivo físico y la franja de compilación del framework
Background Tasks.
El clúster completo del Ecosistema Apple: el criterio de decisión para la especialización integrada; el protocolo Tool en el corazón del framework; la división de workflow agéntico entre LLMs in-app y de tooling; App Intents vs MCP para la cuestión más amplia de enrutamiento. El hub está en la Serie del Ecosistema Apple. Para un contexto más amplio de iOS-con-agentes-de-IA, consulta la guía de iOS Agent Development.
FAQ
¿Cómo sé si mi app realmente necesita un adaptador personalizado?
Lee la sección “When to consider an adapter” de Apple en la guía del kit.2 Las señales: un LLM ajustado en servidor existente que quieres reflejar en el dispositivo, un techo documentado de prompt engineering en precisión, un requisito estricto de adherencia a un estilo/formato/política específicos, o un objetivo de latencia que el prompt engineering por sí solo no alcanza. La mayoría de las apps no cumple ninguno de estos y Apple lo dice.
¿Qué controla en realidad el entitlement?
El despliegue, no el entrenamiento. La documentación de Apple lo dice explícitamente: “You don’t need this entitlement to train or locally test adapters.”3 El Account Holder de tu membresía del Apple Developer Program solicita com.apple.developer.foundation-model-adapter desde la página de Foundation Models Framework Adapter Entitlement, y el entitlement abre la vía para publicar en la App Store.2
¿Qué tan grandes son los adaptadores y dónde residen?
La cifra documentada por Apple: “Each adapter will take approximately 160 MB of storage space in your app.”2 Los adaptadores no residen en el bundle de la app. Apple los enruta a través de Background Assets, alojados en servidores de Apple (managed) o en tu propio servidor (managed o unmanaged), y el dispositivo descarga la versión que coincide con su modelo del sistema actual.3
¿Qué pasa cuando Apple actualiza el modelo base?
Reentrenas. Según los docs: “Each adapter is compatible with a single specific system model version. You must train a new adapter for every new base model version. A runtime error occurs if your app runs on a person’s device without a compatible adapter.”3 En la práctica, tu pila de servicio aloja múltiples versiones de adaptador concurrentemente y el dispositivo descarga la correcta basándose en isCompatible(_:).
¿Cuál es la relación entre los adaptadores y .contentTagging?
.contentTagging es una de las dos propiedades estáticas de SystemLanguageModel.UseCase que Apple ofrece, cubierta en el post compañero. Es una especialización gestionada por Apple, sin entitlement, incluida con el framework. Un Adapter personalizado es el equivalente gestionado por el desarrollador para tareas que los casos de uso de Apple no cubren. Los docs de Apple usan la palabra “adapted” al describir cómo funciona .contentTagging internamente, pero eso no es lo mismo que el tipo formal Adapter. El tipo formal es el que cubre este post.
¿Tengo que entrenar un draft model?
No. Los docs de Apple: “If you choose not to train the draft model, speculative decoding will not be available for your adapter’s use case.”2 El adaptador igual carga y sirve; simplemente no obtienes la aceleración de inferencia por speculative decoding. El draft model es un segundo paso de entrenamiento opcional.
Referencias
-
Apple Developer, “SystemLanguageModel.Adapter”. Descripción del tipo, recomendación contra su uso para la mayoría de las apps, “Use custom adapters only if you’re comfortable training foundation models in Python.” Recuperado el 2026-05-04. ↩↩↩↩
-
Apple Developer, “Get started with Foundation Models adapter training”. Visión general del kit, mecanismo LoRA, requisitos de hardware, forma del dataset, CLI de entrenamiento, opción de draft model, tabla de versiones del kit, flujo de solicitud del entitlement. Recuperado el 2026-05-04. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩
-
Apple Developer, “Loading and using a custom adapter with Foundation Models”. Hosting de asset packs, claves de Info.plist, ejemplo de
shouldDownload(_:), secuencia de estado, límite de tasa, exclusión del Simulador, restricción de compatibilidad de versión única. Recuperado el 2026-05-04. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer, “SystemLanguageModel.Adapter”. Superficie API del struct:
init(fileURL:),init(name:),compile(),creatorDefinedMetadata,removeObsoleteAdapters(),compatibleAdapterIdentifiers(name:),AssetError. Recuperado el 2026-05-04. ↩↩↩ -
Hu et al., “LoRA: Low-Rank Adaptation of Large Language Models”, arXiv:2106.09685. El paper original de LoRA al que hace referencia la técnica que Apple usa. ↩
-
Apple Developer, “SystemLanguageModel”. El inicializador de conveniencia
init(adapter:guardrails:)y la descripción del entitlementcom.apple.developer.foundation-model-adapter. Recuperado el 2026-05-04. ↩↩ -
Leviathan et al., “Fast Inference from Transformers via Speculative Decoding”, arXiv:2211.17192, y Chen et al., “Accelerating Large Language Model Decoding with Speculative Sampling”, arXiv:2302.01318. La técnica de speculative decoding que el draft model de Apple usa, citada directamente por la guía de entrenamiento de adaptadores. ↩