スプレッドシートのデータをDifyで分析してSlackに定期通知する

日々のちょっとしたデータの保存先として、Google スプレッドシートを利用することはあると思う。しかし、保存するだけ保存して、活用しない・見なくなることもよく起こる。

この記事では、Google スプレッドシートのデータを Dify で分析し、その結果を週に1回Slackに通知する仕組みを簡易に作ってみたので紹介する。

構成

今回は、活動量や体重、食事内容などを記録したGoogle スプレッドシートのデータを対象とする。

データの蓄積先が Google スプレッドシートであること、定期的に自動実行したかったことから、Google Apps Script を用いて以下のような構成で実行した。

Google Apps Script が Google スプレッドシートからデータを取得し、Dify にそのデータを送り、Dify で分析結果を生成し、 Google Apps Script が受け取り、Slack に通知を行う。Google Apps Script のトリガーを日時単位で設定することで、定期的な通知を行うようにしている。

※ 上図で別記事となっているところは以下

https://yrarchi.net/health_data_into_spreadsheet

設定方法

1. Dify の設定

Dify はデータを受け取り、分析を行う役割をする。下図のようなシンプルな構成にしている。開始ブロックで3種類のデータを受け取り、LLMブロックで分析を生成し、終了ブロックでその結果を出力している。

LLMブロックのプロンプトは以下のようにした。今回、プロンプトの改善は色々試さなかったが、もっと適切な分析結果が得られる書き方がありそう。

あなたは優秀な栄養士です。以下のデータを用いて、以下の観点から分析を行ってください。

# データ
- {#health_data}は、体重、体脂肪などの健康に関するデータと歩数や消費カロリーなどの運動量に関するデータです。
- {#nutrition_data}は、摂取した栄養素に関するデータです。
- {#foods_data} は、今週1週間で摂取した食品とその栄養素です。

# 分析の観点
先週1週間に比べて、今週1週間で大きく変わった項目はあるか、あるならどのように変わったのか、その要因は何か。特に、以下の観点で分析してください。
- 体重および体脂肪の変化に対して、取得した栄養素がどのように影響しているか、さらにその栄養素はどの食品が影響しているか。
- 体重および体脂肪の変化に対して、運動量は影響しているか。

# 結果の出力条件
分析結果は以下のフォーマットで出力してください。

1. *大きな変化があった項目*
・{項目名}: {変化量}({先週の値}→{今週の値})
 ※ 2〜3項目程度にとどめてください
2. *考察*
 ※ 影響を与えた食品についても触れてください
3. *結論*
 ※ 変化とその背景をまとめ、どのように改善すべきかを簡潔にまとめてください

2. Slack の設定

https://api.slack.com/quickstart を参照して、今回必要な部分のみ設定した。

  1. Slack App の作成(1. Creating an app参照)
    https://api.slack.com/apps より、アプリ名を入力し、ワークスペースを選択
  1. スコープの設定(2. Requesting scopes参照)
    左のメニューから OAuth & Permissions を選択し、Scopes で chat:write を設定
  1. Slack App のインストール(3. Installing and authorizing the app参照)
    左のメニューから Install App を選択し、App をインストールする
  2. Slackの通知先のチャンネルに App を招待する

3. Google Apps Script の設定

  1. 環境変数の設定 スクリプトに直接書きたくない機密情報はプロパティサービスに登録する。 プロジェクトの設定 > スクリプト プロパティ と進み、key-valueのペアで設定する。

2. スクリプトの作成

function main() {
  const data = fetchData();
  const analysisResult = postToDifyAPI(data);
  postMessageToSlack(analysisResult);
}

function fetchData() {
  const sheets = [
    { name: 'health', id: 'health_spreadsheet_id', range: 'A1:N4' },  //各データを保存しているスプレッドシートのIDとセル範囲を指定する
    { name: 'foods', id: 'foods_spreadsheet_id', range: 'A1:Z10' },
    { name: 'nutrition', id: 'nutrition_spreadsheet_id', range: 'A1:Z4' }
  ];
  const data = {};
  sheets.forEach(sheetInfo => {
    try {
      const { id, name, range } = sheetInfo;
      const spreadsheet = SpreadsheetApp.openById(id);
      const sheet = spreadsheet.getSheetByName(name);

      if (!sheet) {
        throw new Error(`Sheet "${name}" not found in spreadsheet ID: ${id}`);
      }
      const values = sheet.getRange(range).getValues();
      data[name + '_data'] = JSON.stringify(values);
    } catch (error) {
      console.error(`Error fetching data for sheet "${sheetInfo.name}": ${error.message}`);
    }
  });
  return data;
}

function postToDifyAPI(data) {
  const apiKey = PropertiesService.getScriptProperties().getProperty('DIFY_API_KEY');
  const url = 'https://api.dify.ai/v1/workflows/run';

  const payload = {
    'inputs': data,
    'response_mode': 'blocking',
    'user': 'GoogleAppsScript'
  };
  const response = makeApiRequest_(url, apiKey, payload);
  return response.data.outputs.text;
}

function postMessageToSlack(message) {
  const token = PropertiesService.getScriptProperties().getProperty('SLACK_BOT_TOKEN');
  const channel_id = 'channek_id'; // 投稿するSlackのチャンネルIDを指定する
  const url = 'https://slack.com/api/chat.postMessage';

  const payload = {
    'channel': channel_id,
    'text': message
  };
  makeApiRequest_(url, token, payload);
}

function makeApiRequest_(url, token, payload) {
  const options = {
    method: 'POST',
    contentType: 'application/json',
    headers: {
      Authorization: `Bearer ${token}`
    },
    payload: JSON.stringify(payload)
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const responseText = response.getContentText();
    if (response.getResponseCode() !== 200) {
      throw new Error(`HTTP Error ${response.getResponseCode()}: ${responseText}`);
    }
    return JSON.parse(responseText);
  } catch (error) {
    console.error(error.message);
    return { error: error.message };
  }
}
  1. トリガーの設定 左のメニューからトリガーを選択し、任意の間隔でスクリプトが実行されるようにする

結果

Slack に以下のような形で通知されるようになった。

感想

Google スプレッドシートに保存している各種データに対して、色々と活用できる可能性がありそうだと感じた。一方で、分析のプロンプトはまだ改善の余地がある。今回は試行錯誤しなかったけど、精度を上げる工夫ができそう。

タイトルとURLをコピーしました