# Terminal
bundle add ruby-openai
bin/rails credentials:edit
bin/rails g model products name stock:integer
bin/rails g controller assistants index
# config/initializers/openai.rb
OpenAI.configure do |config|
config.access_token = Rails.application.credentials.dig(:openai, :access_token)
end
# config/routes.rb
Rails.application.routes.draw do
resources :assistants, only: [ :index, :create ]
root to: "assistants#index"
get "up" => "rails/health#show", as: :rails_health_check
end
# Credentials File
openai:
access_token: YOURTOKEN
# app/controllers/assistants_controller.rb
class AssistantsController < ApplicationController
def index
end
def create
@question = params[:question]
@answer = Assistant.new.ask(@question)
render :index, status: :see_other
end
end
# app/views/assistants/index.html.erb
<div class="mx-auto mt-16 max-w-xl px-4">
<h1 class="text-2xl font-bold">Inventory Assistant</h1>
<%= form_with url: assistants_path, method: :post, class: "mt-6 glex gap-2" do |form| %>
<%= form.text_field :question,
value: @question,
placeholder: "How many road helmets do we have?",
class: "flex-1 rounded border border-gray-300 px-3 py-2" %>
<%= form.submit "Ask", class: "rounded bg-blue-600 px-4 py-2 font-medium text-white cursor-pointer" %>
<% end %>
<% if @answer %>
<div class="mt-6 rounded border border-gray-200 bg-gray-50 p-4">
<%= @answer %>
</div>
<% end %>
</div>
# app/models/assistant.rb
class Assistant
SYSTEM_PROMPT = "You are an inventory assistant for our store. " \
"Always use the check_inventory tool to answer questions about products " \
"and stock levels. Only discuss products that the tool returns."
TOOLS = [
{
type: "function",
function: {
name: "check_inventory",
description: "Search the store's products by name and return matching products with their stock levels. Use a broad term like 'helmet' to list all helmets.",
parameters: {
type: "object",
properties: { product_name: { type: "string" } },
required: [ "product_name" ]
}
}
}
]
def initialize
@client = OpenAI::Client.new
end
def ask(question)
messages = [
{ role: "system", content: SYSTEM_PROMPT },
{ role: "user", content: question }
]
message = chat(messages).dig("choices", 0, "message")
if (tool_call = message.dig("tool_calls", 0))
Rails.logger.info "✅✅✅✅✅✅ #{tool_call}"
messages << message
messages << tool_result(tool_call)
message = chat(messages).dig("choices", 0, "message")
end
message["content"]
end
private
def chat(messages)
@client.chat(parameters: { model: "gpt-4o-mini", messages:, tools: TOOLS })
end
def tool_result(tool_call)
args = JSON.parse(tool_call.dig("function", "arguments"))
products = Product.where("name LIKE ?", "%#{args["product_name"]}%")
{
role: "tool",
tool_call_id: tool_call["id"],
content: products.any? ? products.map { |p| "#{p.name}: #{p.stock} in stock" }.join("\n") : "not found"
}
end
end