Open13

ToDoリスト導入編📝(LLMアプリケーションを活用したプロトタイプ)

VanVan

ToDoリストとLLMを組み合わせたプロトタイプを作っていきたい

背景

  • 多忙で新生活の準備が進んでいない。
  • パートナーがイライラしている。
  • ToDoリストを作って課題を見える化しよう。

ただ、買うものは列挙できても何を買うかがなかなか決まらない。。。
 ↓
AIが何を買えば良いか提案してくれるToDoリストがあれば便利!

VanVan

Difyを呼び出すためのGASは以下の記事を参照して作成。
GAS初めて触る。

https://qiita.com/cog1t0/items/5ad8da7a09ac2d29ab2a

Google Apps Script
function sendChildrenQuestion() {
  // スプレッドシートのIDは、スプレッドシートのURLに含まれています。
  // 例えば、URLが https://docs.google.com/spreadsheets/d/abcd1234efgh5678ijkl90mnopqrstuv の場合、
  // abcd1234efgh5678ijkl90mnopqrstuv がスプレッドシートのIDです
  var spreadsheetId = 'MY SPREADSHEET ID'; // TODO スプレッドシートのIDを書き換え
  var sheetName = 'シート1';

  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);

  // B列のデータをすべて取得
  var data = sheet.getRange('B:B').getValues();

  // 最後の行を特定(空のセルは除外)
  var lastRow = data.filter(String).length;

  // 最後の行の質問を取得
  var question = sheet.getRange('B' + lastRow).getValue();

  var url = 'https://api.dify.ai/v1/workflows/run';  // リクエストを送るURL

  // リクエストのヘッダー情報
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer {MY Dify API}'  // TODO DifyワークフローのAPIキーで書き換え
  };

  // リクエストのボディデータ
  var payload = {
    'inputs': {'Input':question},  // TODO Difyのワークフローの開始で設定した変数名
    'response_mode': 'blocking',
    'user': 'user123'
  };

  // オプションの設定
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true  // これをtrueにすると、エラーが発生しても例外がスローされません
  };

  // HTTP POSTリクエストの送信
  try {
    var response = UrlFetchApp.fetch(url, options);
    // Logger.log(response.getContentText());  // レスポンスの内容をログに出力
    var jsonResponse = response.getContentText()

    // JSONをオブジェクトに変換
    var responseObject = JSON.parse(jsonResponse);

    // 必要なデータを取得
    var taskId = responseObject.task_id;
    var workflowRunId = responseObject.workflow_run_id;
    var data = responseObject.data;
    var status = data.status;
    var outputs = data.outputs;
    var 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);  // エラーメッセージをログに出力
  }
}
VanVan

Difyのワークフローは授業資料を参照して作成。
授業ではLLMはGPTを使ったが、今回はGeminiを使ってみる。

VanVan

Difyのプロンプト

プロンプト
## 命令
あなたは「引っ越しのプロ」です。
ユーザーは"{{#1721071307658.Input#}}"の購入を検討しています。
以下の条件を参考に、具体的にどのような物を購入すればよいか結果だけをシンプルに200文字以内でアドバイスしてください。

## 条件
・20代夫婦
・1LDK
・北欧好き
・ミニマリスト
・節約志向
VanVan

AppSheetに新規の項目を追加してもLLMから回答が出力されない。。。

AppSheetのモニターを見るとエラーが出ているみたい

エラーメッセージ

エラーメッセージ
Error encountered in step with name [New step]: The tasks argument included a null value. (Parameter 'tasks')
VanVan

stack overflow 日本語版に回答がついた。
"マルチポスト"とやらを注意されただけだった。

以下の文言を質問文に追記した。

※本件は他サイトでマルチポストしております。(teratail、Qiita、stack overflow)
他のサイトで回答やヒントなどの情報が得られた場合、自己回答で情報共有させていただきます。
VanVan

回答はなかったけど、変数名にシングルクォーテーションが不要なだけだった。
QAサイトの方は明日、自己回答でクローズさせる。

修正箇所
- 'inputs': {'Input':question},  // TODO Difyのワークフローの開始で設定した変数名
+ 'inputs': {Input:question},  // TODO Difyのワークフローの開始で設定した変数名
VanVan

更新通知をLINE Notifyに送るようにGASを修正した。というかClaude 3.5 Sonnetに修正してもらった。

プロンプト
## 命令
あなたはプロのWebエンジニアです。
以下のGoogle Apps Scriptのコードに最後にG列に追加した内容をLINE notifyに通知する実装を加えたい。追加内容を教えて。
ただし、不要な修正はしないでください。
また、誤った情報が含まれている場合、罰金100万ドルを請求します。

```Google Apps Script
function sendChildrenQuestion() {
  // スプレッドシートのIDは、スプレッドシートのURLに含まれています。
  // 例えば、URLが https://docs.google.com/spreadsheets/d/abcd1234efgh5678ijkl90mnopqrstuv の場合、
  // abcd1234efgh5678ijkl90mnopqrstuv がスプレッドシートのIDです
  var spreadsheetId = '1ikJ1P-UwAYD2pmoVZ4BzYcvUMswz1TAPqOBTryN00Lg'; // TODO スプレッドシートのIDを書き換え
  var sheetName = 'シート1';

  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);

  // B列のデータをすべて取得
  var data = sheet.getRange('B:B').getValues();

  // 最後の行を特定(空のセルは除外)
  var lastRow = data.filter(String).length;

  // 最後の行の質問を取得
  var question = sheet.getRange('B' + lastRow).getValue();

  var url = 'https://api.dify.ai/v1/workflows/run';  // リクエストを送るURL

  // リクエストのヘッダー情報
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer app-YMvobLPFQYDvJmFNY2U7axKH'  // TODO DifyワークフローのAPIキーで書き換え
  };

  // リクエストのボディデータ
  var payload = {
    'inputs': {Input:question},  // TODO Difyのワークフローの開始で設定した変数名
    'response_mode': 'blocking',
    'user': 'user123'
  };

  // オプションの設定
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true  // これをtrueにすると、エラーが発生しても例外がスローされません
  };

  // HTTP POSTリクエストの送信
  try {
    var response = UrlFetchApp.fetch(url, options);
    // Logger.log(response.getContentText());  // レスポンスの内容をログに出力
    var jsonResponse = response.getContentText()

    // JSONをオブジェクトに変換
    var responseObject = JSON.parse(jsonResponse);

    // 必要なデータを取得
    var taskId = responseObject.task_id;
    var workflowRunId = responseObject.workflow_run_id;
    var data = responseObject.data;
    var status = data.status;
    var outputs = data.outputs;
    var 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);  // エラーメッセージをログに出力
  }
}
修正後のGoogle Apps Scriptコード
function sendChildrenQuestion() {
  // スプレッドシートのIDは、スプレッドシートのURLに含まれています。
  // 例えば、URLが https://docs.google.com/spreadsheets/d/abcd1234efgh5678ijkl90mnopqrstuv の場合、
  // abcd1234efgh5678ijkl90mnopqrstuv がスプレッドシートのIDです
  var spreadsheetId = 'YOUR_SPREADSHEET_ID'; // TODO スプレッドシートのIDを書き換え
  var sheetName = 'シート1';

  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);

  // B列のデータをすべて取得
  var data = sheet.getRange('B:B').getValues();

  // 最後の行を特定(空のセルは除外)
  var lastRow = data.filter(String).length;

  // 最後の行の質問を取得
  var question = sheet.getRange('B' + lastRow).getValue();

  var url = 'https://api.dify.ai/v1/workflows/run';  // リクエストを送るURL

  // リクエストのヘッダー情報
  var headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer {YOUR_DIFY_API_KEY}'  // TODO DifyワークフローのAPIキーで書き換え
  };

  // リクエストのボディデータ
  var payload = {
    'inputs': {Input:question},  // TODO Difyのワークフローの開始で設定した変数名
    'response_mode': 'blocking',
    'user': 'user123'
  };

  // オプションの設定
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true  // これをtrueにすると、エラーが発生しても例外がスローされません
  };

  // HTTP POSTリクエストの送信
  try {
    var response = UrlFetchApp.fetch(url, options);
    // Logger.log(response.getContentText());  // レスポンスの内容をログに出力
    var jsonResponse = response.getContentText()

    // JSONをオブジェクトに変換
    var responseObject = JSON.parse(jsonResponse);

    // 必要なデータを取得
    var taskId = responseObject.task_id;
    var workflowRunId = responseObject.workflow_run_id;
    var data = responseObject.data;
    var status = data.status;
    var outputs = data.outputs;
    var 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用の処理を追加
  var lineNotifyToken = 'YOUR_LINE_NOTIFY_TOKEN'; // TODO: LINE Notifyのトークンを設定してください
  var lineNotifyUrl = 'https://notify-api.line.me/api/notify';
  
  // B列(質問)とG列(回答)の最新の内容を取得
  var latestQuestion = sheet.getRange('B' + lastRow).getValue();
  var latestAnswer = sheet.getRange('G' + lastRow).getValue();
  
  // LINE Notifyに送信するメッセージを作成
  var message = '\n【' + latestQuestion + '】 が追加されました!' + '\n\n【AI提案】\n' + latestAnswer + '\nhttps://www.appsheet.com/newshortcut/fd7b2962-439e-4648-a638-d6846a068992';
  
  var lineNotifyOptions = {
    'method': 'post',
    'headers': {
      'Authorization': 'Bearer ' + lineNotifyToken,
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    'payload': 'message=' + encodeURIComponent(message)
  };
  
  // LINE Notifyに通知を送信
  try {
    var lineResponse = UrlFetchApp.fetch(lineNotifyUrl, lineNotifyOptions);
    Logger.log('LINE Notify Response: ' + lineResponse.getContentText());
  } catch (error) {
    Logger.log('LINE Notify Error: ' + error.message);
  }
}

一瞬しかLINE使ってないけど、またLINE DCのLT出れたらいいな。

VanVan

宿題提出しようとしたら出題意図から外れていることに気づいた

今回は LLMアプリケーション担う部分のプロトタイプ(※チャットアプリケーションのようなアウトプットを想定しています) だった。
時間ないけど急いで記事の方向性を修正する。

VanVan

とりあえず、RAGになるようにワークフローを修正。
DALL-E 3のモジュールも発見したので、AppSheet(ToDoリスト)の各項目のサムネイルで使えないか検討していきたい。

VanVan

LLMのTempretureとTop_pとTop_kについて調べてみた。

https://www.promptingguide.ai/jp/introduction/settings

https://qiita.com/simakihiro/items/ac4074f5f5e632fae895

TempretureとTop_pは一般的にはどちらを変更して制御するみたい。
デフォルトではTempretureが0.7でTop_pはオフになっていた。

Tempretureを小さくすると回答のバリエーションが少なくなり、大きくすると精度が悪い回答をするようになった。
デフォルトの0.7がちょうどいいのかも。