🤖

GoogleのAI(Gemini)をRailsから簡単に使うためのClassを作りました。※活用事例の説明あり

2024/10/01に公開

最近流行りのAI。
今まではWEB版のChatGPTで満足していたのですが、個人開発(自社開発)してる Asked Q&A というサービスにAIを導入してみたい欲が高まり、唐突にAPIでAIを使ってみることにしました。
https://asked.page

なぜGeminiを選んだのか?

Titleにも書いている通り、今回はGoogleのGeminiを採用しました。
https://gemini.google.com

Geminiを採用した理由は

です。

単純作業を自動化するくらいならどれを選んでも変わらないと思うので、とりあえず気軽に使えるものを選んだ感じですね。

問題点

GeminiはRails向けのgemを提供しておらず、Get codeの選択肢に Rubyがないのです。。。

弊社では、フロントエンドにNext.js、バックエンドにRailsを採用しています。
そのため、バックエンドで使いたい場合はGeminiにリクエストする機能を自作する必要があるのです。

作ってみた

ないのであれば作ればよいだけなので、サクッと実装してみました。
今回自動化したい内容は、

  • センシティブ判定
    • 投稿されたコンテンツにセンシティブな内容が含まれているか?
    • ※この結果次第でページに表示する広告を切り替えたいため
  • ハラスメント判定
    • 投稿されたメッセージに悪意が含まれているか?
    • ※悪意が含まれたメッセージをユーザーに見せたくないため

の確認なので、「渡された値をAIで真偽判定するClass」という設計にしてます。

実装したClass

# frozen_string_literal: true

class Gemini::Flash::GenerateContent::Boolean
  def initialize(system_instruction:, text:)
    @system_instruction = system_instruction
    @text = text
  end

  def run
    http_request = prepare_http_request
    http_request[:request].body = request_body
    response = http_request[:http].request(http_request[:request])

    raise "Gemini: response is not success" unless response.is_a?(Net::HTTPSuccess)

    parsed_response = JSON.parse(response.body)
    text_content = parsed_response.dig("candidates", 0, "content", "parts", 0, "text")
    json = JSON.parse(text_content).symbolize_keys

    ActiveRecord::Type::Boolean.new.cast(json[:response])
  end

  private

  def prepare_http_request
    endpoint = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent"
    url = URI.parse("#{endpoint}?key=#{Rails.application.credentials.gemini[:api_key]}")
    http = Net::HTTP.new(url.host, url.port)
    http.use_ssl = true
    request = Net::HTTP::Post.new(url, { "Content-Type" => "application/json" })
    { http:, request: }
  end

  def request_body
    {
      systemInstruction: {
        parts: [{
          text: @system_instruction
        }],
        role: "model"
      },
      contents: [{
        parts: [{
          text: @text
        }],
        role: "user"
      }],
      generationConfig: {
        responseMimeType: "application/json",
        responseSchema: {
          type: "object",
          properties: {
            response: {
              type: "boolean"
            }
          }
        }
      }
    }.to_json
  end
end

面倒なので細かい実装の説明は省きます。
書いてある通りに動きます。

使い方

命令文( system_instruction )と検証対象( text )を渡すだけです。
試しに、簡単な真偽判定を実行してみるとこんな感じの結果が返ります。

workspace(dev)> Gemini::Flash::GenerateContent::Boolean.new(system_instruction: "計算結果が正しことを確認してください", text: "1+1は2ですか?").run
=> true
workspace(dev)> Gemini::Flash::GenerateContent::Boolean.new(system_instruction: "計算結果が正しことを確認してください", text: "1+1は999ですか?").run
=> false

有効活用できそうな人へ

汎用的に作ったこともあって、他の使い道も多いと思います。
使いたい人がいたらほとんどそのまま使えるので、もしよければコピペしてどうぞ。


活用事例

既にこの機能は本番環境に適用されているので、実際にどのように動作しているか?について興味のある人は使ってみてください。
センシティブ判定は管理画面の機能なこともあって、ユーザー視点ではリアルタイム確認が難しいのですが、ハラスメント判定はAsked Q&Aのアカウントを持ってる人なら誰でも簡単に動作を確認することができます。
https://asked.page

どこで使われてる?

ハラスメント判定が使われている箇所は

  • ご意見箱
  • フィードバック機能

の2箇所です。

ご意見箱・フィードバック機能について

名前の通り、不特定多数から匿名で

  • メッセージ
  • 質問
  • 意見
  • 要望

などを受け取ることができる機能です。

ご意見箱・フィードバック機能は継続的なコンテンツの改善を目的にしているため、悪意や嫌がらせを含んだ内容は不要と判断し、今回の対応から 「AIにハラスメント判定された内容(レコード)はユーザーに表示されない(クライアントサイドに送信されない)仕様」 になりました。
※法的な対応を視野に入れてDBにはデータを残してますよ(圧)

-- Before --

悪意のある匿名ユーザー
「困らせてやるぜ!嫌がらせのメッセージ送信だ!きもちぃ〜」

Asked Q&Aを健全に使ってるユーザー
「おっ、新しいメッセージ届いてる!どれどれ?」
「🥺」

-- After --

悪意のある匿名ユーザー
「困らせてやるぜ!嫌がらせのメッセージ送信だ!きもちぃ〜」

Asked Q&Aを健全に使ってるユーザー
「今日はまだメッセージ届いてないな〜」


ここだよ(リンク)

実際のページを見ていただくとイメージしやすいと思うのでリンクを貼っておきます。
※ハラスメント判定の動作を確認したい場合は、ご自身のアカウントにメッセージを送信する必要があります。

ご意見箱

https://asked.page/@asked/questions/new

フィードバック機能(このFAQは役に立ちましたか?から送信できます)

https://asked.page/@asked/faqs/4


匿名募集機能の課題と今後

最近ではよく見かける匿名募集機能ですが、これには匿名ゆえに「誹謗中傷」が永遠の課題となってます。
匿名募集が可能な他社のサービスでは、NGワードを伏せ字にすることで、この誹謗中傷の対策を行なっているようです。(間違ってたらごめんなさい...)

Asked Q&Aも同じ対策方法を検討しましたが、「伏せ字では文脈から推測できてしまうので不十分」と判断し、しばらく対策方法について悩み続けてました。(エンタメ系サービスであれば伏せ字を大喜利に活用できるからアリなんですけどね...)
そこで、「最近流行りのAIを使えば精度高く文脈を元にフィルタリングできるのでは?」と思い、お試しで実装してみたら意外と良い感じになって、その場の勢いで本番適用してましたw

結果、精度高く不適切なコンテンツの排除には成功しました。
まぁしかし、毎回外部のAPIを叩いているのでコストはかかりますね...
サービスを健全化する方が大事なので全く後悔はしてません。

悪意のあるユーザーが多いと、表示できないコンテンツが増えてサービスが潰れる
そうでなければ、サービスが健全に成長する
どちらの結果になっても個人的には楽しめますが、法人の代表としては健全に成長してほしいなぁ。と願っております。


よかったら使ってね。
https://asked.page

以上!

Discussion