Το Foundation Models Framework στο iOS 26: Οδηγός On-Device AI με Swift

Πλήρης οδηγός για το Foundation Models framework στο iOS 26. Δείτε πώς να χρησιμοποιήσετε on-device AI με Swift, guided generation, custom tools, streaming και SwiftUI ενσωμάτωση.

Εισαγωγή: Η Νέα Εποχή της Τεχνητής Νοημοσύνης On-Device

Στο WWDC 2025, η Apple αποκάλυψε ένα από τα πιο σημαντικά frameworks των τελευταίων χρόνων: το Foundation Models framework. Ειλικρινά, όταν το είδα πρώτη φορά σε action, εντυπωσιάστηκα. Πρόσβαση σε ένα on-device LLM μέσα από ένα καθαρό Swift API — χωρίς servers, χωρίς κόστος, χωρίς internet.

Το framework τρέχει σε iOS 26, iPadOS 26 και macOS 26, σε κάθε συσκευή συμβατή με Apple Intelligence. Και το καλύτερο; Όλη η επεξεργασία γίνεται τοπικά στη συσκευή.

Σε αυτόν τον οδηγό θα εξερευνήσουμε τα πάντα: από τη δημιουργία sessions και τη βασική χρήση, μέχρι guided generation με τα macros @Generable και @Guide, custom tools, streaming, και ενσωμάτωση με SwiftUI. Θα δούμε πρακτικά παραδείγματα κώδικα που μπορείτε να χρησιμοποιήσετε αμέσως στα projects σας.

Τι Είναι το Foundation Models Framework

Το Foundation Models framework σας δίνει πρόσβαση σε ένα γλωσσικό μοντέλο περίπου 3 δισεκατομμυρίων παραμέτρων, που τρέχει αποκλειστικά στη συσκευή. Το μοντέλο τα πάει αρκετά καλά σε μια σειρά εργασίες κειμένου:

  • Σύνοψη κειμένου
  • Εξαγωγή οντοτήτων (entity extraction)
  • Κατανόηση και επεξεργασία κειμένου
  • Σύντομοι διάλογοι
  • Δημιουργία περιεχομένου
  • Ταξινόμηση και ετικετοποίηση

Τα βασικά πλεονεκτήματα:

  • Ιδιωτικότητα: Κανένα δεδομένο δεν φεύγει από τη συσκευή — τίποτα δεν πηγαίνει σε servers
  • Offline λειτουργία: Δουλεύει χωρίς internet
  • Μηδενικό κόστος: Χωρίς χρεώσεις API ή όρια χρήσης
  • Native Swift: Σχεδιασμένο εξαρχής για τη Swift

Προαπαιτούμενα και Ρύθμιση Περιβάλλοντος

Πριν ξεκινήσετε, βεβαιωθείτε ότι έχετε τα εξής:

  • Xcode 26 ή νεότερο
  • macOS Tahoe (macOS 26) για ανάπτυξη
  • Συσκευή με Apple Silicon (iPhone 16+, iPad με M-series chip, Mac με Apple Silicon)
  • Το Apple Intelligence ενεργοποιημένο στις Ρυθμίσεις
  • Targets: iOS 26, iPadOS 26, ή macOS 26

Για να ξεκινήσετε, το μόνο που χρειάζεται είναι ένα import:

import FoundationModels

Ναι, τόσο απλά.

Πρόσβαση στο Μοντέλο: SystemLanguageModel

Η κλάση SystemLanguageModel είναι το κεντρικό σημείο πρόσβασης στο on-device μοντέλο. Υπάρχουν δύο βασικοί τρόποι:

import FoundationModels

// Βασικό μοντέλο γλώσσας (γενική δημιουργία κειμένου)
let generalModel = SystemLanguageModel.default

// Εξειδικευμένο για ετικετοποίηση περιεχομένου
let taggingModel = SystemLanguageModel(useCase: .contentTagging)

Το .default μοντέλο είναι κατάλληλο για γενικές εργασίες, ενώ το .contentTagging χρησιμοποιεί έναν εξειδικευμένο adapter βελτιστοποιημένο για ετικέτες, entities και ανίχνευση θεμάτων.

Έλεγχος Διαθεσιμότητας Μοντέλου

Αυτό είναι κάτι που πολλοί ξεχνάνε, αλλά είναι κρίσιμο. Πριν χρησιμοποιήσετε το μοντέλο, ελέγξτε αν είναι διαθέσιμο:

let model = SystemLanguageModel.default

switch model.availability {
case .available:
    // Το μοντέλο είναι έτοιμο για χρήση
    print("Το μοντέλο είναι διαθέσιμο!")
case .unavailable(let reason):
    switch reason {
    case .deviceNotEligible:
        print("Η συσκευή δεν υποστηρίζει αυτή τη λειτουργία.")
    case .appleIntelligenceNotEnabled:
        print("Ενεργοποιήστε το Apple Intelligence στις Ρυθμίσεις.")
    case .modelNotReady:
        print("Το μοντέλο δεν είναι ακόμα έτοιμο. Δοκιμάστε ξανά αργότερα.")
    @unknown default:
        print("Μη αναμενόμενη κατάσταση.")
    }
}

Σε production εφαρμογές, αυτός ο έλεγχος σας δίνει τη δυνατότητα να προσφέρετε fallback λύσεις για συσκευές που δεν υποστηρίζουν το μοντέλο.

Δημιουργία Session και Βασική Χρήση

Η LanguageModelSession είναι ο κύριος τρόπος αλληλεπίδρασης με το μοντέλο. Κάθε session κρατάει ιστορικό συνομιλίας, ώστε το μοντέλο να «θυμάται» τι έχει ειπωθεί πριν.

Βασική Δημιουργία Session

// Απλή δημιουργία session με default ρυθμίσεις
let session = LanguageModelSession()

// Αποστολή prompt και λήψη απάντησης
let response = try await session.respond(to: "Εξήγησε τι είναι το SwiftUI σε 2 προτάσεις.")
print(response.content)

Τρεις γραμμές κώδικα. Αυτό είναι όλο. Κυριολεκτικά μπορείτε να αρχίσετε να δημιουργείτε κείμενο AI σε λιγότερο χρόνο απ' όσο σας πήρε να διαβάσετε αυτή την παράγραφο.

Session με Instructions

Οι instructions λειτουργούν σαν system prompt — καθοδηγούν τη συμπεριφορά του μοντέλου σε ολόκληρη τη συνομιλία:

let session = LanguageModelSession {
    """
    Είσαι ένας ειδικός βοηθός προγραμματισμού Swift.
    Απάντα πάντα στα ελληνικά.
    Δίνε συνοπτικές και πρακτικές απαντήσεις
    με παραδείγματα κώδικα όπου χρειάζεται.
    """
}

let response = try await session.respond(
    to: "Πώς μπορώ να δημιουργήσω μια custom View στο SwiftUI;"
)
print(response.content)

Session με Πλήρη Παραμετροποίηση

let session = LanguageModelSession(
    model: .default,
    guardrails: .default,
    tools: [myCustomTool],
    instructions: {
        "Είσαι ένας φιλικός βοηθός τεχνικής υποστήριξης."
    }
)

Ρυθμίσεις Δημιουργίας: GenerationOptions

Θέλετε περισσότερο έλεγχο; Με το GenerationOptions μπορείτε να ρυθμίσετε πώς συμπεριφέρεται η δημιουργία κειμένου:

let options = GenerationOptions(
    sampling: .greedy,        // Ντετερμινιστική επιλογή
    temperature: 0.8,         // Βαθμός τυχαιότητας (0.0 - 1.0)
    maximumResponseTokens: 200 // Μέγιστο μήκος απάντησης
)

let answer = try await session.respond(
    to: "Γράψε μια σύντομη περιγραφή του protocol-oriented programming στη Swift",
    options: options
)

Μια σημαντική λεπτομέρεια: η παράμετρος temperature ελέγχει πόσο «δημιουργικές» είναι οι απαντήσεις. Χαμηλές τιμές (π.χ. 0.2) δίνουν πιο προβλέψιμα αποτελέσματα, ενώ υψηλότερες (π.χ. 0.8) παράγουν πιο ποικίλο κείμενο. Για structured data, κρατήστε τη χαμηλά.

Guided Generation: @Generable και @Guide

Λοιπόν, εδώ τα πράγματα γίνονται πραγματικά ενδιαφέροντα. Το guided generation είναι, κατά τη γνώμη μου, το πιο ισχυρό χαρακτηριστικό αυτού του framework.

Αντί να παίρνετε ελεύθερο κείμενο και να προσπαθείτε να το κάνετε parse (κάτι που ξέρουμε πόσο εύθραυστο μπορεί να είναι), ορίζετε ακριβώς τη δομή δεδομένων που θέλετε, χρησιμοποιώντας τα Swift macros @Generable και @Guide.

Η τεχνική ονομάζεται constrained decoding και εγγυάται δομική ορθότητα. Με απλά λόγια; Το μοντέλο θα παράγει πάντα δεδομένα που ταιριάζουν στο schema που ορίσατε. Πάντα.

Βασικό Παράδειγμα @Generable

import FoundationModels

@Generable
struct WeatherReport: Equatable {
    let temperature: Double
    let condition: String
    let humidity: Double
}

// Χρήση
let session = LanguageModelSession()
let response = try await session.respond(
    to: "Ποιος είναι ο καιρός σε μια τυπική ηλιόλουστη μέρα στην Αθήνα;",
    generating: WeatherReport.self
)

print("Θερμοκρασία: \(response.content.temperature)°C")
print("Συνθήκες: \(response.content.condition)")
print("Υγρασία: \(response.content.humidity)%")

Το @Generable macro λέει στον compiler να δημιουργήσει αυτόματα το schema και να αναλάβει τη μετατροπή από την έξοδο του μοντέλου σε Swift struct. Εσείς απλά δηλώνετε τον τύπο σας — τα υπόλοιπα τα αναλαμβάνει ο compiler.

Χρήση @Guide για Περιορισμούς

Χρειάζεστε πιο λεπτομερή έλεγχο σε κάθε πεδίο; Εκεί μπαίνει το @Guide:

@Generable
struct Movie {
    @Guide(description: "Ο τίτλος της ταινίας")
    let title: String

    @Guide(description: "Είδος ταινίας δράσης")
    let genre: String

    @Guide(.anyOf(["PG-13", "R", "PG", "G"]))
    let rating: String
}

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Πρότεινε μια ταινία δράσης",
    generating: Movie.self
)

print("Τίτλος: \(response.content.title)")
print("Είδος: \(response.content.genre)")
print("Αξιολόγηση: \(response.content.rating)")

Βλέπετε τον .anyOf περιορισμό; Εξασφαλίζει ότι το μοντέλο θα επιλέξει μόνο από τις τιμές που καθορίσατε. Τέλος στα σφάλματα ελεύθερου κειμένου — αυτό κι αν είναι game changer.

Ιεραρχικές Δομές με @Generable

Φυσικά, μπορείτε να ορίσετε και εμφωλευμένες δομές για πιο σύνθετα δεδομένα:

@Generable
struct Recipe {
    @Guide(description: "Το όνομα της συνταγής")
    let name: String
    @Guide(description: "Περιγραφή της συνταγής")
    let description: String
    @Guide(description: "Χρόνος προετοιμασίας σε λεπτά", .range(0...120))
    let prepTimeMinutes: Int
    @Guide(description: "Λίστα υλικών")
    let ingredients: [Ingredient]
}

@Generable
struct Ingredient {
    @Guide(description: "Όνομα υλικού")
    let name: String
    @Guide(description: "Ποσότητα")
    let quantity: Double
    @Guide(description: "Μονάδα μέτρησης")
    let unit: String
}

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Δώσε μου μια ελληνική συνταγή για μουσακά",
    generating: Recipe.self
)

print("Συνταγή: \(response.content.name)")
print("Χρόνος: \(response.content.prepTimeMinutes) λεπτά")
for ingredient in response.content.ingredients {
    print("- \(ingredient.quantity) \(ingredient.unit) \(ingredient.name)")
}

Προσέξτε τη χρήση .range(0...120) — διασφαλίζει ότι ο χρόνος προετοιμασίας θα είναι πάντα μέσα σε λογικά πλαίσια. Πολύ χρήσιμο για να αποφύγετε παράλογες τιμές.

Enums με @Generable

Τα enums δουλεύουν εξίσου καλά με το @Generable:

@Generable
enum Sentiment: String, Codable {
    case positive
    case negative
    case neutral
}

@Generable
struct ReviewAnalysis {
    @Guide(description: "Η γενική αίσθηση της κριτικής")
    let sentiment: Sentiment
    @Guide(description: "Σύντομη περίληψη σε μία πρόταση")
    let summary: String
    @Guide(description: "Βαθμολογία αξιοπιστίας (0.0 - 1.0)")
    let confidence: Double
}

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Ανάλυσε αυτή την κριτική: 'Εξαιρετική εφαρμογή, πολύ γρήγορη και εύχρηστη!'",
    generating: ReviewAnalysis.self
)

print("Συναίσθημα: \(response.content.sentiment)")
print("Περίληψη: \(response.content.summary)")
print("Αξιοπιστία: \(response.content.confidence)")

Streaming Απαντήσεων

Για μεγαλύτερες απαντήσεις, θα θέλετε σίγουρα streaming. Κανένας χρήστης δεν θέλει να κοιτάει ένα spinner για 10 δευτερόλεπτα — θέλει να βλέπει την απάντηση να εμφανίζεται σταδιακά.

Streaming Απλού Κειμένου

let session = LanguageModelSession()
let stream = session.streamResponse(
    to: "Γράψε ένα σύντομο tutorial για το SwiftUI Navigation"
)

for try await partialResponse in stream {
    print(partialResponse)
}

Streaming με Guided Generation

let session = LanguageModelSession()
let stream = session.streamResponse(
    to: "Πρότεινε μια συνταγή για παστίτσιο",
    generating: Recipe.self
)

for try await partial in stream {
    // partial είναι τύπου Recipe.PartiallyGenerated
    // Τα πεδία είναι optional μέχρι να ολοκληρωθεί η δημιουργία
    if let name = partial.name {
        print("Συνταγή: \(name)")
    }
    if let description = partial.description {
        print("Περιγραφή: \(description)")
    }
}

Κατά τη διάρκεια του streaming, παίρνετε ένα PartiallyGenerated αντικείμενο με optional πεδία. Καθώς το μοντέλο παράγει δεδομένα, τα πεδία γεμίζουν σταδιακά — κομψό, σωστά;

Δημιουργία Custom Tools

Τα Tools είναι ο τρόπος με τον οποίο το μοντέλο αλληλεπιδρά με τον «έξω κόσμο». Θέλετε να ψάξει σε μια βάση δεδομένων; Να καλέσει ένα API; Να κάνει κάτι συγκεκριμένο στην εφαρμογή σας; Εδώ μπαίνουν τα tools.

Δομή ενός Tool

Κάθε tool υλοποιεί το πρωτόκολλο Tool:

struct FindRestaurantsTool: Tool {
    let name = "findRestaurants"
    let description = "Βρίσκει κοντινά εστιατόρια βάσει αναζήτησης."

    @Generable
    struct Arguments {
        @Guide(description: "Ο τύπος εστιατορίου που αναζητείται")
        let query: String
        @Guide(description: "Μέγιστος αριθμός αποτελεσμάτων", .range(1...10))
        let maxResults: Int
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        // Εδώ θα κάνετε την πραγματική αναζήτηση
        // (π.χ. κλήση API, αναζήτηση σε βάση δεδομένων)
        let restaurants = [
            "Ταβέρνα ο Κώστας",
            "Ψαροταβέρνα Θαλασσινά",
            "Σουβλάκια ο Θανάσης"
        ]

        let results = Array(restaurants.prefix(arguments.maxResults))
        return ToolOutput(results.joined(separator: ", "))
    }
}

Εγγραφή Tool στο Session

let restaurantTool = FindRestaurantsTool()
let session = LanguageModelSession(tools: [restaurantTool]) {
    """
    Είσαι ένας βοηθός που βοηθάει τους χρήστες
    να βρίσκουν εστιατόρια κοντά τους.
    Χρησιμοποίησε το findRestaurants tool
    για κάθε αναζήτηση εστιατορίου.
    """
}

let response = try await session.respond(
    to: "Βρες μου ελληνικά εστιατόρια"
)
print(response.content)

Το μοντέλο αποφασίζει μόνο του πότε χρειάζεται εξωτερικά δεδομένα, καλεί αυτόματα το σωστό tool, και μετά χρησιμοποιεί τα αποτελέσματα στην απάντησή του. Αρκετά κομψό, πρέπει να παραδεχτώ.

Tool με HTTP Client

Ας δούμε ένα πιο ρεαλιστικό παράδειγμα, με κλήση σε εξωτερικό API:

struct WeatherTool: Tool {
    let name = "getWeather"
    let description = "Ανακτά τον τρέχοντα καιρό για μια πόλη."

    @Generable
    struct Arguments {
        @Guide(description: "Το όνομα της πόλης")
        let city: String
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        // Σε πραγματική εφαρμογή θα καλούσατε ένα weather API
        let url = URL(string: "https://api.weather.example/current?city=\(arguments.city)")!
        let (data, _) = try await URLSession.shared.data(from: url)
        let weatherData = String(data: data, encoding: .utf8) ?? "Δεν βρέθηκαν δεδομένα"
        return ToolOutput(weatherData)
    }
}

Ενσωμάτωση με SwiftUI

Εδώ είναι που τα πράγματα γίνονται πρακτικά. Το Foundation Models framework ενσωματώνεται φυσικά με το SwiftUI, κάτι που κάνει τη δημιουργία AI-powered εφαρμογών πραγματικά απλή.

Έλεγχος Διαθεσιμότητας σε SwiftUI View

struct AIFeatureView: View {
    private let model = SystemLanguageModel.default

    var body: some View {
        switch model.availability {
        case .available:
            ChatView()
        case .unavailable(.appleIntelligenceNotEnabled):
            ContentUnavailableView(
                "Apple Intelligence Απαιτείται",
                systemImage: "brain",
                description: Text("Ενεργοποιήστε το Apple Intelligence στις Ρυθμίσεις.")
            )
        case .unavailable(.deviceNotEligible):
            ContentUnavailableView(
                "Μη Υποστηριζόμενη Συσκευή",
                systemImage: "iphone.slash",
                description: Text("Η συσκευή σας δεν υποστηρίζει αυτή τη λειτουργία.")
            )
        case .unavailable(.modelNotReady):
            ProgressView("Λήψη μοντέλου...")
        default:
            Text("Η λειτουργία δεν είναι διαθέσιμη αυτή τη στιγμή.")
        }
    }
}

Πλήρης Chat View με Streaming

Ας φτιάξουμε μια ολοκληρωμένη AI chat εφαρμογή. Αυτό το παράδειγμα μπορείτε κυριολεκτικά να το αντιγράψετε στο project σας:

@MainActor
@Observable
class ChatViewModel {
    var messages: [ChatMessage] = []
    var currentResponse: String = ""
    var isGenerating = false

    private let session: LanguageModelSession

    init() {
        self.session = LanguageModelSession {
            """
            Είσαι ένας φιλικός βοηθός προγραμματισμού
            που εξειδικεύεται στη Swift και το SwiftUI.
            Απάντα πάντα στα ελληνικά.
            """
        }
    }

    func sendMessage(_ text: String) async {
        let userMessage = ChatMessage(role: .user, content: text)
        messages.append(userMessage)

        isGenerating = true
        currentResponse = ""

        do {
            let stream = session.streamResponse(to: text)
            for try await partial in stream {
                currentResponse = partial
            }

            let assistantMessage = ChatMessage(
                role: .assistant,
                content: currentResponse
            )
            messages.append(assistantMessage)
        } catch {
            let errorMessage = ChatMessage(
                role: .assistant,
                content: "Σφάλμα: \(error.localizedDescription)"
            )
            messages.append(errorMessage)
        }

        isGenerating = false
        currentResponse = ""
    }
}

struct ChatMessage: Identifiable {
    let id = UUID()
    let role: Role
    let content: String

    enum Role {
        case user, assistant
    }
}

struct ChatView: View {
    @State private var viewModel = ChatViewModel()
    @State private var inputText = ""

    var body: some View {
        VStack {
            ScrollView {
                LazyVStack(alignment: .leading, spacing: 12) {
                    ForEach(viewModel.messages) { message in
                        MessageBubble(message: message)
                    }

                    if viewModel.isGenerating {
                        Text(viewModel.currentResponse)
                            .padding()
                            .background(.ultraThinMaterial)
                            .clipShape(RoundedRectangle(cornerRadius: 12))
                    }
                }
                .padding()
            }

            HStack {
                TextField("Γράψτε ένα μήνυμα...", text: $inputText)
                    .textFieldStyle(.roundedBorder)

                Button("Αποστολή") {
                    let text = inputText
                    inputText = ""
                    Task {
                        await viewModel.sendMessage(text)
                    }
                }
                .disabled(inputText.isEmpty || viewModel.isGenerating)
            }
            .padding()
        }
        .navigationTitle("AI Βοηθός")
    }
}

struct MessageBubble: View {
    let message: ChatMessage

    var body: some View {
        HStack {
            if message.role == .user { Spacer() }

            Text(message.content)
                .padding(12)
                .background(
                    message.role == .user
                        ? Color.blue
                        : Color.gray.opacity(0.2)
                )
                .foregroundStyle(
                    message.role == .user ? .white : .primary
                )
                .clipShape(RoundedRectangle(cornerRadius: 16))

            if message.role == .assistant { Spacer() }
        }
    }
}

Custom Adapters: Προσαρμόστε το Μοντέλο στις Ανάγκες σας

Πέρα από τα ενσωματωμένα μοντέλα, η Apple σας δίνει τη δυνατότητα να εκπαιδεύσετε custom adapters. Σκεφτείτε τους σαν ένα «fine-tuning layer» πάνω στο βασικό μοντέλο, που προσαρμόζει τη συμπεριφορά του στη δική σας εφαρμογή.

Η διαδικασία, σε γενικές γραμμές:

  1. Προετοιμασία δεδομένων: Δημιουργήστε ένα dataset με παραδείγματα εισόδου-εξόδου
  2. Fine-tuning: Χρησιμοποιήστε το Python-based toolkit της Apple
  3. Πακετάρισμα: Μετατρέψτε τον adapter σε μορφή συμβατή με το framework
  4. Ενσωμάτωση: Φορτώστε τον στην εφαρμογή σας μέσω SystemLanguageModel

Οι custom adapters αξίζουν τον κόπο ειδικά αν χρειάζεστε:

  • Εξειδικευμένη ορολογία (ιατρική, νομική, τεχνική)
  • Συγκεκριμένο ύφος γραφής που ταιριάζει στο brand σας
  • Βελτιωμένη ακρίβεια σε domain-specific εργασίες

Βέλτιστες Πρακτικές και Συμβουλές

1. Πάντα Ελέγχετε τη Διαθεσιμότητα

Μην υποθέτετε ποτέ ότι το μοντέλο είναι εκεί. Χρησιμοποιήστε πάντα τον έλεγχο model.availability και δώστε εναλλακτικές. Οι χρήστες σε παλαιότερες συσκευές δεν πρέπει να βλέπουν λευκή οθόνη.

2. Προτιμήστε Guided Generation

Αντί να κάνετε parse ελεύθερο κείμενο (που ξέρουμε ότι σπάει σε edge cases), χρησιμοποιήστε @Generable structs. Εγγυημένη δομική ορθότητα, μηδέν σφάλματα parsing.

3. Δώστε Καλές Instructions

Σαφείς οδηγίες = καλύτερες απαντήσεις. Τόσο απλά:

// Αποφύγετε αυτό
let session = LanguageModelSession()

// Προτιμήστε αυτό
let session = LanguageModelSession {
    """
    Είσαι ειδικός στο SwiftUI.
    Οι απαντήσεις σου πρέπει να:
    - Είναι σύντομες και πρακτικές
    - Περιλαμβάνουν παραδείγματα κώδικα
    - Ακολουθούν τις τελευταίες βέλτιστες πρακτικές
    """
}

4. Χειριστείτε τα Σφάλματα Σωστά

Το μοντέλο μπορεί να αποτύχει — guardrails, timeout, προβλήματα μοντέλου. Μην αγνοείτε αυτές τις περιπτώσεις:

do {
    let response = try await session.respond(to: userPrompt)
    displayResult(response.content)
} catch let error as LanguageModelSession.GenerationError {
    handleGenerationError(error)
} catch {
    showErrorAlert(error.localizedDescription)
}

5. Streaming για Καλύτερη Εμπειρία Χρήστη

Για μεγαλύτερες απαντήσεις, το streaming κάνει τεράστια διαφορά. Κανείς δεν θέλει να περιμένει — ο χρήστης θέλει να βλέπει πρόοδο.

6. Διαχείριση Μνήμης Session

Κάθε LanguageModelSession κρατάει ιστορικό. Αν θέλετε «καθαρή αρχή», φτιάξτε νέο session. Αυτό βοηθά και στη μνήμη και αποτρέπει τη συσσώρευση context που μπορεί να μπερδέψει το μοντέλο.

Πρακτικό Παράδειγμα: Εφαρμογή Ανάλυσης Κειμένου

Ας βάλουμε τα πάντα μαζί. Παρακάτω θα φτιάξουμε μια πλήρη εφαρμογή ανάλυσης κειμένου με guided generation — ένα παράδειγμα που μπορείτε να χρησιμοποιήσετε ως βάση για τα δικά σας projects:

import SwiftUI
import FoundationModels

// MARK: - Μοντέλα Δεδομένων

@Generable
struct TextAnalysis {
    @Guide(description: "Σύντομη περίληψη σε 1-2 προτάσεις")
    let summary: String
    @Guide(description: "Κύρια θέματα του κειμένου")
    let topics: [String]
    @Guide(description: "Συναισθηματική ανάλυση")
    let sentiment: SentimentResult
    @Guide(description: "Αριθμός λέξεων στο αρχικό κείμενο", .range(1...100000))
    let wordCount: Int
}

@Generable
struct SentimentResult {
    @Guide(.anyOf(["θετικό", "αρνητικό", "ουδέτερο", "μικτό"]))
    let label: String
    @Guide(description: "Βαθμός βεβαιότητας 0.0 - 1.0")
    let confidence: Double
}

// MARK: - ViewModel

@MainActor
@Observable
class TextAnalyzerViewModel {
    var analysis: TextAnalysis?
    var isAnalyzing = false
    var errorMessage: String?

    private let session: LanguageModelSession

    init() {
        self.session = LanguageModelSession {
            """
            Είσαι ένα εξειδικευμένο εργαλείο ανάλυσης κειμένου.
            Αναλύεις κείμενα σε ελληνικά και αγγλικά.
            Δίνεις ακριβείς και χρήσιμες αναλύσεις.
            """
        }
    }

    func analyze(text: String) async {
        isAnalyzing = true
        errorMessage = nil
        analysis = nil

        do {
            let response = try await session.respond(
                to: "Ανάλυσε το ακόλουθο κείμενο:\n\n\(text)",
                generating: TextAnalysis.self
            )
            analysis = response.content
        } catch {
            errorMessage = "Σφάλμα ανάλυσης: \(error.localizedDescription)"
        }

        isAnalyzing = false
    }
}

// MARK: - Views

struct TextAnalyzerView: View {
    @State private var viewModel = TextAnalyzerViewModel()
    @State private var inputText = ""

    var body: some View {
        NavigationStack {
            Form {
                Section("Κείμενο προς Ανάλυση") {
                    TextEditor(text: $inputText)
                        .frame(minHeight: 150)
                }

                Section {
                    Button("Ανάλυση") {
                        Task {
                            await viewModel.analyze(text: inputText)
                        }
                    }
                    .disabled(inputText.isEmpty || viewModel.isAnalyzing)
                }

                if viewModel.isAnalyzing {
                    Section {
                        ProgressView("Ανάλυση σε εξέλιξη...")
                    }
                }

                if let error = viewModel.errorMessage {
                    Section {
                        Text(error)
                            .foregroundStyle(.red)
                    }
                }

                if let analysis = viewModel.analysis {
                    Section("Περίληψη") {
                        Text(analysis.summary)
                    }

                    Section("Θέματα") {
                        ForEach(analysis.topics, id: \.self) { topic in
                            Label(topic, systemImage: "tag")
                        }
                    }

                    Section("Συναίσθημα") {
                        LabeledContent("Ετικέτα", value: analysis.sentiment.label)
                        LabeledContent("Βεβαιότητα") {
                            Text("\(Int(analysis.sentiment.confidence * 100))%")
                        }
                    }

                    Section("Στατιστικά") {
                        LabeledContent("Αριθμός λέξεων", value: "\(analysis.wordCount)")
                    }
                }
            }
            .navigationTitle("Ανάλυση Κειμένου")
        }
    }
}

Περιορισμοί που Πρέπει να Γνωρίζετε

Ας είμαστε ρεαλιστές — το framework δεν είναι τέλειο. Υπάρχουν περιορισμοί που αξίζει να γνωρίζετε πριν ξεκινήσετε:

  • Μέγεθος μοντέλου: Με ~3B παραμέτρους, είναι σημαντικά μικρότερο από cloud μοντέλα. Μην περιμένετε GPT-4 επίπεδο ακρίβειας σε πολύ σύνθετες εργασίες.
  • Γλωσσική υποστήριξη: Τα Αγγλικά είναι η κύρια γλώσσα. Η απόδοση σε ελληνικά (και άλλες γλώσσες) μπορεί να ποικίλλει.
  • Guardrails: Υπάρχουν ενσωματωμένοι μηχανισμοί ασφαλείας που μπορεί να αρνηθούν κάποια αιτήματα. Αυτό είναι σκόπιμο — μην προσπαθείτε να τους παρακάμψετε.
  • Context window: Περιορισμένο σε σχέση με cloud μοντέλα. Για μεγάλα κείμενα, ίσως χρειαστεί τμηματοποίηση.
  • Συμβατότητα: Μόνο συσκευές με Apple Silicon — δεν υπάρχει backward compatibility.

Foundation Models vs Cloud APIs: Πότε τι Χρησιμοποιώ;

ΧαρακτηριστικόFoundation ModelsCloud APIs
ΙδιωτικότηταΠλήρης (on-device)Τα δεδομένα στέλνονται σε servers
OfflineΝαιΌχι
ΚόστοςΔωρεάνPay per use
Λανθάνων χρόνοςΧαμηλόςΕξαρτάται από δίκτυο
Δυνατότητες μοντέλουΠεριορισμένες (~3B)Πολύ ισχυρές (100B+)
ΕυελιξίαΣυγκεκριμένο APIΠολλαπλοί πάροχοι

Η καλύτερη στρατηγική; Ένας hybrid σχεδιασμός. Χρησιμοποιήστε Foundation Models για εργασίες που χρειάζονται privacy, offline πρόσβαση ή χαμηλό latency. Για πιο σύνθετα tasks, συνδυάστε με cloud APIs. Δεν χρειάζεται να διαλέξετε — μπορείτε να έχετε και τα δύο.

Συμπέρασμα

Το Foundation Models framework αλλάζει τα δεδομένα για τους iOS developers. Πρόσβαση σε ένα ισχυρό γλωσσικό μοντέλο, on-device, δωρεάν, χωρίς internet, με πλήρη ιδιωτικότητα. Και η ενσωμάτωση με Swift μέσω @Generable και @Guide είναι εξαιρετικά φυσική — type-safe, compile-time checked, κομψή.

Τα key takeaways:

  • SystemLanguageModel για πρόσβαση στο μοντέλο
  • LanguageModelSession με σαφείς instructions για αλληλεπίδραση
  • @Generable για structured output — ξεχάστε το manual parsing
  • @Guide για constraints σε κάθε πεδίο
  • Custom tools για σύνδεση με εξωτερικά δεδομένα
  • Streaming για responsive UI
  • Πάντα availability check — μην υποθέτετε τίποτα

Ξεκινήστε σήμερα να πειραματίζεστε. Με λίγες γραμμές κώδικα, μπορείτε να προσθέσετε AI δυνατότητες στις εφαρμογές σας που πριν λίγα χρόνια θα φαίνονταν science fiction — και μάλιστα χωρίς να θυσιάσετε την ιδιωτικότητα των χρηστών σας.

Σχετικά με τον Συγγραφέα Editorial Team

Our team of expert writers and editors.