【Ruby】RailsでChatGPT(ruby-openai)導入する方法
完成イメージ
Railsのプロジェクトさえ作成していれば、だれでも簡単に以下のようなチャットボットが作成できます。
前提
- Railsのプロジェクトを作成済みである。
1.OpenAIのwebサイトにログイン
2.クレジットカードの登録
⚙からBilling
>Payment methods
>Add payment method
を選択。
クレカの登録を行う。
3.APIキーの作成
🔒からDashboard
>API keys
>Create new secret key
をクリックする。
必要な項目を入力しCreate secret key
をクリックする。
dotenv-rails
のインストール
4.Gemファイルにgem 'dotenv-rails'
と記載し、ターミナルでbundle install
を実行する。
gem 'dotenv-rails'
bundle install
.env
を作成
5.Railsのプロジェクト内に.env
ファイルを作成するためターミナルでtouch .env
を実行。(手動でプロジェクトのルートディレクトリに.env
ファイルを作成してもよい)
touch .env
作成した、.env
ファイル内にOPENAI_ACCESS_TOKEN=your_openai_api_key
の記載を行う。your_openai_api_key
の部分に手順3で取得したAPIキーを貼り付け、””(ダブルクォーテーション)で囲む。
OPENAI_ACCESS_TOKEN="your_openai_api_key"
.gitignore
ファイルの編集
6..gitignore
ファイル内に/.env
と記載し、先ほどのAPIキーを機密情報として管理する。
ruby-openai
のインストール
7.Gemfile
にruby-openai
と記載し、ターミナルでbundle install
を実行する。
gem 'ruby-openai'
bundle install
8.設定ファイルの作成
ターミナルで以下のコマンドを実行するか、initializers
ファイル内にopenai.rb
ファイルを手動で作成する。
touch config/initializers/openai.rb
ファイル内に下記の記述を行う。
OpenAI.configure do |config|
config.access_token = ENV.fetch("OPENAI_ACCESS_TOKEN")
config.organization_id = ENV.fetch("OPENAI_ORGANIZATION_ID", nil) # Optional
config.log_errors = true # Optional, but recommended for development
end
9.コントローラの作成
ターミナルで以下のコマンドを実行し、コントローラを作成する。
rails g controller openai/tests
$ rails g controller openai/tests
Running via Spring preloader in process 306032
create app/controllers/openai/tests_controller.rb
invoke erb
create app/views/openai/tests
invoke test_unit
create test/controllers/openai/tests_controller_test.rb
invoke helper
create app/helpers/openai/tests_helper.rb
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/openai/tests.scss
10.コントローラの編集
以下コントローラの内容を先ほど作成したtests_controller.rb
ファイル内に張り付ける。
class Openai::TestsController < ApplicationController
protect_from_forgery
def show
end
def generate_text
@user_input = params[:user_input]
if @user_input.present?
client = OpenAI::Client.new
response = client.chat(
parameters: {
model: "gpt-3.5-turbo",
messages: [
{ role: "system", content: "関西弁で返答してください" },
{ role: "user", content: @user_input }
],
temperature: 0.7
}
)
@generated_text = response.dig("choices", 0, "message", "content")
render json:{text: @generated_text}
end
end
end
コントローラの記述内容について詳しく知りたい方はこちら
client = OpenAI::Client.new
response = client.chat(
parameters: {
model: "gpt-3.5-turbo", # 使用するモデルを指定
messages: [
{ role: "system", content: "関西弁で返答してください" },
{ role: "user", content: @user_input }
],
temperature: 0.7 # オプション:生成の多様性を制御
}
)
<Parameters>
model
:GPTのモデルを指定する
- gpt-4o (リリース:2024-05-13 / 学習データ : 2023年10月)
- gpt-4-turbo (リリース:2024-04-09 / 学習データ : 2023年12月)
- gpt-3.5-turbo(リリース:2023-6-13 / 学習データ :2021年9月)
modelの詳細
Settings
>Personal
>Limits
から確認可能
messages
:配列でroleとcontentを指定し、返答の方法を決める。
【role】
:
- system:役割、設定、前提などの入力に使用
- user:ユーザの入力
- assistant:AIからの出力
例:関西弁で返答してほしいとき
response = client.chat(
parameters: {
model: "gpt-3.5-turbo",
messages: [
{ role: "system", content: "関西弁で返答してください"},
{ role: "user", content: "こんにちは"}
],
temperature: 0.7
}
)
出力結果
temperature
:0~2の範囲で設定できる。値が低いほど回答のランダム性が減る。特定の用途に使いたい場合などは0に近づけて、会話などのランダム性を楽しみたい場合には2に近づけていく。
max_tokens
:サンプルには記載していないパラメータであるが、返答の最大トークン数を設定できる。無駄に長い返答がきたりすることもあるので料金面などを気にする場合はきちんと設定してあげた方が良い。
APIからのレスポンス(JSON形式)の中身について
@generated_text = response.dig("choices", 0, "message", "content")
response
(ChatGPTAPI
のレスポンスの中身)は、以下のようなJSON形式で返される。
{
"id": "XXXXXXXX",
"object": "chat.completion",
"created": 99999999,
"model": "gpt-3.5-turbo-0301",
"usage": { "prompt_tokens": 19, "completion_tokens": 27, "total_tokens": 46 },
"choices": [
{
"message": {
"role": "assistant",
"content": "おおきに!元気かい?"
},
"finish_reason": "stop",
"index": 0
}
]
}
11.ビューの作成
app/views/openai
フォルダ内にshow.html.erb
ファイルを作成し、以下のコードをファイル内に張り付ける。
show.html.erbファイル内に張り付けるコード
<div class="flex flex-col items-center justify-center py-6 w-full h-full">
<div class="max-w-4xl w-full space-y-8 h-full">
<div id="conversation" class="space-y-4 p-4 rounded overflow-y-auto">
</div>
<form id="text_form" class="flex items-center px-2 py-1 border-t border-gray-200 bg-white">
<label for="file_input" class="cursor-pointer inline-block relative">
<svg class="w-6 h-6 fill-current text-gray-600 transition-colors duration-200" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M364.2 83.8c-24.4-24.4-64-24.4-88.4 0l-184 184c-42.1 42.1-42.1 110.3 0 152.4s110.3 42.1 152.4 0l152-152c10.9-10.9 28.7-10.9 39.6 0s10.9 28.7 0 39.6l-152 152c-64 64-167.6 64-231.6 0s-64-167.6 0-231.6l184-184c46.3-46.3 121.3-46.3 167.6 0s46.3 121.3 0 167.6l-176 176c-28.6 28.6-75 28.6-103.6 0s-28.6-75 0-103.6l144-144c10.9-10.9 28.7-10.9 39.6 0s10.9 28.7 0 39.6l-144 144c-6.7 6.7-6.7 17.7 0 24.4s17.7 6.7 24.4 0l176-176c24.4-24.4 24.4-64 0-88.4z"/>
</svg>
<input type="file" id="file_input" class="hidden">
</label>
<input type="text" placeholder="入力してください" id="user_input" class="flex-1 px-3 py-2 border rounded border-gray-300 focus:border-blue-500 focus:outline-none focus:ring">
<button id="button_post" class="px-4 py-2 bg-green-500 text-white rounded-md transition duration-300 ease-in-out hover:bg-green-600 focus:outline-none focus:ring focus:ring-green-200 ml-2">送信</button>
</form>
</div>
</div>
<script>
const button_post = document.getElementById('button_post');
button_post.addEventListener('click', function(event) {
event.preventDefault();
const formText = document.querySelector("#user_input");
if (formText.value.trim() === "") {
return;
}
const url = "<%= openai_tests_path('json') %>?user_input=" + formText.value;
const postOptions = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "GET"
};
fetch(url, postOptions)
.then(response => response.json())
.then(item => {
const list = document.getElementById("conversation");
const inputHTML = `
<div class="text-right flex items-end justify-end space-x-2">
<div class="chat-bubble inline-block bg-blue-100 px-4 py-2 rounded-lg max-w-3/4 break-words">
<span>${formText.value}</span>
</div>
<div class="icon-container w-8 h-8 flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="w-full h-full"><path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"/></svg>
</div>
</div>`;
list.insertAdjacentHTML("beforeend", inputHTML);
const responseHTML = `
<div class="text-left flex items-start space-x-2">
<div class="icon-container w-8 h-8 flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="w-full h-full"><path d="M320 0c17.7 0 32 14.3 32 32V96H472c39.8 0 72 32.2 72 72V440c0 39.8-32.2 72-72 72H168c-39.8 0-72-32.2-72-72V168c0-39.8 32.2-72 72-72H288V32c0-17.7 14.3-32 32-32zM208 384c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H208zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H304zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H400zM264 256a40 40 0 1 0 -80 0 40 40 0 1 0 80 0zm152 40a40 40 0 1 0 0-80 40 40 0 1 0 0 80zM48 224H64V416H48c-26.5 0-48-21.5-48-48V272c0-26.5 21.5-48 48-48zm544 0c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H576V224h16z"/></svg>
</div>
<div class="chat-bubble inline-block bg-gray-100 px-4 py-2 rounded-lg max-w-3/4 break-words">
<span>${item.text}</span>
</div>
</div>`;
list.insertAdjacentHTML("beforeend", responseHTML);
formText.value = "";
list.scrollTop = list.scrollHeight;
})
.catch(error => console.error('Error:', error));
});
</script>
fetch()メソッド
12.Tailwindcssの導入(CDN)
以下のコードをapp/views/layouts/application.html.erb
内の<head></head>
タグ内に張り付ける。
<script src="https://cdn.tailwindcss.com"></script>
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<script src="https://cdn.tailwindcss.com"></script> //ここ
</head>
13.ルーティングの設定
app/config/routes.rb
ファイル内に以下の記述を行う。
namespace :openai do
get 'tests/', to: 'tests#generate_text'
get 'tests/show', to: 'tests#show'
end
14.サーバーを立ち上げて表示を確認
rails s
お疲れ様でした。
Discussion