まじで最近忙しくないですか??
皆さん最近忙しくないですか?
私はとても忙しいです。
当たり前と言われるかもしませんが、仕事をして、勉強もして、家事もして、とてもゆっくり過ごせるような日々ではありません。
加えて、私は今月から新生活を送るために、新居に引っ越しをします。
今月といってもあと10日余り。。。
本当に新生活ができるのでしょうか。不安でたまりません。
そんな状況にパートナーもイライラしています。
「仕事?勉強?そんな言い訳聞きたくな~い!!!」
そうだToDoリストを作ろう
新生活の何が不安かというと、今までと同等以上の生活を送ることができるか分からないという点にあると思います。
ということでパートナーと共同編集ができるToDoリストを作って必要なものを洗い出すことにしました。
LINEのノート機能案:×
まずはLINEのノート機能を利用してToDoリストを作成しました。
ただ、作成してから気づいたのですが、LINEのノート機能には共同編集機能がありませんでした。
ということでLINEのノート機能案はボツです。
Googleスプレッドシート案:△
次に共同編集ができるGoogleスプレッドシートを利用してToDoリストを作成しました。
ただ、PC画面では問題ないのですが、スマホ画面で編集しようとすると操作性が悪く、イライラを解消するためのToDoリストに対してイライラするという本末転倒な状況になってしまいました。
ということでGoogleスプレッドシート案もボツです。
AppSheet案:○
そして、GoogleのAppSheetというサービスでToDoリストを作成してみました。
以下のサイトを参考にして簡単に作成することができました。
Googleスプレッドシートをインポートするだけで、それなりのUI画面ができてきます。
さらに、AppSheetの編集画面で直感的にアプリ画面のUIをいじることができます。
初学者にとっては直観的に操作できることは大きなメリットです。
ということで、めでたしめでたし。
-完-
とはなりませんでした。
イライラは解消されなかった
上記でToDoリストがAppSheetでできました。
ただ実際、何を購入するかは事前に調査をする必要があります。
「その調査をする時間がないんじゃんか。。。」
ということに気が付きました。
じゃあAIに提案してもらえばいいんじゃね?
ということで、何を購入すればよいか、AIに提案しもらえば貴重な時間を消費せずに、新生活の準備を進められるのではないかと考えました。
追加内容
以下のDifyのワークフロー機能を使って、ToDoリストに追加した項目について、具体的に何を購入したらよいかを提案してもらえるようにしました。
Tempreture
というパラメータは0~1で設定可能ですが、
- 高いほど多様な出力
- 低いほど一貫性のある出力
が得られました。
結果として、デフォルトの0.7
がちょうど良さそうでした。
具体的なフロー
- AppSheet(ToDoリスト)に新規項目追加
↓ - AppSheetに紐づいたGoogle Apps Scriptを実行させ、Difyを呼び出し
↓ - Difyで新規項目について何を購入すればよいか回答を生成させる
↓ - 生成した回答をAppSheet(ToDoリスト)に反映させる
Difyの設定
Difyのワークフローは以下の記事を参考に作成しました。
今回LLMは Gemini 1.5 Pro
を選択し、LLMのプロンプトは以下のように設定しました。
## 命令
あなたは「引っ越しのプロ」です。
ユーザーは"{{#1721071307658.Input#}}"の購入を検討しています。
以下の条件を参考に、具体的にどのような物を購入すればよいか結果だけをシンプルに200文字以内でアドバイスしてください。
## 条件
・20代夫婦
・1LDK
・北欧好き
・ミニマリスト
・節約志向
Google Apps Scriptの設定
Google Apps ScriptのコードおよびAppSheetとの連携は以下の記事を参考にしました。
ここまで読んでいただけた方ならお分かりかもしれませんが、他の方のプロダクトを参考にしまくって何一つ自分で作っていません。
OSS(オープン・ソース・ソフトウェア)文化のありがたみをひしひしと感じています。
完成したソースコード
←の三角マークを押すと確認することができます。
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);
} 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);
}
}
でも流用ばかりしていては芸がないので、AppSheet(ToDoリスト)に新規項目が追加されたときにLINE Notifyに通知を送るコードを忍ばせてみました。
これで自分とパートナーとLINE NotifyのグループLINEを作成し、どちらかがAppSheet(ToDoリスト)を更新すればLINEで通知を確認できるようになりました。
また、LLMからの提案内容についても 【AI提案】 という形で一目で確認できるようにしました。
デモ
パートナーからの反応
シンプルですが、反応があるのはうれしいですね。
今後の展望
現状は、AppSheet(ToDoリスト)に新規項目を追加した際に、その項目名をそのままLLMに入力して、何を買うべきか回答を生成させています。
ですので、 ハルシネーション と呼ばれる誤った情報を出力する場合があります。
それを解決するためには RAG(Retrieval-Augmented Generation:検索拡張生成) の仕組みを活用することが有効です。
今後はより質の高いToDoリストに昇華させるためにも、以下のように入力文をいったんGoogleなどに検索をかけ、入力文とWeb検索結果を併用してLLMから回答を得る仕組みに修正していきたいと考えています。
そして、DifyにはDALL-E 3のモジュールがあることも発見しました。
入力文から生成した画像をAppSheetの各項目のサムネイルとして利用できないかも検討していきたいと考えています。
いったん、Dify上だけで動作確認
これまで作製したプロダクトのご紹介