---
title: "Análisis de texto"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Análisis de texto}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
```

> *English version:* `vignette("text-analysis", package = "ellmercodex")`.

Los modelos de lenguaje son útiles en investigación cuando devuelven algo con
lo que R puede trabajar, no solo prosa. Este artículo usa intervenciones de
un debate parlamentario uruguayo sobre la regulación del cannabis para
mostrar dos funciones de `ellmer` que funcionan con `ellmercodex`: los datos
estructurados y las llamadas a herramientas. Los ejemplos están inspirados en
el [artículo sobre `ellmercodex` en el blog](https://simxnherrera.github.io/blog/ellmercodex/).

Las salidas que se muestran provienen de una sesión real con `gpt-6-luna`.

```{r}
library(ellmercodex)

chat <- chat_codex(model = "gpt-6-luna", effort = "low")
```

## Clasifica un fragmento

Con `$chat_structured()` describes el resultado que quieres con el sistema de
tipos de `ellmer`, y el modelo tiene que responder con esa forma. Aquí pedimos
una orientación sobre política de drogas, a partir de un conjunto fijo de
códigos, junto con una breve justificación:

```{r}
# Fragmento real del debate
fragmento <- paste(
  "seguimos pensando que además de la represión y del combate al",
  "narcotráfico que debe seguir con firmeza y decisión como sociedad",
  "debemos apostar a dos o tres pilares fundamentales la educación",
  "la prevención y la rehabilitación"
)

resultado <- chat$chat_structured(
  paste(
    "Clasifica el fragmento como PREVENTIVA, PUNITIVA, MIXTA o",
    "SIN_ORIENTACIÓN_ATRIBUIBLE.",
    "Usa MIXTA cuando se defiendan simultáneamente respuestas",
    "preventivas y punitivas.",
    "\n\nFragmento:",
    fragmento
  ),
  type = ellmer::type_object(
    codigo = ellmer::type_enum(c(
      "PREVENTIVA",
      "PUNITIVA",
      "MIXTA",
      "SIN_ORIENTACIÓN_ATRIBUIBLE"
    )),
    justificacion = ellmer::type_string()
  )
)

resultado
#> $codigo
#> [1] "MIXTA"
#>
#> $justificacion
#> [1] "El fragmento defiende explícitamente la represión y el combate al
#> narcotráfico, que son respuestas punitivas, y simultáneamente propone la
#> educación, la prevención y la rehabilitación, de carácter preventivo o
#> rehabilitador."
```

El resultado es una lista de R, así que puedes ponerlo en un `data.frame`,
validarlo o analizarlo junto a los códigos de codificadores humanos. El
artículo de `ellmer` sobre
[datos estructurados](https://ellmer.tidyverse.org/articles/structured-data.html)
(en inglés) explica el sistema de tipos en detalle, incluido cómo extraer
varias filas a la vez.

## Deja que el modelo llame a tus funciones de R

Supongamos que ya clasificaste muchas intervenciones:

```{r}
intervenciones <- data.frame(
  partido = c("FA", "FA", "PN", "PN", "PN"),
  orientacion = c("PREVENTIVA", "MIXTA", "PUNITIVA", "PUNITIVA", "MIXTA")
)
```

En vez de pegar los datos en el prompt, puedes darle al modelo una
herramienta: una función de R que puede llamar cuando necesita información.
Esta cuenta las orientaciones de un partido y devuelve JSON, que es lo que
`ellmer` espera de una herramienta:

```{r}
resumir_partido <- function(partido_buscado) {
  intervenciones |>
    dplyr::filter(partido == partido_buscado) |>
    dplyr::count(orientacion) |>
    jsonlite::toJSON()
}

resumen_tool <- ellmer::tool(
  resumir_partido,
  name = "resumir_partido",
  description = "Cuenta las orientaciones discursivas de un partido.",
  arguments = list(partido_buscado = ellmer::type_string())
)

chat$register_tool(resumen_tool)

chat$chat(
  paste(
    "Compara las orientaciones discursivas de FA y PN.",
    "Usa la herramienta disponible."
  )
)
#> FA presenta una orientación **mixta** y una **preventiva** (una observación
#> de cada tipo). PN presenta una orientación **mixta** y dos **punitivas**.
#> En común, ambos tienen una orientación mixta; la diferencia es que FA
#> incluye una preventiva, mientras que PN se inclina más hacia lo punitivo.
```

El modelo decidió llamar a `resumir_partido()` para cada partido, recibió los
resultados calculados por R y los usó en su respuesta. Una herramienta
también podría consultar una base de datos, buscar en un corpus o llamar a
una API.

R siempre ejecuta las funciones: el modelo solo puede usar las herramientas
que registres, y decide cuándo llamarlas y con qué argumentos. El artículo de
`ellmer` sobre
[llamadas a herramientas](https://ellmer.tidyverse.org/articles/tool-calling.html)
(en inglés) explica cómo diseñar buenas herramientas.

## Combina las dos cosas

Como en `ellmer`, `$chat_structured()` no usa las herramientas registradas.
Cuando necesites ambas cosas, reúne primero el contexto con `$chat()` y
después extrae el resultado con `$chat_structured()` en la misma
conversación:

```{r}
chat$chat("Usa la herramienta para obtener las orientaciones de FA y PN.")

conteos <- chat$chat_structured(
  "Devuelve los conteos que acabas de obtener.",
  type = ellmer::type_array(
    ellmer::type_object(
      partido = ellmer::type_string(),
      orientacion = ellmer::type_string(),
      n = ellmer::type_integer()
    )
  )
)

conteos
#> # A tibble: 4 x 3
#>   partido orientacion     n
#>   <chr>   <chr>       <int>
#> 1 FA      MIXTA           1
#> 2 FA      PREVENTIVA      1
#> 3 PN      MIXTA           1
#> 4 PN      PUNITIVA        2
```
