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. Oconfidence_scoresai 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ão0.3). A rodada principal usatemperature = 0(determinística); ask - 1rodadas de consistência usam este valor para diversificar as saídas. Desde a v0.3.3 é de fato repassado ao provedor viaellmer::params()— versões anteriores aceitavam o parâmetro mas não o aplicavam, inflandoconfidence_scoreespuriamente. -
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 progressoclicom 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. Requershinyecallr. 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
livevocê 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. Comlive = "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+Ce 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:
-
ac_qual_sample(): seleciona uma amostra representativa (estratificada, ou priorizando casos incertos viastrategy = "uncertainty"). -
ac_qual_export_for_review(): exporta para.xlsxcom a colunacategoria_humanoem branco para o revisor preencher. -
ac_qual_import_human(): reimporta o Excel preenchido. -
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:
-
Metadados — data, versão do
acR, autor(es), método - Codebook completo — categorias, definições, exemplos ± e pesos
- Histórico de modificações — auditoria das alterações no codebook
- Configuração da LLM — provedor, modelo, temperatura, k, reasoning
-
Resultados — distribuição por categoria, quartis de
confidence_score, contagem de casos com confiança < 0.80 -
Confiabilidade inter-codificador — se
ac_qual_reliability()foi rodado, tabela completa com IC 95% - Referências metodológicas — Krippendorff, Landis-Koch, Gilardi, Wang, Gwet
- 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
