今回は2週間前の記事の発展ということで、まだご覧になっていない方は先にご覧いただくことをおすすめします。
今回は何をするか(おさらい)
前回は、AppSheet(ToDoリスト)に新規項目を追加した際に、その項目名をそのままLLMに入力して、何を買うべきか回答を生成させていました。

ですので、 ハルシネーション と呼ばれる誤った情報を出力する場合がありました。
それを解決するためには RAG(Retrieval-Augmented Generation:検索拡張生成) の仕組みを活用することが有効であるとお伝えしました。
具体的には入力文をいったんGoogleなどに検索をかけ、入力文とWeb検索結果を併用してLLMから回答を得る仕組みに修正していきたいと考えています。
作成したワークフロー図
早速ですが、今回作成したDifyのワークフロー図です。

フローとしては、以下のようになります。
- 新規項目を追加
- 1.をGoogle翻訳で英語に変換
- 2.のイメージ画像を
Stable Diffusion
で生成 - 「2.の内容+recommend」でGoogle検索で検索
- 2.の内容に4.の内容を加えて
LLM
におすすめを提案させる - 5.の内容をGoogle翻訳で日本語に変換
- 3.の画像と6.の内容を出力させる
今回利用したLLM
前回はLLMにGoogle社のGemini 1.5 Pro
を利用しました。
今回は違ったLLMに挑戦しようということで、Meta社から一週間前(7月23日)に発表されたばかりのLlama3.1
を利用することにしました。
GPT-4o超えとの噂もありますが、今回は軽量で高速処理が特徴の8Bモデル
を利用しました。
Llama3.1
は日本語でプロンプトを入力してもnull
しか返してきませんでした。
ですので、先述の作成したワークフロー図のように、Llama3.1
への入力を日本語⇒英語にし、出力を英語⇒日本語にするためにLLMの前後にGoogle翻訳のブロックを挟んでいます。
Llama3.1
に入力するプロンプトは以下の通りです。
前回のプロンプトをベースに英語で作成しました。
## Commands
* You are a "moving pro".
A user is considering buying "{{#1722207567888.text#}}".
* Provide quick recommendations on what to buy based on the following criteria and "Google search results".
"Google search results": {{#1721200265906.text#}}
* Suggest specific manufacturers and product names.
## Criteria
- Couple in their 20s
- 1LDK
- Love Scandinavian style
- Minimalist
- Save money
Llama3.1をどうやって利用するか
私のような初学者にとってはここが一番のハードルではないかと思います。
できるだけ丁寧に説明したいと思いますが、結論として2024年7月30日現在Llama3.1
をHugging FaceのInference APIで利用するにはPro Account($9/month)に登録する必要があります。
Freeプランであれば前モデルのLlama3が使えると思うので、読み替えてください。

Llama3.1の利用申請
手順としては、まず以下のHugging FaceのMeta-Llama-3.1-8B-Instruct
モデルのページに移動します。
そこで、Llama3.1
モデルの利用申請をSubmitします。
私の場合、数十分で承認のメールがきました。

Hugging Faceのアクセストークン発行
続いて、Hugging Faceのアクセストークンを発行します。
- Hugging Faceの右上メニューからSettings
↓ - Access Tokens
↓ - Create new token
↓ - Token typeとして
Read
を選択
↓ - 任意のToken nameを入力
↓ - Create token
↓ -
hf
から始まるアクセストークンをCopy
↓ - Done

DifyにLlama3.1追加
続いて、DifyにLlama3.1
を追加します。
- Difyの右上メニューから設定
↓ - モデルプロバイダー
↓ - Hugging Faceのモデルの追加
↓ - Model Typeは
LLM
↓ - Model Nameは
meta-llama/Meta-Llama-3.1-8B-Instruct
↓ - Endpoint Typeは
Hosted Inference API
↓ - API Tokenは先ほどHugging Faceでコピーしたものを貼り付け
↓ - 保存

Llama3.1のトークン数の設定
最後にLlama3.1
のトークン数を設定します。デフォルトは20
ですが、出力文が非常に短くなってしまいます。
トークン数が多くし過ぎると回答の生成が遅くなってしまうので適度な値に設定します。
私は今回101
に設定しました。

これでセットアップ完了です。
あとは、先述の作成したワークフロー図の通りにブロックを追加して接続していきます。
なぜStable Diffusionを挟んでいるのか
前回から相違点としてLLMの違いの他に、今回新たにStable Diffusion
のブロックを挟んでいます。
これは、AppSheet(ToDoリスト)の各項目のサムネイル用として、アイキャッチ画像を生成したいと考えて追加しました。
ちなみにStable DiffusionのAPIは従量課金制のため、クレジットをもっていなければStability AI Developer Platformから課金をする必要があります。
今回は生成スピード重視でStable Diffuison 3 Turbo
を使用しました。
Stable Diffuison 3 Turbo
も割と最近(2024年4月18日)にAPIが公開された高性能なモデルになります。

APIキーはログインして以下から+ Create API Key
で発行します。
Google Apps Scriptコードの修正
Stable Diffusionで生成した画像のURLをAppSheetに紐づいたGoogleスプレッドシートに書き込む必要があります。
ですので前回のコードに、コメントを含めて以下の6行だけ追加すればOKです。
// 画像URLの抽出と書き込み(新規追加)
if (outputs.files && outputs.files.length > 0) {
const imageUrl = outputs.files[0].url;
// H列に画像URLを書き込み
sheet.getRange('H' + lastRow).setValue(imageUrl);
}
←全体のコードは三角マークを押すと確認することができます。
function sendChildrenQuestion() {
// スプレッドシートのIDは、スプレッドシートのURLに含まれています。
// 例えば、URLが https://docs.google.com/spreadsheets/d/abcd1234efgh5678ijkl90mnopqrstuv の場合、
// abcd1234efgh5678ijkl90mnopqrstuv がスプレッドシートのIDです
const spreadsheetId = 'YOUR_SPREADSHEET_ID'; // TODO スプレッドシートのIDを書き換え
const sheetName = 'シート1';
const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
const sheet = spreadsheet.getSheetByName(sheetName);
// B列のデータをすべて取得
const data = sheet.getRange('B:B').getValues();
// 最後の非空の行を特定
let lastRow = data.length;
while (lastRow > 0 && data[lastRow - 1][0] === '') {
lastRow--;
}
// 最後の非空の行の質問を取得
const question = sheet.getRange('B' + lastRow).getValue();
const url = 'https://api.dify.ai/v1/workflows/run'; // リクエストを送るURL
// リクエストのヘッダー情報
const headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer {YOUR_DIFY_API_KEY}' // TODO DifyワークフローのAPIキーで書き換え
};
// リクエストのボディデータ
const payload = {
'inputs': {Input:question}, // TODO Difyのワークフローの開始で設定した変数名
'response_mode': 'blocking',
'user': 'user123'
};
// オプションの設定
const options = {
'method': 'post',
'headers': headers,
'payload': JSON.stringify(payload),
'muteHttpExceptions': true // これをtrueにすると、エラーが発生しても例外がスローされません
};
// HTTP POSTリクエストの送信
try {
const response = UrlFetchApp.fetch(url, options);
// Logger.log(response.getContentText()); // レスポンスの内容をログに出力
const jsonResponse = response.getContentText()
// JSONをオブジェクトに変換
const responseObject = JSON.parse(jsonResponse);
// 必要なデータを取得
const taskId = responseObject.task_id;
const workflowRunId = responseObject.workflow_run_id;
const responseData = responseObject.data;
const status = responseData.status;
const outputs = responseData.outputs;
const answer = outputs.text;
// ログに出力
Logger.log('Task ID: ' + taskId);
Logger.log('Workflow Run ID: ' + workflowRunId);
Logger.log('Status: ' + status);
Logger.log('Outputs Text: ' + answer);
// G列に返却された値を書き込み
sheet.getRange('G' + lastRow).setValue(answer);
// 画像URLの抽出と書き込み(新規追加)
if (outputs.files && outputs.files.length > 0) {
const imageUrl = outputs.files[0].url;
// H列に画像URLを書き込み
sheet.getRange('H' + lastRow).setValue(imageUrl);
}
} catch (e) {
Logger.log('Error: ' + e.message); // エラーメッセージをログに出力
}
// LINE Notify用の処理を追加
const lineNotifyToken = 'YOUR_LINE_NOTIFY_TOKEN'; // TODO: LINE Notifyのトークンを設定してください
const lineNotifyUrl = 'https://notify-api.line.me/api/notify';
// B列(質問)とG列(回答)の最新の内容を取得
const latestQuestion = sheet.getRange('B' + lastRow).getValue();
const latestAnswer = sheet.getRange('G' + lastRow).getValue();
// LINE Notifyに送信するメッセージを作成
const message = '\n【' + latestQuestion + '】 が追加されました!' + '\n\n【AI提案】\n' + latestAnswer + '\n"ご自身のAppSheetの共有リンク"';
const lineNotifyOptions = {
'method': 'post',
'headers': {
'Authorization': 'Bearer ' + lineNotifyToken,
'Content-Type': 'application/x-www-form-urlencoded'
},
'payload': 'message=' + encodeURIComponent(message)
};
// LINE Notifyに通知を送信
try {
const lineResponse = UrlFetchApp.fetch(lineNotifyUrl, lineNotifyOptions);
Logger.log('LINE Notify Response: ' + lineResponse.getContentText());
} catch (error) {
Logger.log('LINE Notify Error: ' + error.message);
}
}
デモ
あとがき
Hugging FaceのInference APIを利用してみたくてLLMをLlama3.1
にしてみましたが、デモ動画からわかる通り、出力結果は翻訳感の強い残念な結果となってしまいました。。。

翻訳を使わずとも日本語入力に対応できれば、また可能性は広がりそうですね!
これまで作製したプロダクトのご紹介