FoundationModels: LLM no Dispositivo (iOS 26)
Padrões para integrar o modelo de linguagem local da Apple em aplicativos usando o framework FoundationModels. Abrange geração de texto, saída estruturada com @Generable, chamada de ferramentas personalizadas e streaming de snapshot — tudo executado no dispositivo para privacidade e suporte offline.
Quando Ativar
- Construindo funcionalidades baseadas em IA usando Apple Intelligence no dispositivo
- Gerando ou resumindo texto sem dependência de nuvem
- Extraindo dados estruturados de entrada de linguagem natural
- Implementando chamada de ferramentas personalizadas para ações de IA específicas de domínio
- Transmitindo respostas estruturadas para atualizações de interface em tempo real
- Necessidade de IA que preserve 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("Ative o Apple Intelligence nas Configurações")
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
// Conversa única: crie 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)
// Múltiplas conversas: reuse a 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 ("Para solicitações perigosas, responda 'Não posso ajudar com isso'")
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 abrigo 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
Permite 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 que correspondam a um termo 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 uma 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 Ferramentas
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 {
// Trate erro específico da ferramenta
}
}
Padrão Principal — Streaming de Snapshot
Transmita respostas estruturadas para interface 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 emocionantes de viagem?",
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 snapshot (não deltas) | Amigável para saída estruturada; cada snapshot é um estado parcial completo |
Macro @Generable | Segurança em tempo de compilação para geração estruturada; gera automaticamente o tipo PartiallyGenerated |
| Solicitação única por sessão | isResponding impede solicitações concorrentes; crie várias sessões se necessário |
response.content (não .output) | API correta — sempre acesse resultados via propriedade .content |
Boas 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 prompts - Verifique
isRespondingantes de enviar uma nova solicitação — as sessões tratam uma solicitaçã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 + prompt + 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 Xcode Instruments para analisar o desempenho das solicitações
Antipadrões a Evitar
- Criar sessões sem verificar
model.availabilityprimeiro - Enviar entradas que excedem a janela de contexto de 4.096 tokens
- Tentar solicitações concorrentes em uma única sessão
- Usar
.outputem vez de.contentpara acessar dados de resposta - Analisar respostas de string bruta quando a saída estruturada
@Generablefuncionaria - Construir lógica complexa de várias etapas em um único prompt — divida em prompts focados
- Supor 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
- Interface de streaming que mostra progressivamente conteúdo gerado
- Ações de IA específicas de domínio via chamada de ferramentas (pesquisa, computação, consulta)