This function adds the ability for the a LLM to call R functions.
Users can specify a list of functions that the LLM can call, and the
prompt will be modified to include information, as well as an
accompanying extraction function to call the functions (handled by
send_prompt()
). Documentation for the functions is extracted from
the help file (if available), or from documentation added by
add_tools_add_documentation()
.
Usage
add_tools(prompt, tool_functions = list())
Arguments
- prompt
A single string or a
tidyprompt()
object- tool_functions
An R function or a list of R functions that the LLM can call. If the function has been documented in a help file (e.g., because it is part of a package), the documentation will be parsed from the help file. If it is a custom function, documentation should be added with
add_tools_add_documentation()
Value
A tidyprompt()
with an added prompt_wrap()
which
will allow the LLM to call R functions
Details
Note that this method of function calling is purely text-based. This makes it suitable for any LLM and any LLM provider. However, 'native' function calling (where the LLM model provider restricts the model to special tokens that can be used to call functions) may perform better in terms of accuracy and efficiency. 'tidyprompt' may support 'native' function calling in the future
See also
answer_as_code()
add_tools_get_documentation()
Other pre_built_prompt_wraps:
add_text()
,
answer_as_boolean()
,
answer_as_code()
,
answer_as_integer()
,
answer_as_json()
,
answer_as_list()
,
answer_as_named_list()
,
answer_as_regex()
,
answer_by_chain_of_thought()
,
answer_by_react()
,
prompt_wrap()
,
quit_if()
,
set_system_prompt()
Other llm_tools:
answer_as_code()
Other add_tools:
add_tools_get_documentation()
Examples
# Example fake weather function to add to the prompt:
temperature_in_location <- function(
location = c("Amsterdam", "Utrecht", "Enschede"),
unit = c("Celcius", "Fahrenheit")
) {
location <- match.arg(location)
unit <- match.arg(unit)
temperature_celcius <- switch(
location,
"Amsterdam" = 32.5,
"Utrecht" = 19.8,
"Enschede" = 22.7
)
if (unit == "Celcius") {
return(temperature_celcius)
} else {
return(temperature_celcius * 9/5 + 32)
}
}
# Add documentation to a function:
temperature_in_location <- add_tools_add_documentation(
temperature_in_location,
description = "Get the temperature in a location",
arguments = list(
location = "Location, must be one of: 'Amsterdam', 'Utrecht', 'Enschede'",
unit = "Unit, must be one of: 'Celcius', 'Fahrenheit'"
),
return_value = "The temperature in the specified location and unit"
)
# Attempt to extract documentation as it is extracted by add_tools():
add_tools_get_documentation(temperature_in_location)
#> $name
#> [1] "temperature_in_location"
#>
#> $description
#> [1] "Get the temperature in a location"
#>
#> $arguments
#> $arguments$location
#> [1] "Location, must be one of: 'Amsterdam', 'Utrecht', 'Enschede'"
#>
#> $arguments$unit
#> [1] "Unit, must be one of: 'Celcius', 'Fahrenheit'"
#>
#>
#> $return_value
#> [1] "The temperature in the specified location and unit"
#>
# You can also pass functions which are included in packages;
# documentation is then extracted from help files
# (so you don't need to manually add documentation):
add_tools_get_documentation(list.files)
#> $name
#> [1] "list.files"
#>
#> $description
#> [1] "List the Files in a Directory/Folder: These functions produce a character vector of the names of files\nor directories in the named directory."
#>
#> $arguments
#> $arguments$path
#> [1] "a character vector of full path names; the default corresponds to the working directory, ‘getwd()’. Tilde expansion (see ‘path.expand’) is performed. Missing values will be ignored. Elements with a marked encoding will be converted to the native encoding (and if that fails, considered non-existent)."
#>
#> $arguments$pattern
#> [1] "an optional regular expression. Only file names which match the regular expression will be returned."
#>
#> $arguments$all.files
#> [1] "a logical value. If ‘FALSE’, only the names of visible files are returned (following Unix-style visibility, that is files whose name does not start with a dot). If ‘TRUE’, all file names will be returned."
#>
#> $arguments$full.names
#> [1] "a logical value. If ‘TRUE’, the directory path is prepended to the file names to give a relative file path. If ‘FALSE’, the file names (rather than paths) are returned."
#>
#> $arguments$recursive
#> [1] "logical. Should the listing recurse into directories?"
#>
#> $arguments$ignore.case
#> [1] "logical. Should pattern-matching be case-insensitive?"
#>
#> $arguments$include.dirs
#> [1] "logical. Should subdirectory names be included in recursive listings? (They always are in non-recursive ones)."
#>
#> $arguments$no..
#> [1] "logical. Should both ‘\".\"’ and ‘\"..\"’ be excluded also from non-recursive listings?"
#>
#>
#> $return_value
#> [1] "A character vector containing the names of the files in the\nspecified directories (empty if there were no files). If a path\ndoes not exist or is not a directory or is unreadable it is\nskipped.\nThe files are sorted in alphabetical order, on the full path if\n‘full.names = TRUE’.\n‘list.dirs’ implicitly has ‘all.files = TRUE’, and if ‘recursive =\nTRUE’, the answer includes ‘path’ itself (provided it is a\nreadable directory).\n‘dir’ is an alias for ‘list.files’."
#>
# Example usage:
prompt1 <- "Hi, what is the weather in Enschede? Give me Celcius degrees" |>
add_tools(temperature_in_location)
prompt2 <- "What are the files in my current directory?" |>
add_tools(list.files)
if (FALSE) { # \dontrun{
prompt1 |>
send_prompt(llm_provider_ollama())
# --- Sending request to LLM provider (llama3.1:8b): ---
# Hi, what is the weather in Enschede? Give me Celcius degrees
#
# If you need more information, you can call functions to help you.
# To call a function, output a JSON object with the following format:
#
# {
# "function": "<function name>",
# "arguments": {
# "<argument_name>": <argument_value>,
# ...
# }
# }
#
# (Note: you cannot call other functions within arguments.)
#
# The following functions are available:
#
# function name: temperature_in_location
# description: Get the temperature in a location
# arguments:
# - location: Location, must be one of: 'Amsterdam', 'Utrecht', 'Enschede'
# - unit: Unit, must be one of: 'Celcius', 'Fahrenheit'
# return value: The temperature in the specified location and unit
#
# After you call a function, wait until you receive more information.
# Use the information to decide your next steps or provide a final response.
# --- Receiving response from LLM provider: ---
# To get the weather in Enschede, I'll need to call the
# `temperature_in_location` function.
#
# Here's my JSON object:
# ```
# {
# "function": "temperature_in_location",
# "arguments": {
# "location": "Enschede",
# "unit": "Celcius"
# }
# }
# ```
#
# I'll wait for your response...
# --- Sending request to LLM provider (llama3.1:8b): ---
# function called: temperature_in_location
# arguments used: location = Enschede, unit = Celcius
# result: 22.7
# --- Receiving response from LLM provider: ---
# The current temperature in Enschede is 22.7°C.
#
# So, the final answer is:
# **22.7°C**
#
# Is there anything else I can help you with?
# [1] "The current temperature in Enschede is 22.7°C.\n\nSo, the final answer
# is:\n**22.7°C**\n\nIs there anything else I can help you with?"
prompt2 |>
send_prompt(llm_provider_ollama())
# ...
} # }