Perplexity APIとClaude APIを使って収集したデータの要約を作成する

# Perplexity APIとClaude APIを使って収集したデータの要約を作成する

Perplexity APIを使用して情報を収集し、その結果をClaude APIで要約する方法を紹介します。Google Apps Script (GAS)を使用してスプレッドシート上で効率的に管理する実装方法をご説明します。

シートの選択したセルの内容を、指定したセルに記入したプロンプトを利用して、指定したセルに結果を表示します。
## 必要な準備

- PerplexityのアカウントとAPIキー
- Anthropic（Claude）のアカウントとAPIキー
- Google Spreadsheetのアクセス権限

## 実装手順

### 1. 前提情報/APIキーの設定

まず、スクリプトプロパティにAPIキーを設定します。シート名、セル名は都合の良いように’’内に入力してください。

```javascript
// Constants
const PERPLEXITY_MODEL = 'sonar-reasoning-pro';
const PERPLEXITY_SCRIPT_PROP_KEY = 'PERPLEXITY_API_KEY';
const PERPLEXITY_PROMPT_SHEET_NAME = 'Per';
const PERPLEXITY_PROMPT_CELL = 'C1';
const PERPLEXITY_INPUT_CELL = 'B1';
const PERPLEXITY_OUTPUT_CELL = 'C2';
const MAX_RETRIES = 3;
const RETRY_DELAY_MS = 1000;
```
```javascript
// APIキーを設定する関数
function setPerplexityApiKey() {
  try {
    const ui = SpreadsheetApp.getUi();
    const response = ui.prompt(
      'Perplexity APIキーの設定',
      'APIキーを入力してください:',
      ui.ButtonSet.OK_CANCEL
    );

    if (response.getSelectedButton() == ui.Button.OK) {
      const apiKey = response.getResponseText().trim();
      if (apiKey) {
        PropertiesService.getScriptProperties().setProperty(PERPLEXITY_SCRIPT_PROP_KEY, apiKey);
        ui.alert('Perplexity APIキーが保存されました。');
      } else {
        ui.alert('有効なAPIキーを入力してください。');
      }
    }
  } catch (error) {
    Logger.log('Error in setPerplexityApiKey: ' + error);
    throw new Error('APIキーの設定中にエラーが発生しました。');
  }
}
```
```javascript
// APIキーを取得する関数
function getPerplexityApiKey() {
  const apiKey = PropertiesService.getScriptProperties().getProperty(PERPLEXITY_SCRIPT_PROP_KEY);
  if (!apiKey) {
    throw new Error('Perplexity APIキーが設定されていません。メニューから「APIキーを設定」を選択してください。');
  }
  return apiKey;
}
```

```javascript
// プロンプトのテンプレートを取得する関数
function getPromptTemplate() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const promptSheet = ss.getSheetByName(PROMPT_SHEET_NAME);
  if (!promptSheet) {
    throw new Error(`"${PROMPT_SHEET_NAME}"シートが見つかりません。`);
  }
  return promptSheet.getRange(PROMPT_CELL).getValue();
}
```
### 2. Perplexity APIを使用した情報収集

```javascript
// プロンプトのテンプレートを取得する関数
function getPerplexityPromptTemplate() {
  try {
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const promptSheet = ss.getSheetByName(PERPLEXITY_PROMPT_SHEET_NAME);
    if (!promptSheet) {
      throw new Error(`"${PERPLEXITY_PROMPT_SHEET_NAME}"シートが見つかりません。`);
    }
    const template = promptSheet.getRange(PERPLEXITY_PROMPT_CELL).getValue();
    if (!template) {
      throw new Error(`${PERPLEXITY_PROMPT_SHEET_NAME}シートの${PERPLEXITY_PROMPT_CELL}にプロンプトが設定されていません。`);
    }
    return template;
  } catch (error) {
    Logger.log('Error in getPerplexityPromptTemplate: ' + error);
    throw error;
  }
}
```
```javascript
// リトライ付きでPerplexity APIにリクエストを送信する関数
function callPerplexityAPIWithRetry(prompt) {
  let lastError;
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
    try {
      const result = callPerplexityAPI(prompt);
      if (!result.startsWith('APIエラー:')) {
        return result;
      }
      lastError = result;
    } catch (error) {
      lastError = error;
      Logger.log(`Attempt ${attempt} error: ${error}`);
      if (attempt < MAX_RETRIES) {
        Utilities.sleep(RETRY_DELAY_MS * attempt);
      }
    }
  }
  throw new Error(`Perplexity API呼び出しに失敗しました: ${lastError}`);
}
```
```javascript
// Perplexity APIにリクエストを送信する関数
function callPerplexityAPI(prompt) {
  const url = 'https://api.perplexity.ai/chat/completions';
  const headers = {
    'Authorization': `Bearer ${getPerplexityApiKey()}`,
    'Content-Type': 'application/json'
  };

  const payload = {
    'model': PERPLEXITY_MODEL,
    'messages': [
      {
        'role': 'user',
        'content': prompt
      }
    ]
  };

  const options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true,
    'timeout': 30,
    'followRedirects': true
  };

  const response = UrlFetchApp.fetch(url, options);
  const statusCode = response.getResponseCode();
  const contentText = response.getContentText();

  if (statusCode !== 200) {
    throw new Error(`Perplexity APIエラー: HTTP ${statusCode} - ${contentText}`);
  }

  const jsonResponse = JSON.parse(contentText);
  if (!jsonResponse?.choices?.[0]?.message?.content) {
    throw new Error('無効なAPIレスポンス: ' + contentText);
  }

  return jsonResponse.choices[0].message.content;
}
```

セル固定で処理する
```javascript
// Perplexityセルを処理する関数
function processPerplexityCell() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const inputCell = sheet.getRange(PERPLEXITY_INPUT_CELL);
  const outputCell = sheet.getRange(PERPLEXITY_OUTPUT_CELL);
  
  try {
    const inputValue = inputCell.getValue();
    if (!inputValue) {
      outputCell.setValue('入力セルが空です。');
      return;
    }

    outputCell.setValue('処理中...');
    
    const promptTemplate = getPerplexityPromptTemplate();
    const finalPrompt = promptTemplate.replace('{input}', inputValue);
    const response = callPerplexityAPIWithRetry(finalPrompt);
    
    outputCell.setValue(response);
    
  } catch (error) {
    Logger.log('Error in processPerplexityCell: ' + error);
    outputCell.setValue('エラー: ' + error.message);
  }
}
```

### 3. Claude APIを使用した要約生成

```javascript
// Constants
const CLAUDE_MODEL = 'claude-3-sonnet-20240229';
const CLAUDE_SCRIPT_PROP_KEY = 'ANTHROPIC_API_KEY';
const CLAUDE_PROMPT_SHEET_NAME = 'Per';
const CLAUDE_PROMPT_CELL = 'D1';
const CLAUDE_INPUT_CELL = 'C2';
const CLAUDE_OUTPUT_CELL = 'D2';
```
```javascript
// APIキーを設定する関数
function setClaudeApiKey() {
  const ui = SpreadsheetApp.getUi();
  const response = ui.prompt(
    'Claude APIキーの設定',
    'APIキーを入力してください:',
    ui.ButtonSet.OK_CANCEL
  );

  if (response.getSelectedButton() == ui.Button.OK) {
    const apiKey = response.getResponseText().trim();
    if (apiKey) {
      PropertiesService.getScriptProperties().setProperty(CLAUDE_SCRIPT_PROP_KEY, apiKey);
      ui.alert('Claude APIキーが保存されました。');
    } else {
      ui.alert('有効なAPIキーを入力してください。');
    }
  }
}
```
```javascript
// APIキーを取得する関数
function getClaudeApiKey() {
  const apiKey = PropertiesService.getScriptProperties().getProperty(CLAUDE_SCRIPT_PROP_KEY);
  if (!apiKey) {
    throw new Error('Claude APIキーが設定されていません。メニューから「APIキーを設定」を選択してください。');
  }
  return apiKey;
}
```
```javascript
// プロンプトのテンプレートを取得する関数
function getClaudePromptTemplate() {
  try {
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const promptSheet = ss.getSheetByName(CLAUDE_PROMPT_SHEET_NAME);
    if (!promptSheet) {
      throw new Error(`"${CLAUDE_PROMPT_SHEET_NAME}"シートが見つかりません。`);
    }
    const template = promptSheet.getRange(CLAUDE_PROMPT_CELL).getValue();
    if (!template) {
      throw new Error(`${CLAUDE_PROMPT_SHEET_NAME}シートの${CLAUDE_PROMPT_CELL}にプロンプトが設定されていません。`);
    }
    return template;
  } catch (error) {
    Logger.log('Error in getClaudePromptTemplate: ' + error);
    throw error;
  }
}
```
```javascript
// Claude APIにリクエストを送信する関数
function callClaudeAPI(prompt) {
  const url = 'https://api.anthropic.com/v1/messages';
  const headers = {
    'x-api-key': getClaudeApiKey(),
    'anthropic-version': '2023-06-01',
    'content-type': 'application/json'
  };
  
  const payload = {
    'model': CLAUDE_MODEL,
    'max_tokens': 4096,
    'messages': [
      {
        'role': 'user',
        'content': prompt
      }
    ]
  };
  
  const options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };
  
  try {
    const response = UrlFetchApp.fetch(url, options);
    const jsonResponse = JSON.parse(response.getContentText());
    return jsonResponse.content[0].text;
  } catch (error) {
    Logger.log('Error in callClaudeAPI: ' + error);
    throw new Error('Claude APIエラーが発生しました: ' + error);
  }
}
```

セル固定で処理する
```javascript
// Claudeセルを処理する関数
function processClaudeCell() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const inputCell = sheet.getRange(CLAUDE_INPUT_CELL);
  const outputCell = sheet.getRange(CLAUDE_OUTPUT_CELL);
  
  try {
    const inputValue = inputCell.getValue();
    if (!inputValue) {
      outputCell.setValue('入力セルが空です。');
      return;
    }

    outputCell.setValue('処理中...');
    
    const promptTemplate = getClaudePromptTemplate();
    const finalPrompt = promptTemplate.replace('{input}', inputValue);
    const response = callClaudeAPI(finalPrompt);
    
    outputCell.setValue(response);
    
  } catch (error) {
    Logger.log('Error in processClaudeCell: ' + error);
    outputCell.setValue('エラー: ' + error.message);
  }
}
```



### 4. メニュー項目の追加

```javascript
// ===== Common Trigger and Menu Functions =====
// トリガー関数
function onEdit(e) {
  if (e && e.range.getA1Notation() === PERPLEXITY_INPUT_CELL) {
    processPerplexityCell();
  }
}

// メニュー設定
function onOpen() {
  try {
    const ui = SpreadsheetApp.getUi();
    ui.createMenu('API')
      .addItem('Perplexityセルを処理', 'processPerplexityCell')
      .addItem('Perplexity APIキーを設定', 'setPerplexityApiKey')
      .addSeparator()
      .addItem('Claudeセルを処理', 'processClaudeCell')
      .addItem('Claude APIキーを設定', 'setClaudeApiKey')
      .addToUi();
  } catch (error) {
    Logger.log('Error in onOpen: ' + error);
  }
}
```

## スプレッドシートの設定

スプレッドシートは以下のような構成になります：

- 任意の列：検索クエリを入力
- 任意の列：Perplexity APIからの検索結果が表示
- 任意の列：Claude APIによる要約が表示

例）B1にPerplexityへの入力内容、C1にPerplexity用のプロンプト、C2に結果表示。D1にClaude用のプロンプト、D2に結果表示。

## 使用方法

1. スプレッドシートのA列に検索したいクエリを入力します
2. メニューから「processPerplexityCell」→「processClaudeCell」を選択します
3. 自動的に検索結果、要約が表示されます

## 注意点

- APIの利用制限に注意してください
- 大量のデータを処理する場合は、適切な待機時間を設定することをお勧めします
- APIキーは必ず適切に管理し、公開しないように注意してください
- Perplexityモデルは、sonar-reasoning-proを利用していますが、その他以下のモデルが利用可能です
https://docs.perplexity.ai/guides/model-cards
- Claudeモデルは、claude-3-sonnet-20240229を利用していますが、その他以下のモデルが利用可能です
https://docs.anthropic.com/ja/docs/about-claude/models

## まとめ

この実装により、Perplexity APIとClaude APIを組み合わせて、効率的な情報収集と要約が可能になります。特に以下の点で有用です：

- 自動化された情報収集プロセス
- 高品質な要約生成
- スプレッドシートでの管理のしやすさ

必要に応じてクエリの調整や要約の長さを変更することで、より柔軟な運用が可能です。

*修正必要な点を見つけた場合はコメント等にてお教え頂ければ幸いです。
*2/22 コードの全面的な修正を入れております。