Skip to contents

Visão geral

O módulo qualitativo do acR implementa um pipeline completo de análise de conteúdo assistida por modelos de linguagem (LLMs), seguindo as diretrizes metodológicas de Krippendorff (2018) e as recomendações empíricas de Gilardi, Alizadeh e Kubli (2023), que mostraram que LLMs modernas superam trabalhadores de plataformas em várias tarefas de anotação de textos políticos — desde que guiadas por um codebook bem construído e validadas por revisão humana.

O pipeline tem sete etapas encadeadas:

ac_fetch_camara() / ac_fetch_senado()        ← coleta
         ↓
ac_corpus()                                  ← estruturação
         ↓
ac_qual_codebook() + funções de gestão       ← codebook
         ↓
as_prompt()                                  ← system prompt (opcional)
         ↓
ac_qual_code()                               ← classificação com LLM
         ↓
ac_qual_sample() + ac_qual_reliability()     ← validação humana
         ↓
ac_qual_report()                             ← relatório reprodutível

A função ac_qual_code() aceita o argumento chat =, que recebe qualquer objeto Chat do pacote ellmer (Wickham et al., 2025). Isso permite usar qualquer provedor — Groq, OpenAI, Anthropic, Google Gemini, Ollama, Mistral, DeepSeek, OpenRouter — sem alterar a lógica de análise. Também suporta modelos locais via Ollama para pesquisas com dados sensíveis.

Por que um codebook explícito? A alternativa naïve seria pedir para o modelo “classificar o texto como positivo ou negativo”. Isso funciona mal porque cada LLM tem prior próprio sobre o que essas palavras significam. Um codebook transforma a classificação em operacionalização reproduzível: outro pesquisador (humano ou LLM) chega aos mesmos rótulos aplicando as mesmas definições e exemplos.

Quando usar LLM (e quando não usar)

Antes de acender API, vale escolher a técnica certa para a pergunta. LLM não é o melhor caminho para todo problema de análise de conteúdo — é caro, tem variabilidade estocástica e depende de um provedor externo. A tabela abaixo resume quando cada abordagem do acR faz sentido:

Você quer… Use
Rotular textos com categorias teóricas pré-definidas (populismo, framing) LLM + codebook — esta vignette
Medir valência afetiva (positivo/negativo/neutro) em português ac_sentiment() (léxico OpLexicon) — mais rápido, determinístico, sem chave de API
Descobrir tópicos emergentes sem categorias a priori ac_lda() — modelo probabilístico não supervisionado
Detectar tipologias latentes para amostragem estratificada ac_cluster_documents() — hard clustering; veja a vignette("cluster")
Comparar vocabulário entre dois ou mais grupos ac_keyness() — teste estatístico, sem LLM
Explorar vocabulário distintivo dentro de cada documento ac_tf_idf()

Regra prática: se a categoria pode ser detectada por palavras-chave, use léxico ou keyness — é mais barato, reprodutível e defensável. LLM brilha quando a categoria depende de contexto, ironia, referência implícita ou raciocínio sobre argumento — populismo, framing, posicionamento, tom, estilo retórico.

Custo real de um estudo com LLM

Para calibrar expectativas, estimativa em dólares para um corpus típico brasileiro (500 discursos parlamentares, ~800 tokens cada) com k_consistency = 3 e reasoning = TRUE, reasoning_length = "medium":

Modelo Custo estimado Tempo estimado
groq/llama-3.3-70b-versatile US$ 0,50 – 1 ~10 min
openai/gpt-4o-mini US$ 3 – 5 ~15 min
anthropic/claude-sonnet-4-5 US$ 10 – 20 ~25 min
anthropic/claude-opus-4-7 US$ 40 – 80 ~40 min
ollama/llama3.1:70b (local) US$ 0 (GPU) ~2 h em RTX 4090

ac_qual_recommend_model() (chamado a seguir) sugere o modelo mais custo-efetivo para o tamanho e a dificuldade da sua tarefa.


Instalação e configuração

Antes de começar, garanta que o ellmer está instalado e que a chave da API do seu provedor está no ambiente (.Renviron é o local usual — nunca no código versionado):

# Instalar acR
remotes::install_github("andersonheri/acR")

# Instalar ellmer
install.packages("ellmer")

# Configurar chave de API (exemplo: Anthropic)
Sys.setenv(ANTHROPIC_API_KEY = "sk-ant-...")

Se você não sabe qual provedor escolher, use ac_qual_recommend_model() — a função consulta um banco interno com custo, contexto e qualidade estimada por tipo de tarefa, sem precisar de rede.


Etapa 1 — Criar o codebook

O codebook é o instrumento central da análise de conteúdo. Ele define as categorias analíticas, suas definições operacionais, exemplos positivos (o que É a categoria) e negativos (o que NÃO é, para desambiguar categorias vizinhas), e pesos relativos que orientam a LLM em fronteiras difíceis.

Dica metodológica: exemplos negativos são frequentemente subestimados. Um exemplo negativo para “populista” — como “Apresento esta emenda técnica” — ajuda muito mais que dois positivos adicionais. Eles servem de contraste, reduzindo confusões entre categorias similares.

library(acR)

cb <- ac_qual_codebook(
  name         = "tom_discurso",
  instructions = "Classifique o tom geral do discurso parlamentar.",
  categories   = list(
    positivo = list(
      definition   = "Discurso com tom propositivo e colaborativo.",
      examples_pos = c("Proponho que trabalhemos juntos nesta agenda."),
      examples_neg = c("Este governo é um desastre completo."),
      weight       = 1
    ),
    negativo = list(
      definition   = "Discurso com tom crítico ou confrontacional.",
      examples_pos = c("Esta proposta vai arruinar o país."),
      examples_neg = c("Apresento esta emenda para melhorar o texto."),
      weight       = 1.5  # categoria mais difícil: peso maior no prompt
    ),
    neutro = list(
      definition   = "Discurso descritivo, sem posicionamento claro.",
      examples_pos = c("O projeto foi apresentado na sessão de hoje."),
      weight       = 1
    )
  ),
  multilabel = FALSE,   # cada doc recebe UMA categoria; se TRUE, a coluna
                        # `categoria` na saida vira uma string pipe-separada
                        # ("tecnica|politica") quando mais de uma se aplicar.
  lang       = "pt"
)
#> ! Categoria "neutro": sem exemplos negativos (examples_neg).
#>  Exemplos negativos reduzem confusão entre categorias similares.

print(cb)
#> 
#> ── Codebook acR: "tom_discurso" ────────────────────────────────────────────────
#>  Modo: "manual"
#>  Categorias (3): "positivo", "negativo", and "neutro"
#>  Multilabel: FALSE
#>  Idioma: "pt"
#>  Criado em: 07/08/2026 17:03
#> 
#> Instrução geral:
#> Classifique o tom geral do discurso parlamentar.
#> 
#> Categorias:
#> • "positivo": Discurso com tom propositivo e colaborativo.
#> Ex+: Proponho que trabalhemos juntos nesta agenda.
#> • "negativo" [peso: 1.5]: Discurso com tom crítico ou confrontacional.
#> Ex+: Esta proposta vai arruinar o país.
#> • "neutro": Discurso descritivo, sem posicionamento claro.
#> Ex+: O projeto foi apresentado na sessão de hoje.

Adicionar e remover categorias

Codebooks quase nunca ficam corretos na primeira tentativa. As funções ac_qual_codebook_add() e ac_qual_codebook_remove() permitem refinamento iterativo — comum quando você roda o codebook numa amostra piloto e percebe que faltou uma categoria (“técnico”) ou que duas se sobrepõem.

Todas as modificações ficam registradas em codebook$history para reprodutibilidade metodológica.

# Adicionar categoria iterativamente
cb <- ac_qual_codebook_add(cb,
  tecnico = list(
    definition   = "Discurso com linguagem técnica e referências normativas.",
    examples_pos = c("Conforme o art. 37 da CF, a administração pública..."),
    weight       = 1
  )
)

# Remover se necessário
cb <- ac_qual_codebook_remove(cb, "tecnico")

Etapa 2 — Enriquecer o codebook com literatura

Definir categorias apenas com base no conhecimento do pesquisador funciona, mas ancorar as definições em literatura publicada aumenta a validade de construto. O acR oferece dois modos automatizados:

  • Híbrido (ac_qual_codebook_hybrid): parte do seu codebook manual e enriquece cada categoria com citações teóricas relevantes, sem alterar os exemplos que você já validou.
  • Literature (mode = "literature"): constrói o codebook do zero a partir da literatura sobre um conceito. Útil quando você tem clareza do conceito teórico mas quer que o pacote extraia as dimensões operacionais.

Modo híbrido: definições ancoradas em referências

ac_qual_codebook_hybrid() re-ancora as definições manuais em referências bibliográficas buscadas via LLM, preservando os exemplos originais:

cb_hybrid <- ac_qual_codebook_hybrid(
  codebook = cb,
  model    = "anthropic/claude-sonnet-4-5",
  journals = "default",  # periódicos de CP/CS brasileiros e internacionais
  n_refs   = 3L,
  lang     = "pt"
)

# Ver definição enriquecida da categoria "negativo"
cat(cb_hybrid$categories$negativo$definition)
cat("\nReferências:\n")
print(cb_hybrid$categories$negativo$references)

Modo literature: construção inteiramente baseada em literatura

Aqui você fornece o conceito que quer capturar (em inglês, para melhor recall na base OpenAlex) e o pacote gera categoria, definição e exemplos.

cb_lit <- ac_qual_codebook(
  name         = "frames_politicos",
  instructions = "Identifique o frame predominante no discurso.",
  categories   = list(
    conflito    = list(definition = "", concept = "conflict framing politics"),
    consenso    = list(definition = "", concept = "consensus framing politics"),
    moralidade  = list(definition = "", concept = "moral framing political discourse")
  ),
  mode  = "literature",
  model = "anthropic/claude-sonnet-4-5",
  lang  = "pt"
)

Etapa 3 — Fundir e traduzir codebooks

Análises multidimensionais frequentemente combinam duas ou mais tipologias (tom + estilo, posicionamento + tema, etc.). ac_qual_codebook_merge() faz essa combinação, com controle de conflitos entre categorias de mesmo nome.

Traduzir codebooks é útil para: replicabilidade internacional (publicar versão em inglês do instrumento), corpora bilíngues, ou apenas para checar se as definições sobrevivem sem ambiguidade em outra língua.

Fundir dois codebooks

cb_estilo <- ac_qual_codebook(
  name         = "estilo_retórico",
  instructions = "Classifique o estilo retórico dominante.",
  categories   = list(
    pathos  = list(definition = "Apelo emocional predominante."),
    logos   = list(definition = "Apelo racional/argumentativo predominante."),
    ethos   = list(definition = "Apelo à autoridade ou credibilidade do orador.")
  )
)

# Fundir: tom + estilo retórico em um único codebook
cb_completo <- ac_qual_codebook_merge(
  cb1          = cb_hybrid,
  cb2          = cb_estilo,
  name         = "discurso_parlamentar",
  on_conflict  = "rename_second",
  instructions = "Classifique o tom e o estilo retórico do discurso."
)

Traduzir para inglês

cb_en <- ac_qual_codebook_translate(
  codebook           = cb_completo,
  to                 = "en",
  model              = "anthropic/claude-sonnet-4-5",
  translate_examples = TRUE
)

Etapa 4 — Inspecionar histórico e gerar system prompt

Todo codebook mantém um history das modificações — quem alterou, quando e o quê. Isso é essencial para publicação: o revisor pode auditar exatamente como o instrumento foi construído.

O as_prompt() converte o objeto ac_codebook em uma string de system prompt formatada, pronta para ser injetada num objeto Chat do ellmer. Você pode usar essa string diretamente se quiser rodar a classificação com sua própria lógica de retry/paralelismo.

# Ver todas as modificações feitas no codebook
ac_qual_codebook_history(cb_completo)
# Gerar system prompt para uso direto com ellmer
prompt <- as_prompt(
  cb_completo,
  reasoning        = TRUE,           # pede raciocinio estruturado
  reasoning_length = "medium"        # "short" | "medium" | "detailed"
)

# O prompt pode ser passado diretamente a um objeto Chat:
# chat$set_system_prompt(prompt)

Sobre reasoning: pedir raciocínio aumenta a qualidade da classificação em casos difíceis (o modelo “pensa antes de responder”), mas dobra o custo por documento (mais tokens gerados). Use "short" como padrão; "detailed" só quando você planeja auditar decisões individualmente.


Etapa 5 — Classificar o corpus

Com codebook pronto e corpus estruturado, ac_qual_code() faz a classificação em lotes. Três parâmetros são chave:

  • k_consistency: número de repetições de self-consistency (Wang et al., 2022). O mesmo texto é classificado k× (padrão 3) com pequena variação de temperatura, e a categoria final é a moda. O confidence_score sai daí — 1.0 = todas as k rodadas concordaram; 0.67 = 2 de 3.
  • temperature: variação estocástica entre as rodadas de self-consistency (padrão 0.3). A rodada principal usa temperature = 0 (determinística); as k - 1 rodadas de consistência usam este valor para diversificar as saídas. Desde a v0.3.3 é de fato repassado ao provedor via ellmer::params() — versões anteriores aceitavam o parâmetro mas não o aplicavam, inflando confidence_score espuriamente.
  • reasoning: pede raciocínio estruturado em JSON.
  • live: visualização em tempo real da classificação, tirando o processo do “caixa-preta”. Ver blockquote abaixo.
library(ellmer)

# Coletar e estruturar corpus
discursos <- ac_fetch_camara(
  id_deputado = 204379,
  data_inicio = "2023-01-01",
  data_fim    = "2023-06-30"
)
corpus <- ac_corpus(discursos, text_col = "transcricao", id_col = "id")
# Classificar
chat <- chat_anthropic(model = "claude-sonnet-4-5")

resultado <- ac_qual_code(
  corpus        = corpus,
  codebook      = cb_completo,
  chat          = chat,
  k_consistency = 3,               # self-consistency
  live          = "terminal"       # ver a maquina classificando ao vivo
)

head(resultado)

O tibble de saída traz: doc_id, categoria, confidence_score (via self-consistency), reasoning (raciocínio do modelo, se pedido) e os metadados originais do corpus.

Live view: tirando o LLM do caixa-preta

O argumento live de ac_qual_code() mostra a máquina classificando em tempo real. Três modos:

  • live = "off" (padrão) — sem visualização.
  • live = "terminal" — barra de progresso cli com doc atual, categoria, confiança e início do raciocínio. Zero dependências, roda em qualquer setup (batch, CI, servidor sem interface gráfica).
  • live = "shiny" — janela Shiny em background com tabela atualizando a cada 500 ms, destaque para casos de baixa confiança. Requer shiny e callr. Ideal para apresentações e demonstrações.
resultado <- ac_qual_code(
  corpus, cb_completo, chat = chat,
  live = "terminal"
)
# 42/120 | ============>              35% | ETA 2m10s
#   doc_042 -> populista  (conf 1.00) "Apela ao povo contra elite..."

Por que isso importa? Numa rodada de 500 documentos, sem live você não sabe se o modelo travou, se está devolvendo lixo, ou se está indo bem — só descobre no fim, depois de gastar todo o orçamento de tokens. Com live = "terminal", você vê nos primeiros 10 docs se o raciocínio faz sentido e a distribuição de categorias está plausível. Se algo estiver errado, Ctrl+C e ajusta.


Etapa 6 — Salvar e carregar

Codebook e resultados devem ser serializados para replicabilidade. YAML foi escolhido por ser legível por humanos e versionável em Git.

# Salvar em YAML para replicabilidade
ac_qual_save_codebook(cb_completo, path = "codebook_discurso.yaml")

# Carregar em outra sessão
cb_recarregado <- ac_qual_load_codebook("codebook_discurso.yaml")

Etapa 7 — Validação e confiabilidade

Sem validação humana, não há análise de conteúdo publicável. LLM é uma ferramenta poderosa, mas nenhuma referência metodológica aceita hoje um estudo de análise categorial sem um subconjunto codificado por humano e métricas de concordância entre codificadores.

O fluxo mínimo:

  1. ac_qual_sample(): seleciona uma amostra representativa (estratificada, ou priorizando casos incertos via strategy = "uncertainty").
  2. ac_qual_export_for_review(): exporta para .xlsx com a coluna categoria_humano em branco para o revisor preencher.
  3. ac_qual_import_human(): reimporta o Excel preenchido.
  4. ac_qual_reliability(): calcula percent agreement, alpha de Krippendorff, AC1 de Gwet e F1 macro, com IC 95% via bootstrap.

Quanto amostrar? Regra prática (Krippendorff 2018): pelo menos 10% do corpus, no mínimo 30 documentos, priorizando os casos com confidence_score < 0.8 (é onde o modelo mais tende a errar).

# Amostrar 30 documentos priorizando os incertos
amostra <- ac_qual_sample(resultado, n = 30, strategy = "uncertainty")
ac_qual_export_for_review(amostra, path = "revisao.xlsx", corpus = corpus)

# Após preenchimento manual, calcular IRR
revisado <- ac_qual_import_human("revisao_preenchida.xlsx")
irr      <- ac_qual_reliability(llm = resultado, human = revisado)
print(irr)

Interpretação (Landis & Koch, 1977 / Gwet, 2014):

Alpha/Kappa Interpretação
< 0.20 Baixa concordância
0.21 – 0.40 Razoável
0.41 – 0.60 Moderada
0.61 – 0.80 Substancial
> 0.80 Quase perfeita

Para análises publicáveis, valores acima de 0.67 (Krippendorff) ou 0.61 (Landis-Koch) são geralmente aceitos, com discussão dos casos divergentes na seção de método.


Diagnosticando problemas comuns

Antes de partir para o relatório final, vale conhecer os quatro modos de falha mais frequentes de uma rodada com LLM — e o que cada um pede.

1. Categorias com concordância baixa entre k_consistency

Sintoma: confidence_score mediano abaixo de 0,80.

Causa provável: definições ambíguas ou exemplos negativos ausentes. A LLM está literalmente hesitando entre categorias em cada repetição.

Fix: rodar ac_qual_codebook(check_overlap = TRUE) para detectar definições semanticamente próximas; adicionar exemplos negativos cruzados (o que uma categoria não é, especialmente perto das vizinhas); revisar manualmente 5–10 documentos com confidence_score < 0.67 e verificar se o problema está no texto (documento genuinamente ambíguo) ou no codebook.

2. LLM devolve categoria fora do codebook

Sintoma: categoria como “misto”, “outro” ou uma variante da sua (“populismo_forte”) que não está no codebook$categories.

Causa: o modelo entendeu a tarefa mas achou a categoria correta insuficiente.

Fix: ou você acrescenta a categoria emergente (via ac_qual_codebook_add()), ou reforça no prompt que só as categorias listadas são válidas. A segunda geralmente é preferível — categorias emergentes indicam refinamento metodológico, não desvio da LLM.

3. Distribuição colapsada em uma categoria

Sintoma: 90%+ dos documentos vão para o mesmo rótulo.

Causa: categoria com definição muito abrangente ou peso implicitamente alto (mais exemplos positivos que as outras).

Fix: equilibrar o número de exemplos positivos por categoria; usar o argumento weight para dar peso extra às categorias mais raras; considerar multilabel = TRUE se os documentos genuinamente cobrem mais de um tema.

4. IRR humana × LLM inaceitável

Sintoma: ac_qual_reliability() retorna α < 0,60.

Causa: três possibilidades, em ordem de frequência: (i) codebook não está claro nem para humanos (peça a outro humano codificar; se dois humanos discordam, a LLM não vai salvar), (ii) modelo escolhido é pequeno demais para a tarefa (troque para um tier acima), (iii) o próprio conceito teórico é contestado — reformule a pergunta.

Regra de ouro: se a IRR entre dois humanos está abaixo de 0,70, o problema é do codebook, não da LLM. Nenhum modelo vai ter mais concordância consigo mesmo do que humanos treinados têm entre si.

Etapa 8 — Replicabilidade e transparência

Análises com LLM têm um risco de comunicação: o revisor ou leitor não consegue reproduzir a rodada sem saber exatamente que modelo, que codebook, que parâmetros e que amostra foram usados. ac_qual_report() gera um documento estruturado com todas essas decisões, pronto para anexar como material suplementar de um artigo ou como apêndice metodológico.

O relatório cobre oito seções:

  1. Metadados — data, versão do acR, autor(es), método
  2. Codebook completo — categorias, definições, exemplos ± e pesos
  3. Histórico de modificações — auditoria das alterações no codebook
  4. Configuração da LLM — provedor, modelo, temperatura, k, reasoning
  5. Resultados — distribuição por categoria, quartis de confidence_score, contagem de casos com confiança < 0.80
  6. Confiabilidade inter-codificador — se ac_qual_reliability() foi rodado, tabela completa com IC 95%
  7. Referências metodológicas — Krippendorff, Landis-Koch, Gilardi, Wang, Gwet
  8. Sugestão de citação — formatada em ABNT (PT) ou APA (EN)

Uso mínimo:

# Apos ter o resultado da classificacao
ac_qual_report(
  coded     = resultado,
  codebook  = cb_completo,
  chat      = chat,                     # opcional: extrai provedor/modelo
  author    = "Anderson Henrique",
  path      = "relatorio_metodo.md"
)

Uso completo (com IRR e HTML autocontido):

irr <- ac_qual_reliability(llm = resultado, human = revisado)

ac_qual_report(
  coded       = resultado,
  codebook    = cb_completo,
  reliability = irr,
  chat        = chat,
  title       = "Codificação do posicionamento parlamentar - 57ª legislatura",
  author      = "Silva, A.; Souza, B.",
  method      = "Discursos coletados via API da Câmara (jan-jun/2023, n=847).",
  format      = "html",
  path        = "metodo_apendice.html",
  lang        = "pt"       # ou "en" para submissao internacional
)

O arquivo gerado é autocontido (HTML embute todos os estilos; MD é Markdown puro versionável em Git). Pode ser anexado como material suplementar do artigo, incluído como apêndice, ou publicado num repositório de dados junto com o codebook em YAML (ac_qual_save_codebook()) e a saída bruta da LLM.

Nota metodológica. A prática de publicar codebook + rodada da LLM + métricas IRR num único documento é a transparência mínima para análise assistida por LLM em ciências sociais. Sem isso, o leitor tem que confiar no autor — algo que a tradição de análise de conteúdo (Krippendorff, 2018) sempre rejeitou. O ac_qual_report() empacota essa transparência num único comando.


Referências

Gilardi, F., Alizadeh, M., & Kubli, M. (2023). ChatGPT outperforms crowd workers for text-annotation tasks. PNAS, 120(30). https://doi.org/10.1073/pnas.2305016120

Gwet, K. L. (2014). Handbook of Inter-Rater Reliability (4th ed.). Advanced Analytics.

Krippendorff, K. (2018). Content Analysis: An Introduction to Its Methodology (4th ed.). SAGE.

Landis, J. R., & Koch, G. G. (1977). The measurement of observer agreement for categorical data. Biometrics, 33(1), 159-174.

Maerz, S., & Benoit, K. (2025). quallmer: Qualitative Analysis with Large Language Models in R. GitHub. https://github.com/quallmer/quallmer

Sampaio, R. C., & Lycarião, D. (2021). Análise de conteúdo categorial: manual de aplicação. Brasília: ENAP.

Wickham, H., et al. (2025). ellmer: Chat with Large Language Models. R package. https://ellmer.tidyverse.org