【初心者ハマりポイント】DifyからGASへのPOSTが通らない理由と対処法_ 文字列化されたJSON配列の扱い

以下は、あなたの内容をマークダウン形式に整えたものです。

---

# Dify × GASで自動化！LLMで生成したペルソナをスプレッドシートに出力する方法

## はじめに

> 「Difyのワークフローで、**LLMで生成したデータをうまくGASにPSOTできずにハマっている…**」


この記事では、**Difyのワークフローで生成された文字列化されたJSON配列をGoogle Apps Script（GAS）にPOSTする処理**について、**LLM（今回はGemini）が分析したペルソナ情報を自動でGoogle スプレッドシートに書き出す方法**を事例に、初心者の方にも分かりやすく解説します！

今回の事例では以下のような情報をもとに、Geminiがペルソナを分析します。

* 収集したサイトの概要
* 想定される訪問目的

そして分析結果をスプレッドシートに整理します。

> 「なんだか難しそう…」と感じた方も大丈夫！
> 各ステップを丁寧に解説していきますので、ぜひ最後まで読んで、業務効率化の一歩を踏み出しましょう！

---

## どんなワークフロー？全体像をざっくり解説

今回のワークフローは、以下のような流れでLLMの分析結果をスプレッドシートに書き出します。

### 各ステップの概要

1. **初期データ取得ノード**
   分析の元となるサイト概要と訪問目的のデータを取得。

2. **Codeノード（LLM入力整形）**
   取得したデータをLLMが分析しやすい形式に整形。

3. **LLMノード（ペルソナ分析）**
   Geminiが3つのペルソナ情報を分析。

4. **Codeノード（LLM出力検証・整形）**
   JSON形式で出力されたデータを検証・整形。

5. **HTTPリクエストノード**
   整形済みのデータをGASのWebアプリにPOST。

6. **Google Apps Script（GAS）**
   POSTされたデータを受け取り、スプレッドシートに書き込む。

今回は、**ステップ2〜ステップ6**を詳しく解説します！

---

## 前提：Difyで生成されるデータの構造

Difyのワークフローを通じて、以下のような形式をLLMで生成したと仮定します。

```json
{
  "result": "[{\"siteSummary\":\"サイトの概要1\",\"visitPurpose\":\"訪問目的1\"}, {\"siteSummary\":\"サイトの概要2\",\"visitPurpose\":\"訪問目的2\"}]"
}
```

このように、`result`キーに、**サイト概要と訪問目的のペアが配列形式で格納された文字列**が含まれています。

---

## 各ステップの詳細と設定方法

### ステップ2：Codeノード（LLM入力整形）

* **ノードタイプ**：Code
* **入力変数**：`initialData`（初期データ取得ノードの出力）
* **出力変数**：`llmPromptText`

#### JavaScriptコード

```javascript
function main({ initialData }) {
  if (typeof initialData !== 'object' || initialData === null || !initialData.hasOwnProperty('result')) {
    return { llmPromptText: "", error: "初期データの形式が不正です。" };
  }
  let jsonString = initialData.result;
  if (typeof jsonString !== 'string' || jsonString.trim() === '') {
    return { llmPromptText: "", error: "'result' の値が空の文字列です。" };
  }
  let dataArray;
  try {
    dataArray = JSON.parse(jsonString);
  } catch (error) {
    return { llmPromptText: "", error: "'result' のJSONパースに失敗しました: " + error.message };
  }
  if (!Array.isArray(dataArray)) {
    return { llmPromptText: "", error: "'result' の値が配列ではありません。" };
  }

  let formattedText = "以下のサイト概要と訪問目的のリストを分析し..."; // 省略（全文は元のコード参照）

  return { llmPromptText: formattedText };
}
```

> 出力形式を**純粋なJSON配列**に限定するように明確に指示している点が重要です。

---

### ステップ3：LLMノード（ペルソナ分析）

* **ノードタイプ**：LLM
* **AIモデル**：Gemini（または任意のLLM）
* **プロンプト**：`{{llmPromptText}}`

> LLM設定で「出力形式：JSON」を指定できる場合は、必ず設定しておきましょう。

---

### ステップ4：Codeノード（LLM出力検証・整形）

* **ノードタイプ**：Code
* **入力変数**：`llmOutputString`（LLMノードの出力）
* **出力変数**：`finalPersonaJsonString`

#### JavaScriptコード

```javascript
function main({ llmOutputString }) {
  if (typeof llmOutputString !== 'string' || llmOutputString.trim() === '') {
    return { personaJsonString: "", error: "LLMの出力が空です。" };
  }

  let jsonString = llmOutputString;

  let dataArray;
  try {
    dataArray = JSON.parse(jsonString);
  } catch (error) {
    return { personaJsonString: "", error: "LLMの出力のJSONパースに失敗しました: " + error.message };
  }

  if (!Array.isArray(dataArray) || dataArray.length < 3) {
    return { personaJsonString: "", error: "LLMの出力が3件未満の配列です。" };
  }

  const requiredKeys = ["searchIntent", "details", "personaName", "age", "gender", "job", "companyCategory", "position", "challengesToSolve", "needsToSatisfy", "marketingFunnel", "nextExpectedKeywords"];
  const firstItem = dataArray[0];
  const hasRequiredKeys = requiredKeys.every(key => Object.prototype.hasOwnProperty.call(firstItem, key));
  if (!hasRequiredKeys) {
    return { personaJsonString: "", error: "LLM output objects are missing required keys." };
  }

  const finalJsonString = JSON.stringify(dataArray);

  return { personaJsonString: finalJsonString };
}
```

---

### ステップ5：HTTPリクエストノード

* **ノードタイプ**：HTTPリクエスト
* **メソッド**：POST
* **URL**：GASのWebアプリURL
* **ボディタイプ**：JSON
* **ボディ**：

```json
{
  "method": "createPersonaSheet",
  "spreadsheetId": "YOUR_SPREADSHEET_ID",
  "personaData": {{Codeノード名_LLM出力検証.finalPersonaJsonString}}
}
```

※`spreadsheetId`は書き込み先のスプレッドシートIDに置き換えてください。
※`personaData`の値は**ダブルクォート不要**です。

---

### ステップ6：Google Apps Script（GAS）

1. スプレッドシートを開き、「ツール」>「スクリプトエディタ」
2. 以下のコードを貼り付けて保存（例：`writePersonaToSheet`）
3. 「デプロイ」>「新しいデプロイ」>「ウェブアプリ」を選択

#### GASコード

```javascript
function doPost(e) {
  let requestData;
  try {
    requestData = JSON.parse(e.postData.contents);
  } catch (error) {
    return ContentService.createTextOutput(JSON.stringify({ status: "error", message: "JSONパース失敗: " + error.message })).setMimeType(ContentService.MimeType.JSON);
  }

  const method = requestData.method;
  const spreadsheetId = requestData.spreadsheetId;
  const personaDataString = requestData.personaData;

  let response;
  switch (method) {
    case "createPersonaSheet":
      response = createPersonaSheet(spreadsheetId, personaDataString);
      break;
    default:
      response = { status: "error", message: "不明なメソッド: " + method };
  }

  return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}

function createPersonaSheet(spreadsheetId, personaDataString) {
  try {
    const personaData = JSON.parse(personaDataString);
    const ss = SpreadsheetApp.openById(spreadsheetId);
    const sheetName = "ペルソナ分析結果";
    let sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
    
    sheet.clearContents();
    sheet.appendRow(["検索意図", "詳細", "ペルソナ名", "年齢", "性別", "職業", "企業のカテゴリー", "役職", "課題", "ニーズ", "ファネル", "想定キーワード"]);

    personaData.forEach(p => {
      sheet.appendRow([
        p.searchIntent, p.details, p.personaName, p.age, p.gender,
        p.job, p.companyCategory, p.position, p.challengesToSolve,
        p.needsToSatisfy, p.marketingFunnel, p.nextExpectedKeywords
      ]);
    });

    return { success: true, message: "スプレッドシートに書き込みました。", spreadsheetUrl: ss.getUrl() };
  } catch (error) {
    return { status: "error", message: error.message };
  }
}
```

---

## Difyワークフローの構成要素まとめ

| ステップ  | ノードタイプ     | 説明                   |
| ----- | ---------- | -------------------- |
| 初期データ | 初期データ取得ノード | サイト概要・訪問目的を含むJSON出力  |
| ステップ2 | Code       | JSONを整形してLLM用プロンプト作成 |
| ステップ3 | LLM        | Geminiでペルソナ分析        |
| ステップ4 | Code       | LLM出力のJSON整形・検証      |
| ステップ5 | HTTPリクエスト  | GASにPOST送信           |
| ステップ6 | GAS        | スプレッドシートに書き込み        |

---

## おわりに

DifyとGASを連携させれば、**面倒な転記作業を自動化し、業務の効率化を大きく推進**できます。

ぜひこのワークフローを試してみてください！質問があれば気軽にどうぞ。