30  🏗 btw

Published

2025-10-31

Alert

The contents for section are being revised. Thank you for your patience.

In the previous chapters, we used the chores package to develop helpers—pre-written prompts—for repetitive tasks such as creating roxygen2 documentation and updating testthat tests.1 In the gander chapter, we used the addin to send code and environment context to the LLM.2

chores and gander assist us in automating or enhancing the prompts we submit to the LLM. The addins and shortcuts streamline our interactions with the model, allowing for a more intuitive and efficient experience that resembles a browser interface (like ChatGPT).

I’ve created the shinypak R package in an effort to make each section accessible and easy to follow. Install shinypak using pak (or remotes):

install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

List the apps in this chapter:

list_apps(regex = '^30')

Launch apps with launch()

launch(app = '30_llm-btw')

Download apps with get_app()

get_app(app = '30_llm-btw')

In this chapter we’ll focus on the btw package, which is fundamentally different than chores and gander because it,

provides a default set of tools to to peruse the documentation of packages you have installed, check out the objects in your global environment, and retrieve metadata about your session and platform.3

I’ll cover using btw to add documentation and improve the contents of the downloadable report in our sap application.

30.1 Configuration

We can place the btw configuration options in the .Rprofile (similar to other ellmer configurations).4 Recall that the .Rprofile file can exist at the user and/or the project-level.

For example, I’ve added a project-level .Rprofile file this branch of the sap package and included a system_prompt and model:

if (interactive()) {
  require(ellmer, quietly = TRUE)
}
if (interactive()) {
  require(btw, quietly = TRUE)
}
options(
  btw.client = ellmer::chat_anthropic(
    system_prompt =
    "You are an expert R/Python programmer who loves explaining complex topics to non-technical audiences. 
    - When writing R code, use base R functions, but follow the tidyverse style guide.     
    - Avoid using `for` loops and prefer functional programming patterns like `apply()` or `purrr`.    
    - When creating graphs/plots, use `ggplot2`. 
    - If writing R Shiny code, use `bslib` for all layout functions (unless explicitly  instructed otherwise).
    - If writing Python Shiny code, use shiny core (not express) to build apps and include explanations in comments. ",
    model = "claude-sonnet-4-5-20250929"
    )
)
1
Ensure ellmer package
2
Ensure btw package
3
btw config
4
System prompt for all conversations with chat
5
model argument for most current Claude model

Launch app with the shinypak package:

launch('30_llm-btw')

30.2 btw tools

btw comes with a set of pre-defined tools for examining package documentation, environments, directories and files, git and GitHub, our development environment, CRAN packages, R sessions, and general web searches.5

We can use btw to work interactively in the Positron (or RStudio ) console by creating a btw-enhanced chat client and passing messages directly.6

chat <- btw_client()

We can view the btw enhancements by examining the chat object in the console:

chat

btw has added ‘System and Session Context’ and ‘Tools’ sections to the chat client.

# System and Session Context

Please account for the following R session and system settings in all 
responses.

<system_info>
R_VERSION: R version 4.5.1 (2025-06-13)
OS: macOS Tahoe 26.0.1
SYSTEM: x86_64, darwin20
UI: Positron (a VS Code equivalent)
LANGUAGE: (EN)
LOCALE: en_US.UTF-8
ENCODING: en_US.UTF-8
TIMEZONE: America/Phoenix
DATE: Tuesday, October 28, 2025 (2025-10-28)
</system_info>


# Tools

You have access to tools that help you interact with the user's R session and
workspace. Use these tools when they are helpful and appropriate to complete 
the user's request. These tools are available to augment your ability to help
the user, but you are smart and capable and can answer many things on your 
own. It is okay to answer the user without relying on these tools.

We can see the model will have some additional instructions/methods at it’s disposal (beyond what we’ve provided in the system_prompt argument of ellmer::chat_anthropic()).

30.3 Project context

As we learned in the ellmer chapter, project prompts should be stored in inst/prompts/. btw also provides the use_btw_md() function, which creates a project ‘context file.’

use_btw_md(scope = "project")

The btw.md file comes with some default content for project context:

Use btw.md to inform the LLM of your preferred code style, to provide domain-specific terminology or definitions, to establish project documentation, goals and constraints, to include reference materials such or technical specifications, or more. Storing this kind of information in btw.md may help you avoid repeating yourself and can be used to maintain coherence across many chat sessions.

client

The YAML header in our newly created btw.md is where can specify the client (along with the provider and model).

---
client: claude/claude-4-5-sonnet-latest
---

The default values in btw.md will automatically use the latest Claude model from Anthropic. The YAML values above are similar to using ellmer’s chat_* functions.7

tools

The tools section of the YAML header contains a list of the groups from btw_tools(). Each of these groups contains a collection of functions “that allow the chat to interface with your computational environment.

---
tools:
  - docs
  - env
  - files
  - git
  - github
  - ide
  - search
  - session
  - web
---

Now that we have a btw.md file for project context, we’ll start an interactive chat session to help us write our context file.

30.4 Console chat: context file

We can use the btw tools to add more project context to the btw.md by calling btw_task_create_btw_md() and setting the mode to "console".

btw_task_create_btw_md(mode = "console", client = "anthropic")

This opens an interactive chat in the console:

30.5 How tool calling works

LLMs can’t execute R code, but if we’ve registered tools (i.e., R functions) with the model, they can be used to help provide additional information.

As mentioned above, btw has a collection of tools at our disposal, so when we start the interactive chat, the model informs us it will be examining the sap package contents for more information.

>>> "Let's get started."
I'll help you create a comprehensive project summary. Let me start by 
exploring your project structure to understand what we're working with.

30.5.1 Registered tools

When a tool is registered with the LLM, we include the name of the function, a description of what the function does, a list of function arguments and their type (boolean, integer, number, string, etc.). 8

We can view all the tools registered with the LLM using:

chat$get_tools()

The btw_tools() function lists all the tools we could register, but chat$get_tools() returns the tools that are registered with the model.

30.5.2 Tool descriptions

The output from chat$get_tools() is rather lengthy, but we can use Ctrl/Cmd + F to locate btw_tool_files_list_files(), the first tool called by the LLM.

Recall the model was going to start by exploring your project structure, and we can see this aligns with the’ @description:

List files or directories in the project.

WHEN TO USE:
* Use this tool to discover the file structure of a project.
* When you want to understand the project structure, use `type = "directory"` to list all directories.
* When you want to find a specific file, use `type = "file"` and `regexp` to filter files by name or extension.

CAUTION: Do not list all files in a project, instead prefer listing files in a specific directory with a `regexp` to
filter to files of interest.

30.5.3 Tool calls

The tool calls and a preview of their results are printed in the console. Below is the first call to btw_tool_files_list_files():

◯ [tool call] btw_tool_files_list_files(path = ".", `_intent` = "Get initial 
overview of project structure")

The function includes an _intent argument, where the model, “explain[s] why it called the tool.9

30.5.4 Tool results

The results are a markdown formatted table of the project contents (file/folder names, their size, and when they were last changed):10

● #> | path        | type      | size | modification_time   |
  #> |-------------|-----------|------|---------------------|
  #> | DESCRIPTION | file      | 736  | 2025-10-20 14:04:24 |
  #> | NAMESPACE   | file      | 932  | 2025-10-22 08:01:23 |
  #> | R           | directory | 1K   | 2025-10-22 08:01:23 |
  #> …

30.5.5 Model response

The table output is sent to the LLM, which them provides a summary of the contents:

Perfect! I can see this is an **R package** with a Shiny application. The structure shows:

- `DESCRIPTION` and `NAMESPACE` files (R package essentials)
- Extensive `R/` directory with many Shiny modules (`mod_*.R` files)
- `app.R` file (Shiny app entry point)
- `data/` directory with datasets
- `tests/testthat/` (comprehensive testing)
- `vignettes/` (package documentation)
- `renv.lock` (dependency management)

  1. The prompt directory is where chores stores the helpers used in the addin.↩︎

  2. This is explained in What is gander actually doing?↩︎

  3. This description of btw actually comes from the mcptools documentation.↩︎

  4. You can easily open this file with usethis::edit_r_profile().↩︎

  5. Read a complete list of available tools from btw in the package documentation.↩︎

  6. An ellmer::Chat client which provides a “sequence of user and assistant Turns sent to a specific Provider.↩︎

  7. For more information on client values, read the Chat Settings documentation.↩︎

  8. We can register tools using ellmer’s create_tool_def() and tool() functions.↩︎

  9. The _intent argument is “An optional string describing the intent of the tool use. When the tool is used by an LLM, the model will use this argument to explain why it called the tool.↩︎

  10. I’ve cleaned up the formatting on this markdown table so it’s easier to read.↩︎