Table of Contents
Model Tools
Raif supports the creation of custom tools that the LLM can invoke in your tasks, conversations, and agents.
Two example tools are provided:
Raif::ModelTools::WikipediaSearch- View SourceRaif::ModelTools::FetchUrl- View Source
To create a new model tool, run the generator:
rails generate raif:model_tool GoogleSearch
This will create a new model tool in app/models/raif/model_tools/google_search.rb and a partial in app/views/raif/model_tool_invocations/_google_search.html.erb to display the tool invocation in the conversation interface.
Below is an example of a model tool that executes a Google search and returns the results:
class Raif::ModelTools::GoogleSearch < Raif::ModelTool
tool_description do
"Searches Google for the given query and returns the results as JSON."
end
tool_arguments_schema do
string :query, description: "The query to search the web for"
integer :max_results, description: "The maximum number of results to return"
boolean :include_images, description: "Whether to include images in the results"
array :exclude_domains do
items type: "string"
end
end
class << self
def observation_for_invocation(tool_invocation)
return "No results found" unless tool_invocation.result.present?
JSON.pretty_generate(tool_invocation.result)
end
# When your tool is invoked in a Raif::Conversation, should the result be automatically provided back to the model?
# When true, observation_for_invocation will be used to produce the observation provided to the model
def triggers_observation_to_model?
false
end
def process_invocation(tool_invocation)
# tool_invocation.tool_arguments will be a JSON object matching your tool_arguments_schema
query = tool_invocation.tool_arguments["query"]
max_results = tool_invocation.tool_arguments["max_results"]
include_images = tool_invocation.tool_arguments["include_images"]
exclude_domains = tool_invocation.tool_arguments["exclude_domains"]
# Assumes your application has a GoogleSearchService that can execute a Google search
# and return an array of results
search_results = GoogleSearchService.execute(query: query, max_results: max_results, include_images: include_images, exclude_domains: exclude_domains)
# Store the results in the tool_invocation
tool_invocation.update!(result: search_results)
end
end
end
Tool Arguments Schema
When the LLM invokes your tool, it will include a JSON object of arguments. You can use the tool_arguments_schema method to define the schema for these arguments. When the model invokes your tool, the arguments it provides will be validated against this schema using JSON::Validator from the json-schema gem.
See JSON Schemas for more information about defining JSON schemas.
Processing Model Tool Invocations
When the LLM invokes your tool, Raif will call your tool’s process_invocation method with a Raif::ModelToolInvocation record as an argument.
You should implement process_invocation to perform whatever actions are appropriate for your tool and store the results in the tool_invocation.result JSON column.
Model Tool Observations
When your tool is being invoked in a conversation or agent, the results of the tool invocation are provided back to the LLM as an observation.
When triggers_observation_to_model? returns true, Raif will call observation_for_invocation to build the model-facing observation. This observation can differ from the raw tool_invocation.result, which remains persisted for rendering and inspection.
To control the manner in which the result is provided to the LLM, implement the observation_for_invocation method.
Using Model Tools
Raif::Task, Raif::Conversation, and Raif::Agent all have an available_model_tools array to support the use of model tools. To make your tool available to the LLM, all you have to do is include it in the available_model_tools array.
Read more for each:
Provider-Managed Tools
In addition to the ability to create your own model tools, Raif supports provider-managed tools. These are tools that are built into certain LLM providers and run on the provider’s infrastructure:
Raif::ModelTools::ProviderManaged::WebSearch- Performs real-time web searches and considers relevant results when generating a responseRaif::ModelTools::ProviderManaged::CodeExecution- Executes code in a secure sandboxed environment (e.g. Python)Raif::ModelTools::ProviderManaged::ImageGeneration- Generates images based on text descriptions
Current provider-managed tool support:
| Provider | Web Search | Code Execution | Image Generation |
|---|---|---|---|
| OpenAI Responses API | ✅ | ✅ | ✅ |
| OpenAI Completions API | ❌ | ❌ | ❌ |
| Anthropic Claude | ✅ | ✅ | ❌ |
| AWS Bedrock (Claude) | ❌ | ❌ | ❌ |
| OpenRouter | ❌ | ❌ | ❌ |
| Google AI | ✅ | ✅ | ❌ |
To use provider-managed tools, include them in the available_model_tools array, just like any other model tool:
# In a conversation
conversation = Raif::Conversation.create!(
creator: current_user,
available_model_tools: [
"Raif::ModelTools::ProviderManaged::WebSearch",
"Raif::ModelTools::ProviderManaged::CodeExecution"
]
)
# In a task definition
class MyTask < Raif::Task
before_create ->{
self.available_model_tools = [
"Raif::ModelTools::ProviderManaged::WebSearch",
"Raif::ModelTools::ProviderManaged::CodeExecution"
]
}
end
# Or directly in a chat
llm = Raif.llm(:open_ai_responses_gpt_4_1)
model_completion = llm.chat(
messages: [{ role: "user", content: "What are the latest developments in Ruby on Rails?" }],
available_model_tools: ["Raif::ModelTools::ProviderManaged::WebSearch"]
)
Read next: Web Admin