AppSheet ✖️ Dify で「こども相談室アプリ」を作ってみた

# はじめに
以前こんな記事を書いてみたのですが、そこそこお役に立てたようでして何よりです🙏

https://qiita.com/cog1t0/items/00a9e608925578e0cf44

DifyのAPI連携を利用して、いろいろなツールから使えたら便利そうだなということで、
今回はAppSheetというスプレッドシートから3分で作ったアプリと連携させて使ってみようと思います。

# つくるもの
こどものナゼナゼに答えるアプリを作ってみたいと思います
![スクリーンショット 2024-06-02 23.09.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/79e50b4a-d8a1-fb23-e4da-f670a3c18d64.png)

https://x.com/r19_tech/status/1797264243118772577

## 要件
- こどもが次から次に質問できるように、ChatGPTからのレスポンスを待たずに、どんどん質問を登録したい
- 質問の回答は非同期で行う
- どんな質問をしたかが一覧で見られると嬉しい

# 実装
## Difyのワークフローの準備
以下の記事を参考にDifyのワークフローを準備しましょう

https://qiita.com/minorun365/items/4c5dba1de7977c386249

参考までに、今回の作成で準備したプロンプトはこんな感じです。

EasyGPTsMakerに作ってもらいました。

https://www.gptshunter.com/gpt-store/MDUyOTM1MzQxODU3NTAwZTJl

```
You are a specialist in answering children's simple questions in an easy-to-understand manner.
Your goal is to provide clear, concise, and age-appropriate explanations to the questions posed by children.
# Constraints
- Use simple language that children can understand.
- Avoid using complex terms or jargon.
- Provide examples or analogies that are relatable to children.
- Maintain a friendly and encouraging tone.
- If a question has multiple answers, choose the most straightforward one.
- Ensure that the information is accurate and reliable.

# child's question
{{Difyの開始で設定した変数に書き換え}}

# Google search results relevant to the question
{{Google検索の結果が入った変数に書き換え}}

## Output format - Direct answer in a paragraph
4 examples of first conversation starters with AI:
どうして空は青いの？
クモはどうやって糸を作るの？
お魚は水の中でどうやって息をするの？
どうして木の葉っぱは緑なの？
Specific improvements to the above instruction document to AI
- Add more context for complex questions to help the AI break down the information step by step.
- Include a method for the AI to ask follow-up questions if the initial question is too broad or unclear.
- Provide examples of how to simplify complex scientific terms.
- Offer guidance on how to handle questions that don't have a straightforward answer, suggesting ways to explain uncertainty.
```

ワークフローを作成できたら、以下の記事を参考にAPIキーを発行して控えておきましょう。

https://qiita.com/cog1t0/items/00a9e608925578e0cf44#dify%E3%81%AE%E6%93%8D%E4%BD%9C

### チェックポイント
:::note warn
- [ ] Difyのワークフローを作った
- [ ] DifyのワークフローのAPIキーを控えた
:::

## GASの準備
今回AppSheetからGASの関数を呼び出す関係で、GASは**スタンドアロン方式**で作成します

スプレッドシートの拡張機能からじゃなくて、Googleフォルダを右クリックして「その他」「Google Apps Script」で作成
![スクリーンショット 2024-06-02 23.28.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/4aacbaf4-cc6f-5209-5681-35cf50befdfc.png)

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

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

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

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

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

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

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

  // リクエストのボディデータ
  var payload = {
    'inputs': {'Difyのワークフローの開始で設定した変数名':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);

    // B列に返却された値を書き込み
    sheet.getRange('B' + lastRow).setValue(answer);
  } catch (e) {
    Logger.log('Error: ' + e.message);  // エラーメッセージをログに出力
  }
}
```

デプロイはしなくても大丈夫です。

### チェックポイント
:::note warn
- [ ] GASをスタンドアロンで作成した
- [ ] コードをコピーした
- [ ] コードの「var spreadsheetId = 'スプレッドシートのID'」を実際の値で書き換えた
- [ ] コードの「DifyワークフローのAPIキー」を実際の値で書き換えた
- [ ] コードの「Difyのワークフローの開始で設定した変数名」を実際の値で書き換えた
:::

## スプレッドシートの準備
まずはAppSheetでアプリを作るためのサンプルデータを用意します。

こんな感じで、「**A1**に質問」「**B1**に回答」と入力し、適当に質問と回答を埋めていきます。
![スクリーンショット 2024-06-02 23.13.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/5986ae54-8f73-e111-46d2-856a39820535.png)

## AppSheetでアプリの作成
スプレッドシートのサンプルデータができたら、「拡張機能」の「AppSheet」から「アプリを作成」をクリックします
![スクリーンショット 2024-06-02 12.14.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/4ecbffcc-f807-1d8e-0900-8692cc2008a5.png)

そしたらもうなんとなく使えるものができちゃいます。
いろいろカスタマイズしたいところですが、今回はそのままでいきます。

![スクリーンショット 2024-06-02 12.15.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/f1c86d6a-8f4c-ba4a-dbd2-1a44275d08c1.png)

画面左のロボットのアイコンの「Automation」を選択し画面右の「Create my first automation」をクリック
![スクリーンショット 2024-06-02 12.16.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/6113aaf5-41d8-e597-6db1-41321143464d.png)

サジェスチョンは無視し「Create a new bot」をクリック
![スクリーンショット 2024-06-02 12.17.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/ad5c1370-3428-834d-de9d-b06188725ec2.png)

イベントの設定をしていきます
![スクリーンショット 2024-06-02 23.42.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/531c6946-68fd-5461-a410-df21113e4d97.png)

続いてプロセスの設定です
Create a custom stepを選択して
![スクリーンショット 2024-06-02 23.43.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/a56032e8-ddb9-c833-751d-e56e7f6c29a0.png)

以下のように設定
![スクリーンショット 2024-06-02 23.47.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/bfdd6f81-3b62-80db-c1c6-f170c19b0c0c.png)

### チェックポイント
:::note warn
- [ ] スプレッドシートとサンプルデータを用意した
- [ ] AppSheetを作った
- [ ] AppSheetのAutomationを設定どうりに設定できた
:::

これで完成です！
実際に動かしてみるとこんな感じですね。
レスポンスを待つことなく、非同期で回答が準備されているのがわかるかと思います。

https://x.com/r19_tech/status/1797264243118772577

# 応用のしかた
今回はすごくシンプルですが、
AppSheetとDifyのワークフローを組み合わすことでいろんなことができる気がしています。

例えば、AppSheetのオートメーションはスケジュールでも動かすことができるので、
AppSheetに情報を溜め込んで、要約したり、思いついたアイデアをまとめたりもできそうです。

- 溜め込んだデータを１日1回Difyのワークフローに投げ、要約させ文章として保存したのち、それをメール送信
- AppSheetに気になるワードをメモしておき、それを１日1回まとめて論文検索
- AppSheetに小説に書きたいシーンをメモしておき、それを1週間に1回まとめて小説にしてもらう、小説執筆補助アプリ

Difyのワークフローも本当にいろんなことができるので、想像力で無限に使い方が生まれそうです！

この記事が何かの役に立てば幸いです！