Bevezetés: Az on-device AI új korszaka
Képzeld el, hogy az alkalmazásod természetes nyelven válaszol a felhasználóknak, strukturált adatokat generál egyetlen Swift hívással, és mindezt teljesen az eszközön teszi — internet nélkül, ingyen. Tudom, kicsit úgy hangzik, mint egy álom. De ez a valóság 2026-ban az Apple Foundation Models keretrendszerével.
A WWDC 2025-ön bemutatott Foundation Models az iOS 26, iPadOS 26, macOS Tahoe 26 és visionOS 26 egyik legnagyobb újítása. Gyakorlatilag az Apple Intelligence-t hajtó, körülbelül 3 milliárd paraméteres nagy nyelvi modellhez (LLM) kapsz közvetlen hozzáférést a Swift kódodból. Nem kell API kulcs. Nem kell felhőköltség. Nem kell internet.
Ebben az útmutatóban végigmegyünk a keretrendszer minden fontos részén: az alapvető szöveggenerálástól a strukturált kimeneten (@Generable) és a streaming válaszokon át egészen a tool calling funkcióig. Minden szekcióhoz adok működő kódpéldákat, szóval azonnal elkezdhetsz kísérletezni.
Rendszerkövetelmények és eszköztámogatás
Mielőtt belevágnánk a kódolásba, tisztázzuk: a Foundation Models keretrendszer sajnos nem minden Apple eszközön érhető el. Íme a pontos követelmények:
- Operációs rendszer: iOS 26+, iPadOS 26+, macOS Tahoe 26+, vagy visionOS 26+
- Processzor: iPhone-on A17 Pro vagy újabb (tehát iPhone 15 Pro és felfelé), Mac-en M1 chip vagy újabb
- Apple Intelligence: A felhasználónak engedélyeznie kell az Apple Intelligence funkciót a Beállításokban
- Fejlesztői környezet: Xcode 26, macOS Tahoe
Nagyon fontos: A Foundation Models keretrendszer nem működik az iOS Simulátorban — fizikai eszköz kell a teszteléshez. Ezen nem egyszer elakadtam magam is a fejlesztés során. Viszont az Xcode Playgrounds kiválóan alkalmas a promptok gyors kipróbálására, szóval nem kell minden apró változtatáshoz újra buildelni az egész appot.
Modell elérhetőség ellenőrzése kódból
Mivel nem minden felhasználó eszköze támogatja a keretrendszert, mindig ellenőrizd az elérhetőséget. Komolyan, ne hagyd ki ezt a lépést — különben a felhasználóid egy része rejtélyes hibákat fog kapni:
import FoundationModels
func checkModelAvailability() {
let availability = SystemLanguageModel.default.availability
switch availability {
case .available:
print("A modell elérhető és használatra kész.")
case .unavailable(.deviceNotEligible):
print("Ez az eszköz nem támogatja a Foundation Models-t.")
case .unavailable(.appleIntelligenceNotEnabled):
print("Az Apple Intelligence nincs engedélyezve a Beállításokban.")
case .unavailable(.modelNotReady):
print("A modell még töltődik, próbáld újra később.")
default:
print("A modell jelenleg nem érhető el.")
}
}
Alapvető szöveggenerálás: Az első lépések
Na, térjünk a lényegre. A Foundation Models keretrendszer belépési pontja a LanguageModelSession osztály. Ezzel az objektummal kommunikálsz az on-device nyelvi modellel.
Lássuk a legalapvetőbb használatot:
import FoundationModels
func generateSimpleResponse() async throws -> String {
let session = LanguageModelSession()
let response = try await session.respond(to: "Adj 3 tippet a Swift kód optimalizálásához.")
return response.content
}
Ennyi. Két sor érdemi kód, és máris kommunikálsz egy nyelvi modellel. De a valódi erő az instrukciókban rejlik — ezekkel szabályozhatod, hogyan viselkedjen a modell:
import FoundationModels
func generateWithInstructions() async throws -> String {
let session = LanguageModelSession(
instructions: """
Te egy tapasztalt iOS fejlesztő asszisztens vagy.
Válaszolj tömören, gyakorlati kódpéldákkal.
Mindig Swift 6 szintaxist használj.
"""
)
let response = try await session.respond(
to: "Hogyan kezeljek hálózati hibákat async/await-tel?"
)
return response.content
}
Kontextus megőrzése több üzenet során
A LanguageModelSession automatikusan megőrzi a beszélgetés kontextusát, ami azt jelenti, hogy egymás után több kérést is küldhetsz, és a modell „emlékszik" a korábbiakra. Ez nagyon kényelmes chatbot-szerű felületeknél:
import FoundationModels
func multiTurnConversation() async throws {
let session = LanguageModelSession(
instructions: "Te egy Swift programozási tutor vagy."
)
// Első kérdés
let response1 = try await session.respond(
to: "Mi az a protocol Swift-ben?"
)
print(response1.content)
// Követő kérdés – a modell emlékszik az előzőre
let response2 = try await session.respond(
to: "Mutass egy gyakorlati példát rá!"
)
print(response2.content)
}
Egy dolgot viszont tartsd észben: az on-device modell 4096 token kontextusablakkal rendelkezik (bemenet és kimenet együtt). Ez elég egy átlagos beszélgetéshez, de hosszabb kontextus esetén hamar betelik. Ilyenkor érdemes új session-t indítani.
Strukturált kimenet a @Generable makróval
Oké, az egyszerű szöveggenerálás hasznos, de őszintén szólva a Foundation Models keretrendszer igazi ereje a strukturált kimenetben rejlik. A @Generable makró segítségével közvetlenül Swift típusokba generálhatsz adatokat — nincs szükség JSON parszolásra, nincs string manipuláció, nincs kézi validáció.
Ez volt az a funkció, ami engem a legjobban lenyűgözött.
Hogyan működik?
Amikor a @Generable makrót alkalmazod egy típusra, a fordító a háttérben:
- Létrehoz egy JSON sémát a típus alapján
- A modellt arra kényszeríti, hogy érvényes kimenetet produkáljon (constrained decoding)
- Az eredményt automatikusan dekódolja a Swift típusodba
import FoundationModels
@Generable
struct ReceptAjanlat {
var nev: String
var hozzavalok: [String]
@Guide(description: "Elkészítési idő percben, 5 és 180 között")
var elkeszitesiIdoPerc: Int
@Guide(description: "Nehézségi szint: könnyű, közepes vagy haladó")
var nehezseg: String
var lepiések: [String]
}
func generateRecipe() async throws -> ReceptAjanlat {
let session = LanguageModelSession()
let response = try await session.respond(
to: "Ajánlj egy egyszerű magyar levesreceptet.",
generating: ReceptAjanlat.self
)
return response.content
}
Mezők finomhangolása a @Guide makróval
A @Guide makróval pontosabban irányíthatod az egyes mezők generálását. Leírásokat, értékkészlet-korlátozásokat, tömbméret-megszorításokat és akár regex mintákat is megadhatsz:
import FoundationModels
@Generable
struct AppOtlet {
@Guide(description: "Az alkalmazás neve, maximum 3 szó")
var nev: String
@Guide(.anyOf(["Produktivitás", "Egészség", "Szórakozás", "Oktatás", "Pénzügy"]))
var kategoria: String
@Guide(description: "Rövid, egy mondatos leírás")
var leiras: String
@Guide(description: "3 fő funkció felsorolása", .count(3))
var foFunkciok: [String]
@Guide(description: "Becsült fejlesztési idő hetekben, 1-52 között")
var fejlesztesiIdoHet: Int
}
Gyors összefoglaló a @Guide lehetőségeiről:
description:— szöveges útmutatás a modellnek az adott mezőről.anyOf([...])— a megengedett értékek listája (lényegében enum-szerű korlátozás).count(n)— a tömb pontos elemszáma- Regex minta — string formátum korlátozása reguláris kifejezéssel
Egymásba ágyazott Generable típusok
A strukturált kimenet támogatja az egymásba ágyazott @Generable típusokat is. Ez azt jelenti, hogy összetettebb adatstruktúrákat is generálhatsz — mondjuk egy könyvet a szerzőjével együtt:
import FoundationModels
@Generable
struct Szerzo {
var nev: String
var nemzetiseg: String
}
@Generable
struct KonyvAjanlat {
var cim: String
var szerzo: Szerzo
@Guide(description: "Rövid, 2-3 mondatos összefoglalás")
var osszefoglalas: String
@Guide(.anyOf(["Regény", "Szakkönyv", "Sci-fi", "Krimi", "Önfejlesztés"]))
var mufaj: String
}
func generateBookRecommendation() async throws -> KonyvAjanlat {
let session = LanguageModelSession(
instructions: "Magyar nyelvű könyvajánlóként működj."
)
return try await session.respond(
to: "Ajánlj egy jó sci-fi könyvet.",
generating: KonyvAjanlat.self
).content
}
Streaming válaszok: Valós idejű felhasználói élmény
A teljes válasz bevárása helyett a streaming segítségével valós időben jelenítheted meg a generált tartalmat, ahogy az készül. Ez nagyon sokat dob a felhasználói élményen — az ember azonnal látja, hogy valami történik, nem csak egy üres képernyőt bámul.
Egyszerű szöveges streaming
import FoundationModels
import SwiftUI
struct StreamingTextView: View {
@State private var displayedText = ""
@State private var isGenerating = false
var body: some View {
VStack(spacing: 16) {
ScrollView {
Text(displayedText)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
Button(isGenerating ? "Generálás folyamatban..." : "Generálás indítása") {
Task { await generateStreamingText() }
}
.disabled(isGenerating)
}
.padding()
}
func generateStreamingText() async {
isGenerating = true
displayedText = ""
let session = LanguageModelSession()
do {
let stream = session.streamResponse(
to: "Írj egy rövid összefoglalót a Swift nyelv történetéről."
)
for try await partialResponse in stream {
displayedText = partialResponse.content
}
} catch {
displayedText = "Hiba történt: \(error.localizedDescription)"
}
isGenerating = false
}
}
Strukturált streaming: A PartiallyGenerated típus
Ez az egyik kedvenc funkcióm. A @Generable makró automatikusan létrehoz egy PartiallyGenerated típust is, amelyben minden mező opcionális. Ennek köszönhetően a strukturált kimenet mezőit egyenként jelenítheted meg, ahogy azok elkészülnek:
import FoundationModels
import SwiftUI
@Generable
struct UticelAjanlat {
@Guide(description: "A város neve")
var varosNev: String
@Guide(description: "Az ország neve")
var orszag: String
@Guide(description: "Miért érdemes meglátogatni, 2-3 mondat")
var leiras: String
@Guide(description: "3 kötelező látnivaló", .count(3))
var latvnivalok: [String]
@Guide(description: "Legjobb időszak a látogatásra")
var legjobbIdoszak: String
}
struct TravelRecommendationView: View {
@State private var partial: UticelAjanlat.PartiallyGenerated?
@State private var isLoading = false
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
if let varos = partial?.varosNev {
Text(varos)
.font(.largeTitle.bold())
.transition(.opacity)
}
if let orszag = partial?.orszag {
Text(orszag)
.font(.title2)
.foregroundStyle(.secondary)
}
if let leiras = partial?.leiras {
Text(leiras)
.font(.body)
.padding(.top, 4)
}
if let latvnivalok = partial?.latvnivalok, !latvnivalok.isEmpty {
Text("Látnivalók")
.font(.headline)
.padding(.top, 8)
ForEach(latvnivalok, id: \.self) { hely in
Label(hely, systemImage: "mappin.circle.fill")
}
}
if let idoszak = partial?.legjobbIdoszak {
Label(idoszak, systemImage: "calendar")
.font(.subheadline)
.padding(.top, 4)
}
}
.padding()
.animation(.easeInOut, value: partial?.varosNev)
}
.task { await streamRecommendation() }
}
func streamRecommendation() async {
isLoading = true
let session = LanguageModelSession()
do {
let stream = session.streamResponse(
to: "Ajánlj egy kevésbé ismert európai úticélt.",
generating: UticelAjanlat.self
)
for try await partialResult in stream {
partial = partialResult.content
}
} catch {
print("Streaming hiba: \(error)")
}
isLoading = false
}
}
A strukturált streaming különösen hasznos, mert a felhasználó már olvashatja az első mezőket (mondjuk a város nevét), miközben a modell még dolgozik a többin. Dinamikusabb érzést ad az egész alkalmazásnak — és őszintén, sokkal profibb is, mintha csak egy spinner pörögne.
Tool calling: A modell kibővítése egyéni funkciókkal
Az on-device modell nyilván nem tud mindent. Nem éri el az internetet, nem ismeri a valós idejű adatokat, és nincs hozzáférése az alkalmazásod saját adataihoz. A tool calling pont erre ad megoldást: lehetővé teszi, hogy a modell meghívja az alkalmazásod Swift függvényeit, amikor külső adatra van szüksége.
Ez az a pont, ahol az on-device AI igazán praktikussá válik.
Hogyan működik a tool calling?
A folyamat négy lépésből áll:
- Definiálsz egy Tool-t, amely leírja, mit tud csinálni és milyen paramétereket vár
- A modell a prompt alapján eldönti, hogy szüksége van-e az eszközre
- Ha igen, meghívja a megfelelő eszközt a megfelelő paraméterekkel
- Az eszköz visszaadja az eredményt, a modell pedig beépíti a válaszába
Lássuk kódban:
import FoundationModels
// 1. Definiáljuk az eszközt
struct IdojarasLekerdezo: Tool {
let name = "idojaras_lekerdezese"
let description = "Lekérdezi az aktuális időjárást egy adott városban."
// A paraméterek, amelyeket a modell kitölt
@Generable
struct Arguments {
@Guide(description: "A város neve, amelynek időjárását le kell kérdezni")
var varos: String
}
// Az eszköz végrehajtása
func call(arguments: Arguments) async throws -> String {
// Valós alkalmazásban itt API hívás lenne
let idojarasAdatok: [String: String] = [
"Budapest": "Napos, 22°C",
"Debrecen": "Felhős, 19°C",
"Szeged": "Napos, 24°C",
"Pécs": "Részben felhős, 21°C"
]
if let adat = idojarasAdatok[arguments.varos] {
return "Az időjárás \(arguments.varos) városában: \(adat)"
} else {
return "Nincs elérhető időjárás-adat \(arguments.varos) városához."
}
}
}
// 2. Használjuk az eszközt egy session-ben
func weatherQuery() async throws -> String {
let session = LanguageModelSession(
instructions: "Segíts a felhasználónak az időjárással kapcsolatos kérdésekben.",
tools: [IdojarasLekerdezo()]
)
let response = try await session.respond(
to: "Milyen idő van most Budapesten?"
)
return response.content
}
Tool calling valós alkalmazásban: HealthKit integráció
A tool calling igazi ereje a saját alkalmazásod adatainak elérésében rejlik. Nézzünk egy gyakorlati példát, amelyben a modell hozzáfér a felhasználó edzésadataihoz:
import FoundationModels
struct EdzesAdatLekerdezo: Tool {
let name = "edzes_adat_lekerdezese"
let description = "Lekérdezi a felhasználó legutóbbi edzésadatait az elmúlt héten."
@Generable
struct Arguments {
@Guide(description: "Az edzés típusa: futás, kerékpár, úszás, vagy mind")
var edzesTipus: String
}
func call(arguments: Arguments) async throws -> String {
// Valós alkalmazásban itt HealthKit lekérdezés lenne
return """
Elmúlt 7 nap edzésadatai (\(arguments.edzesTipus)):
- Hétfő: 5.2 km futás, 28 perc
- Szerda: 3.8 km futás, 22 perc
- Péntek: 7.1 km futás, 39 perc
Összesen: 16.1 km, 89 perc
Átlagos tempó: 5:32 perc/km
"""
}
}
struct EdzesOsszegzoGeneralo {
func generateSummary() async throws -> String {
let session = LanguageModelSession(
instructions: """
Te egy személyi edző asszisztens vagy.
Elemezd a felhasználó edzésadatait és adj személyre szabott tanácsot.
Legyél motiváló, de realista.
""",
tools: [EdzesAdatLekerdezo()]
)
let response = try await session.respond(
to: "Hogyan alakult az edzésem az elmúlt héten? Adj tanácsot a fejlődéshez!"
)
return response.content
}
}
Teljesítmény-optimalizálás és best practices
Az on-device AI természetesen erőforrásokat igényel. Az alábbiakban összeszedtem a legfontosabb tanácsokat, amiket érdemes betartani a hatékony használathoz.
Modell előtöltése (prewarm)
Ha tudod, hogy hamarosan szükség lesz a modellre, az előtöltéssel csökkentheted az első válasz késleltetését. Ez különösen hasznos, ha mondjuk egy chat képernyőre navigál a felhasználó:
import FoundationModels
class AIService {
private var session: LanguageModelSession?
func prewarm() async {
session = LanguageModelSession(
instructions: "Te egy alkalmazás-asszisztens vagy."
)
// Előtölti a modellt a memóriába
try? await SystemLanguageModel.default.prewarm(
promptPrefix: "Segíts a felhasználónak"
)
}
func respond(to prompt: String) async throws -> String {
guard let session else {
throw AIError.sessionNotInitialized
}
return try await session.respond(to: prompt).content
}
}
enum AIError: Error {
case sessionNotInitialized
}
Hibakezelés
Az on-device AI használata során különféle hibák merülhetnek fel — és higgyétek el, fel is merülnek. Érdemes felkészülni rájuk:
import FoundationModels
func safeGeneration(prompt: String) async -> String {
let session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
return response.content
} catch let error as LanguageModelSession.GenerationError {
switch error {
case .guardrailViolation:
return "A kérés nem teljesíthető biztonsági okokból."
case .contextWindowExceeded:
return "A kérés túl hosszú. Próbálj rövidebb promptot."
default:
return "Generálási hiba: \(error.localizedDescription)"
}
} catch {
return "Váratlan hiba: \(error.localizedDescription)"
}
}
Fontos best practices
- Mindig ellenőrizd az elérhetőséget — Használd a
SystemLanguageModel.availability-t és biztosíts fallback élményt. Ez nem opcionális, hanem kötelező. - Tartsd rövidre a promptokat — A 4096 tokenes kontextusablak gyorsan betelik, ne pazarold felesleges szöveggel
- Használj instrukciókat — A
LanguageModelSessioninicializálásakor megadott instrukciók drámaian javítják a válaszok minőségét - Preferáld a strukturált kimenetet — A
@Generabletípusokkal megbízhatóbb és kiszámíthatóbb eredményeket kapsz, mint nyers szöveggel - Kezeld a guardrail hibákat — A modell biztonsági korlátai néha hamis pozitívokat produkálnak, légy felkészülve erre
- Ne használd kódgenerálásra vagy matematikára — Az Apple kifejezetten nem javasolja ezekre a feladatokra az on-device modellt (és tapasztalatból mondom, van is miért)
- Tesztelj Xcode Playground-ban — Ez a leggyorsabb módja a promptok kipróbálásának
A Foundation Models korlátai: Mire ne számíts
Fontos reálisan látni, mire képes és mire nem az on-device modell. Nem akarom eltúlozni a képességeit — íme a legfontosabb korlátozások:
- Kontextusablak: 4096 token (bemenet + kimenet együtt). Egy átlagos interakcióhoz ez bőven elég, de hosszú dokumentumok feldolgozására nem alkalmas
- Csak szöveges bemenet és kimenet: Képek, hangok, PDF-ek nem támogatottak — kizárólag szöveggel dolgozik
- Korlátozott tudás: A ~3B paraméteres modell jóval kevesebbet „tud", mint a felhőalapú LLM-ek. Hallucinálhat, és logikai hibákat is véthet
- Nem finomhangolható: A modellt nem lehet újratanítani — instrukciókkal, few-shot példákkal és tool calling-gal kell irányítani
- Nyelvtámogatás: Angolul teljesít a legjobban, más nyelveken (így magyarul is) a minőség változó
- Tartalmi guardrail-ek: A modell automatikusan szűri az érzékeny tartalmakat, ami időnként bosszantó hamis pozitívokat eredményezhet
Mindezek ellenére a Foundation Models keretrendszer kiválóan alkalmas tartalomgenerálásra, osztályozásra, összegzésre, keresési javaslatok készítésére és — tool calling-on keresztül — alkalmazás-specifikus feladatokra is. Különösen akkor éri meg használni, ha fontos a magánélet védelme és az offline működés.
Gyakran ismételt kérdések
Működik-e a Foundation Models keretrendszer internet nélkül?
Igen, teljes mértékben. Az on-device modell az eszközön fut, internetkapcsolat nélkül. Használhatod repülőgépen, metróban, vagy akár a Bakonyban, ahol nincs térerő. Az adataid soha nem hagyják el az eszközt, ami teljes adatvédelmet biztosít.
Mennyibe kerül a Foundation Models API használata fejlesztőknek?
Semennyibe. A Foundation Models keretrendszer teljesen ingyenes — nincs API díj, nincs token-alapú számlázás, nincs felhőköltség. A modell az Apple Silicon chipen fut az Apple Intelligence részeként. Az egyetlen „költség" az Apple Developer Program tagdíja (99 USD/év), ami amúgy is szükséges iOS alkalmazások publikálásához.
Helyettesítheti-e a felhőalapú AI szolgáltatásokat?
Nem teljesen. A ~3B paraméteres on-device modell lényegesen kisebb, mint a felhőalapú társai (OpenAI, Claude stb.). Kódgenerálásra, komplex logikai feladatokra vagy nagy tudásbázist igénylő kérdésekre továbbra is a felhőalapú megoldások az ajánlottak. Viszont tartalomgenerálás, osztályozás, összegzés és alkalmazás-specifikus feladatok terén kiváló alternatíva — különösen ha fontos az adatvédelem és az offline működés.
Milyen eszközökön fut?
iPhone 15 Pro és újabb (A17 Pro chip), iPad M1 chippel és újabb, Mac M1 chippel és újabb, valamint Apple Vision Pro. Az eszközön iOS 26+, iPadOS 26+, macOS Tahoe 26+ vagy visionOS 26+ operációs rendszernek kell futnia, és az Apple Intelligence-nek engedélyezve kell lennie.
Hogyan tesztelhetem a kódomat fejlesztés közben?
A leghatékonyabb módszer az Xcode Playground használata. A #Playground makróval közvetlenül Swift fájlokban tesztelheted a promptjaidat anélkül, hogy az egész alkalmazást újra kellene buildelned. Az Instruments alkalmazásban emellett új profiling sablon is elérhető a modellkérések vizsgálatához. És ne feledd: az iOS Simulator nem támogatja a keretrendszert, fizikai eszköz kell hozzá.