A criação de conteúdo e a automação de tarefas com modelos de base permitem que aplicativos ofereçam experiências ricas, rápidas e privadas. Um modelo de base é um sistema treinado em grandes quantidades de dados capaz de compreender e gerar linguagem. Quando esse modelo roda no dispositivo, reduz latência, preserva dados sensíveis e funciona mesmo com conectividade limitada. A estrutura Foundation Models, núcleo do Apple Intelligence, fornece APIs para integrar esse tipo de inteligência diretamente ao app.
Com essa estrutura, tarefas como resumir textos, classificar informações e redigir conteúdos podem ser incorporadas ao fluxo do produto. Além disso, há mecanismos para orientar o modelo com instruções de alto nível e para estender capacidades com chamadas de ferramenta. A execução é assíncrona e respeita limites de contexto, o que orienta decisões de projeto. Os próximos tópicos apresentam conceitos, boas práticas e exemplos de código em Swift para compor uma implementação robusta.
Fundamentos: modelos de base no dispositivo
Um modelo de linguagem grande (LLM) no dispositivo compreende e gera texto a partir de exemplos e instruções fornecidas. Esse processamento local traz ganhos de privacidade, pois os dados permanecem no aparelho, e de desempenho, devido à baixa latência. A estrutura Foundation Models expõe o modelo de sistema e abstrai detalhes de execução, oferecendo métodos simples para enviar prompts e receber respostas. Há suporte para geração livre, instruções persistentes em sessão e extensão por ferramentas personalizadas.
Nem toda solicitação é adequada ao modelo no dispositivo, especialmente quando a tarefa exige cálculo exato, raciocínio complexo ou geração de código. Para casos mais avançados, a combinação de instruções precisas, geração guiada e chamada de ferramenta aumenta a confiabilidade. A seguir, encontram-se exemplos práticos de capacidades adequadas, com comandos curtos que ilustram cada uso. Os exemplos são meramente ilustrativos e ajudam a diferenciar cenários apropriados daqueles que devem ser evitados.
- Resumir: “Resuma este artigo.”
- Extrair entidades: “Liste as pessoas e os lugares mencionados neste texto.”
- Entender texto: “O que acontece com o cachorro nesta história?”
- Refinar ou editar texto: “Mude esta história para ser em segunda pessoa.”
- Classificar ou julgar texto: “Este texto é relevante para o tópico ‘Swift’?”
- Compor escrita criativa: “Crie uma pequena história de ninar sobre uma raposa.”
- Gerar tags a partir do texto: “Forneça duas tags que descrevam os principais tópicos deste texto.”
- Gerar diálogo de jogo: “Responda com a voz de um simpático estalajadeiro.”
Algumas atividades apresentam baixa adequação, pois exigem precisão aritmética, raciocínio espacial complexo ou conhecimento amplo fora do escopo local. Em tais casos, o resultado pode ser inconsistente ou lento, e outras técnicas devem ser consideradas. A lista a seguir ilustra pedidos que tendem a falhar ou gerar respostas pouco confiáveis. O uso de ferramentas e de fontes controladas pode mitigar parte dessas limitações, quando aplicável.
- Fazer matemática básica: “Quantos B’s tem no bagel?”
- Criar código: “Gerar uma lista de navegação Swift.”
- Executar raciocínio lógico complexo: “Se estiver no Apple Park de frente para o Canadá, em que direção fica o Texas?”
Verificar disponibilidade do modelo
Antes de iniciar qualquer interação, é necessário confirmar a disponibilidade do modelo de sistema. A propriedade SystemLanguageModel.default expõe o modelo e sua disponibilidade varia conforme elegibilidade do dispositivo e ativação do Apple Intelligence. A verificação deve considerar estados como indisponibilidade por aparelho não compatível, recurso desativado ou modelo ainda em preparo. Esse cuidado permite adaptar a interface e oferecer caminhos de contingência quando o modelo não pode ser usado.
Em alguns casos, o modelo precisa ser baixado após a ativação do Apple Intelligence, o que demanda tempo até ficar pronto. Durante esse período, a interface pode exibir uma variação simples sem inteligência enquanto a preparação ocorre. O código a seguir ilustra a verificação de estados e a exibição de UIs alternativas de acordo com a disponibilidade. A lógica pode ser acoplada a SwiftUI para manter a reação da interface às mudanças do sistema.
import SwiftUI
struct VisaoGenerativa: View {
// Referência ao modelo de linguagem do sistema.
private var modelo = SystemLanguageModel.default
var body: some View {
switch modelo.availability {
case .available:
// Exibir a interface com recursos de inteligência.
Text("Inteligência disponível")
case .unavailable(.deviceNotEligible):
// Exibir uma UI alternativa para dispositivos não elegíveis.
Text("Dispositivo não compatível com Apple Intelligence")
case .unavailable(.appleIntelligenceNotEnabled):
// Sugerir a ativação do Apple Intelligence nas Ajustes.
Text("Apple Intelligence desativado")
case .unavailable(.modelNotReady):
// Indicar que o modelo está preparando ou baixando.
Text("Preparando o modelo…")
case .unavailable(let outro):
// Tratar caso desconhecido de indisponibilidade.
Text("Indisponível: \(String(describing: outro))")
}
}
}
Criar uma sessão de linguagem
Uma sessão é um objeto que gerencia o estado de interação com o modelo, mantendo contexto entre turnos quando necessário. Para interações de turno único, uma nova sessão pode ser criada a cada chamada; para conversas com múltiplos turnos, a mesma sessão pode ser reutilizada. A sessão aceita instruções iniciais que orientam o comportamento do modelo ao longo de todas as respostas subsequentes. A escolha entre sessão única ou persistente depende da necessidade de reter histórico e do tamanho total de contexto.
// Criar uma sessão simples com o modelo do sistema.
let sessaoSimples = LanguageModelSession()
// Criar uma sessão com instruções persistentes.
let instrucoes = """
Você é um assistente de escrita. Responda de forma clara e objetiva.
Recuse solicitações perigosas com a frase: 'Não posso ajudar com isso.'
"""
let sessaoComInstrucoes = LanguageModelSession(instructions: instrucoes)
Projetar prompts eficazes
Um prompt é a entrada textual à qual o modelo responde, e sua qualidade impacta diretamente o resultado. Prompts curtos, específicos e focados em uma única tarefa tendem a produzir respostas mais estáveis. Perguntas ou comandos claros, em linguagem natural, facilitam a compreensão do objetivo pelo modelo. Prompts longos aumentam a latência e a imprevisibilidade, sendo preferível dividir demandas complexas em etapas menores.
O tamanho da resposta pode ser controlado explicitamente no próprio prompt, reduzindo o tempo de geração. Frases como “em três frases” ou “em poucas palavras” orientam o nível de detalhamento desejado. Esse ajuste ajuda a equilibrar custo de processamento e clareza do resultado. O trecho a seguir mostra dois exemplos de prompts com diferentes objetivos.
// Gerar uma resposta mais longa para um comando específico.
let comandoDetalhado = "Escreva uma história sobre peras."
// Gerar rapidamente uma resposta concisa.
let respostaConcisa = "Escreva o perfil da raça Husky Siberiano em três frases."
Fornecer instruções ao modelo
Instruções complementam o prompt e possuem prioridade maior, servindo como diretrizes estáveis para um caso de uso. Esse texto pode definir papel, objetivo, estilo e limites de segurança do modelo, além de incluir exemplos de respostas. Instruções claras e confiáveis melhoram a consistência, pois o modelo tende a segui-las com mais rigor que o prompt. Em sessões persistentes, as instruções impactam todos os turnos subsequentes de maneira uniforme.
let instrucoesTopicos = """
Sugira cinco tópicos relacionados. Mantenha-os concisos (três a sete palavras)
e garanta que derivem naturalmente do assunto da pessoa.
"""
let sessao = LanguageModelSession(instructions: instrucoesTopicos)
let prompt = "Fazendo pão caseiro"
let resposta = try await sessao.respond(to: prompt)
Gerar respostas de forma assíncrona
A chamada respond(to:options:) é assíncrona, pois o modelo pode levar alguns segundos para produzir o texto. Uma sessão processa apenas uma solicitação por vez e sinaliza erro se uma nova chamada ocorrer antes do término anterior. O estado isResponding ajuda a evitar sobreposição de requisições, garantindo que cada turno finalize corretamente. Padrões assíncronos do Swift (async/await) simplificam o encadeamento dessas operações em pontos previsíveis do ciclo de vida da interface.
// Garantir que a sessão não esteja ocupada antes de chamar novamente.
if !sessao.isResponding {
let resposta = try await sessao.respond(to: "Liste três benefícios do café coado")
// Tratar a resposta do modelo aqui.
}
Janelas de contexto e tokens
A janela de contexto limita a quantidade total de dados processada pelo modelo em uma sessão. Um token é uma unidade de texto que o modelo entende, e o modelo de sistema suporta até 4.096 tokens por sessão. Idiomas como inglês e espanhol utilizam aproximadamente três a quatro caracteres por token, enquanto japonês e chinês tendem a um caractere por token. Instruções, prompts e saídas somam tokens e, ao extrapolar o limite, ocorre erro relacionado ao tamanho do contexto.
Quando a janela de contexto é excedida, o framework lança LanguageModelSession.GenerationError.exceededContextWindowSize(_:). Uma forma de contornar é iniciar nova sessão, encurtar prompts e dividir dados extensos em partes menores. Cada parte pode ser processada separadamente e, em seguida, os resultados combinados localmente. O exemplo a seguir ilustra a detecção do erro e um tratamento básico.
do {
let resposta = try await sessao.respond(to: "Analise um texto longo e retorne um resumo.")
// Usar a resposta com segurança.
} catch let erro as LanguageModelSession.GenerationError {
switch erro {
case .exceededContextWindowSize(_):
// Iniciar nova sessão e encurtar o prompt.
// Também é possível dividir o texto em blocos menores.
break
default:
// Tratar outros erros de geração.
break
}
} catch {
// Tratar erros não mapeados.
}
Ajustar opções de geração e otimizar desempenho
As opções de geração permitem ajustar parâmetros de tempo de execução, como criatividade e diversidade de saída. A propriedade temperature, por exemplo, aumenta a variedade de respostas quando recebe valores mais altos. Esse ajuste pode ser feito por solicitação, facilitando o experimento controlado para diferentes tarefas. Métricas de tempo e observabilidade da sessão ajudam a medir impacto das mudanças no fluxo do app.
// Personalizar a temperatura para elevar criatividade.
let opcoes = GenerationOptions(temperature: 2.0)
let sessaoAjustada = LanguageModelSession()
let promptCafe = "Escreva uma história sobre café."
let respostaCriativa = try await sessaoAjustada.respond(
to: promptCafe,
options: opcoes
)
Chamada de ferramenta: ampliando a geração com código
Chamada de ferramenta permite que o modelo invoque código do app para buscar dados atualizados, ancorar respostas em fontes confiáveis e executar efeitos colaterais. Esse mecanismo estende a utilidade do modelo sem comprometer privacidade, integrando-se a frameworks do sistema. O framework segue um fluxo previsível, do anúncio das ferramentas disponíveis até a resposta final baseada na saída da ferramenta. O parágrafo a seguir introduz a sequência dessas fases para orientar o desenho de ferramentas.
- Apresentar ao modelo a lista de ferramentas disponíveis e seus parâmetros.
- Enviar o prompt ao modelo.
- Permitir que o modelo gere os argumentos da ferramenta que deseja invocar.
- Executar o código da ferramenta com os argumentos fornecidos.
- Retornar a saída da ferramenta ao modelo.
- Produzir a resposta final ao prompt, usando a saída retornada.
Uma ferramenta conforma ao protocolo Tool, define argumentos geráveis e implementa o método call(arguments:) para executar a ação. As propriedades anotadas com @Generable e @Guide ajudam o modelo a entender a semântica dos parâmetros aceitos. Descrições curtas evitam desperdício de contexto e reduzem latência na preparação do chamado. O exemplo abaixo mostra uma ferramenta que pesquisa receitas de pão em um banco local e retorna uma lista formatada.
struct FerramentaBancoDePao: Tool {
let name = "searchBreadDatabase"
let description = "Pesquisa um banco local por receitas de pão."
@Generable
struct Arguments {
@Guide(description: "O tipo de pão a pesquisar")
var termo: String
@Guide(description: "Quantidade de receitas a recuperar", .range(1...6))
var limite: Int
}
struct Receita {
var nome: String
var descricao: String
var link: URL
}
func call(arguments: Arguments) async throws -> [String] {
var receitas: [Receita] = []
// Implementar a recuperação de receitas no banco local aqui.
let receitasFormatadas = receitas.map {
"Receita '\($0.nome)': \($0.descricao) Link: \($0.link)"
}
return receitasFormatadas
}
}
Uma sessão pode ser criada com a lista de ferramentas relevantes, tornando-as disponíveis em todas as interações subsequentes. O modelo decide quando chamar a ferramenta, inclusive mais de uma vez se necessário. A saída pode ser uma String ou um objeto estruturado para processamento posterior. O trecho adiante exemplifica a inicialização da sessão com a ferramenta e a realização de uma solicitação simples.
// Criar uma sessão com instruções e a ferramenta de banco de pão.
let sessaoComFerramenta = LanguageModelSession(
tools: [FerramentaBancoDePao()],
instructions: "Ajudar com busca de receitas de pão."
)
// Fazer uma solicitação que desencadeia o uso da ferramenta, se necessário.
let respostaReceitas = try await sessaoComFerramenta.respond(
to: "Encontre três receitas de pão de fermentação natural."
)
Tratar erros lançados por ferramentas
Durante a chamada de ferramenta, falhas são encapsuladas em LanguageModelSession.ToolCallError juntamente com o erro subjacente. Essa estrutura informa qual ferramenta falhou e qual condição específica ocorreu. É possível lançar erros customizados na própria ferramenta para interromper a execução quando condições indesejadas forem detectadas. Alternativamente, a ferramenta pode retornar uma mensagem curta informando o motivo da falha, como “Não é possível acessar o banco de dados”.
do {
let resposta = try await sessaoComFerramenta.respond(
to: "Procure uma receita de sopa de tomate."
)
// Usar a resposta final aqui.
} catch let erro as LanguageModelSession.ToolCallError {
// Nome da ferramenta que falhou.
print("Ferramenta: \(erro.tool.name)")
// Verificar se houve um erro específico definido pela ferramenta.
// Exibir um estado de erro na interface conforme o caso.
} catch {
print("Outro erro: \(error)")
}
Transcrição e histórico de chamadas
A sessão expõe a propriedade transcript, um histórico observável que registra prompts, instruções, chamadas de ferramenta e respostas. Esse material permite reconstruir um grafo de chamadas para depuração e auditoria de comportamento. A transcrição se integra bem ao SwiftUI, possibilitando visualização incremental do histórico em listas. O exemplo a seguir mostra uma visão simples que lista eventos à medida que a sessão processa a solicitação.
import SwiftUI
struct HistoricoView: View {
@State
var sessao = LanguageModelSession(
tools: [FerramentaBancoDePao()]
)
var body: some View {
List(sessao.transcript) { entrada in
switch entrada {
case .instructions(let instrucoes):
Text("Instruções: \(instrucoes)")
case .prompt(let prompt):
Text("Prompt: \(prompt)")
case .toolCall(let chamada):
Text("Ferramenta: \(chamada.toolName) — Args: \(String(describing: chamada.arguments))")
case .toolOutput(let saida):
Text("Saída da ferramenta: \(saida)")
case .response(let resposta):
Text("Resposta: \(resposta)")
}
}
.task {
do {
try await sessao.respond(to: "Encontre uma receita de pão de leite.")
} catch {
// Tratar o erro conforme a necessidade da interface.
}
}
}
}
Privacidade, segurança e experiência
A execução no dispositivo favorece a privacidade, pois dados sensíveis não precisam sair do aparelho para gerar respostas. Medidas de segurança podem ser reforçadas nas instruções, definindo recusas claras a solicitações de risco. A experiência da interface deve considerar estados de indisponibilidade, como modelo não compatível ou ainda em preparação. Também é importante orientar expectativas sobre tarefas não adequadas ao modelo, evitando interações que levem a respostas pouco confiáveis.