commons

commons builds self-service data science agents for your organization: agents that answer data questions using the definitions your data team already maintains.

An agent is built from a data_source(), which is what it can query, and a semantic_layer(), which is a pool of trusted calculations. When a question matches a measure in the semantic layer, the agent runs that measure. When nothing matches, it falls back to reading your data documentation and writing a SQL query.

Installation

install.packages("commons")

Usage

library(commons)

Point a data source at a database and, optionally, at a data dictionary describing it:

con <- DBI::dbConnect(duckdb::duckdb())
DBI::dbWriteTable(con, "orders", data.frame(
  region = c("EMEA", "Americas", "EMEA", "APAC"),
  revenue = c(500, 900, 1200, 300),
  refunded = c(0, 100, 0, 0)
))

sales <- data_source(con, tables = "orders")

Define the calculations you want the agent to prefer. Arguments that aren’t in the arguments schema are hidden from the model. An argument named after a data source receives that source’s connection.

measure_file <- tempfile(fileext = ".R")
writeLines(
  c(
    "#' Net Revenue by Region",
    "#'",
    "#' @param region `enum[EMEA, Americas, APAC]` Sales region.",
    "#' @measure",
    "net_revenue_by_region <- function(region, warehouse) {",
    "  DBI::dbGetQuery(",
    "    warehouse,",
    "    'SELECT sum(revenue - refunded) AS net FROM orders WHERE region = ?',",
    "    params = list(region)",
    "  )",
    "}"
  ),
  measure_file
)

layer <- semantic_layer(measure_file)
unlink(measure_file)

Then, assemble the pieces with commons(). The function outputs an ellmer::Chat, so it works with shinychat out of the box.

agent <- commons(
  ellmer::chat_anthropic(),
  data_sources = list(warehouse = sales),
  semantic_layer = layer
)

agent$chat("What was net revenue in EMEA?")
#> Net revenue in EMEA was $1,700.

That answer came from net_revenue_by_region, not from SQL the model wrote, so “net revenue” means what your organization says it means.

See vignette("commons") to learn more.