Criação de conteúdo e automação de tarefas utilizando modelos de base

Published on: 2025-10-07
Post image
pt llm foundation-mode apple ios swift inteligencia artificial modelo model tool tooling framework swiftui

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.