Tool Calling

LLMs for Data Analysis in R

R/Medicine 2026

How do LLMs actually work?

On their own, can LLMs… access the internet? run code? send an email? interact with the world?

chat <- chat("anthropic/claude-haiku-4-5")

chat$chat("What's the weather like in San Francisco?")
I don't have access to real-time weather data, so I can't tell
you the current conditions in San Francisco.



LLMs don’t have access to real-time data!

chat <- chat("anthropic/claude-haiku-4-5")

chat$chat("Write df to data/data.csv")
I'm not able to actually execute code or 
write files to your system. I can only provide 
code snippets and guidance.



LLMs can’t affect the world!

Tool = function + metadata

  • Bring real-time or up-to-date information to the model
  • Let the model interact with the world

Tools in R

Tool = function + metadata

Step 1

Write (or find) an R function that carries out your desired functionality.

get_weather <- function(latitude, longitude) {
  # Return weather for that location
}

Tool = function + metadata

Step 2

Document that function for the LLM.

get_weather <- function(latitude, longitude) {
  # Return weather for that location
}

get_weather_tool <- tool(
  fun = get_weather,
  description = "Get the weather for a location",
  arguments = # Specify arguments
)

Tool = function + metadata

Step 3: Register the tool

get_weather <- function(latitude, longitude) {
  # Return weather for that location
}

get_weather_tool <- tool(
  fun = get_weather,
  description = "Get the weather for a location",
  arguments = # Specify arguments
)

chat$register_tool(get_weather_tool)

Tool = function + metadata

get_weather <- function(latitude, longitude) {
  weathR::point_forecast(latitude, longitude)
}

get_weather_tool <- tool(
  fun = get_weather,
  description = "Get the weather for a location",
  arguments = 
    list(
      latitude = type_number("Latitude"),
      longitude = type_number("Longitude")
  )
)

chat$register_tool(get_weather_tool)

Tool = function + metadata

get_weather <- tool(
  \(lat, lon) weathR::point_forecast(lat, lon),
  name = "get_weather",
  description = "Get the weather for a location.",
  arguments = list(
    lat = type_number("Latitude"),
    lon = type_number("Longitude")
  )
)

chat$register_tool(get_weather)

Now the model can tell us the weather

chat$chat("What's the weather in San Francisco?")
[tool call] get_weather(lat = 37.7749, lon = -122.4194)
● #> [{"time":"2026-05-01 14:00:00 PDT","temp":65,"dewpoint":1…
Here's the current weather for San Francisco:

**Current conditions (May 1, 2:00 PM PDT):**
- Temperature: 65°F
- Conditions: Mostly Sunny
- Humidity: 72%
- Wind: WSW at 7 mph
- Chance of rain: 0%

type_() functions

get_weather <- tool(
  \(lat, lon) weathR::point_forecast(lat, lon),
  name = "get_weather",
  description = "Get the weather for a location.",
  arguments = list(
    lat = type_number("Latitude"),
    lon = type_number("Longitude")
  )
)

chat$register_tool(get_weather)

type_() functions

type_boolean(description = NULL, required = TRUE)

type_integer(description = NULL, required = TRUE)

type_number(description = NULL, required = TRUE)

type_string(description = NULL, required = TRUE)

type_enum(values, description = NULL, required = TRUE)

type_array(items, description = NULL, required = TRUE)

type_object(
  .description = NULL,
  ...,
  .required = TRUE,
  .additional_properties = FALSE
)

How does tool calling work?

The LLM cannot run code by itself!

It just requests that tools be run.

The LLM controls:

  1. When the tool is called
  2. How the tool is called (i.e., the arguments)

Helper function: create_tool_def()

create_tool_def(rnorm)
Using model = "gpt-4.1".
tool(
  stats::rnorm,
  "Generates random deviates from the normal distribution with specified mean and standard deviation.",
  n = type_integer(
    "Number of observations. If length(n) > 1, the length is taken to be the number required.",
    required = TRUE
  ),
  mean = type_number(
    "Mean(s) of the normal distribution. Defaults to 0.",
    required = FALSE
  ),
  sd = type_number(
    "Standard deviation(s) of the normal distribution. Defaults to 1.",
    required = FALSE
  )
)

Your Turn 06_tool-calling

  1. Write a get_country_spending() function that takes a country name and year and returns health spending by purpose.

  2. Wrap it as a tool with tool() and register it.

  3. Ask the model about spending for a specific country.

06:00

Some questions

Does the model have access to the data?

No.

What does the model control?

  • When to use the tool.
  • What arguments to pass to the function. In this case, just the year and country.

How could we make this function better?

  • Give the model access to the list of countries and years available.
  • Think through failure modes.

Built-in web search tools

chat <- chat_anthropic()
chat$register_tool(claude_tool_web_search())
chat$chat("Who are the keynote speakers at R/Medicine 2026?")

Also available: openai_tool_web_search(), google_tool_web_search()