Skip to contents

This article builds an agent that explains an R package by reading its source. You will run a task, check what the agent did, and then give a second agent permission to write a file, and undo what it wrote.

Setup

Install Deputy from GitHub:

# install.packages("pak")
pak::pak("JamesHWade/deputy")

The examples use OpenAI’s gpt-6-luna, so they need OPENAI_API_KEY. Put it in your .Renviron file (usethis::edit_r_environ() opens it) and restart R. Keep keys out of scripts and version control.

To use another provider, pass a different ellmer chat. Deputy uses whatever model and settings that chat has.

Create an agent

Start R in the package you want to explain, then run:

library(deputy)

workspace <- normalizePath(getwd(), winslash = "/", mustWork = TRUE)

agent <- Agent$new(
  chat = ellmer::chat("openai/gpt-6-luna"),
  tools = tools_preset("minimal"),
  permissions = permissions_readonly(),
  usage_limits = UsageLimits(max_requests = 6, max_tool_calls = 8),
  working_dir = workspace
)
  • chat is the ellmer chat that talks to the model.
  • tools gives the model three tools: read_file, read_markdown and list_files.
  • permissions allows reading and denies writing files, running code and using the web.
  • usage_limits ends the run after six model requests or eight tool calls.
  • working_dir is where relative paths start. To explain a package somewhere else, use its path instead of getwd().

Read-only stops the agent from changing anything. It does not limit what it reads: the agent can open any file your R session can open. Permissions decide which tools may run; they are not a filesystem sandbox.

Run a task

Ask a question that the files can answer:

result <- agent$run_sync(
  "Read DESCRIPTION and list R/. Explain what this package does and cite the files you used."
)

result$response

run_sync() blocks until the run ends and returns an AgentResult. Before you use the response, check why the run ended:

result$stop_reason
result$usage

"complete" means the model finished. A limit stops the run with a reason such as "request_limit" or "tool_call_limit", and the response may be partial. result$usage counts requests, tool calls and tokens, and the cost when ellmer can price the model.

To see which files the agent read, list its tool calls and their results:

?AgentResult describes the other fields, including the full event log and run identifiers.

Other ways to run a task

run_sync() suits scripts. An agent also works like an ellmer chat, so pick the method that fits your program:

Method Returns Use it for
chat() the final text quick questions at the console
run() a generator of events printing progress as it happens
run_async() a promise of an AgentResult async code and background work
stream_async() an ellmer stream Shiny apps

Every method goes through the same permission checks, hooks and limits.

Watch tool calls as they happen

Add a hook that prints each tool call:

agent$add_hook(hook_log_tools())
result <- agent$run_sync("Read DESCRIPTION and name the package's imported dependencies.")

Hooks shows how to write your own.

Let an agent write a file

An agent’s permissions can be narrowed after it is created but never widened, so writing needs a new agent. This one may write inside workspace. It still can’t run R or shell code, use the web or install packages.

write_agent <- Agent$new(
  chat = ellmer::chat("openai/gpt-6-luna"),
  tools = tools_file(),
  permissions = Permissions(file_write = workspace),
  usage_limits = UsageLimits(max_requests = 6, max_tool_calls = 8),
  enable_file_checkpointing = TRUE,
  working_dir = workspace
)

checkpoint_id <- write_agent$checkpoint("before package summary")

write_result <- write_agent$run_sync(
  "Read DESCRIPTION and write a short package summary to deputy-summary.md. Leave other files unchanged."
)

write_result$stop_reason
result_tool_calls(write_result)

Open deputy-summary.md and check the change in Git. Because checkpointing is on, Deputy saved the old contents of every file its write tools touched after the checkpoint. To undo the agent’s changes:

write_agent$rewind_files(checkpoint_id)

Checkpoints cover Deputy’s write_file, edit_file and multi_edit tools. Changes made by R code, shell commands or other programs are not recorded.

Troubleshooting

The provider can’t authenticate
Check the credential your ellmer provider expects. Try a plain ellmer chat before adding Deputy.
A tool call is denied
Check that the tool is registered and that the permission it needs is on. Custom tools also need accurate annotations; see Tools.
The run stops early
Look at result$stop_reason and result$usage, then give the agent a smaller task or raise the limit it hit. "tool_loop" means the model made the same tool call with the same result three times in a row. "cost_unavailable" means you set a cost limit but ellmer couldn’t price the model, so Deputy stopped rather than guess. ?UsageLimits lists every limit.

Next steps