フォーム読み込み中
システムエンジニアの成瀬です。
日々刻々と進化を続けているAIやクラウドサービスを検証したり、AIを活用したアプリを作ったりしています。
前回(中編)のプロンプト実験室で題材にした「おそうじボット」について、一問一答型のアプリを作ってみましょう。
このアプリでは、プロンプト実験室の最後に提起した、「質問に関連する情報をどのように用意すればいいか?」という問いに対する一つの解決策として、前編で作成したテキストの類似度を判定する関数を利用して、取扱説明書の中から質問に最も関連する部分を抽出するアプローチをとります。
作成するアプリの画面構成は以下の通りです。
メニューの「おそうじボットアンサー」->「回答生成」を選択することで、関数を実行してモデルが生成した回答を表示するようにします。
アプリが質問に回答するための処理は、以下の3つのフェーズに分かれています。
まず、取扱説明書を適当な単位に分割しておきます。
つぎに分割した取扱説明書の各テキストをベクトル化しておきます。
質問が入力された時点で、質問テキストをベクトル化します。
つぎに質問ベクトルと各取扱説明書ベクトル間のコサイン類似度を算出して、最も関連性の高い取扱説明書テキストをコンテキストとして抽出します。
メニューから回答生成を実行した時点で、「指示、コンテキスト、質問」からプロンプトを構築します。
つぎに生成AIにプロンプトを送信して、回答を生成します。
このアプリのために新たに作成する関数は以下の3つです。
(処理概要図の緑の箱に相当します)
A. ベクトル化
B. 比較・抽出
C. プロンプトの構築
※「C1. 回答の生成」は中編で作成した「GENERATE_TEXT」関数を使用します。
この関数はVertex AI の Embedding APIを呼び出して、テキストに対する埋め込みベクトルを取得します。
埋め込みベクトルって何?と思った方は前編をご覧ください。
埋め込みベクトルを取得するカスタム関数は前編でも作成しましたが、前編の記事作成後にテキスト埋め込みモデルの多言語対応版「textembedding-gecko-multilingual」が公開されていたので、対応するカスタム関数を作成します。
参考:テキストエンベディング
Vertex AI APIを呼び出してテキストに対する埋め込みベクトルを取得する関数です。
TEXT_EMBEDDINGS_MULTILINGUAL( テキスト)
TEXT_EMBEDDINGS_MULTILINGUAL( "トイレに行きたいのですが" )
文字列化された768次元のベクトルです。
(扱いやすくするために、文字列化します)
対象ベクトルと最も関連している情報を複数のベクトル化したテキストから検索します。
ベクトルどうしのコサイン類似度を比較して、最も高い類似度のテキストを抽出します。
CONTENT_SEARCH(対象ベクトル, 検索範囲)
(1列目:テキスト、2列目:ベクトル) 例:A1:B2
CONTENT_SEARCH("[-0.03439481556415558, ...]", A1:B2)
[[ 最も関連するテキスト, 類似度 ]]
シート上の質問・指示・コンテキストからプロンプトを組み立てて、カスタム関数の「GENERATE_TEXT」を実行して結果を表示します。
メニューから回答生成を実行するための内部関数です。
generate_answer_()
中編のプロンプト実験室で作ったコードの更新と新しいコードを追加します。
/**
* スプレッドシートを開いたときにカスタムメニューを表示します。
*
* copyright (c) 2024 SoftBank Corp.
*/
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("プロンプト実験室")
.addItem('開く', 'show_prompt_labo_')
.addToUi();
ui.createMenu('おそうじボットアンサー')
.addItem('回答生成', 'generate_answer_')
.addToUi();
}
/**
* サイドバーを表示します。
*
* copyright (c) 2024 SoftBank Corp.
*/
function show_prompt_labo_() {
const html = HtmlService.createHtmlOutputFromFile('PROMPT_LABO')
.setTitle('プロンプト実験室');
SpreadsheetApp.getUi()
.showSidebar(html);
}
/**
* 入力情報からテキスト生成関数を実行して、結果をシートに表示します。
*
* copyright (c) 2024 SoftBank Corp.
*/
function generate_answer_() {
const spread_sheet = SpreadsheetApp.getActive()
const sheet = spread_sheet.getSheetByName("アンサー")
const question = sheet.getRange(1, 2).getValue()
const instructions = sheet.getRange(3, 2).getValue()
const context = sheet.getRange(4, 2).getValue()
const prompt = `${instructions}\n\n${context}\n\n[質問]:${question}\n[回答]:`
const answer = GENERATE_TEXT(prompt)
sheet.getRange(5, 2, 2, 1).setValues([[answer], [prompt]])
}
/**
* 生成AI APIを使用して、テキストを生成します。
*
* @param {"トイレはどこですか"} PROMPT プロンプト {文字列}
* プロンプトのテキストです。
* @return {string}
*
* @customfunction
* copyright (c) 2024 SoftBank Corp.
*/
function GENERATE_TEXT(PROMPT, MODEL = "text-bison") {
let access_info;
try {
access_info = get_access_token_(GCP_CREDS.client_email, GCP_CREDS.private_key)
}
catch (e) {
console.error(e)
throw "内部エラーが発生しました"
}
const access_token = access_info.access_token
const headers = {
"Authorization": "Bearer " + access_token
}
const API_ENDPOINT = "us-central1-aiplatform.googleapis.com"
const url = `https://${API_ENDPOINT}/v1/projects/${GCP_CREDS.project_id}/locations/us-central1/publishers/google/models/${MODEL}:predict`
const payload = {
instances: [
{
prompt: PROMPT
},
],
parameters: {
"candidateCount": 1,
"maxOutputTokens": 1024,
"temperature": 0.0,
"topP": 0.1,
"topK": 1,
"opt_out": true,
}
}
const params = {
method: "post",
contentType: "application/json",
headers: headers,
payload: JSON.stringify(payload),
muteHttpExceptions: true,
}
const res = UrlFetchApp.fetch(url, params)
if (res.getResponseCode() != 200) {
console.error(res.getContentText())
throw "内部エラー"
}
const res_json = JSON.parse(res.getContentText())
return res_json.predictions[0].content
}
/**
* 対象ベクトルと最も関連している情報を複数のテキストから検索します。
*
* @param {"[-0.03439481556415558, ...]"} VECTOR 対象ベクトル {文字列}
* 埋め込みベクトルです。
* @param {A2:B7} RANGE 検索する範囲 {レンジ}
* 検索する範囲を2列N行のレンジ形式で指定します。(1列目:テキスト、2列目:ベクトル)
* @return [[ string, float ]]
*
* @customfunction
* copyright (c) 2024 SoftBank Corp.
*/
function CONTENT_SEARCH(VECTOR, RANGE) {
let top = {
similarity: -10,
index: 0,
}
for (let i = 0; i < RANGE.length; i += 1) {
let s = VECTOR_SIMILARITY(VECTOR, RANGE[i][1])
if (s > top.similarity) {
top.similarity = s
top.index = i
}
}
return [[RANGE[top.index][0], top.similarity]]
}
/**
* テキスト埋め込みAPIを使用して、テキストに対する埋め込みベクトルを取得します。
* 結果は文字列化されたベクトルです。
*
* @param {"トイレはどこですか"} TEXT テキスト {文字列}
* 埋め込み対象のテキストです。
* @return {string}
*
* @customfunction
* copyright (c) 2024 SoftBank Corp.
*/
function TEXT_EMBEDDINGS_MULTILINGUAL(TEXT) {
if ( TEXT === "" ) {
throw "テキストが空白です"
}
const url = `https://us-central1-aiplatform.googleapis.com/v1/projects/${GCP_CREDS.project_id}/locations/us-central1/publishers/google/models/textembedding-gecko-multilingual:predict`
let access_info;
try {
access_info = get_access_token_(GCP_CREDS.client_email, GCP_CREDS.private_key)
}
catch (e) {
console.error(e)
throw "内部エラーが発生しました"
}
const access_token = access_info.access_token
const headers = {
"Authorization": "Bearer " + access_token
}
const payload = {
"instances": [
{
"content": TEXT,
}
],
"parameters": {
"opt_out": true,
}
}
const params = {
method: "POST",
headers: headers,
contentType: "application/json",
payload: JSON.stringify(payload),
muteHttpExceptions: true,
}
const res = UrlFetchApp.fetch(url, params)
const res_json = JSON.parse(res.getContentText())
if ("error" in res_json) {
console.error(res_json.error)
throw "内部エラーが発生しました"
}
const vector = res_json.predictions[0].embeddings.values
return JSON.stringify(vector)
}
質問に関連する情報を検索するためのデータシートを作成します。
| A列 | B列 | |
|---|---|---|
行1 | 取扱説明書 | ベクトル |
行2 | # 家庭用全自動掃除機「おそうじボット」について 家庭用全自動掃除機「おそうじボット™」はソウジング・テクノロジー社が開発した最新鋭の全自動掃除ロボットです。 掃除だけでなく、色々なお手伝いをすることができます。 | =TEXT_EMBEDDINGS_MULTILINGUAL(A2) |
行3 | # こんなことができます おそうじボットは掃除はもちろん、他にも色々なことができます。 ・部屋の掃除 ・電話をかける ・目覚まし時計 ・写真撮影 ・お湯を沸かす ・その他いろいろ | =TEXT_EMBEDDINGS_MULTILINGUAL(A3) |
行4 | # 電源のオン・オフ ## 電源をオンにする。 おそうじボットの電源ボタンを押すと電源がオンになり、おそうじボットの両目(LED)が点灯します。 ## 電源をオフにする。 おそうじボットの電源ボタンを30秒以上押すと電源がオフになり、おそうじボットの両目(LED)が消灯します。 | =TEXT_EMBEDDINGS_MULTILINGUAL(A4) |
行5 | # 掃除について おそうじボットは掃き掃除と雑巾がけができます。 ただし、掃き掃除と雑巾がけは同時にできません。 ## 掃き掃除 おそうじボットの右手に箒をセットします。 自動的に掃き掃除を開始します。 ## 雑巾がけ おそうじボットの左手に雑巾をセットします。 自動的に雑巾がけを開始します。 | =TEXT_EMBEDDINGS_MULTILINGUAL(A5) |
行6 | # 電話をかける ## 電話をかける おそうじボットの頭にあるおかっぱ型受話器を外します。 次に受話器の数字ボタンで電話をかける相手の番号を押します。 ## 電話を切る 受話器をおそうじボットの頭に戻します。 | =TEXT_EMBEDDINGS_MULTILINGUAL(A6) |
行7 | # アラームの設定方法 ## アラームのセット おそうじボットの背中のアラームボタンを押します。 アラームボタンの下にある時刻ダイヤルで時刻を設定します。 その後、もう一度アラームボタンを押します。 ## アラームの解除 おそうじボットの背中のアラームボタンを3秒以上長押しします。 | =TEXT_EMBEDDINGS_MULTILINGUAL(A7) |
次にアプリのインターフェースとなる、アンサーシートを作成します。
| A列 | B列 | |
|---|---|---|
行1 | 質問 | |
行2 | 質問の埋め込みベクトル | =TEXT_EMBEDDINGS_MULTILINGUAL(B1) |
行3 | 指示 | 以下の情報だけを使用して質問に回答してください。 |
行4 | コンテキスト | =CONTENT_SEARCH(B2,'データ'!A2:B7) |
行5 | 回答 | |
行6 | プロンプト |
それでは、アプリに質問をしてみましょう。
まず、セルB1の「質問」に以下のテキストを入力します。
おそうじボットはどんなことができるの?
質問の入力と連動して、セルB2の「質問の埋め込みベクトル」に質問に対応するベクトルが表示されます。
つづいて、データシートの中から質問と最も関連するテキストが、セルB4の「コンテキスト」に表示されます。
次に、メニューの「おそうじボットアンサー」をクリックして、「回答生成」を選択します。
セルB5に生成された回答が表示されます。
また、実際に送信したプロンプトの内容が、セルB6に表示されます。
今度は、質問に以下のテキストを入力して、回答生成を実行してみましょう。
山田くんに電話したいんだけど
回答結果は「わかりません」と表示されてしまいました。
コンテキストには最も関連していそうな「電話のかけ方」が与えられているのに、なぜ回答がわからなかったのでしょうか。
プロンプト実験室で同じ質問をした結果は以下のようになりました。
もしかすると、山田くんの電話番号がわからなったので、答えるのをあきらめてしまったのかもしれませんね...
それでは、生成AIモデルの使命感?を引き出すために、指示の内容に「回答者としての役割」を追加してみましょう。
セルB3の指示の内容を以下の文言に変更します。
あなたはお客様問い合わせ窓口のベテラン係員です。
以下の情報だけを使用して、質問に回答してください。
該当する回答が見つからなかった場合は、「わかりません」とだけ答えてください。
では、もう一度、回答生成を実行してみましょう。
今度は取扱説明書に基づいた回答をしてくれましたね。
ちなみに、指示を以下のように変えるだけでも役割を与えたときと、ほぼ同様の効果がありました。
以下の情報だけを参照して、困っている人を助けてあげてください。
該当する回答が見つからなかった場合は、「わかりません」とだけ答えてください。
役割を与えたり、指示の仕方を変えるだけで結果が変わるのは興味深いですね。
大規模言語モデルの知識の背景には、多種多様な人格が潜んでいるため、役割や立場による回答の仕方も記憶しているのかもしれません。
今回のアプリでのグラウンディングの方法について整理しておきます。
今回はテキスト生成AIをQ&Aに利用するために、生成AIモデルの知識を一時的に補強して望ましい回答を引き出すための「グラウンディング」という方法について説明しました。
また、コンテキストに与えるテキストを、ベクトルどうしの類似度から抽出する方法を説明しました。
グラウンディングによるQ&Aで回答精度を上げるためにはどうすればいいか、以下の点について検討してみてください。
今回試したプロンプトはあくまでも一例です。
解答例を追加したり、指示やコンテキストの区切り方を変えたり、それらの順序を変えるなど、色々試して、ご自身のユースケースでのベストプラクティスを見つけてみてください。
「活用のためのアイデア」でも触れた通り、Q&Aの精度を上げるためには色々な試行錯誤が必要です。また、データセットが大規模になるほど、精度を維持することも難しくなります。
そこで、生成AI導入やシステム構築に不安がある方向けに「Vertex AI DIYプラン」をご紹介します。
Vertex AI DIYプランでは、Vertex AI Search による、 社内文書などのエンタープライズ検索の構築をエンジニアがサポートしてくれます。
興味のある方は、一度相談してみてはいかがでしょうか?
詳細については、関連サービスの「Vertex AI DIYプラン」からご確認ください。
Vertex AI Search を使って社内文書を検索する生成AIを構築してみませんか?
ソフトバンクのエンジニアが構築をサポートします。
Google の生成AIの導入を考えている方はもちろん、どのようなものか確認したいという方でもご活用いただけます。
Google スプレッドシート、Gmail、Google カレンダー、Google Chat、Google ドライブ、Google Meet などのさまざまなサービスがあらゆる働き方に対応する業務効率化を実現します。
Google サービスを支える、信頼性に富んだクラウドサービスです。お客さまのニーズにあわせて利用可能なコンピューティングサービスに始まり、データから価値を導き出す情報分析や、最先端の機械学習技術が搭載されています。
条件に該当するページがございません