この記事のポイント: Ruby on RailsアプリにChatGPTの出力を組み込んでみました。APIを叩けるか試してみるのが主目的で、アプリの機能(使い道)の提案としては大したものではありません。
一部有料の内容を含みます(OpenAI API利用料)
Rails初学者が学習目的で執筆しております。誤り、改善点などぜひご指摘ください
はじめに
ChatGPTをはじめとするLLM(大規模言語モデル、生成系AI)の発展により、身近なソリューションに様々な変革が起きています。自分は普段Pythonで自然言語関連のタスクを扱うことが多いのですが、最近学習を始めたRuby on Railsにおいても、LLMを組み合わせることで何かおもしろいことができるのではないかと自然に考えが及びましす。
ということで今回はRailsアプリにChatGPTの出力を表示させられるのか、その動線を確かめてみました。
参考にしたサイト
Use ChatGPT API with Ruby on Rails
この記事の取り組みの完成品
掲示板アプリの上部にAIで生成したウェルカムメッセージを表示させています。また、その日の誕生日の有名人について紹介させています。実用性については一旦棚上げしています。
前提
実際はこれも必要ないのですが、ここではベースとしてシンプルな掲示板アプリを予め用意しておきました
-
Boards
コントローラ:index
,create
アクションをもつ -
Board
モデル:title
,body
をもつ
全体のながれ
- OpenAI APIキーを取得する
- Rails credentialにAPIキーを記述する
- OpenAIのAPIを叩く関数を定義する
- Controllerを記述する
- Viewを記述する
OpenAI APIキーを取得する
OpenAI APIは有料です。利用は自己責任でお願いします。
RailsでChatGPTを用いる事前準備として、OpenAI社公式サイトにてアカウント登録をし、APIキーを取得します。APIキーは他人に知られることがないように注意しましょう。
OpenAI APIサイト
- アカウントがない場合は新規作成します。
- 右側APIを選択します
- 必ずしも必要な作業ではありませんが、左側ペインからModelの確認をしましょう。
GPT-3.5 turbo
やGPT-4
が有名です。リンクを辿ると料金表を確認することができます。下の画像はあくまでこの記事投稿時点のものです。
クレジットカードの登録
- Settings, Billing, Payment methodsと順に辿り、APIキーを利用するためにクレジットカードを登録します。なお、Pricingでは過去を含む使用状況を確認することができます
利用上限額の設定
- 月の上限額やアラート連絡を送付する基準額を予め設定することが可能です。安全に使用するためには重要な設定ですね。Settings, Limits から設定しましょう。
私は月15ドルを超えたらリクエストがrejectされる設定です。
APIキーの発行
このAPIキーはウインドウを閉じると2度とフルで表示されませんので、この時点で安全な場所にコピペし保存してください。絶対に他人に教えてはいけません。
Rails credentialにAPIキーを記述する
個人的にはOpenAI APIを叩くことよりも今回勉強になった内容かもしれません。
外部のAPIキーなどの機密情報をコードにベタ打ちすることは御法度ですが、なんらか別のファイルに書き記していたとしてもそれごと流出してしまうと意味がありません。悪意のあるサイバー攻撃のみではなく、Githubに自らうっかりアップしてしまうというケースだって考えられます。
そういったリスクを極力防ぐために、RailsにはパスワードやAPIキーといった秘匿すべき情報を暗号化させる仕組みが整えられています。これを用いて先ほど取得したOpenAI APIキーを安全に保存してみましょう。
credentialの記述
機密情報を保存しているconfig/credentials.yml.enc
ファイル自体が暗号化されている関係で若干特殊な方法で編集します。なお、Dockerの場合はコンテナ内から操作してください。
- テキストエディタであるvimがインストールされていない場合は先にインストールしましょう
apt-get update
apt-get install vim
- 編集を開始する
EDITOR="vim" bin/rails credentials:edit
git cloneした場合など、秘密鍵がローカルにない場合はエラーになるのでクレデンシャルファイルを一度削除します
rm config/credentials.yml.enc
- vimで開くと下記のように表示されます。ここに
chatgpt_api_key: skxxxxxxxxxx
と追記することが目標です。 -
矢印キー
でカーソルを操作し、任意の箇所でi
を押すと挿入モードに切り替わります。chatgpt_api_key: skxxxxxxxxxx(上で入手したAPIキー)
を入力した上でescキー
で挿入モードを終えたのち、:wq
そしてEnter
を押せば保存完了です。
- vimの操作は初めてだと戸惑うのでネットの情報を参考にしましょう
.gitignoreファイルの確認
gitを使う場合、.gitignore
ファイルに/config/master.key
の記述が含まれていることを確認しておきます。これがあれば秘密鍵をgithubに上げてしまうようなことはありません。おそらくデフォルトで記述されていると思いますが念のため。
OpenAIのAPIを叩く関数を定義する
実装に入ります。ここでは、OpenAIのAPIを叩く関数はapp/service/chatgpt_service.rb
に記述しています。コントローラに関数を定義するとコードが肥大化したり、他に流用できなくなったりするので今回みたいな独立性の高いコードは単体のファイルで定義する方が好ましいようです。
必要なGemのインストール
HTTPクライアントであるhttpartyを用います。Gemfile
にgem 'httparty’
と記述し、インストールしましょう。
bundle install
なお、httpartyは重いといった言説を見かけるので他によい方法はあるかもしれません。未検証です。
関数の定義
app/service/chatgpt_service.rb
に下記のように定義します。このファイルは元々は存在しないので、新規で作成しましょう。細かい説明はコメントアウトでメモしました。
class ChatgptService
include HTTParty
attr_reader :api_url, :options, :model, :message
# モデルは予め設定しておく ここでは3.5-turboを使う
def initialize(message, model = 'gpt-3.5-turbo')
# 機密ファイルを呼び出している
api_key = Rails.application.credentials.chatgpt_api_key
@options = {
headers: {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{api_key}" # ここで 'api_key' を使用
}
}
@api_url = 'https://api.openai.com/v1/chat/completions'
@model = model
@message = message
end
def call
# userからのメッセージに対し一つ回答を与える
body = {
model:,
messages: [{ role: 'user', content: message }]
}
response = HTTParty.post(api_url, body: body.to_json, headers: options[:headers], timeout: 100)
raise response['error']['message'] unless response.code == 200
# レスポンスはjson形式のため、下記の形で返答本文"content"のみを抽出する
response['choices'][0]['message']['content']
end
class << self
def call(message, model = 'gpt-3.5-turbo')
new(message, model).call
end
end
end
Controllerを記述する
実用性はあまり考えず、とりあえずOpenAI APIとRailsアプリを連携できているかの確認に重きを置いています。有益な活用方法は今後改めて考えることとします。
ベースとなるアプリのboards#index
アクションにて、上で定義したChatgptService
を実行させるようにします。
Controller
-
app/controller/boards_controller.rb
に下記のように記述します。 - indexページでウェルカムメッセージと今日誕生日の有名人を紹介させる内容にしました。
- APIのタイムアウトエラーが発生してもページが表示されるように処理を分岐させています。
class BoardsController < ApplicationController
def new
@board = Board.new
end
def index
# 日付を入手
current_date = Date.today
# 日付のフォーマットを調整
formatted_date = current_date.strftime('%m月%d日')
# ChatgptServiceでウェルカムメッセージを生成するが、タイムアウトエラーが起きたら別の処理を行う
begin
# プロンプトに日付を代入し、今日の誕生日の有名人を紹介させる
@chatgpt = ChatgptService.call("簡単に挨拶してください。その際、#{formatted_date}が誕生日の有名人を簡単に紹介してください。")
# タイムアウトエラーが起きたときの処理。この場合は無難なAPIを使わず無難な内容にする
rescue Net::ReadTimeout
@chatgpt = "ようこそ!今日は#{formatted_date}です。"
end
@boards = Board.all.order(created_at: :desc)
@board = Board.new
end
# 略
end
Viewの記述
ブラウザでの見た目を整えます。といっても変数@chatgpt
を表示しただけのもので、決してセンスある状態ではないのでご容赦ください。
View
app/views/index
を編集します。
<nav class="navbar navbar-light bg-light">
<span class="navbar-text">
<%= @chatgpt %>
</span>
</nav>
完成
以上で完成です。問題なく動けば画像のように生成文を表示させられます。
やってみて幾つか気になったことを書くと・・・
- OpenAI APIのアクセスは数秒かかりますので、今回の活用だとページ表示時に毎回待たされることとなり正直いってナンセンスです。使い方はしっかりを考えないといけません。
- 今回の実験中、結構な頻度でタイムアウトエラーを起こしました。timeout設定を長めにとっても変わらず、Docker再起動するとしれっと直ったり・・まだ原因を特定できずにおります。HTTPクライアントも他にいいものがあるのかもしれません。
と、課題はまだまだ残りますが一旦はここまで。これからもGeek Outしていきます!