Foundation Models trong iOS 26: Xây Dựng AI On-Device với SwiftUI

Hướng dẫn chi tiết cách dùng Foundation Models framework trong iOS 26: LanguageModelSession, @Generable, @Guide, Tool Calling và streaming — kèm code ví dụ thực tế để xây dựng ứng dụng AI on-device với SwiftUI.

Giới Thiệu: Kỷ Nguyên AI Trực Tiếp Trên Thiết Bị Apple

Nếu bạn là một iOS developer đang theo dõi xu hướng công nghệ năm 2026, chắc hẳn bạn đã nghe đến Foundation Models framework. Nói thật, đây có lẽ là bước tiến lớn nhất của Apple trong lĩnh vực AI kể từ khi Core ML ra đời. Được giới thiệu tại WWDC 2025, framework này cho phép bạn tích hợp mô hình ngôn ngữ lớn (LLM) chạy hoàn toàn trên thiết bị — không cần internet, không gửi dữ liệu lên cloud. Nghe hấp dẫn đúng không?

Khác với các dịch vụ AI đám mây như ChatGPT hay Claude — nơi dữ liệu người dùng phải gửi đến server bên ngoài — Foundation Models tận dụng Apple Silicon để chạy mô hình ngôn ngữ 3 tỷ tham số ngay trên iPhone, iPad hoặc Mac. Ba lợi thế cốt lõi: bảo mật tuyệt đối (dữ liệu không rời khỏi thiết bị), độ trễ cực thấp (không phụ thuộc mạng), và hoạt động offline.

Trong hướng dẫn này, mình sẽ đi từ khái niệm cơ bản nhất đến các kỹ thuật nâng cao. Bạn sẽ học cách dùng LanguageModelSession, tạo output có cấu trúc với @Generable@Guide, stream phản hồi realtime, triển khai Tool Calling, và tích hợp tất cả vào ứng dụng SwiftUI hoàn chỉnh. Mỗi phần đều có code chạy được ngay, nên bạn cứ code along thoải mái nhé.

Yêu Cầu Hệ Thống và Thiết Lập

Trước khi bắt tay vào code, bạn cần đảm bảo môi trường phát triển đáp ứng các yêu cầu sau:

  • Nền tảng: iOS 26+, iPadOS 26+, macOS Tahoe (macOS 26)+, hoặc visionOS 26+
  • Phần cứng: Thiết bị hỗ trợ Apple Intelligence — chip A17 Pro, M1 trở lên với Neural Engine
  • Công cụ phát triển: Xcode 26 trở lên
  • Giới hạn token: Tổng input + output tối đa 4096 token mỗi lần gọi
  • Ngôn ngữ tối ưu: Model hoạt động tốt nhất với prompt bằng tiếng Anh

Lưu ý quan trọng: Mô hình on-device này không phải ChatGPT thu nhỏ. Nó không được thiết kế cho kiến thức thế giới hay suy luận phức tạp. Thay vào đó, nó được tối ưu cho các tác vụ cụ thể: tạo văn bản, tóm tắt nội dung, phân loại dữ liệu, trích xuất thực thể, và trả lời câu hỏi dựa trên ngữ cảnh. Hiểu rõ giới hạn này giúp bạn thiết kế tính năng AI phù hợp hơn rất nhiều.

Kiểm Tra Tính Khả Dụng Của Model

Framework chỉ chạy trên một số thiết bị nhất định, nên bạn bắt buộc phải kiểm tra tính khả dụng trước khi sử dụng. May mà Apple cung cấp API khá rõ ràng cho việc này:

import FoundationModels

let model = SystemLanguageModel.default

switch model.availability {
case .available:
    print("Model sẵn sàng sử dụng")
case .unavailable(let reason):
    switch reason {
    case .deviceNotEligible:
        print("Thiết bị không hỗ trợ Apple Intelligence")
    case .appleIntelligenceNotEnabled:
        print("Apple Intelligence chưa được bật trong Cài đặt")
    case .modelNotReady:
        print("Model đang được tải xuống hoặc chuẩn bị")
    @unknown default:
        print("Model không khả dụng")
    }
}

Trong ứng dụng SwiftUI thực tế, bạn nên hiển thị UI phù hợp với từng trạng thái. Đây là pattern mà mình thấy khá clean:

struct AIFeatureGate: View {
    let model = SystemLanguageModel.default

    var body: some View {
        switch model.availability {
        case .available:
            AIChatView()
        case .unavailable(.appleIntelligenceNotEnabled):
            ContentUnavailableView(
                "Cần bật Apple Intelligence",
                systemImage: "brain",
                description: Text("Vào Cài đặt > Apple Intelligence để kích hoạt.")
            )
        case .unavailable(.deviceNotEligible):
            ContentUnavailableView(
                "Thiết bị không hỗ trợ",
                systemImage: "iphone.slash",
                description: Text("Tính năng AI cần chip A17 Pro hoặc M1 trở lên.")
            )
        default:
            ProgressView("Đang chuẩn bị mô hình AI...")
        }
    }
}

Bắt Đầu với LanguageModelSession

LanguageModelSession là class trung tâm mà bạn sẽ tương tác nhiều nhất. Nó đóng vai trò "cầu nối" giữa code Swift và mô hình ngôn ngữ on-device. Session tự động duy trì ngữ cảnh hội thoại, hỗ trợ nhiều chế độ tạo nội dung — từ text thuần túy đến structured output và streaming.

Tạo Session Cơ Bản

Cách đơn giản nhất? Chỉ cần ba dòng code:

import FoundationModels

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Giải thích protocol-oriented programming trong một đoạn ngắn."
)
print(response.content)

Code trên tạo một session mặc định, gửi prompt đến model, và nhận phản hồi dưới dạng đối tượng Response với thuộc tính content chứa text được sinh ra. Đơn giản vậy thôi.

Session với System Instructions

Khi cần kiểm soát hành vi model chi tiết hơn, hãy dùng system instructions. Đây là cách bạn thiết lập "tính cách" và quy tắc cho AI:

let session = LanguageModelSession(instructions: """
    Bạn là một trợ lý lập trình Swift có kinh nghiệm.
    Trả lời ngắn gọn, đi thẳng vào vấn đề.
    Luôn kèm theo ví dụ code khi có thể.
    Sử dụng ngôn ngữ dễ hiểu cho người mới bắt đầu.
""")

System instructions được ưu tiên cao hơn prompt người dùng — rất hữu ích cho kiểm soát an ninh và đảm bảo model luôn hoạt động trong phạm vi bạn mong muốn.

Hội Thoại Nhiều Lượt (Multi-turn)

Một điểm mình thấy rất hay ở LanguageModelSession là khả năng duy trì ngữ cảnh qua nhiều lượt hội thoại:

let session = LanguageModelSession()

// Lượt 1
let r1 = try await session.respond(to: "Tên tôi là Minh")
// Lượt 2 — model nhớ ngữ cảnh trước đó
let r2 = try await session.respond(to: "Tên tôi là gì?")
// r2.content sẽ chứa "Minh"

// Truy cập lịch sử hội thoại
for entry in session.transcript {
    print(entry)
}

Session tự động lưu transcript toàn bộ cuộc hội thoại. Nhờ vậy, bạn có thể xây dựng chatbot hoặc trợ lý ảo khá dễ dàng.

Output Có Cấu Trúc với @Generable

Thú thật, đây là tính năng mà mình ấn tượng nhất khi tìm hiểu Foundation Models. Thay vì nhận text thuần túy rồi phải parse thủ công (ai đã từng parse JSON từ LLM output thì biết cảm giác đau đầu), macro @Generable cho phép bạn yêu cầu model trả về đúng kiểu dữ liệu Swift mà bạn định nghĩa.

Cách Hoạt Động

Khi đánh dấu struct với @Generable, compiler tự động tạo JSON schema tương ứng. Khi gọi model, framework ép buộc output phải tuân thủ schema này ở cấp token — model bị buộc phải tạo output hợp lệ, không phải kiểu "hy vọng" nó trả đúng format.

import FoundationModels

@Generable
struct BookRecommendation {
    let title: String
    let author: String
    let genre: String
    let summary: String
    let rating: Int
}

let session = LanguageModelSession()
let response = try await session.respond(
    to: "Gợi ý một cuốn sách lập trình Swift hay cho người mới",
    generating: BookRecommendation.self
)

let book = response.content
print("Tên sách: \(book.title)")
print("Tác giả: \(book.author)")
print("Đánh giá: \(book.rating)/5")

Tất cả stored properties của kiểu @Generable phải là các kiểu dữ liệu framework hỗ trợ: String, Int, Double, Bool, Array, và cả struct lồng nhau (miễn là cũng được đánh dấu @Generable).

Thứ Tự Properties Rất Quan Trọng

Đây là chi tiết dễ bị bỏ qua nhưng cực kỳ quan trọng. Model tạo giá trị tuần tự theo thứ tự khai báo properties trong struct. Nếu property B phụ thuộc vào giá trị property A, thì A phải khai báo trước B:

@Generable
struct QuizQuestion {
    // Câu hỏi được tạo trước
    let question: String
    // Đáp án được tạo dựa trên câu hỏi
    let correctAnswer: String
    // Giải thích được tạo dựa trên cả câu hỏi và đáp án
    let explanation: String
}

Nếu bạn đặt explanation trước correctAnswer, model có thể tạo giải thích không khớp với đáp án — vì lúc đó đáp án chưa được xác định. Mình đã mắc lỗi này lúc mới bắt đầu, và kết quả khá khó hiểu cho đến khi nhận ra nguyên nhân.

Tinh Chỉnh Output với @Guide

Macro @Guide hoạt động cùng @Generable, cho phép bạn cung cấp hướng dẫn cụ thể cho từng property. Nói cách khác, đây là cách bạn "điều hướng" chính xác những gì model sinh ra.

Các Loại Ràng Buộc Chính

@Generable
struct MovieReview {
    // Mô tả bằng ngôn ngữ tự nhiên
    @Guide(description: "Tên phim bằng tiếng Anh gốc")
    let title: String

    // Giới hạn giá trị cụ thể
    @Guide(.anyOf(["PG", "PG-13", "R", "G"]))
    let rating: String

    // Mô tả + ràng buộc kết hợp
    @Guide(description: "Tóm tắt nội dung trong một câu ngắn gọn")
    let summary: String

    // Ràng buộc số lượng phần tử mảng
    @Guide(.count(3))
    let topActors: [String]

    // Giới hạn khoảng số lượng
    @Guide(.minimumCount(2))
    let genres: [String]
}

Tóm lại, các ràng buộc chính bao gồm:

  • description: — mô tả bằng ngôn ngữ tự nhiên về giá trị cần tạo
  • .anyOf([]) — giới hạn output trong tập giá trị cố định
  • .count(n) — ép mảng có đúng n phần tử
  • .minimumCount(n) — mảng tối thiểu n phần tử
  • .maximumCount(n) — mảng tối đa n phần tử

Ví Dụ Thực Tế: App Quiz Từ Vựng

Giờ hãy xem cách kết hợp @Generable@Guide trong một app quiz thực tế. Đây là ví dụ khá sát với những gì bạn sẽ làm trong production:

@Generable
struct VocabQuestion {
    @Guide(description: "Câu hỏi trắc nghiệm kiểm tra từ vựng tiếng Anh")
    let text: String

    @Guide(.count(4))
    let choices: [String]

    let answer: String

    @Guide(description: "Giải thích ngắn gọn tại sao đáp án đúng")
    let explanation: String
}

// Sử dụng trong SwiftUI
struct QuizView: View {
    @State private var session = LanguageModelSession(
        instructions: "Bạn là giáo viên tiếng Anh. Tạo câu hỏi từ vựng phù hợp trình độ trung cấp."
    )
    @State private var question: VocabQuestion?
    @State private var isLoading = false

    var body: some View {
        VStack(spacing: 20) {
            if let question {
                Text(question.text)
                    .font(.title2)
                    .fontWeight(.semibold)

                ForEach(question.choices, id: \.self) { choice in
                    Button(choice) {
                        // Xử lý chọn đáp án
                    }
                    .buttonStyle(.bordered)
                    .frame(maxWidth: .infinity)
                }
            } else if isLoading {
                ProgressView("Đang tạo câu hỏi...")
            }

            Button("Câu hỏi tiếp theo") {
                Task { await generateQuestion() }
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
        .task { await generateQuestion() }
    }

    func generateQuestion() async {
        isLoading = true
        defer { isLoading = false }
        do {
            let response = try await session.respond(
                to: "Tạo một câu hỏi trắc nghiệm từ vựng tiếng Anh.",
                generating: VocabQuestion.self
            )
            question = response.content
        } catch {
            print("Lỗi: \(error)")
        }
    }
}

Streaming Phản Hồi Theo Thời Gian Thực

Với các tác vụ tạo nội dung dài, bạn không muốn người dùng phải ngồi chờ đến khi toàn bộ phản hồi hoàn tất. Foundation Models hỗ trợ streaming — hiển thị kết quả dần dần khi model đang sinh, tương tự trải nghiệm chat trên ChatGPT mà bạn đã quen thuộc.

Streaming Văn Bản Thuần Túy

let session = LanguageModelSession()
let stream = session.streamResponse(
    to: "Viết một đoạn văn ngắn về tương lai của lập trình mobile."
)

for try await partial in stream {
    // partial.content chứa văn bản được tạo đến thời điểm hiện tại
    print(partial.content, terminator: "")
}

// Lấy phản hồi hoàn chỉnh cuối cùng
let finalResponse = try await stream.response

Streaming Output Có Cấu Trúc

Điều khá thú vị là streaming cũng hoạt động với kiểu @Generable. Framework không stream từng token riêng lẻ mà gửi các snapshot của struct đang được điền dần:

@Generable
struct ArticleSummary {
    let title: String
    let keyPoints: [String]
    let conclusion: String
}

let stream = session.streamResponse(
    to: "Tóm tắt bài viết này...",
    generating: ArticleSummary.self
)

for try await partial in stream {
    // partial là ArticleSummary.PartiallyGenerated
    // Các property bắt đầu là nil và được điền dần
    if let title = partial.title {
        print("Tiêu đề: \(title)")
    }
    if let points = partial.keyPoints {
        print("Số điểm chính: \(points.count)")
    }
}

Kiểu PartiallyGenerated là phiên bản "mirror" của struct gốc với tất cả properties là optional. Compiler tự động tạo kiểu này, giúp bạn cập nhật UI progressively mà vẫn type-safe. Khá elegant phải không?

Tích Hợp Streaming vào SwiftUI

struct StreamingChatView: View {
    @State private var session = LanguageModelSession()
    @State private var displayedText = ""
    @State private var isStreaming = false

    var body: some View {
        VStack {
            ScrollView {
                Text(displayedText)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding()
            }

            Button(isStreaming ? "Đang tạo..." : "Tạo nội dung") {
                Task { await streamContent() }
            }
            .disabled(isStreaming)
            .buttonStyle(.borderedProminent)
            .padding()
        }
    }

    func streamContent() async {
        isStreaming = true
        displayedText = ""
        defer { isStreaming = false }

        do {
            let stream = session.streamResponse(
                to: "Viết 5 mẹo tối ưu hiệu suất cho ứng dụng SwiftUI"
            )
            for try await partial in stream {
                displayedText = partial.content
            }
        } catch {
            displayedText = "Đã xảy ra lỗi: \(error.localizedDescription)"
        }
    }
}

Tool Calling: Mở Rộng Khả Năng Của Model

Model on-device có giới hạn rõ ràng — nó không biết thời tiết hiện tại, không truy cập được database ứng dụng, và không gọi được API bên ngoài. Tool Calling giải quyết vấn đề này bằng cách cho phép model "gọi" các hàm Swift do bạn định nghĩa khi cần dữ liệu hoặc hành động ngoài khả năng của nó.

Cơ chế này quen thuộc với ai đã dùng function calling của OpenAI hay tool use của Claude. Nhưng điểm khác biệt là mọi thứ chạy on-device.

Tạo Một Tool

Để tạo tool, bạn cần conform protocol Tool với ba thành phần: name, description (giúp model hiểu khi nào nên gọi), và struct Arguments đánh dấu @Generable:

import FoundationModels

struct SearchRecipesTool: Tool {
    let name = "searchRecipes"
    let description = "Tìm kiếm công thức nấu ăn theo tên món hoặc nguyên liệu"

    @Generable
    struct Arguments {
        @Guide(description: "Từ khóa tìm kiếm: tên món ăn hoặc nguyên liệu")
        let query: String
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        // Trong thực tế, đây sẽ là lời gọi API hoặc truy vấn database
        let recipes = [
            "Phở bò Hà Nội - 45 phút",
            "Bún chả Hà Nội - 30 phút",
            "Bánh mì thịt nướng - 20 phút"
        ]
        return ToolOutput(recipes.joined(separator: "\n"))
    }
}

struct GetWeatherTool: Tool {
    let name = "getWeather"
    let description = "Lấy thông tin thời tiết hiện tại theo thành phố"

    @Generable
    struct Arguments {
        @Guide(description: "Tên thành phố cần tra cứu thời tiết")
        let city: String
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        // Gọi API thời tiết thực tế
        return ToolOutput("Hà Nội: 28°C, trời nắng, độ ẩm 75%")
    }
}

Kết Nối Tools với Session

let tools: [any Tool] = [SearchRecipesTool(), GetWeatherTool()]
let session = LanguageModelSession(
    instructions: "Bạn là trợ lý thông minh. Sử dụng tools khi cần tra cứu thông tin.",
    tools: tools
)

// Model tự quyết định khi nào gọi tool
let response = try await session.respond(
    to: "Thời tiết Hà Nội hôm nay thế nào?"
)
// Model sẽ tự động gọi GetWeatherTool và dùng kết quả để trả lời

Điểm hay là model tự động quyết định khi nào cần gọi tool dựa trên nội dung prompt. Kết quả từ tool được chèn ngược vào transcript, cho phép model tiếp tục tạo phản hồi dựa trên dữ liệu thực tế. Bạn không cần viết logic điều phối phức tạp — framework lo hết.

Tùy Chỉnh Nâng Cao với GenerationOptions

Foundation Models cho phép bạn kiểm soát chiến lược sampling — tức cách model chọn token tiếp theo — thông qua GenerationOptions:

// Greedy: luôn chọn token có xác suất cao nhất — kết quả nhất quán
let deterministicOptions = GenerationOptions(sampling: .greedy)

// Top-p sampling: cho phép sáng tạo hơn
let creativeOptions = GenerationOptions(
    sampling: .random(probabilityThreshold: 0.9, seed: 42)
)

// Top-k sampling: giới hạn số lượng token ứng viên
let balancedOptions = GenerationOptions(
    sampling: .random(top: 40, seed: 42)
)

let response = try await session.respond(
    to: "Viết một câu slogan sáng tạo cho ứng dụng đọc sách",
    options: creativeOptions
)

Vậy khi nào dùng chế độ nào?

  • Greedy: Khi cần kết quả nhất quán, lặp lại được — ví dụ phân loại nội dung, trích xuất dữ liệu
  • Top-p (probabilityThreshold): Sáng tạo vừa phải — tạo mô tả sản phẩm, tóm tắt
  • Top-k: Sáng tạo cao — tạo câu chuyện, dialog game

Prewarming: Tối Ưu Thời Gian Phản Hồi

Lần gọi model đầu tiên thường chậm hơn bình thường vì framework cần load model vào bộ nhớ. Để giảm độ trễ này, bạn có thể "khởi động trước" model bằng prewarm:

struct AIAssistantView: View {
    @State private var session = LanguageModelSession()

    var body: some View {
        // UI code...
        NavigationStack {
            // ...
        }
        .task {
            // Load model trước khi người dùng tương tác
            try? await session.prewarm(promptPrefix: "Bạn là trợ lý hữu ích")
        }
    }
}

Bằng cách gọi prewarm sớm (ví dụ khi view xuất hiện), bạn đảm bảo rằng lúc người dùng gửi prompt đầu tiên, model đã sẵn sàng phản hồi ngay. Trải nghiệm người dùng tốt hơn hẳn.

Xử Lý Lỗi và Guardrails

Foundation Models tích hợp hệ thống guardrails (bộ lọc an toàn) mặc định, thực thi nguyên tắc nội dung của Apple. Bạn không thể tắt guardrails này — nhưng cần xử lý đúng các lỗi nó tạo ra:

do {
    let response = try await session.respond(to: userPrompt)
    displayResult(response.content)
} catch let error as LanguageModelSession.GenerationError {
    switch error {
    case .guardrailViolation:
        showAlert("Yêu cầu vi phạm chính sách nội dung. Vui lòng thử lại với nội dung khác.")
    case .exceededContextWindowSize:
        showAlert("Nội dung quá dài. Vui lòng rút gọn prompt.")
    default:
        showAlert("Đã xảy ra lỗi: \(error.localizedDescription)")
    }
} catch {
    showAlert("Lỗi không xác định: \(error.localizedDescription)")
}

Ngoài ra, thuộc tính isResponding trên session giúp bạn ngăn gửi prompt trùng lặp:

Button("Gửi") {
    Task { await sendMessage() }
}
.disabled(session.isResponding) // Vô hiệu hóa khi model đang xử lý

Xây Dựng Ứng Dụng Hoàn Chỉnh: AI Writing Assistant

Được rồi, giờ hãy kết hợp tất cả những gì đã học để xây dựng một ứng dụng trợ lý viết bài hoàn chỉnh. App này sẽ tạo dàn bài, viết nội dung, và stream kết quả theo thời gian thực.

import SwiftUI
import FoundationModels

@Generable
struct ArticleOutline {
    @Guide(description: "Tiêu đề hấp dẫn cho bài viết")
    let title: String

    @Guide(.count(4))
    let sections: [String]

    @Guide(description: "Đoạn mở đầu thu hút người đọc, khoảng 2-3 câu")
    let introduction: String
}

struct WritingAssistantView: View {
    @State private var session = LanguageModelSession(
        instructions: """
        Bạn là trợ lý viết bài chuyên nghiệp.
        Viết nội dung bằng tiếng Việt, phong cách tự nhiên và dễ đọc.
        Tập trung vào giá trị thực tế cho người đọc.
        """
    )
    @State private var topic = ""
    @State private var outline: ArticleOutline?
    @State private var generatedContent = ""
    @State private var isGenerating = false

    var body: some View {
        NavigationStack {
            Form {
                Section("Chủ đề") {
                    TextField("Nhập chủ đề bài viết...", text: $topic)
                }

                if let outline {
                    Section("Dàn bài") {
                        Text(outline.title)
                            .font(.headline)
                        Text(outline.introduction)
                            .font(.subheadline)
                            .foregroundStyle(.secondary)
                        ForEach(outline.sections, id: \.self) { section in
                            Label(section, systemImage: "list.bullet")
                        }
                    }
                }

                if !generatedContent.isEmpty {
                    Section("Nội dung") {
                        Text(generatedContent)
                    }
                }

                Section {
                    Button(isGenerating ? "Đang tạo..." : "Tạo dàn bài") {
                        Task { await generateOutline() }
                    }
                    .disabled(topic.isEmpty || isGenerating)

                    if outline != nil {
                        Button("Viết nội dung chi tiết") {
                            Task { await generateContent() }
                        }
                        .disabled(isGenerating)
                    }
                }
            }
            .navigationTitle("AI Writing Assistant")
        }
    }

    func generateOutline() async {
        isGenerating = true
        defer { isGenerating = false }
        do {
            let response = try await session.respond(
                to: "Tạo dàn bài cho bài viết về: \(topic)",
                generating: ArticleOutline.self
            )
            outline = response.content
        } catch {
            print("Lỗi tạo dàn bài: \(error)")
        }
    }

    func generateContent() async {
        guard let outline else { return }
        isGenerating = true
        generatedContent = ""
        defer { isGenerating = false }

        do {
            let prompt = """
            Dựa trên dàn bài sau, viết nội dung chi tiết:
            Tiêu đề: \(outline.title)
            Mở đầu: \(outline.introduction)
            Các phần: \(outline.sections.joined(separator: ", "))
            """
            let stream = session.streamResponse(to: prompt)
            for try await partial in stream {
                generatedContent = partial.content
            }
        } catch {
            generatedContent = "Lỗi: \(error.localizedDescription)"
        }
    }
}

Mẹo Tối Ưu Hiệu Suất

Để app của bạn chạy mượt khi dùng Foundation Models, đây là vài điều mình rút ra được:

  • Giảm thiểu properties trong @Generable: Model phải điền tất cả properties bất kể bạn dùng hay không. Chỉ khai báo field thực sự cần cho màn hình hiện tại.
  • Tái sử dụng session: Tạo session một lần rồi tái sử dụng qua nhiều lời gọi. Session mới = mất ngữ cảnh hội thoại.
  • Dùng prewarm: Gọi prewarm sớm để giảm độ trễ cho lần phản hồi đầu tiên. Đừng bỏ qua bước này.
  • Prompt ngắn gọn, rõ ràng: Với giới hạn 4096 token, prompt càng ngắn gọn thì kết quả càng tốt và nhanh hơn.
  • Background thread: Tất cả lời gọi model đều async, nhưng nhớ đảm bảo UI updates luôn chạy trên main thread qua @MainActor.

Câu Hỏi Thường Gặp (FAQ)

Foundation Models framework có hoạt động offline không?

Có, và đây là ưu điểm lớn nhất. Model chạy hoàn toàn trên thiết bị sử dụng Apple Silicon (Neural Engine, GPU, CPU). Không cần internet. Tuy nhiên, lần đầu sau khi cài iOS 26, hệ thống cần tải model về — quá trình này thì vẫn cần mạng.

Foundation Models có thể thay thế ChatGPT hoặc Claude không?

Không hoàn toàn. Model on-device của Apple chỉ có 3 tỷ tham số — nhỏ hơn nhiều so với các mô hình cloud. Nó tối ưu cho tác vụ cụ thể: tóm tắt, phân loại, trích xuất dữ liệu, tạo văn bản ngắn. Với tác vụ cần kiến thức sâu rộng hoặc suy luận phức tạp, bạn vẫn nên dùng API cloud. Chiến lược tốt nhất? Kết hợp cả hai — on-device cho tác vụ nhanh và riêng tư, cloud cho những gì phức tạp hơn.

Thiết bị nào hỗ trợ Foundation Models?

iPhone 15 Pro (chip A17 Pro) trở lên, iPad và Mac với chip M1 trở lên, chạy iOS 26+. Thiết bị cũ hơn thì không dùng được, tiếc là vậy.

@Generable khác gì so với JSON parsing truyền thống?

@Generable sử dụng constrained decoding — model bị ép phải tạo output hợp lệ ở cấp token. Khác hoàn toàn với việc nhờ model tạo JSON rồi parse thủ công (kiểu "cầu nguyện" nó trả đúng format). Với @Generable, bạn có đảm bảo 100% output sẽ khớp kiểu dữ liệu Swift đã định nghĩa.

Có thể dùng Foundation Models trong UIKit không?

Hoàn toàn được. Framework không phụ thuộc SwiftUI. Bạn có thể dùng trong UIKit, AppKit, hay thậm chí command-line tools. Chỉ cần import FoundationModels và dùng API async/await bình thường.

Về Tác Giả Editorial Team

Our team of expert writers and editors.