The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Esta vignette demonstra o pipeline completo do acR
aplicado à classificação do posicionamento de textos legislativos. O
corpus cobre três proposições de alta saliência na 57ª legislatura
brasileira: a jornada 6x1 (PLP 300/2023), a anistia dos atos de 8 de
Janeiro (PL 2858/2022) e a Reforma Administrativa (PEC 32/2020).
O objetivo é classificar cada texto em uma categoria de posicionamento (Favorável, Contrário, Neutro/Técnico, Populista ou Ambíguo), calcular o grau de certeza via self-consistency e validar os resultados com revisão humana.
remotes::install_github("andersonheri/acR")
install.packages("ellmer")
# Configurar chave de API no .Renviron
usethis::edit_r_environ()
# Adicione: GROQ_API_KEY=sua_chaveO corpus é criado a partir de um data.frame com os
textos e metadados. Cada proposição tem três documentos: a ementa
técnica, um discurso favorável e um discurso contrário.
library(acR)
library(ellmer)
library(dplyr)
textos <- c(
"Altera a CLT para instituir a escala 4x3, vedando a jornada 6x1.",
"Conquista histórica: votaremos favoráveis com convicção.",
"Gerará desemprego em massa nas micro e pequenas empresas.",
"Concede anistia aos condenados pelos atos de 8 de janeiro de 2023.",
"Presos políticos: votar pela anistia é votar pela democracia.",
"Anistiar quem atacou o Congresso é uma afronta à democracia.",
"Modifica a CF/88, extinguindo a estabilidade para novos servidores.",
"Modernização necessária para aumentar eficiência e reduzir custos.",
"Ataca direitos conquistados e abre espaço para perseguição política."
)
df <- data.frame(
id = c("plp300_ementa", "plp300_favor", "plp300_contra",
"pl2858_ementa", "pl2858_favor", "pl2858_contra",
"pec32_ementa", "pec32_favor", "pec32_contra"),
texto = textos,
tema = c("6x1", "6x1", "6x1",
"anistia", "anistia", "anistia",
"reform", "reform", "reform"),
tipo = c("ementa", "favoravel", "contrario",
"ementa", "favoravel", "contrario",
"ementa", "favoravel", "contrario"),
stringsAsFactors = FALSE
)
corpus <- ac_corpus(df, text = texto, docid = id)
print(corpus)## -- Corpus acR --
## * Documentos: 9
## * Metadados: 2 colunas (tema, tipo)
## * Idioma: "pt"
O codebook define as cinco categorias de posicionamento com suas
definições operacionais. A instrução orienta o modelo a usar
Populista para textos com apelo emocional sem argumentação
substantiva — uma distinção importante para a análise de discursos
parlamentares brasileiros.
codebook <- ac_qual_codebook(
name = "posicionamento_proposicoes",
instructions = paste(
"Classifique o posicionamento do texto em relação à proposição.",
"Use Populista para apelo emocional sem argumentação substantiva."
),
categories = list(
favoravel = list(definition = "Apoio explícito à proposição com argumentação substantiva."),
contrario = list(definition = "Oposição à proposição com argumentação substantiva."),
neutro_tecnico = list(definition = "Descrição jurídica ou técnica sem valoração explícita."),
populista = list(definition = "Apelo emocional sem evidência ou argumentação técnica."),
ambiguo = list(definition = "Posicionamento contraditório ou impossível de classificar.")
),
mode = "manual"
)
print(codebook)## -- Codebook acR: "posicionamento_proposicoes" --
## * Modo: "manual"
## * Categorias (5): "favoravel", "contrario", "neutro_tecnico",
## "populista" and "ambiguo"
## * Multilabel: FALSE
## * Idioma: "pt"
O acR oferece um conjunto de funções para refinar o
codebook iterativamente antes de iniciar a classificação. Este passo é
opcional mas recomendado para análises com categorias complexas ou
sobrepostas.
Exemplos positivos e negativos melhoram a precisão da classificação,
especialmente em categorias limítrofes como populista e
ambiguo (Sampaio & Lycarião, 2021):
# Recriar com exemplos explícitos para as categorias mais difíceis
codebook <- ac_qual_codebook(
name = "posicionamento_proposicoes",
instructions = paste(
"Classifique o posicionamento do texto em relação à proposição.",
"Use Populista para apelo emocional sem argumentação substantiva."
),
categories = list(
favoravel = list(
definition = "Apoio explícito à proposição com argumentação substantiva.",
examples_pos = c("Esta reforma aumentará a produtividade e reduzirá custos."),
examples_neg = c("Votaremos com o coração pela causa do povo.")
),
contrario = list(
definition = "Oposição à proposição com argumentação substantiva.",
examples_pos = c("Os dados mostram que esta medida aumentará o desemprego."),
examples_neg = c("Esta proposta é uma vergonha nacional.")
),
neutro_tecnico = list(
definition = "Descrição jurídica ou técnica sem valoração explícita.",
examples_pos = c("Altera o art. 58 da CLT, estabelecendo nova escala."),
examples_neg = c("Apoiamos esta proposta de modernização.")
),
populista = list(
definition = "Apelo emocional sem evidência ou argumentação técnica.",
examples_pos = c("Presos políticos: este é o momento da justiça!"),
examples_neg = c("Estudos mostram que a medida reduz 15% dos custos."),
weight = 1.5 # categoria mais difícil: peso maior
),
ambiguo = list(
definition = "Posicionamento contraditório ou impossível de classificar.",
examples_pos = c("Apoio a reforma mas temo suas consequências sociais."),
weight = 1.2
)
),
mode = "manual"
)ac_qual_codebook_hybrid() re-ancora as definições em
referências bibliográficas buscadas via LLM, preservando os exemplos
manuais:
chat_obj <- chat_groq(
model = "llama-3.3-70b-versatile",
echo = "none"
)
codebook_enriquecido <- ac_qual_codebook_hybrid(
codebook = codebook,
chat = chat_obj,
n_refs = 2L,
lang = "pt"
)
# Ver definição enriquecida da categoria populista
cat(codebook_enriquecido$categories$populista$definition)
cat("\nReferências:\n")
print(codebook_enriquecido$categories$populista$references)Para análises multidimensionais, é possível fundir dois codebooks. Aqui adicionamos uma dimensão de estilo retórico ao codebook de posicionamento:
codebook_retorico <- ac_qual_codebook(
name = "estilo_retorico",
instructions = "Identifique o estilo retórico dominante.",
categories = list(
pathos = list(
definition = "Apelo emocional predominante.",
examples_pos = c("Não podemos trair a esperança do povo brasileiro.")
),
logos = list(
definition = "Apelo racional e argumentativo predominante.",
examples_pos = c("Os dados do IBGE confirmam redução de 12% no emprego.")
)
)
)
# Fundir: posicionamento + estilo retórico
codebook_completo <- ac_qual_codebook_merge(
cb1 = codebook_enriquecido,
cb2 = codebook_retorico,
name = "posicionamento_e_estilo",
on_conflict = "rename_second",
instructions = paste(
"Classifique o posicionamento e o estilo retórico do texto.",
"Use Populista para apelo emocional sem argumentação."
)
)# Gerar system prompt para uso com ellmer
prompt <- as_prompt(
codebook_enriquecido,
reasoning = TRUE,
reasoning_length = "short"
)
# Inspecionar todas as modificações feitas no codebook
ac_qual_codebook_history(codebook_enriquecido)## -- Histórico: "posicionamento_proposicoes" --
## # A tibble: 1 x 3
## timestamp action detail
## <chr> <chr> <chr>
## 1 2025-06-01 14:23:01 hybrid favoravel, contrario, neutro_tecnico, ...
O argumento chat = recebe qualquer objeto
Chat do ellmer. Aqui usamos o Groq com
llama-3.3-70b-versatile, que oferece plano gratuito e
latência baixa — ideal para corpora de tamanho médio como este.
chat_obj <- chat_groq(
model = "llama-3.3-70b-versatile",
echo = "none"
)
resultado <- ac_qual_code(
corpus = corpus,
codebook = codebook_enriquecido,
chat = chat_obj,
confidence = "total",
k_consistency = 3L,
reasoning = TRUE,
reasoning_length = "short"
)Os resultados esperados para este corpus, dado o conteúdo inequívoco dos textos, são classificações com confiança alta (≥ 0.80) em todos os documentos:
## # A tibble: 9 × 5
## doc_id tema tipo categoria confidence_score
## <chr> <chr> <chr> <chr> <dbl>
## 1 plp300_ementa 6x1 ementa neutro_tecnico 1.000
## 2 plp300_favor 6x1 favoravel favoravel 1.000
## 3 plp300_contra 6x1 contrario contrario 1.000
## 4 pl2858_ementa anistia ementa neutro_tecnico 1.000
## 5 pl2858_favor anistia favoravel populista 0.667
## 6 pl2858_contra anistia contrario contrario 1.000
## 7 pec32_ementa reform ementa neutro_tecnico 1.000
## 8 pec32_favor reform favoravel favoravel 1.000
## 9 pec32_contra reform contrario contrario 1.000
O texto pl2858_favor (“Presos políticos: votar pela
anistia é votar pela democracia”) recebeu populista com
confiança 0.667 — caso limítrofe que ilustra a utilidade do
self-consistency para identificar documentos ambíguos que merecem
revisão humana prioritária.
A amostragem por strategy = "uncertainty" prioriza
documentos com menor confiança, otimizando o esforço do codificador
humano.
# Exportar amostra priorizando casos incertos
amostra <- ac_qual_sample(
resultado,
n = 5,
strategy = "uncertainty"
)
ac_qual_export_for_review(
sample = amostra,
path = "revisao_proposicoes.xlsx",
corpus = corpus
)
# Após preenchimento da coluna categoria_humano no Excel:
humano <- ac_qual_import_human(
path = "revisao_proposicoes.xlsx",
cat_col = "categoria_humano",
id_col = "doc_id"
)
concordancia <- ac_qual_irr(
gold = humano,
predicted = resultado,
method = "all",
id_col = "doc_id",
cat_col = "categoria"
)
print(concordancia)## -- Confiabilidade inter-anotador (acR) --
## * Documentos comparados: 5
##
## Metrica Estimativa Interpretacao
## ──────────────────────────────────────────────────────────
## Percent Agreement 1.000 Perfeita
## Cohen's Kappa (unweighted) 1.000 Perfeita
## Fleiss' Kappa 1.000 Perfeita
## Krippendorff's Alpha (nominal) 1.000 Perfeita
Concordância perfeita é esperada para este corpus, dado que os textos foram construídos com posicionamentos inequívocos. Em corpora reais com textos ambíguos, valores de kappa entre 0.61 e 0.80 são considerados adequados para publicação (Landis & Koch, 1977).
# Distribuição de categorias por tema
resultado |>
count(tema, categoria) |>
arrange(tema, desc(n))
# Verificar se ementas são sempre neutro_tecnico
resultado |>
filter(tipo == "ementa") |>
select(doc_id, tema, categoria, confidence_score)## # A tibble: 3 × 4
## doc_id tema categoria confidence_score
## <chr> <chr> <chr> <dbl>
## 1 plp300_ementa 6x1 neutro_tecnico 1
## 2 pl2858_ementa anistia neutro_tecnico 1
## 3 pec32_ementa reform neutro_tecnico 1
As três ementas foram corretamente classificadas como
neutro_tecnico com confiança máxima — resultado consistente
com a natureza jurídica desses textos.
# CSV para análise posterior
ac_export(resultado, formato = "csv", arquivo = "proposicoes_codificadas.csv")
# Excel para compartilhamento
ac_export(resultado, formato = "xlsx", arquivo = "proposicoes_codificadas.xlsx")
# LaTeX para inclusão em artigo
ac_export(resultado, formato = "latex", arquivo = "proposicoes_codificadas.tex")BARDIN, L. Análise de conteúdo. Edições 70, 2011.
GILARDI, F.; ALIZADEH, M.; KUBLI, M. ChatGPT outperforms crowd workers for text-annotation tasks. PNAS, v. 120, n. 30, 2023.
KRIPPENDORFF, K. Content Analysis: An Introduction to Its Methodology. 4. ed. SAGE, 2018.
LANDIS, J. R.; KOCH, G. G. The measurement of observer agreement for categorical data. Biometrics, v. 33, n. 1, p. 159-174, 1977.
SAMPAIO, R. C.; LYCARIAO, D. Análise de conteúdo categorial: manual de aplicação. Brasília: ENAP, 2021.
WICKHAM, H. et al. ellmer: Chat with Large Language Models. Posit, 2025. Disponível em: https://ellmer.tidyverse.org.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.