背景
弊社では、カスタマーサポートチームやエンドユーザーから問い合わせが来たとき、社内SREチームが対応をしています。
このとき、過去の対応を参考にするケースも多いため、問い合わせが来た時に自動で類似する過去の問い合わせのリンクを検索し貼り付けるbotを作成しようとしました。
この記事では、この検索ロジック部分をBigqueryで作れたため、その手法の紹介します。
(リクエストおよび結果の取得・展開に関する話は省略しています。)
前準備
1. usリージョンデータセットで応対履歴を触れる状態にする
検索対象となるデータは当然bqで触れる必要があります。弊社の場合、問い合わせはスプレッドシートで管理されていたため、bqに外部テーブルとして読み込ませるだけで同期されるようにできました。
また、この時注意しなければいけないのは、今回扱うMML関連のモデルは、今現在usリージョンのデータセットでなければ使えない点です。リージョンを跨いでクエリを実行することはできないため、検索対象となるデータも当然usリージョンに配置する必要があります。 もし既に日本リージョンで管理しているデータを利用したい場合は、データ転送等を用いてusリージョンに配置しましょう
2. 接続を作成する
公式ドキュメントに書いてある通りなので、詳しくはこちらを見てほしいのですが、ざっくり
- usリージョンで BigLake とリモート関数(クラウド リソース) の接続を作成
- この接続のサービスアカウントに、 Vertex AI ユーザーロール を割り当てる
の二つをします。 また接続とvertexAIの利用のためにそれぞれAPIの有効化もすることになります。
どうやらVertexAIの方で提供されている事前学習済みのLLMモデルを、bigqueryからリモートモデルとして利用できる という仕組みのようです。
3. LLMモデルを作成する
コンソールでCREATE MODEL を実行します。(MY_DATASET, MY_CONNECTIONは自分のプロジェクトに合わせて読み替えてください)
CREATE MODEL MY_DATASET.llm_remote_model
REMOTE WITH CONNECTION us.MY_CONNECTION
OPTIONS (remote_service_type = 'CLOUD_AI_LARGE_LANGUAGE_MODEL_V1')
;
重要なのはoptionsの中で、 今回は CLOUD_AI_LARGE_LANGUAGE_MODEL_V1
を指定しています。 他にも CLOUD_AI_TEXT_EMBEDDING_MODEL_V1
などもあるようです。
なんとも簡単すぎでしたが、ここで試しに、適当なタスクをやらせてみます。
SELECT
*
FROM
ML.GENERATE_TEXT(
MODEL MY_DATASET.llm_remote_model
, (SELECT "日本の総理大臣は誰ですか?" AS prompt)
, STRUCT(0.2 AS temperature,100 AS max_output_tokens)
)
そして帰ってきた答えがこちら
{
"predictions":[{
"citationMetadata":{"citations":[]}
,"content":" 岸田文雄"
,"safetyAttributes":{
###以下省略###
ちゃんと日本語の質問の意図を汲み取って返事ができていますね。
また、GPTなどとは違ってチャットボット用にチューニングされているわけでもないため、かなり無味乾燥な返事になってます。 出力に対して色々処理を行うことを考えるとこれくらいの方がむしろ都合がよいです。
本題 類似問い合わせの検索
新たに来た問い合わせに対して、キーワード抽出を行い、過去の問い合わせの中でそのキーワードが多く使われたものを提案する、という方針のもと、以下の処理をおこなうことにしました
- 問い合わせ文字列から、URLを除外して、プロンプトを前に追加し、LLMに入力する文字列を作成する
- これをLLMにかけて、出力JSONをパースし、キーワード配列を作成する
- 過去の問い合わせとCROSS JOIN して、それぞれの問い合わせ内で出現するキーワード数を集計し、その出現数の多い問い合わせから5件出力
WITH keywords AS(
SELECT
split(string(ml_generate_text_result['predictions'][0]['content']), ', ') AS content
FROM
ML.GENERATE_TEXT(
MODEL MY_DATASET.llm_model
, (SELECT CONCAT( -- プロンプトの作成
'以下の文章から、キーワードを抽出してカンマ区切りで出力してください。'
, 'なおキーワードは、その文章の概要を掴むためのものであり、固有の人名や具体的な金額等を含めないです。/n/n'
, REGEXP_REPLACE('{問い合わせ文字列}', r'(https?://[^\s]+)', ''), r'(```.*?```)', '')) AS prompt)
, STRUCT(0.2 AS temperature,100 AS max_output_tokens)
))
SELECT
-- 問い合わせ 管理テーブルには url と text のカラムがある
url
, COUNTIF(text LIKE CONCAT('%', keyword, '%')) AS match_num -- 部分一致の探索
FROM
MY_DATASET.MY_TABLE -- 過去の問い合わせを管理するテーブル
, keywords
, UNNEST(content) AS keyword
GROUP BY
url
ORDER BY
match_num desc
LIMIT
5
余談。
実はこの方法にたどり着く前に、他にも手法を試していました。 一つ目は remote model の時に例に挙げた embedding model を用いた意味検索です。 問い合わせ文字列を全てベクトル化し、このベクトルと新たに来た問い合わせ文字列のベクトルとの距離を計算し、最も近いものを出力する というものなのですが、 意味の肌感覚的な距離とベクトルの距離が結構食い違って(つまりembeddingの精度が悪く)断念しました。
二つ目は、 キーワード抽出を過去の全ての問い合わせ文字列に対して行い、キーワード同士の一致を見る方法です。これは同一の文字列に対しても別のキーワードを選出するケースがあり、この回避策を考えている時に、前章で紹介した方法を思いつきました。
どちらも、モデルの扱い方やプロンプティングで解決できる可能性もあったのですが、usリージョンでしか使えない状況であるところから、モデルが日本語に弱いということにしてとっとと諦めちゃいました。
また、紹介した手法についても、LLMではなくmecabで分かち書きをしてしまった方がよさそうだと思っています。
おわりに
bigquery ML を使うことで、LLMタスクをbigquery上で実行できるということ自体がとんでもないことだと思うのですが、これを実際にビジネス課題に適応しようと思うと、その出力精度をエンジニアが保証できない問題がどうしてもあります。 なので活用ノウハウを貯めるためには社内ツールとしての提供はかなりアリなんじゃないかなと、取り組んでみて一層感じました。
また最近のbigqueryはiPython notebookを扱えるようになったり、bigframeというdataframeをbq上で動かすライブラリを作ったりと、全ての分析や機械学習をbigquery上で行えるようにしていると感じています。 データ分析官としてこれは革命になりそうなので、今後も動向を追っていきたいと思います。