FoundationModels: LLM no Dispositivo (iOS 26)
Padrões para integrar o modelo de linguagem no dispositivo da Apple em aplicativos usando o framework FoundationModels. Abrange geração de texto, saída estruturada com @Generable, chamada de ferramentas personalizada e streaming de instantâneos — tudo executado no dispositivo para privacidade e suporte offline.
Quando Ativar
- Construindo recursos com tecnologia de IA usando Apple Intelligence no dispositivo
- Gerando ou sumarizando texto sem dependência de nuvem
- Extraindo dados estruturados de entrada de linguagem natural
- Implementando chamada de ferramentas personalizada para ações de IA específicas de domínio
- Transmitindo respostas estruturadas para atualizações de UI em tempo real
- Necessidade de IA que preserva a privacidade (nenhum dado sai do dispositivo)
Padrão Principal — Verificação de Disponibilidade
Sempre verifique a disponibilidade do modelo antes de criar uma sessão:
struct GenerativeView: View {
private var model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
ContentView()
case .unavailable(.deviceNotEligible):
Text("Dispositivo não elegível para Apple Intelligence")
case .unavailable(.appleIntelligenceNotEnabled):
Text("Por favor, ative Apple Intelligence em Ajustes")
case .unavailable(.modelNotReady):
Text("O modelo está baixando ou não está pronto")
case .unavailable(let other):
Text("Modelo indisponível: \(other)")
}
}
}
Padrão Principal — Sessão Básica
// Turno único: cria uma nova sessão a cada vez
let session = LanguageModelSession()
let response = try await session.respond(to: "Qual é um bom mês para visitar Paris?")
print(response.content)
// Vários turnos: reutiliza sessão para contexto de conversa
let session = LanguageModelSession(instructions: """
Você é um assistente de culinária.
Forneça sugestões de receitas com base nos ingredientes.
Mantenha as sugestões breves e práticas.
""")
let first = try await session.respond(to: "Tenho frango e arroz")
let followUp = try await session.respond(to: "E uma opção vegetariana?")
Pontos-chave para instruções:
- Defina o papel do modelo ("Você é um mentor")
- Especifique o que fazer ("Ajude a extrair eventos de calendário")
- Defina preferências de estilo ("Responda da forma mais breve possível")
- Adicione medidas de segurança ("Responda com 'Não posso ajudar com isso' para solicitações perigosas")
Padrão Principal — Geração Guiada com @Generable
Gere tipos Swift estruturados em vez de strings brutas:
1. Defina um Tipo Generable
@Generable(description: "Informações básicas de perfil sobre um gato")
struct CatProfile {
var name: String
@Guide(description: "A idade do gato", .range(0...20))
var age: Int
@Guide(description: "Um perfil de uma frase sobre a personalidade do gato")
var profile: String
}
2. Solicite Saída Estruturada
let response = try await session.respond(
to: "Gere um gato de resgate fofo",
generating: CatProfile.self
)
// Acesse campos estruturados diretamente
print("Nome: \(response.content.name)")
print("Idade: \(response.content.age)")
print("Perfil: \(response.content.profile)")
Restrições @Guide Suportadas
.range(0...20)— intervalo numérico.count(3)— número de elementos do arraydescription:— orientação semântica para geração
Padrão Principal — Chamada de Ferramentas
Permita que o modelo invoque código personalizado para tarefas específicas de domínio:
1. Defina uma Ferramenta
struct RecipeSearchTool: Tool {
let name = "recipe_search"
let description = "Pesquise receitas correspondentes a um termo dado e retorne uma lista de resultados."
@Generable
struct Arguments {
var searchTerm: String
var numberOfResults: Int
}
func call(arguments: Arguments) async throws -> ToolOutput {
let recipes = await searchRecipes(
term: arguments.searchTerm,
limit: arguments.numberOfResults
)
return .string(recipes.map { "- \($0.name): \($0.description)" }.joined(separator: "\n"))
}
}
2. Crie Sessão com Ferramentas
let session = LanguageModelSession(tools: [RecipeSearchTool()])
let response = try await session.respond(to: "Encontre algumas receitas de massa")
3. Trate Erros de Ferramenta
do {
let answer = try await session.respond(to: "Encontre uma receita de sopa de tomate.")
} catch let error as LanguageModelSession.ToolCallError {
print(error.tool.name)
if case .databaseIsEmpty = error.underlyingError as? RecipeSearchToolError {
// Trata erro específico da ferramenta
}
}
Padrão Principal — Streaming de Instantâneos
Transmita respostas estruturadas para UI em tempo real com tipos PartiallyGenerated:
@Generable
struct TripIdeas {
@Guide(description: "Ideias para próximas viagens")
var ideas: [String]
}
let stream = session.streamResponse(
to: "Quais são algumas ideias de viagem empolgantes?",
generating: TripIdeas.self
)
for try await partial in stream {
// partial: TripIdeas.PartiallyGenerated (todas as propriedades Opcionais)
print(partial)
}
Integração com SwiftUI
@State private var partialResult: TripIdeas.PartiallyGenerated?
@State private var errorMessage: String?
var body: some View {
List {
ForEach(partialResult?.ideas ?? [], id: \.self) { idea in
Text(idea)
}
}
.overlay {
if let errorMessage { Text(errorMessage).foregroundStyle(.red) }
}
.task {
do {
let stream = session.streamResponse(to: prompt, generating: TripIdeas.self)
for try await partial in stream {
partialResult = partial
}
} catch {
errorMessage = error.localizedDescription
}
}
}
Principais Decisões de Design
| Decisão | Justificativa |
|---|---|
| Execução no dispositivo | Privacidade — nenhum dado sai do dispositivo; funciona offline |
| Limite de 4.096 tokens | Restrição do modelo no dispositivo; divida dados grandes entre sessões |
| Streaming de instantâneos (não deltas) | Amigável a saída estruturada; cada instantâneo é um estado parcial completo |
Macro @Generable | Segurança em tempo de compilação para geração estruturada; gera automaticamente o tipo PartiallyGenerated |
| Requisição única por sessão | isResponding previne requisições concorrentes; crie várias sessões se necessário |
response.content (não .output) | API correta — sempre acesse resultados via propriedade .content |
Melhores Práticas
- Sempre verifique
model.availabilityantes de criar uma sessão — trate todos os casos de indisponibilidade - Use
instructionspara guiar o comportamento do modelo — elas têm prioridade sobre as solicitações - Verifique
isRespondingantes de enviar uma nova requisição — sessões tratam uma requisição por vez - Acesse
response.contentpara resultados — não.output - Divida entradas grandes em partes — o limite de 4.096 tokens se aplica a instruções + solicitação + saída combinados
- Use
@Generablepara saída estruturada — garantias mais fortes do que analisar strings brutas - Use
GenerationOptions(temperature:)para ajustar criatividade (maior = mais criativo) - Monitore com Instruments — use Instruments do Xcode para analisar o desempenho da requisição
Anti-Padrões a Evitar
- Criar sessões sem verificar
model.availabilityprimeiro - Enviar entradas que excedem a janela de contexto de 4.096 tokens
- Tentar requisições concorrentes em uma única sessão
- Usar
.outputem vez de.contentpara acessar dados de resposta - Analisar respostas de string bruta quando saída estruturada com
@Generablefuncionaria - Construir lógica complexa de várias etapas em uma única solicitação — divida em várias solicitações focadas
- Assumir que o modelo está sempre disponível — elegibilidade do dispositivo e configurações variam
Quando Usar
- Geração de texto no dispositivo para aplicativos sensíveis à privacidade
- Extração de dados estruturados de entrada do usuário (formulários, comandos em linguagem natural)
- Recursos assistidos por IA que devem funcionar offline
- UI de streaming que mostra progressivamente conteúdo gerado
- Ações de IA específicas de domínio via chamada de ferramentas (busca, computação, consulta)