🤖

Ruby + Azure OpenAI Serviceでつくる社内ナレッジを活用するチャットシステム

2023/08/22に公開

こんにちは、Happy Elements 株式会社でエンジニアをしておりますryoooです。

概要

本記事では蓄積された社内の情報をデータベースに登録し、チャット形式で情報を引き出すような仕組みを構築します。

この手の記事は何番煎じ?という感じですが、自分たちにとって最適な方法を選択することで少し変わった構成になりましたので共有させていただきます。

構成

  • frontend
    • NextJS製 OSS(ChatGPT-Next-Web)
  • backend
    • Server
      • Ruby on Rails
    • Vector DB
      • PostgreSQL(pgvector)
    • LLM
      • Azure OpenAI Service(GPT-4-32k)

概要図

CloudFrontや認証部分など、本稿でご紹介したい内容から外れた部分は割愛しています。

PostgreSQLからナレッジを検索せず、社内スタッフからの質問をそのままGPT-4に投げる機能もあります。

特徴(工夫した点)

Rubyを採用

最初はLangChainに乗っかっておくことが将来性につながると考え、Ruby on RailsでなくFastAPI + LangChain(Python)で開発していました。
これでもリリース可能なところまで持っていったのですが、正答率を上げるためにプロンプトやナレッジの登録・取得方法をカスタマイズする量が増えていき、LangChainを用いる意味が薄れてしまいました。

LangChainを使わない前提に立って別言語も検討に入ってきたのですが、その中でrubyを採用した意図としては以下のとおりです。

  • 弊社ではRubyを扱える人材のほうが豊富
  • 自分自身が慣れているためリリースまでスムーズに進められる
  • Rubyであればナレッジの情報源となるサービス(Box、Kibela、Slack、MicroCMS)にアクセスするためのクラスや、利用状況のレポート(SpreadSheetやExcel出力してSlack送信)も既存の仕組みを使える。(こちらの仕組みについては以下記事をご参照ください。)
    https://zenn.dev/happy_elements/articles/691e2b7772b759

gemはruby-openaiを利用

RubyでAzure OpenAI Serviceにアクセスするにあたって、以下のGemを利用しています。

https://github.com/alexrudall/ruby-openai

実装時点のバージョン(4.3.1)では、以下の点が開発時にネックになりました。

streamモードでLLMのエラーコードが取得できない。

streamモードでLLMにアクセスした際に、LLMに側でエラーが発生するとレスポンスが取得されません。
これはGem側で対応すれば取得可能であり、以下のPRで修正予定となっています。
https://github.com/alexrudall/ruby-openai/pull/275

Azureへの接続クライアントを複数持てない

接続クライアントを再作成すると既存のクライアントの設定が上書きされてしまうため、複数のモデルに処理を投げたい場合にタイムアウト時間を変えるといったことができませんでした。
これにより、Embeddingとchatなど異なるモデルでもタイムライン時間などの接続設定を変えることができませんでした。
以下のissueの内容なのですが、最新のバージョンv5以降では治っているようです。
https://github.com/alexrudall/ruby-openai/issues/149

Vector DB

テーブル定義

Vector DBではPostgreSQLのpgvectorを利用しました。
テーブル構成はもともとLangChainで開発していた名残で将来的にLangChainに移る可能性も鑑みてLangChainで使われているテーブル定義と揃えています。

CREATE TABLE langchain_pg_collection (
  uuid uuid PRIMARY KEY,
  cmetadata json,
  name varchar(255)
);

CREATE TABLE langchain_pg_embedding (
  uuid uuid PRIMARY KEY,
  collection_id uuid,
  cmetadata json,
  document text, // ← ナレッジ本文
  embedding vector(1536), // ← Azureのtext-embedding-ada-002モデルは1536次元
  custom_id varchar(255)
);"

※ langchain_pg_embedding.cmetadataには、情報源のサービス名やURL、タイトル、改変検知のためのhash値などを保持しています。

コレクション単位でナレッジグループを分離

コレクションという単位でナレッジのグループを管理するような構成になっていますので、全社でのナレッジと、チームごとのナレッジでコレクションを分けておくことが可能です。
これにより、チャットウィンドウごとに異なるナレッジグループを検索対象として指定することが可能です。

BoxやKibelaやNotionに記されたナレッジについては、フォルダやグループ名がコレクション名になるように構成することで、柔軟なナレッジデータベースになったと感じています。

ナレッジからEmbeddingへの変換での工夫

ナレッジとなるドキュメントをチャンクに分けて、チャンクからEmbeddingに変換する形が一般的かと思いますが、弊社の仕組みではこの一般的な方法に加えて、「この情報の概要および、この情報が役に立ちそうな社内スタッフの質問文の例」をGPT-4にいくつか箇条書きで考えてもらい、得られた文字列をEmbeddingに変換して登録しています。
この工夫によって、langchain_pg_embeddingのレコード数は4〜5倍程度になったのですが、(感覚的で恐縮なのですが)質問に対する正答率は上がったように感じており、この状態でサービスインしています。

キーワード検索

通常のcos類似度で取得したナレッジが規定トークン数に達しなかった場合は、LLMを使って質問文言からキーワードを抽出して、そのキーワードでlike文検索することで追加ナレッジを抽出する処理についても実装しています。
このあたりはFunction Callingを活用する余地があるかもしれません。

Azure OpenAI Service

良かった点

  • 事前にGPT-4の利用に申し込み、利用許可を得られましたのでGPT-4-32kのモデルを利用できました。
  • パフォーマンスは非常に高速でした。
  • jsonが破損しているといった話も事前情報では聞いていたのですが、stream有無に関係なく壊れたjsonに悩まされることはありませんでした。

苦労した点

コンテンツフィルタリングが厳格

  • Azureでは、入力プロンプトのフィルタリングと、出力結果のフィルタリングの両面でコンテンツフィルタリングが行われるのですが、これが結構厳しいです。
    • 福利厚生の"マッサージ"に関する質問が手元では毎回ではないけど引っかかったりします。
  • 以下のような設定画面があるのですが、こちらを変更するには申請が必要です。

streamモードで文字の欠損がみられる

「Azure => Rails」および「Rails => フロント」のいずれの経路もstreamモードで実装していたのですが、「Azure => Rails」の経路内で文字の欠損が発生していました。

こちらについてはいろいろ対処法を模索しましたが結果的に対処できず、「(見た目の速度感よりも)欠損がない方が社内スタッフにとってメリットが大きいだろう」という観点から、streamモードを使わないようにしました。
これにより、少しずつメッセージが表示されず一気にレスポンス全文が表示されるようになりました。

GPT-4のパフォーマンスが微妙なら苦労したと思うのですが、レスポンス速度が結構早いので、streamモードでなくてもさほど気にならなかったのが不幸中の幸いでしょうか。

フロントエンド

フロントエンドについては、GitHubで検索すればMicrosoft公式のものも含めて非常に沢山のOSSがみつかります。
今回はその中から、以下を選択しました。
https://github.com/Yidadaa/ChatGPT-Next-Web

Next.jsをベースにしたプロダクトで、Star数も41k超えしています。
こちらを採用した理由は主に以下です。

  • Azureに対応している。
  • システムプロンプトやLLMのパラメーターを社内スタッフ単位で自由に設定・変更できる画面が用意されている。
  • Streamモードにも対応している。
  • コードも綺麗で読みやすく、修正しやすい。
  • 多言語化されていて、日本語にも対応している。
  • モバイルにも対応している。

こちらをベースにして、「情報源となったナレッジドキュメントのURLリンク」を回答の下に表示する機能(※)を追加実装したり、設定のデフォルト値をイジったりしてリリースしています。
※ 弊社ではフィットネスクラブを利用できる福利厚生があるのですが、それについて質問すれば以下のように「ポータルサイトのどの記事の情報をもとにして回答を出力したのか?」をリンクで表示するような機能となっています。

おわりに

最後までみていただきありがとうございます。
以上のように、カカリアスタジオではChatGPTを使って、社内ナレッジへの快適なアクセス、ならびに全従業員がGPT-4を利用できる環境を整えています。
未熟な点も多々あるかとは思いますが、引き続き皆様の記事を参考に改善を重ねて、楽しくゲーム作りに集中できる環境を整えていきたいと思います。

最後に広告させてください。

自分の手がけたゲームを、何万人もの人にあそんでもらえる体験

Happy Elements株式会社は「エリオスライジングヒーローズ」、「あんさんぶるスターズ!!」、「メルクストーリア」などGoogle PlayやApp Store上でいくつもの人気スマートフォンゲームを運営しています。
現在も新規タイトルを鋭意制作中です。

「面白いゲームを作りたい」とお考えの方、「スキルの高い仲間と一緒に働きたい」と感じている方からのご応募をお待ちしています。「自分の手がけたゲームを、何万人もの人に遊んでもらえる」そんな体験を、ぜひ私たちと一緒にしてみませんか?
https://recruitment.happyelements.co.jp/

Happy Elements

Discussion