Tool Calling

Programming with LLMs in R and Python

posit::conf(2026)

2026-09-14

Recall: How do LLMs work?

  1. You write some words

  2. The LLM writes some more words

  3. You use those words

On its own, can a word-writing model…

  • Access the internet?

  • Send an email?

  • Interact with the world?

Let’s try it

library(ellmer)

chat <- chat("openai/gpt-4.1-nano")

chat$chat("What's the weather like in Houston, TX?")
chat$chat("Who are the keynote speakers at posit::conf(2026)?")
chat$chat("What day is it?")
import chatlas

chat = chatlas.Chat(model="openai/gpt-4.1-nano")
chat.chat("What's the weather like in Houston, TX?")
chat.chat("Who are the keynote speakers at posit::conf(2026)?")
chat.chat("What day is it?")

Tools

a.k.a. functions, tool calling or function calling

  • Bring real-time or up-to-date information to the model

  • Let the model interact with the world

Chatbot Systems

How do tool calls work?

What should I wear to posit::conf(2026) in Houston?

Human in the loop

👨‍💻 _demos/15_demo_manual-tools

Wait… I can write code!

👨‍💻 Work through the script to register the tool, then run the app to see it in action

👨‍💻 16_weather-tool-01-start.R16_weather-tool-app.R

Recap: Tool definitions in R

tool_get_weather <- tool(
  tool_fn,
  description = "How and when to use the tool",
  arguments = list(
    .... = type_string(),
    .... = type_integer(),
    .... = type_enum(
      c("choice1", "choice2"),
      required = FALSE
    )
  )
)

Recap: Tool definitions in Python

def get_weather(lat, lon):
    return NWS.GetCurrentForecast(lat, lon)

Recap: Tool definitions in Python

def get_weather(lat: float, lon: float):
    return NWS.GetCurrentForecast(lat, lon)

Recap: Tool definitions in Python

def get_weather(lat: float, lon: float):
    """
    Get forecast data for a specific latitude and longitude.

    Parameters
    ----------
    lat : str
        Latitude of the location.
    lon : str
        Longitude of the location.
    """
    return NWS.GetCurrentForecast(lat, lon)

Your Turn 17_quiz-game-2

  1. I’ve set up a Quiz Game app with a function that plays a sound when called.

  2. Your job: teach the model to play sounds at the right moments in the game.

  3. You can use your prompt or switch _exercises_solutions to use ours.

Tool Annotations

  • Provide hints to clients about tool behavior

  • Help humans and AI systems make informed decisions about tool usage

  • All properties are hints only - not guaranteed to be accurate

  • MCP Tool Annotations Spec

Key Annotations

Property Description
title Human-readable tool name
icon Icon shown next to the tool (shinychat only)
readOnlyHint Tool doesn’t modify environment
destructiveHint May perform destructive updates
idempotentHint Repeated calls with same arguments return same
openWorldHint Interacts with external entities vs. closed domain

Tool Annotations in R

tool_get_weather <- tool(
  get_weather,
  # description, arguments, ...
  annotations = tool_annotations(
    title = "Get Weather",
    readOnlyHint = TRUE,
    icon = fontawesome::fa_i("cloud-sun")
  )
)

Tool Annotations in Python

chat.register_tool(
    get_weather,
    annotations={
        "title": "Get Weather",
        "readOnlyHint": True,
        "extra": {
            "icon": faicons.fa_i("cloud-sun"),
        },
    },
)

Your Turn 18_quiz-game-3

  1. Add tool annotations to our play_sound tools.

  2. Pick an icon from FontAwesome free icons

Tools in Shiny

  • Define the tool function inside the server

  • You can update reactive values in the tool function!

  • You can read reactive values, if you isolate() reads. (Be careful!)

  • We will come back to this later 😉