Приветствую, Хабр!
Макросы Swift предоставляют мощный механизм для генерации кода, позволяя разработчикам уменьшать количество шаблонного кода и повышать читаемость. Фреймворк FoundationModels представляет новые макросы, призванные упростить генерацию данных для определённых типов моделей с использованием языковых моделей.
Обзор @Generable
Макрос @Generable можно применить к структуре Swift, чтобы указать, что экземпляры этого типа могут быть сгенерированы языковой моделью. При использовании этого макроса компилятор автоматически синтезирует необходимые соответствия протоколам и методы для поддержки потоковых ответов от сессии языковой модели.
@Generable struct ShoppingItem: Identifiable { let id: String let value: String }
В этом примере структура ShoppingItem становится совместимой с логикой автоматической генерации, предоставляемой фреймворком. Ожидается, что каждый экземпляр ShoppingItem будет создан на основе структурированного вывода языковой модели.
При желании можно предоставить описание, определяющее контекст или цель генерации:
@Generable(description: "Генерировать элементы для списка покупок") struct ShoppingItem: Identifiable { let id: String let value: String }
Это описание помогает направлять языковую модель к более точным результатам в процессе генерации.
Использование @Guide для контекста на уровне свойств
Для более детального контроля над тем, как языковая модель интерпретирует отдельные свойства, макрос @Guide можно применить к конкретным свойствам. Это позволяет разработчикам предоставлять дополнительные инструкции, непосредственно связанные с ожидаемым содержимым каждого свойства.
@Generable(description: "Генерировать элементы для списка покупок") struct ShoppingItem: Identifiable { let id: String @Guide(description: "Название продукта для покупки") let value: String }
Каждая аннотация @Guide служит директивой для языковой модели при генерации значений для соответствующих полей.
Интеграция в приложение
После того как модель помечена макросом @Generable, её можно использовать внутри LanguageModelSession для запроса данных. Типичный сценарий использования включает запуск задачи генерации в ответ на ввод пользователя.
private func generateShoppingList() { let prompt = "Создать 15 элементов списка покупок" Task { do { let session = LanguageModelSession() let response = session.streamResponse( generating: [ShoppingItem].self ) { prompt } isGenerating = true for try await chunk in response { self.shoppingList = chunk.compactMap { guard let id = $0.id, let task = $0.value else { return nil } return ShoppingItem(id: id, value: task) } } isGenerating = false } catch { print(error.localizedDescription) isGenerating = false } } }
В этом фрагменте метод streamResponse(generating:prompt:) инициирует потоковый запрос на получение массива экземпляров ShoppingItem. По мере поступления порций результатов они обрабатываются и добавляются в состояние представления.
Теперь давайте интегрируем это в наше представление SwiftUI:
struct ContentView: View { @State private var shoppingList: [ShoppingItem] = [] @State private var isGenerating: Bool = false var body: some View { List(shoppingList) { item in Text(item.value) } .toolbar { ToolbarItem(placement: .bottomBar) { Button( "Сгенерировать список покупок", systemImage: "cart.fill.badge.plus" ) { generateShoppingList() } .disabled(isGenerating) } } } private func generateShoppingList() { ... } }
Заключение
Макрос @Generable предоставляет чистый и декларативный способ интеграции генерации на основе языковых моделей в приложения на Swift. В сочетании с @Guide он предлагает структурированный подход к определению ожиданий как на уровне модели, так и на уровне полей для генерируемых данных.
